Guten Tag,
Ich bin totaler anfänger, und versuche eine ganz simple
aufgabenstellung, an der ich scheitere :( ich versuche per ATMEGA8 über
pin PB1 eine LED zu dimmen. Dafür habe ich ein tutorial für den ATMEGA16
gelesen, und wollte dieses nun auf den ATMEGA8 anwenden, was wohl
garnicht so leicht ist.
Folgendes habe ich nun :
Martin Rocks schrieb:> wieso funktioniert da garnichts :(
Weil der OC2 Pin (da wo die PWM vom Timer 2 rauskommt) beim Mega8 nicht
der Pin PB1 sondern PB3 ist
Bitte erneut um Hilfe !
Ich habe jetzt alle 3 PWM Kanäle programmiert, und möchte diese jeweils
unabhängig von 3 Schaltern, die an PC0- PC2 hängen anschalten, und
wieder abschalten können. Angesteuert werden 3 LEDs, die sich an PB1-PB3
befinden. Mein Problem ist, wenn ich alle 3 Taster drücke, läuft jeweils
nur eine LED (PB1) und wenn diese durchlaufen ist, läuft die nächste....
aber ich kann nicht alle 3 gleichzeitig anlassen. wieso?
1
#define F_CPU 1000000U // 1 MHz
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
6
7
voidInitPWM()
8
{
9
DDRB|=(1<<PB1)|(1<<PB2)|(1<<PB3);
10
TCCR2=(1<<WGM20)|(1<<COM21)|(1<<CS20);
11
TCCR1A=(1<<WGM10)|(1<<COM1A1)|(1<<COM1B1);// 8bit-Counter, nicht invert. PWM
Huhuu Ralf =) ja das mit main() ist verwirrend, aber ich wusst enicht
wie ich sonst aus der schleife raus komme G
aber wenn ich die ganze unterschleife entferne (die war dafür dass die
LED den aktuellen helligkeitswert speichert, und dann nicht direkt
ausgeht sondern ausdimmt, wenn ich den schalter umlege), klappt es
immernoch nicht....
ich möchte ja alle 3 kanäle parallel laufen haben,.... also so wäre die
methode ohne unterschleife:
1
#define F_CPU 1000000U // 1 MHz
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
6
7
voidInitPWM()
8
{
9
DDRB|=(1<<PB1)|(1<<PB2)|(1<<PB3);
10
TCCR2=(1<<WGM20)|(1<<COM21)|(1<<CS20);
11
TCCR1A=(1<<WGM10)|(1<<COM1A1)|(1<<COM1B1);// 8bit-Counter, nicht invert. PWM
Karl Heinz Buchegger schrieb:> Martin Rocks schrieb:>>> wieso funktioniert da garnichts :(>> Weil der OC2 Pin (da wo die PWM vom Timer 2 rauskommt) beim Mega8 nicht> der Pin PB1 sondern PB3 ist
...
ähhh...?? hä :D
also timer 2 funktioniert. timer 1 auch mit 2 pwm kanälen... aber nicht
gleichzeitig. das ist ja das problem. ich weiß nicht ob das n fehler in
der schleife ist, oder ob der atmega8 damit generell nicht klar kommt oO
Im Simulator funktioniert alles so, wie es programmiert ist.
Sind die LEDs wirklich so angeschlossen, wie im Schaltplan?
Die Schalter sind 'low-activ' angeschlossen, 'high' wird aber abgefragt.
Zum Warum:
Du fragst in deiner While(1) Loop einen Schalter ab und führst dann zB
Dimmer1 aus. Dabei wird die LED auf- und abgedimmt.
DANACH fragst du den zweiten Schalter ab und es wird ggfs die zweite LED
auf- und abgedimmt.
Das gleiche mit dem dritten Schalter und der dritten LED.
Also alles nacheinander!
Du musst das "parallelisieren".
Gibt verschiedene Wege...
ZB den Zustand in "Dimmer" global merken und bei jedem Aufruf von Dimmer
immer nur einen Schritt weiter gehen.
Der Programmdurchlauf ist schnell genug und du siehst alle LEDs quasi
gleichzeitig dimmen. Ist dann halt von der Programmdurchlaufzeit
abhängig.
Oder du arbeitest mit einem Interrupt.
Der gibt dir einen regelmäßigen Zeittick und davon abhängig gehst du in
deiner Dimmerfunktion einen Schritt weiter. Dann muss der
Programmdurchlauf zwar immer noch zügig sein (was er auch locker ist),
aber du kannst die Geschwindigkeit des Dimmens ohne lästige Waits
steuern und hast noch Zeit für anderes.
Felix schrieb:> Du fragst in deiner While(1) Loop einen Schalter ab und führst dann zB> Dimmer1 aus. Dabei wird die LED auf- und abgedimmt.> DANACH fragst du den zweiten Schalter ab und es wird ggfs die zweite LED> auf- und abgedimmt.> Das gleiche mit dem dritten Schalter und der dritten LED.> Also alles nacheinander!
Und das funktioniert_ _so auch fehlerfrei (Simulator).
Das kann ja sein, aber er fragte doch, warum die nicht gleichzeitig
angehen, wenn er alle Knöpfe gleichzeitig drückt, bzw. was er machen
muss, damit alle gleichzeitig dimmen können.
ahhh "parallelisieren" ist das stichwort !
klasse..... oh man das hört sich aber schwer an mit den interrupts usw.
Urghs. Okay muss ich schauen wie ich das hinbekomme.
Aber vielen dank für eure hilfe :) :)
grüüße
Kannst es ja auch erstmal ohne Interrupts machen.
Du musst dir den Zustand des Dimmens außerhalb der Dimm-Funktion merken.
Stichwort globale Variable.
Bei jedem Aufruf veränderst du den Zustand.
Du kannst ja auch die Funktion aufteilen in Aufdimmen1() und
Abdimmen1().
Über Merker (Flags) signalisierst du dann am Funktionsende, dass zB das
Aufdimmen fertig ist und jetzt Abdimmen an der Reihe ist.
Dann kannst du das Flag mit "if" abfragen und entscheiden, welche
Funktion jetzt dran ist.
Wenn dir die Schritte des Dimmens dann zu schnell sind musst du eben
irgendwo warten (unschön) oder den Aufruf der Dimmfunktionen verzögern.
Soll heißen nicht bei jedem Durchlauf ausführen.
ZB in der While(1) Loop eine Variable hochzählen (Runtime++;)
dann irgendwo sowas wie
if (RunTime>=100) {
RunTime=0;
if (Flag) {
Flag=0;
AbDimmer1();
}
else
AufDimmen1();
}
Dann wird das nur noch alle 100 Durchläufe ausgeführt.
Diesen Zähler kann man dann später in einen Timerinterrupt verlegen,
dann ist er genauer. Ein Programmdurchlauf kann ja unterschiedlich lang
sein, wenn es Aufgaben gibt, die nicht immer zu erledigen sind (zB etas
über UART ausgeben oder so).
So in etwa.
waaaah ich glaub ich muss gleich erstmal meinen kopf entprellen xD
ich komme gerade garnicht mehr zurecht. aber wenigstens weiß ich jetzt
wo der fehler liegt. mal sehen was ich drauß mache. vielen dank nochmal.
falls jemand, der sich damit aus kennt, lust hat mich per icq in dem
vorhaben zu unterstützen darf mich gerne adden : 105565858
würd mich freuen ;)
grüüüßeee
huhu,
ich krisgs nicht gebacken mit den interrupts. keinen plan davon. auch
das tutorial zu interrupts bringt mich kein stück weiter.
kann mir vlt jemand erklären wie das geht? welche interrupts ich brauche
und wofür oO
lg
Hallo Martin,
hast du es denn erst mal ohne Interrupts so umgebaut, dass deine LEDs
quasi gleichzeitig dimmbar sind? Das brauchst du für die
interruptgesteuerte Variante auch. Der Interrupt soll dir ja später nur
einen genaueren Zeitpunkt fürs "Weiterschalten" liefern, also das
Zeitmanagment übernehmen.
Der Interrupt den du dann anschließend verwendest, braucht eine
Auslösebedingung. Für diesen Zweck bietet sich ein TimerOverFlow
Interrupt an. In den meisten Anwendungen läuft dafür beispielsweise
Timer0. 8Bit reichen hier völlig, da wir ja eine möglichst kleine
Zeitbasis haben wollen, zählen wir eh nicht so weit.
Ein Overflowinterrupt alle 10ms wäre zB gut. Das bedeutet, du musst
Timer0 konfigurieren. Dabei spielt dein verwendeter Takt eine Rolle.
Du musst also alles so einstellen, dass dein Timer0 alle 10ms überlauft.
Dann aktivierst du noch die Interrupts über das entsprechende Bit.
Das war es dann schon fast, es fehlt dann nur noch die Interruptroutine.
Das ist im Prinzip nur eine Funktion mit bestimmten Namen, die über eben
diesen Namen an eine bestimmte Bedingung (hier Timer0 Overflow)
gekoppelt wird.
In dieser Funktion zählst du einfach eine Variable. Da der Interrupt ja
fest alle 10ms ausgelöst wird, hast du nun eine feste Zeitbasis.
Du kannst beispielsweise immer bis 10 zählen und ein neues Flag setzen,
dass dir 100ms signalisiert.
Wichtig ist, dass die Interruptsoutine schnell abzuarbeiten ist. Also
hier keine aufwändigen Berechnungen und tief verschachtelte
Funktionsaufrufe!
Außerdem mußt du bedenken, dass der Interrupt ja immer dazwischen haut
und dein Programm kurz unterbricht. Er weiß zwar wo er danach weiter
machen muss, aber es gibt ein paar Dinge zu beachten. Variablen die im
Interrupt geändert werden und dann noch woanders verwendet werden müssen
als volatile deklariert werden. Aufpassen musst du auch bei
zeitkritischen Sachen in deinem Code, beispielsweise wenn bestimmte
Timings für irgendwas eingehalten werden müssen. Dann musst du deinen
Code davor schützen, dass der Interrupt dazwischen haut. Stichwort
"atomic block", "CLI()", "SEI()"...
Hört sich vielleicht alles gefährlich an, ist es aber nicht. Man muss
hier eben nur ein bisschen aufpassen und sorgfältig arbeiten. Dein
Programm ist eine gute Übung!
Also mein Tipp:
Erstmal alles so umbauen, dass deine LEDs gleichzeitig ohne Interrupt
dimmbar sind.
Danach sind deine Stichworte Timer0 und OverFlowInterrupt.
Hoffe das hilft weiter.
Gruß
Felix
wuhuuu felix du bist auf jedenfall der beste ^^
wat würd ich nur ohne dich tun...:P
jahaaa also nach stundenlangen qualen habe ich es irgendwie mittels
globaler variablen hinbekommen jepeee*freu*
ich bin allerdings der meinung, dass es sehr unschön und unkompakt
programmiert ist. wie könnte ich das nun optimieren? Bisher habe ich es
nur mit 2 kanälen gemacht....aber der 3. ist dann auch nimmer das
problem :P
1
#define F_CPU 1000000U // 1 MHz
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<stdlib.h>
6
7
uint8_tZustand,Zustand2;
8
uint8_tbrightness1,brightness2;
9
10
voidInitPWM()
11
{
12
DDRB|=(1<<PB1)|(1<<PB2)|(1<<PB3);
13
TCCR1A=(1<<WGM10)|(1<<COM1A1)|(1<<COM1B1);// 8bit-Counter, nicht invert. PWM
14
TCCR1B=(1<<CS10);// Pre-Scaler = 1, also ohne Teiler
Schön, dass es hilft. Du machst aber auch deinen Teil! Ich gebe ja nur
Denkanstöße... Aber ich bin sicherlich kein Profi.
Jedenfalls gefällt mir dein Programm schon ganz gut.
Schön kommentiert und eingerückt (bis auf die IFs der Pinabfrage, aber
das kann ja dem Upload geschuldet sein). So lernt man Schritt für
Schritt.
Daher immer der Rat von allen, macht das Tutorial durch und fangt mit
ner LED an!
Ich finde man sieht jetzt hier 3 Dinge ganz gut:
1) Die beiden Dimmvorgänge laufen unabhängig voneinander.
2) Du hast 4 Funktionen (auf- und abdimmen), von denen jeweils 2 genau
das gleiche machen, nur eben mit anderen Variablen.
3) Das Wait() in pwmoutput()
Punkt 1) löst erstmal dein urpsprüngliches Problem.
Punkt 2) finde ich auch nicht so schön kompakt, vor allem wenn man
bedenkt, dass ja noch die dritte LED fehlt. Und vielleicht kommt ja noch
eine 4.?
Hier könntest du ja jetzt eine Funktion LEDXaufdimmen() und eine
Funktion LEDxabdimmen() schreiben, die Parameter übergeben bekommt.
Da ja immer exakt das gleiche gemacht wird, ist dies quasi ein Fall aus
dem Lehrbuch. Kriegst du das hin?
Punkt 3) deutet auch schon in Richtung Timer und Interrupt.
Solange du nichts anderes in deinem Programm machst, stört das Wait()
nicht. Aber du musst es dir als aktives Warten vorstellen, dh es wird
erzwungen, dass NICHTS gemacht wird. Und das für einen Zeitraum, der für
einen uControlller verdammt lang ist!
Ohne Timer und Interrupts gibt es jetzt noch einen Weg, das ohne Wait zu
machen. Der sieht aber erstmal umständlich aus. Bei jedem Durchlauf
zählst du eine Variable (zB 16bit) und beispielsweise immer wenn sie bei
30000 ist, setzt du sie zu 0 und führst deine Funktionen aus. Du kannst
dir auch ausrechenen bis wohin du zählen musst, damit du etwa die
gleiche Zeit wartest. Das Funktioniert aber nur bedingt (su).
Das bedeutet dann zwei Dinge:
Es wird nicht mehr aktiv gewartet, sondern es gibt ganz kurze Durchläufe
in denen nichts anderes gemacht wird, als die Variable hochzuzählen.
Und zweitens deine Zählgeschwindigkeit ist genau davon abhängig, ob im
Programm noch was anderes gemacht werden soll oder nicht.
Du hast quasi die Zeit, die vorher aktiv gewartet wurde, für andere
Aufgaben gewonnen. Aber wenn du relativ gleichmäßig Dimmen willst, dann
kommst du um den Timer und den Interrupt nicht herum. Das ist dann eine
saubere Lösung. Und der Systemtick, den du dir mit dem Timer erzeugst,
läßt sich für alles mögliche benutzen. Es ist also immer gut, ihn zu
haben. :-)
Noch zwei Anmerkungen:
- Die globalen Variablen Zustand und Brightness würde ich noch etwas
"sprechender" bennennen. Vielleicht ZustandLED1 und BrightnessLED1 oder
besser LED1Zustand und LED1Brightness. etc.
- Die IF-Abfragen beim Dimmen würde ich eher nicht mit != lösen.
Ist zwar völlig korrekt, aber mit <=255 und >=0 liest es sich meiner
Meinung nach einfacher und praktischer Nebeneffekt ist, du baust eine
weitere Sicherung ein. Ist zwar hier unwahrscheinlich, aber stell dir
vor deine Variable wird irgendwie verändert (Programmerweiterung,
kosmische Strahlung, was weiß ich) und Brightness wird >255, dann zählst
du munter weiter hoch (wenn es 16bit wären). Abfragen auf <= und >= sind
sicherer als Abfragen auf == und !=. Da du nicht auf genau einen Zustand
abfragst.
Klingt vielleicht etwas an den Haaren herbeigezogen, aber warum nicht
diese "Sicherheit" einbauen, wenn es nichts kostet?! Kann ja sein, dass
du irgendwann deinen Datentyp von uint8_t auf int8_t oder uint16_t
änderst...
Weiter so.
Gruß
Felix
Felix schrieb:> - Die IF-Abfragen beim Dimmen würde ich eher nicht mit != lösen.> Ist zwar völlig korrekt, aber mit <=255 und >=0 liest es sich meiner> Meinung nach einfacher und ...
Es muss natürlich <255 und >0 heißen...
Hurraa ich bin on fire, ich habs mal wieder hinbekommen, diesmal sogar
in rekordzeit G
Jetzt muss ich nur noch das mit den interrupts irgendwie schaffen ;)
das programm ist im anhang
vielen dank nochmal
das mit den interrupts schaff ich auch noch G
CPU load steht bei 85%, sieht viel aus oO
Martin Rocks schrieb:> Hurraa ich bin on fire, ich habs mal wieder hinbekommen, diesmal sogar> in rekordzeit *G*>> Jetzt muss ich nur noch das mit den interrupts irgendwie schaffen ;)
Lies dir lieber in deiner C-Lehrbuch das Kapitel über Arrays durch
Karl Heinz Buchegger schrieb:> Martin Rocks schrieb:>> Hurraa ich bin on fire, ich habs mal wieder hinbekommen, diesmal sogar>> in rekordzeit *G*>>>> Jetzt muss ich nur noch das mit den interrupts irgendwie schaffen ;)>> Lies dir lieber in deiner C-Lehrbuch das Kapitel über Arrays durch
hmmm....? nicht so unkonkret bitte herr buchegger :)
Martin Rocks schrieb:> Karl Heinz Buchegger schrieb:>> Martin Rocks schrieb:>>> Hurraa ich bin on fire, ich habs mal wieder hinbekommen, diesmal sogar>>> in rekordzeit *G*>>>>>> Jetzt muss ich nur noch das mit den interrupts irgendwie schaffen ;)>>>> Lies dir lieber in deiner C-Lehrbuch das Kapitel über Arrays durch>>>> hmmm....? nicht so unkonkret bitte herr buchegger :)
Was ist am Stichwort "Array" unkonkret?
>> DDRC|=(0<<PC0) | (0<<PC1) | (0<<PC2); //PC0-PC2 mit "0" als Eingänge
deklarieren
Löschen funktioniert so nicht!
Den Hinweis mit dem Array versteh ich gerade auch nicht...
Felix schrieb:>>> DDRC|=(0<<PC0) | (0<<PC1) | (0<<PC2); //PC0-PC2 mit "0" als Eingänge> deklarieren>> Löschen funktioniert so nicht!>> Den Hinweis mit dem Array versteh ich gerade auch nicht...
brightness1, brightness2
und jetzt machen wir mal 30 PWM Stufen. Willst du da wirklich 30
Variablen einzeln anlegen?
Genau dazu gibt es Arrays!
int brightness[30];
und wir haben die 30 Variablen. Um auf eine einzelne zuzugreifen, genügt
es den Index zu variieren:
brightness[5] = 232;
und den Index kann man sogar über einen Ausdruck berechnen lassen
for( i = 0; i < 30; i++ )
brightness[i] = 0;
Lernt doch die Sprache! Ihr verschenkt doch Power, wenn ihr nur 10% der
Sprache beherrscht.
Felix schrieb:> Ja ok, da kann man das benutzen. Fand ich jetzt nicht so wild, aber ok.
Es ist nie wirklich wild.
Nach dem Array wartet dann das nächste: Strukturen
Auch die kann man hier nutzbringend einsetzen.
Und plötzlich wird dann aus einem Programm, welches bisher hauptsächlich
aus Copy&Paste "Programmierung" für 3 Kanäle besteht, ein simpler 5
Zeiler in der Hauptschleife, der dann auch noch banal zu parametrieren
geht.
Guten Abend Herr Burchegger,
mir ist aufgefallen, dass Sie als Moderator sehr viel darüber reden wie
toll man alles machen kann und wie toll Sie das alles können. Leider
haben Sie mir bei meinem Problem noch nicht so wirklich weiter helfen
können.
Wenn Sie das Programm in 5 Zeilen schaffen, dann lassen Sie uns doch
bitte an Ihrem Wissen teil haben, und zeigen uns wie das funktioniert.
Darüber wäre ich sehr erfreut.
Ich möchte nochmal anmerken, dass das hier mein erstes Programm ist.
Dafür finde ich das schon ziemlich gut.
grüüüßeee
uaaargh nach langem gewusel und gewurschtel ist das jetzt endlich
einigermaßen fertig :)
Ich belasse es vorerst so, und bedanke mich bei allen die mir geholfen
haben. Vlt sehen wir uns ja schon bald im nächsten thread wieder :)
1
#define F_CPU 8000000U // 8 MHz
2
3
#include<avr/io.h>
4
#include<stdlib.h>
5
#include<avr/interrupt.h>
6
//Globale Variablen:
7
volatileunsignedcharmstime[3],pwmwert[3];//0-255, bei 256 wechselt er auf 0
8
volatileuint8_tLED[3];//LED 0 bis LED 2
9
10
voidInitPWM()
11
{
12
DDRB|=(1<<PB1)|(1<<PB2)|(1<<PB3);//PB1-PB3 mit "1" als Eingänge deklarieren
13
DDRC|=(0<<PC0)|(0<<PC1)|(0<<PC2);//PC0-PC2 mit "0" als Eingänge deklarieren
14
PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2);// PULL UPS an für Taster
Martin Rocks schrieb:> uaaargh nach langem gewusel und gewurschtel ist das jetzt endlich> einigermaßen fertig :)
Schon viel besser.
Und, hats weh getan seine Kentnisse zu verbessern?
Das nächste Sprachkonstrukt wartet schon: Strukturen
Martin Rocks schrieb:> Wenn Sie das Programm in 5 Zeilen schaffen, dann lassen Sie uns doch> bitte an Ihrem Wissen teil haben, und zeigen uns wie das funktioniert.> Darüber wäre ich sehr erfreut.
Wissen sie, Herr Rocks:
Ich helfe gerne Leuten durchaus auch mit Code aus.
Aber wenn ich der Meinung bin, dass es sich beim Nichtkönnen um absolute
Grundlagentechniken handelt, die in jedem, aber auch wirklich jedem
C-Buch ohne Probleme zu finden sind, weil es sich um etwas handelt, dass
dort in einem eigenen Kapitel seitenweise beschrieben und erklärt wird,
dann spar ich mir das.
Es ist leider ein weit verbreiteter Irrtum, dass man eine
Programmiersprache ohne ausreichende Unterlagen lernen könnte.
Herr Buchegger, Sie haben recht, ich gebe es zu. Ich glaube wir zwei
können noch gute Freunde werden :P
Aber manchmal ist für einen Anfänger der Code hilfreicher, wenn man null
peil hat.... Das ist so, als wäre man in China Urlaub machen, und kann
kein chinesisch. Selbst mit einem chinesischen Wörterbuch ist man da
manchmal hilflos :P
greetings
... aber nach China fahren, um dort vom ersten Tag an schwieige Dinge
zu besprechen (=MC programmieren), ohne vorher mal ein paar Brocken
chinesisch zu lernen (C-Grundlagen, Tutorial zu AVR), ist nicht
besonders schlau.