Guten Abend zusammen, ich habe da ein kleines Problem ;) Ich soll für einen Atmega162 (Takt 16MHz) ein Programm in C schreiben das eine LED bei an PortB0 mit 10HZ blinken lässt. Nachdem ich ewig gebraucht habe um den vorteiler auszurechnen (garnicht so einfach wenn man das noch nie gemacht ha) Bin ich zu dem Ergebnis gekommen dass ein Teiler von 6,25 benötigt wird. Da es diesen aber nicht gibt verwende ich einen Vorteiler von 8! Jetzt gibt es ja die Möglichkeit den Timer im CTC modus laufen zu lassen und ihn nur zu einem bestimmten wert unter 255 zählen zu lassen, damit ich doch auch die gewünschte blinkfrequenz von 10HZ komme. Leider weiss ich nicht genau wie ich das jetzt in den Code integrieren kann, sollte dieser überhaupt ansatzweise richtig sein. Außerdem wäre ich für ein Beispiel oder so für den Zähleraufruf in der Main sehr dankbar. Der Code: #include <avr/io.h> #include <avr/interrupt.h> void init () { //Led auf PORT B DDRB |=0x01; PORTB |=0x00; //CTC von Timer1 aktivieren TCCR0 &= ~(1<<WGM00); TCCR0 |= (1<<WGM01); TCCR0 |= (1<<CLKPS0); TCCR0 |= (1<<CLKPS1); TCCR0 &= ~(1<<CLKPS2); TCCR0 &= ~(1<<CLKPS3); //Keine ahnung ob die nächsten 3 Zeilen stimmen... OCR0=1 TIMSK |= (1<<OCIE0); sei(); } int main() { init(); while(1) { PORTB |= (1<<0x01); //LED AN //Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen PORTB &= (1<<0x01); //LED AUS //Hier wieder dem Timer aufrufen } }
Hi >Nachdem ich ewig gebraucht habe um den vorteiler auszurechnen (garnicht >so einfach wenn man das noch nie gemacht ha) Bin ich zu dem Ergebnis >gekommen dass ein Teiler von 6,25 benötigt wird. Da es diesen aber nicht >gibt verwende ich einen Vorteiler von 8! 10Hz entspricht einer Periodendauer von 100ms (50ms an, 50ms aus). Der Timer0 (8 Bit) hat aber bei 16MHz und Vorteiler 8, eine Overflow-Zeit von 128µs. Selbst bei Vorteiler 1024 kommst du auf maximal 16,36ms. Die 50ms kannst du also nicht so einfach erreichen. Bist du an Timer0 gebunden? Mit dem Timer1 wäre es zumindest für dich leichter. >//Keine ahnung ob die nächsten 3 Zeilen stimmen... >OCR0=1 >TIMSK |= (1<<OCIE0); >sei(); Ohne Interrupservicetroutine führt das unweigerlich zu Absturz. MfG Spess
Nein ich bin nicht an den Timer0 gebunden! Also soll ich lieber den Timer1 mit Fast PWM benutzen? Wenn nich und ich jetzt einen 16Bit Timer benutze, der bis 65536 Zählt komme ich auf einen Vorteiler von 3,125 (Wenn die Formel stimmt die ich habe, oder ich das richtige einsetze...) Was die ISR angeht. Muss das ganze also so aussehen, oder hab ich das ganze jetzt nurnoch verschlimmert und ich sollte am besten wieder bei 0 anfangen? Das problem denau ich jetzt meinen Timer in der Main() aufrufe konnte ich bis jetzt auch noch nicht lösen. Hat jemand dazu noch nen Tipp oder nen Codebeispiel? Neuer CODE: #include <avr/io.h> #include <avr/interrupt.h> void init () { //Led auf PORT B DDRB |=0x01; PORTB |=0x00; //CTC von Timer1 aktivieren TCCR1A &= ~(1<<WGM00); TCCR1A |= (1<<WGM01); TCCR1A |= (1<<CLKPS0); TCCR1A |= (1<<CLKPS1); TCCR1A &= ~(1<<CLKPS2); TCCR1A &= ~(1<<CLKPS3); //Keine ahnung ob die nächsten 3 Zeilen stimmen... OCR0=1 TIMSK |= (1<<OCIE0); sei(); } ISR(TIMER1_COMP_vect) { PORTD &= ~0x01; int main() { init(); while(1) { PORTB |= (1<<0x01); //LED AN //Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen... habe aber bis jetzt keine Idee wie! PORTB &= (1<<0x01); //LED AUS //Hier wieder dem Timer aufrufen } }
Hallo, Du hast 16000000Hz Takt und willst 10Hz. Für den CTC mit Hardware-Toggle am Pin brauchst Du 20Hz, einmal von L auf H schalten und einmal zurück für Deine 10Hz. 16000000/20 sind 8000000 Teilerfaktor. Das durch die möglichen Vorteiler teilen, bis das Ergebnis ganzzahlig ist. Ich nehme gleich mal den 16Bit Timer. 800000/1024 = 781,25 geht nicht 800000/256 = 3125 ok. Vorteiler also 256, Comaparewert 3125 - 1 = 3124 Macht 10Hz in Hardware. Gruß aus Berlin Michael
Hi >Wenn nich und ich jetzt einen 16Bit Timer benutze, der bis 65536 Zählt >komme ich auf einen Vorteiler von 3,125 (Wenn die Formel stimmt die ich >habe, oder ich das richtige einsetze...) Dafür gibt es ja CTC. Damit lässt sich der Topwert des Timers einstellen. mit WGM-Bits auf Mode 4 OCR1A = 0x30D3 läuft der Timer alle 50ms über und fängt wieder bei Null an. Wenn der OC1A-Interrupt freigegeben ist wird dieser dann ausgelöst. In der ISR invertierst du dann das Portpin und fertig. MfG Spess
Fang erst mal damit an, deinen Code einzurücken! Dazu musst du: dich mit dir selbst auf ein Schema einigen, wie genau du die { } setzen willst. Sagen wir mal: die { kommt immer in die nächste Zeile und steht alleine. Nach der { wird der Code der innerhalb des Blocks steht um 2 Leerzeichen eingerückt. Die Einrückung wird erst durch das } wieder aufgehoben, wobei die } in derselben Spalte steht, in der auch die { stand Dann sieht dein Code so aus:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | void init () |
5 | {
|
6 | //Led auf PORT B
|
7 | DDRB |=0x01; |
8 | PORTB |=0x00; |
9 | |
10 | //CTC von Timer1 aktivieren
|
11 | TCCR1A &= ~(1<<WGM00); |
12 | TCCR1A |= (1<<WGM01); |
13 | |
14 | TCCR1A |= (1<<CLKPS0); |
15 | TCCR1A |= (1<<CLKPS1); |
16 | TCCR1A &= ~(1<<CLKPS2); |
17 | TCCR1A &= ~(1<<CLKPS3); |
18 | |
19 | //Keine ahnung ob die nächsten 3 Zeilen stimmen...
|
20 | OCR0=1 |
21 | TIMSK |= (1<<OCIE0); |
22 | sei(); |
23 | }
|
24 | |
25 | ISR(TIMER1_COMP_vect) |
26 | {
|
27 | PORTD &= ~0x01; |
28 | |
29 | int main() |
30 | {
|
31 | init(); |
32 | while(1) |
33 | {
|
34 | PORTB |= (1<<0x01); //LED AN |
35 | //Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen... habe aber bis jetzt keine Idee wie!
|
36 | PORTB &= (1<<0x01); //LED AUS |
37 | //Hier wieder dem Timer aufrufen
|
38 | }
|
39 | }
|
40 | }
|
Durch diese Struktur sieht man sofort, dass deine Funktion main() innerhalb der ISR geschachtelt wurde. Zumindest würde es das, wenn es in C gehen würde, denn in C kann man keine Funtionen in andere Funktionen schachteln (auch wenn der gcc das kann). Auf jeden Fall ist das nicht das, was du wolltest. Wo liegt der Fehler? Nun, du hast die } von der ISR Funktion vergessen. Durch die Einrückung sieht man solche Dinge! Daher ist eine Einrückung nicht einfach nur etwas, damit der Code schöner aussieht, sondern ist ein wichtiges Hilfsmittel um nicht den Überblick zu verlieren. Zum Code. Du rufst keinen Timer auf! Sondern: Du stellst den Timer in eine bestimmte Betriebsart ein und wenn bestimmte Bedingungen vorliegen (zb der Timer hat seinen Endwert erreicht), dann ruft der Timer die ISR-Funktion auf!
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | ISR(TIMER1_COMPA_vect) |
5 | {
|
6 | PORTB ^= 0x01; |
7 | }
|
8 | |
9 | int main() |
10 | {
|
11 | //Led auf PORT B
|
12 | DDRB |= 0x01; |
13 | |
14 | // CTC Modus
|
15 | // Vorteiler 1024
|
16 | TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); |
17 | OCR1A = 780; |
18 | TIMSK |= (1<<OCIE1A); |
19 | sei(); |
20 | |
21 | while(1) |
22 | {
|
23 | }
|
24 | }
|
Soweit blinkt die LED. Dein Zähler zählt mit 16000000 / 1024 = 15625 Das heist, wenn du nichts weiter tust, dann würde der Timer in 1 Sekunde bis 15625 zählen. Du willst 10 Hz haben, also brauchst du 20 ISR Aufrufe in der Sekunde (10 fürs einschalten, 10 fürs ausschalten). D.h. von einem ISR AUfruf zum nächsten dürfen 1 / 20 = 0.05 Sekunden vergehen. Wenn dein Timer in 1 Sekunde bis 15625 zählen würde, wie weit kommt er dann in 0.05 Sekunden? Na genau bis 15625 * 0.05 = 781.25 Und daher setzt ich diesen Wert als Vergleichswert ins Vergleichsregister. Jetzt zählt der Zähler mit einer bestimmten Taktrate (vorgegeben durch Taktfrequenz und Vorteiler) bis 780. Danach löst er einen Compare Match aus welcher wiederrum die ISR Funktion aufruft. Wegen dem CTC Modus zählt der Timer aber nicht bei 781, 782, ... weiter, sondern fängt wieder bei 0 an. Der Timer zählt also laufend 0, 1, 2, 3, ..., 777, 778, 779, 780, 0, 1, 2, 3, .... , 777, 778, 779, 780 und die 780 sind so gewählt, dass der Timer für einmal von 0 bis 780 zählen 0.05 Sekunden braucht. Bei einem andern Vorteiler ergibt sich ein anderer Vergleichswert. Aber da es nicht allzuviele Vorteiler gibt, kannst du ja einfach mal alle durchprobieren und nachsehen, ob es einen gibt, bei dem sich die Berechnung des Vergleichswertes auf eine glatte 0 in den Nachkommastellen ausgeht. Nichts gegen die Formeln im Datenblatt. Aber IMHO ist es besser, du machst dir klar, was der Timer eigentlich macht und was das für deine Zahlen bedeutet. Dann brauchst du auch keine Formeln mehr lernen oder nachschlagen.
Hallo, VIELEN VIELEN Dank für die super Erklärung. Habe eben nochmal nachgerechnet und bei einem Vorteiler von 256 bekomme ich eine glatten vergleichswert von 3125 raus. Werde den Code morgen mal auf seine Funktionsfähigkeit testen (Wirklich erschreckend, dass der Code von Karl Heinz so viel einfacher aussieht als mein erster versuch...;) )
>denn in C kann man keine Funtionen in andere Funktionen >schachteln (auch wenn der gcc das kann) Wurks! Manchmal kann der gcc ein bischen viel. Wenn das Kernighan und Ritchie wüssten... :-) Naja. Wollen wir mal nicht so undankbar sein.
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.