Hej Leute nach einer langen Pause (wegen Abschlussprüfungen und Nachwuchs) möchte ich mich wieder mit meinem Atmega8 beschäftigen. Ich habe schon mit einfachen ein und ausschalten von Ausgängen (LED Blink Blink) im Verbindung mit Taster und und gearbeitet. Nun möchte ich mich langsam an Timer und später über eine externe PWM z.B. LED Blinkfrequenz Regelungen machen. Das größte Problem, was ich habe ist, ich lese und lese, Google und google schon ewig doch blicke da einfach nicht durch. Es gab noch kein AHAAA Effekt. Erst dann hat man so etwas verstanden und kann es einsätzen. So geht es zu mindest mir so. Bevor mich irgendjemand lyncht da das Thema schon 1000000 mal angesprochen wurde, hab Verständnis bin Anfänger im Mµ Bereich. Ich wäre wirklich wirklich sehr sehr dankbar wenn mir irgendwer an Hand eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit verschiedenen Frequenzen blinken lassen, schritt für Schritt erklären würde. Nur das ist eine Vorstellung habe, worum es da genau geht. Womit fange ich an? Was muss ich festlegen und / Oder Aktivieren? Wonach muss ich im Datenblatt suchen...? Meine Hardware: Atmega8 mit externen Quarz 4MHz. Alles aufm Steckbrett. LEDs, Widerstände, Taster... alles vorhanden. Oszi, Multimeter... auch alles da. Ich bedanke mich schonmal im voraus. LG Shabi
Shabi N. schrieb: > Ich wäre wirklich wirklich sehr sehr dankbar wenn mir irgendwer an Hand > eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit > verschiedenen Frequenzen blinken lassen, schritt für Schritt erklären > würde. Nur das ist eine Vorstellung habe, worum es da genau geht. Arbeite diese Seite durch. https://sites.google.com/site/qeewiki/books/avr-guide Fang bei den Timern an und arbeite dich zur PWM vor. Dann kannst du deine LED blinken lassen in verschiedenen Frequenzen. Cheers, al3ko
Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der praktischen Sicht heraus beleuchtet: FAQ: Timer
Shabi N. schrieb: > wenn mir irgendwer an Hand > eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit > verschiedenen Frequenzen blinken lassen Das wird Dir nichts nützen. Der Trick beim Programmieren besteht nicht darin, ein paar Zeilen C hinzuschreiben, sondern sich erstmal ein Konzept zu überlegen, d.h. in Worten zu beschreiben, wie der Programmablauf erfolgen soll. Wenn man das vollständig und eindeutig hinkriegt, ist der eigentliche Code nur noch etwa 10..20% der Arbeit. Auch muß man erstmal einige Grundkonzepte lernen, wie Mainloop, Bitmanipulation, Entprellung, Zählvariablen, Timer, Statemaschine usw. Das Falschestes ist, sofort drauflos zu programmieren, das muß unbedingt schiefgehen. Peter
Hej Leute Danke für die Links die sind echt gut. So lngsam klappt es mit ´dem Timer. Hab es geschafft eine LED zum Blinken zu bringen... Der folgende Code schaltet meine LED ein und aus und da hätte ich noch ein paar generelle FRagen: #define F_CPU 4000000UL #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #include <inttypes.h> #include <avr/interrupt.h> int main(void) { DDRB = 0xff; PORTB =0xff; TIMSK = (1 << TOIE0); sei (); TCCR0 = (1 << CS02) | (1 << CS00); while(1) { } } ISR (TIMER0_OVF_vect) { PORTB |= 1<<PB5; //_delay_ms(200); PORTB &= ~(1<<PB5); //_delay_ms(200); } Ich benutze den Atmega8 mit 4MHz und einen Prescale von 1024. Das ergibt bei 4 MHz: 4000000/1024= 3906,25Hz 3906,25/256=15,25 mal/s 1/15,25=0,065s pro Durchlauf. Meine LED Blinkt natürlich viel zu schnell. Hab noch nen kleinen Buzzer angeschlossen so das ich das ticken hören kann. Meine 1. Frage: Geht denn nicht mehr als 1024 Vorteiler? 2. wie mache ich es denn wenn ich genau auf 1 ms pro Durchlauf haben will?? Bei 4MHz passt keiner von den Prescales. 2.Wenn ich die delay_ms mit einbaue, blinkt die LED schön im Takt so das man das auch sehen kann. Was passiert denn jetzt genau? Wenn mein Timer einen Overflow erreicht wird mein Interrupt gestartet. Wartet jetzt mein Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter?? Danke schon mal im voraus. LG Shabi
Hi >Geht denn nicht mehr als 1024 Vorteiler? Nicht beim ATMega8. >2. wie mache ich es denn wenn ich genau auf 1 ms pro Durchlauf haben >will?? >Bei 4MHz passt keiner von den Prescales. CTC-Mode des Timers. >2.Wenn ich die delay_ms mit einbaue, blinkt die LED schön im Takt so das >man das auch sehen kann. Was passiert denn jetzt genau? Wenn mein Timer >einen Overflow erreicht wird mein Interrupt gestartet. Wartet jetzt mein >Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter?? Er zählt weiter. Delays in Interrupt-Routinen sind Unsinn. MfG Spess
Shabi N. schrieb: > Wartet jetzt mein > Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter?? Dann könnte man die Timer in die Tonne treten, sie wären nutzlos. Der Witz an den Timern ist ja gerade, daß sie unabhängig von der Programmausführung laufen. Nur so kann man genaue Uhren bauen. Wenn Dir der Zählbereich des Timers nicht reicht, dann zähl einfach noch ne Variable im Interrupthandler.
1 | ISR (TIMER0_OVF_vect) |
2 | {
|
3 | static uint32_t sw_timer = F_CPU * 60UUL * 60 * 24 * 365 / 1024 / 256; |
4 | if( --sw_timer ) |
5 | return; |
6 | sw_timer = F_CPU * 60UUL * 60 * 24 * 365 / 1024 / 256; |
7 | // mache was nach einem Jahr
|
8 | }
|
Peter
hhhhmm Ok ich hab jetzt mak etwas mit CTC rumgespielt doch es klappt noch nicht so wie ich es gern hätte. hier mal ein Beispiel: #define F_CPU 4000000UL #include <avr/io.h> #include <avr/interrupt.h> int main() { DDRB = 0xff; PORTB =0xff; TIMSK = (1 << OCIE2); OCR2 = 241 - 1; TCCR2 = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20); sei (); while(1) { } } ISR (TIMER2_COMP_vect) { PORTB |= 1<<PB5; PORTB &= ~(1<<PB5); } Nehmen wir an ich will das meine LED 1 mal in einer Sekunde Blinkt. Dann mache ich doch erstmal folgendes: 4000000/1024= 3906,25 Hz.mit dieser Frequenz zählt mein Timer. Dann mach ich ja 3906,25/256= 15,25 So oft zäht der Timer von 0 - 255/s. Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen?? Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1 HZ raus kommt. Bin etwas verwirrt. Die Zahl bei (OCR2 = 241 - 1) ist nur ein Beispiel und funktioniert auch. Wenn ich z.B. 21 - 1 eingebe, geht die Frequenz wieder hoch. HHHmm Grübel grübel. LG Shabi
Shabi N. schrieb: > Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen?? > Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu > realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1 > HZ raus kommt. Um mit einem 8-bit Timer auf 1Hz zu kommen ist das Einfachste wenn Du einen Quarz nimmst, dessen Frequenz sich durch Binärteilung (:2) auf 1Hz teilen lässt. http://www.reichelt.de/Quarze/2-097152-HC18/3//index.html?ACTION=3&GROUPID=3173&ARTICLE=1850&SHOW=1&START=0&OFFSET=500& Oder 15Hz: http://www.reichelt.de/Quarze/3-9321-HC18/3//index.html?ACTION=3&GROUPID=3173&ARTICLE=2382&SHOW=1&START=0&OFFSET=500&
Shabi N. schrieb: > Wenn ich jetzt 1Hz haben möchte Dann erinnere Dich an die Grundschule, Faktorenzerlegung: 4000000 = 2^8 * 5^6 = 256 * 125 * 125 Prescaler, Timer, Variable Peter
Shabi N. schrieb: > Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen?? > Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu > realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1 > HZ raus kommt. Du darfst den Timer halt nicht stur von 0 bis 255 zählen lassen, sondern bis zu einem anderen Maximalwert. Wenn er beispielsweise von 0 bis 194 zählst, dann macht er er das 3906,25 / 195 = 20 Mal pro Sekunde. Die ISR wird also alle 50 ms aufgerufen. Darin erhöhst (oder verringerst) Du jedes Mal eine Variable. Wenn die ISR 20 Mal aufgerufen wurde, ist eine Sekunde vergangen und Du schaltest die LED um.
hhhm das hört sich gut an. Ich müsste dann eine Variable bei jeden durchlauf um einen Wert hochzählen lassen z.B. i++. Und wenn i==20 dann Led an und aus und i wieder auf 0. Nur stell ich mir die Frage, i muss ja vorher einen wert haben wie z.B. 0. Dann würde ja aber bei jedem durchgang i erst 0 und dann wieder 1 sein. Ich muss doch irgendwie einmalig i den wert 0 zuweisen so das i nur beim Programm start 0 ist und ab dann immer hochgezählt wird. HHHm wie könnte man das lösen? shabi
Das geht mit einer Modulvariable:
1 | static uint8_t counter = 0; |
2 | |
3 | ISR() |
4 | {
|
5 | counter++; |
6 | if (counter == 20) { |
7 | counter = 0; |
8 | led_toggle(); |
9 | }
|
10 | }
|
oder einer statischen Funktionsvariable:
1 | ISR() |
2 | {
|
3 | static uint8_t counter = 0; |
4 | counter++; |
5 | if (counter == 20) { |
6 | counter = 0; |
7 | led_toggle(); |
8 | }
|
9 | }
|
Der Unterschied ist, dass man im ersten Fall auch von anderen Funktionen (innerhalb der gleichen C-Datei) auf counter zugreifen kann, im zweiten Fall counter dagegen nur in der ISR sichtbar ist.
Ich hab es mal ausprobiert aber irgendwas stimmt da noch nicht. sobal ich spannung am µC anlege dauert es 1 Sek und die LED geht an und das wars. es passiert nichts mehr. Als ob mein Timer nur einen Overflow macht. Die Zeit scheint zu stimmen. #define F_CPU 4000000UL #include <avr/io.h> #include <avr/interrupt.h> int main() { DDRB = 0xff; PORTB =0xff; TIMSK = (1 << OCIE2); OCR2 = 195 - 1; TCCR2 = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20); sei (); while(1) { } } ISR (TIMER2_COMP_vect) { static uint8_t counter = 0; counter++; if (counter == 20) { counter = 0; PORTB |= 1<<PB5; PORTB &= ~(1<<PB5); } } Wo könnte denn der Fehler sein? Vom Compiler bekomme ich keine Fehlermeldung oder so. LG Shabi
Du schaltest die LED in der ISR aus und danach sofort wieder ein. Sie ist also für ca. einen Takt aus und 3999999 Takte lang an ...
So hat geklapt hier meine Lösung.
1 | #define F_CPU 4000000UL
|
2 | #include <avr/io.h> |
3 | #include <avr/interrupt.h> |
4 | |
5 | int main() |
6 | {
|
7 | |
8 | DDRB = 0xff; |
9 | PORTB =0xff; |
10 | |
11 | TIMSK = (1 << OCIE2); |
12 | OCR2 = 195 - 1; |
13 | TCCR2 = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20); |
14 | sei (); |
15 | while(1) |
16 | {
|
17 | }
|
18 | }
|
19 | |
20 | ISR (TIMER2_COMP_vect) |
21 | {
|
22 | static uint8_t counter = 0; |
23 | counter++; |
24 | |
25 | if (counter == 20) |
26 | {
|
27 | PORTB |= 1<<PB5; |
28 | counter++; |
29 | }
|
30 | if (counter == 40) |
31 | {
|
32 | PORTB &= ~(1<<PB5); |
33 | counter = 0; |
34 | }
|
35 | |
36 | }
|
LG Shabi
Shabi N. schrieb: > So hat geklapt hier meine Lösung. > Wenn man es ganz genau nimmt, dann ist deine Led-Auszeit, wegen der zusätzlichen Counter Erhöhung hier > if (counter == 20) > { > PORTB |= 1<<PB5; > counter++; > } um eine Winzigkeit kürzer als die An-zeit (Annahme led leuchtet bei einer 0 am Port) Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den Pin toggelst
1 | ISR (TIMER2_COMP_vect) |
2 | {
|
3 | static uint8_t counter = 0; |
4 | counter++; |
5 | |
6 | if (counter == 20) |
7 | {
|
8 | PORTB ^= 1<<PB5; |
9 | counter = 0; |
10 | }
|
11 | |
12 | }
|
dann erledigt sich dieses "Problem" ganz von alleine. Die An-Auszeiten sind dann trivialerweise EXAKT gleich lang. (Im Rahmen der Interrupt Latenz)
Karl Heinz Buchegger schrieb: > Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den > Pin toggelst > ISR (TIMER2_COMP_vect) > { > static uint8_t counter = 0; > counter++; > > if (counter == 20) > { > PORTB ^= 1<<PB5; > counter = 0; > } > > } Hallo Karl Heinz Das funktioniert. Aber was passiert da genau? Und was ist Toggeln?? 1. Counter ist 0 2. Counter wird um 1 erhöht 3. Wenn Counter == 20 4. Dann diese Zeile PORTB ^= 1<<PB5; (Was passiert hier genau? Diese Schreibweise kenne ich noch nicht) 5. Counter wieder 0 Danke im voraus LG Shabi
Shabi N. schrieb: > Karl Heinz Buchegger schrieb: >> Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den >> Pin toggelst >> ISR (TIMER2_COMP_vect) >> { >> static uint8_t counter = 0; >> counter++; >> >> if (counter == 20) >> { >> PORTB ^= 1<<PB5; >> counter = 0; >> } >> >> } > > Hallo Karl Heinz > > Das funktioniert. Aber was passiert da genau? Und was ist Toggeln?? Toggeln ist einfach nur der technische Ausdruck für 'Umschalten'. > 1. Counter ist 0 > 2. Counter wird um 1 erhöht > 3. Wenn Counter == 20 > 4. Dann diese Zeile PORTB ^= 1<<PB5; (Was passiert hier genau? > Diese Schreibweise kenne ich noch nicht) ^ ist xor. a xor b das ergebnis ist 1, wenn entweder a oder b, aber nicht beide 1 sind a b ergebnis --------------------- 0 0 0 1 0 1 0 1 1 1 1 0 a sei bei dir der Portpin, b ist das eine Bit in der Maske, welches logischerweise 1 ist. D.h. aus der Tabelle sind nur die beiden Zeilen interessant, in denen b auf 1 ist. Und man erkennt: wenn a gleich 0 ist, dann ist das Ergebnis 1 wenn a gleich 1 ist, dann ist das Ergebnis 0 D.h. das ganze läuft darauf hinaus, dass der Portpin bei jedem Ausführen von PORTB ^= (1<<PB5); seinen Zustand wechselt. Ist der Pin vorher auf 0, dann ist er danach auf 1. Ist er vor der Operation auf 1, dann ist er danach auf 0. Eben: er wird ge-toggelt.
Das ist eine Bitweise XOR-Verknüpfung: Die Logiktabelle von XOR sieht so aus 0 0 = 0 0 1 = 1 1 0 = 1 1 1 = 0 Fall LED ist an: PORTB sieht so aus: 0bxx1xxxxx XOR-Verknüpft mit 0bxx1xxxxx ________ 0bxx0xxxxx Ergebnis: LED ist aus Fall LED ist aus: PORTB sieht so aus: 0bxx0xxxxx XOR-Verknüpft mit 0bxx1xxxxx ________ 0bxx1xxxxx Ergebnis: LED ist an Schönen Abend noch edit: och da war jemand schneller ;-)
AAAAhhh Das ist ja genial (^ Xor) Jetzt verstehe ich es. Mir ging es zwar hauptsächlich dadrum den Timer kennen zu lernen, aber es ist immer wieder schön mehr als nur einen AHA Effekt zu haben. Feine Sache... Danke Jungs. Ich werde jetzt erstmal noch ein paar Übungen mit Timer machen und wenn alles gut geht, geht es mit PWM los. Mein Ziel ist ja in endeffekt mit einem externen 50 Hz PWM (RC Bereich) z.B. LED ein und aus zu schalten. Oder über die eine Fernsteuerung die Bilkfreuqenzen zu regeln.... Naja denke da werden noch einige Fragen von mir kommen. LG Shabi
Karl Heinz Buchegger schrieb: > Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der > praktischen Sicht heraus beleuchtet: > FAQ: Timer Auschnitt aus der FAQ Timer: "... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte." ... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis 1000000 sind es 1000001 Zählschritte.
Tickzähler schrieb: > Karl Heinz Buchegger schrieb: >> Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der >> praktischen Sicht heraus beleuchtet: >> FAQ: Timer > > Auschnitt aus der FAQ Timer: > "... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so > schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also > 1 Mio Zählschritte." > > ... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis > 1000000 sind es 1000001 Zählschritte. Tja ja das stimmt. Das alte Problem mit den null basierten Werten. Man soll ja auch schon Programmierer gesehen haben, die drücken im Aufzug die 3 wenn sie in den 4. Stock wollen. gruß cyblord
Tickzähler schrieb: > Auschnitt aus der FAQ Timer: > "... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so > schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also > 1 Mio Zählschritte." > > ... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis > 1000000 sind es 1000001 Zählschritte. Ja, aber. Angenommen der Zähler steht auf 0. Jetzt macht er 1 Zählschritt. Auf welcher Zahl steht er dann. Auf 1. Nach 2 Zählschritten steht er auf ... 2 Nach 3, auf 3 .... Er durchläuft 1000001 verschiedene Zahlen, aber er macht dazu 1000000 Zählvorgänge. Denn die 0 hat er ja quasi umsonst, ohne Zählschritt erhalten (und erst dann, wenn er von 1000000 wieder auf 0 zurückfällt, macht er dann den 1Mio+1-ten Zählschritt, der ihn wieder auf 0 zurückbringt.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.