Hab hier ein Programm, welches einfach mit hile eine Timer2 Overflows hochzählt! Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt. Kann mir jemand sagen woran das leigt? Hier der Code #include<avr/io.h> #include<avr/interrupt.h> #include<avr/sleep.h> #include"timer.h" //#include"uart.h" volatile uint8_t time=0; #include<avr/io.h> void timer2_init() { ASSR|=(1<<EXCLK)|(1<<AS2);//externer Clock, asynchron Modus TCCR2B|=(1<<CS22)|(1<<CS20);//takt durch 128 teilen TIMSK2|=(1<<TOIE2); //timer overflow einschalten while(!(TCCR2B&(1<<CS22))); while(!(TCCR2B&(1<<CS20))); } ISR(TIMER2_OVF_vect) { time++; } int main() { DDRA=0xff; PORTA=0xff; set_sleep_mode(SLEEP_MODE_PWR_SAVE); sleep_enable(); timer2_init(); sei(); while(1) { if(time==5) { time=0; } else { PORTA=time; // sleep_cpu(); } } return(0); } Programmiere einen ATMega644P mit dem STK600 und dem entsprechenden Adapter. Programmieroberfläche ist das Avr Studio
Tommy wrote: > Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt. Woran erkennst du das? > Hier der Code Bitte benutze die entsprechenden Tags, damit dein Code nicht durch die Forensoftware verändert wird, siehe Formatierung im Forum. > while(!(TCCR2B&(1<<CS22))); > while(!(TCCR2B&(1<<CS20))); Für diese beiden Zeilen sehe ich keinen Sinn.
Hab vergessen zu schreiben, dass ich jetzt das JTAGICE mk2 angeschlossen habe.
Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen geht bevor er das Register upgedatet hat.
Ich glaube es gibt gar kein Problem! Wenn ich das Programm laufen lassezählt er bis fünf hoch und fängt wieder von vorne an. Wenn ich aber per Hand debugge, also per Tastendruck durch das Programm gehe läuft er öfters durch die Interrupt routine. Vielleicht liegts auch nur am Debugger!
Ne irgendetwas stimmt nicht! Wenn ich etwas über die Uart schicke bleibt er ständig in der Interrupt Routine!
Tommy wrote: > Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie > sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen > geht bevor er das Register upgedatet hat. Der korrekte Weg dafür steht im Datenblatt: d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and TCR2xUB.
Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt macht? Ich habe das so verstanden, das man Lesen und Schreibernd darauf zugreifen kann, aber wofür ist das gut? Schreibe ich einen Wert dort hinein und trifft der dann mit OCR2x zu, tritt dann ein Interrupt auf? Und schreibe ich keinen Wert dort hinein wird dann das register einfach immer erhöht, vom MC? Und was soll ich in das Register reinschreiben, wenn ich nur ein Timeroverflow machen will! Dann brauchen ich dich gar nicht das Register
Tommy wrote: > Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt > macht? Es ist der aktuelle Zählerwert. Manchmal will man ja genauere Zeitinformation haben als nur einen Überlauf oder compare match. Schreiben wird man das normalerweise nicht, außer vielleicht ggf. Rücksetzen auf 0, wenn man den Timer neu synchronsieren möchte.
Dann habe ich das ja schon mal verstanden, das ist ja schon mal was danke. Ich der Hilfe steht ja zur initialsierung folgendes: • Warning: When switching between asynchronous and synchronous clocking of Timer/Counter2, the Timer Registers TCNT2, OCR2x, and TCCR2x might be corrupted. A safe procedure for switching clock source is: a. Disable the Timer/Counter2 interrupts by clearing OCIE2x and TOIE2. b. Select clock source by setting AS2 as appropriate. c. Write new values to TCNT2, OCR2x, and TCCR2x. d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and TCR2xUB. e. Clear the Timer/Counter2 Interrupt Flags. f. Enable interrupts, if needed. • The CPU main clock frequency must be Also zuerst sollen die Interruptsflags gelöscht werden, was ich schon nicht verstehe, da ich sie ja noch gar nicht gesetzt habe. Punkt b ist klar Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00; TCCR2B dort setzte ich den Teiler auf 128. Dann warte ich auf die drei flags :z.B. while(!(ASSR&(1\leq\leqTCN2UB))); Punkt e. was soll ich löschen hab ja noch nichts gesetzt aber okay TIMSK2 =0x00; und f. wäre dann TIMSK2|=(1<<TOIE2); Wäre das so dann richtig? Vielen Dank im voraus
Im asynchron-Modus wird alles erstmal in ein Zwischenlatch geschrieben. Die CPU schreibt z.B. mit 8MHz was rein und der T2 übernimmt das dann mit seinen 32kHz, also irgendwann später. Das passiert auch mit dem Interruptflag so, blos daß es dafür kein Busy-Bit gibt. Man muß also im Interrupt ein Dummy-Write auf irgendein anderes T2-Register machen und dafür das Busy-Bit abtesten. Ist dann dieses Dummy-Write übernommen worden, ist ja auch das Interruptflag gelöscht und man kann wieder einschlafen. Damit sollte es dann nur einen Interrupt geben. Peter
Ah okay so langsam lichtet sich der Schleier:) Das dann wäre doch diese vorgehensweise, die ich gemacht habe richitg, oder?
Tommy wrote: > Ich der Hilfe steht ja zur initialsierung folgendes: Die ,Hilfe' heißt übrigens offiziell ,Datenblatt'. ;-) > Also zuerst sollen die Interruptsflags Nicht die Interruptflags, sondern die Interrupt-Enable-Bits. > gelöscht werden, was ich schon > nicht verstehe, da ich sie ja noch gar nicht gesetzt habe. Wenn du keine Interrupts freigegeben hast, musst du natürlich auch keine Freigaben zurücknehmen. Das ist eine Prinzip-Beschreibung, du bist nicht im Kindergarten, sondern Mitdenken wird vom Programmierer eines Controllers natürlich schon erwartet. > Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00; > TCCR2B dort setzte ich den Teiler auf 128. Auch hier wieder: du musst nur die Register anfassen, die dich interessieren. Ob du TCNT2 wirklich auf 0 setzen willst/musst, musst du selbst wissen. Wenn du den compare match A nicht benutzt, dann lass das Register OCR2A in Ruhe. > Dann warte ich auf die drei flags :z.B. > while(!(ASSR&(1\leq\leqTCN2UB))); Naja, vor allem willst du:
1 | while ((ASSR & (1 << TCR2BUB)) != 0) |
2 | /* wait until no longer busy */; |
...denn das TCCR2B musst du ja auf jeden Fall anfassen, also musst du auch warten, bis es fertig ist. > Punkt e. was soll ich löschen hab ja noch nichts gesetzt Bei den Umprogrammiervorgängen kann u. U. ein Interrupt triggern, der gar nicht gewollt ist (an dieser Stelle). Daher löscht man danach alle anhängigen Interrupts erstmal (so man hernach mit Interrupts arbeiten will, aber das will man mit dem asynchronen Timer eigentlich am Ende immer). > aber okay > TIMSK2 =0x00; Nein. Das ist die Maske, nicht die Interruptflags.
1 | TIFR2 = 0xff; |
Oder wenn du es ,,politisch korrekt'' machen willst:
1 | TIFR2 = (1 << OCF2B) | (1 << OCF2A) | (1 << TOV2); |
Die while Schleife scheint schon mal falsch zu sein, da der Debugger da gar nicht mehr raus kommt.
Hat geplappt, der Timer läuft jetzt auch im Sleepmodus ohne Probleme! Versuche jetzt die USART zum laufen zu kriegen! Im Moment kommt nämlich kein Zeiche an.
Bin jetzt soweit: #include<avr/io.h> #include<avr/interrupt.h> #include<avr/sleep.h> #include<util/delay.h> #include"timer.h" #include"uart.h" #define F_CPU 4000000UL // 9600 baud #define UART_BAUD_RATE 9600 volatile uint8_t time=0; ISR(TIMER2_OVF_vect) { time++; } int main() { uint8_t test=0; DDRA=0xff; PORTA=0xff; while(test!=100) { _delay_ms(10); test++; }test=0; set_sleep_mode(SLEEP_MODE_PWR_SAVE); sleep_enable(); uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); timer2_init(); sei(); while(1) { if(time==5) { TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20); while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0); uart_puts("test"); time=0; } else { PORTA=time; TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20); while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0); sleep_cpu(); } } return(0); } Aber über die USART kommt nichts! Wenn ich jedoch den Sleepmode auskommentiere klappt alles. Ergo es muss was mit der Aufwachzeit zu tuen haben. Im Datenblatt steht, das er eine gewisse Zeit braucht um wach zu werden aber packe ich z.B. die Funktion _delay_ms(50) vor die uart_puts Funktion schreibt der zwar etwas über die Schnittstelle aber bei "hello world" sehe ich nur world.
Bitte nimm die Code-Markierungen! Das kann doch so keine Sau lesen.
So vielleicht besser:
1 | #include<avr/io.h> |
2 | #include<avr/interrupt.h> |
3 | #include<avr/sleep.h> |
4 | #include<util/delay.h> |
5 | #include"timer.h" |
6 | #include"uart.h" |
7 | |
8 | #define F_CPU 4000000UL
|
9 | |
10 | // 9600 baud
|
11 | #define UART_BAUD_RATE 9600
|
12 | |
13 | volatile uint8_t time=0; |
14 | |
15 | ISR(TIMER2_OVF_vect) |
16 | {
|
17 | time++; |
18 | }
|
19 | int main() |
20 | { uint8_t test=0; |
21 | DDRA=0xff; |
22 | PORTA=0xff; |
23 | |
24 | while(test!=100) |
25 | { _delay_ms(10); |
26 | test++; |
27 | }test=0; |
28 | set_sleep_mode(SLEEP_MODE_PWR_SAVE); |
29 | sleep_enable(); |
30 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); |
31 | |
32 | timer2_init(); |
33 | |
34 | sei(); |
35 | |
36 | while(1) |
37 | {
|
38 | if(time==5) |
39 | {
|
40 | TCCR2B|=(1<<CS22)|(1<<CS20); |
41 | while ((ASSR & (1[<<TCR2BUB)) != 0); |
42 | uart_puts("test"); |
43 | time=0; |
44 | }
|
45 | else
|
46 | {
|
47 | PORTA=time; |
48 | TCCR2B|=(1<<CS22)|(1<<CS20); |
49 | while ((ASSR & (1<<TCR2BUB)) != 0); |
50 | sleep_cpu(); |
51 | }
|
52 | }
|
53 | return(0); |
54 | }
|
Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte ich den String test. Schreibe ich aber statt test z.B. hello world bekomme ich nur cryptische Zeichen
Tommy wrote: > Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte > ich den String test. Wartet dein uart_puts() denn, bis die Zeichen raus sind, oder schreibst du das interruptgesteuert raus? Dann brauchst du dich sonst nicht wundern, dass die UART aufhört zu senden, wenn du ihr den Takt klaust... Das ständige erneute Setzen von TCCR2B verstehe ich überhaupt nicht.
Ich mach das Interruptgesteuert! Habe vor der USART Funtkoin jetzt sleep_disable() geschrieben und der String kamm vollständig an. Kann aber sein, dass er jetzt nicht mehr schlafen geht, das muss ich jetzt noch konrollieren. Hast recht, das neusetzen war überflüssig.
Du solltest jetzt den bestehenden Code wegwerfen und dir erstmal Gedanken über eine Struktur machen...
Ich muss doch der Uart den Takt zurückgeben können und danach wieder zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn allerdinsg wieder aktiviere klappt es wieder nicht!
Ist die Struktur denn so schlecht?
1 | #include<avr/io.h> |
2 | #include<avr/interrupt.h> |
3 | #include<avr/sleep.h> |
4 | #include<util/delay.h> |
5 | #include"timer.h" |
6 | #include"uart.h" |
7 | |
8 | #define F_CPU 4000000UL
|
9 | |
10 | // 9600 baud
|
11 | #define UART_BAUD_RATE 9600
|
12 | |
13 | volatile uint8_t time=0; |
14 | |
15 | ISR(TIMER2_OVF_vect) |
16 | {
|
17 | time++; |
18 | }
|
19 | int main() |
20 | { uint8_t test=0; |
21 | DDRA=0xff; |
22 | PORTA=0xff; |
23 | |
24 | while(test!=100) |
25 | { _delay_ms(10); |
26 | test++; |
27 | }test=0; |
28 | set_sleep_mode(SLEEP_MODE_PWR_SAVE); |
29 | sleep_enable(); |
30 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); |
31 | |
32 | timer2_init(); |
33 | |
34 | sei(); |
35 | |
36 | while(1) |
37 | {
|
38 | if(time==5) |
39 | {
|
40 | sleep_disable(); |
41 | uart_puts("Bin wieder aufgewacht"); |
42 | time=0; |
43 | }
|
44 | else
|
45 | {
|
46 | PORTA=time; |
47 | sleep_cpu(); |
48 | }
|
49 | }
|
50 | return(0); |
51 | }
|
Was meinst du denn?
Tommy wrote: > Ich muss doch der Uart den Takt zurückgeben können und danach wieder > zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze > einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch > verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn > allerdinsg wieder aktiviere klappt es wieder nicht! Eben, derartige Gedanken meine ich mit "Struktur". Vor dem Coden sollte erst einmal eine Phase stehen, in der man sich Gedanken um den Programmfluss macht. Da gehören solche Betrachtungen mit hinein, dass man den IOclk eben nicht abschalten kann, solange die UART noch etwas sendet. Erst, wenn du diese Struktur hast, kannst du sinnvoll anfangen (von kleinen Experimenten natürlich abgesehen), sie in Code umzusetzen. Ob du für die Struktur nun ein Struktogramm, einen Programmablaufplan, ein bisschen Gekritzel auf einem Zettel oder einer Wandtafel oder was auch immer brauchst, ist rein deine Sache. Sie nur im Kopf zu behalten, funktioniert offensichtlich im Moment erst einmal (noch) nicht, sonst wäre dir das mit dem Schlafen gehen, während die UART noch sendet, nicht passiert.
Verstehe was du meinst! Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich das gelesen habe gibt es kein Bit welches angibt, das der komplette String übertragen ist, sonst könnte ich einfach solange in einer Schleife bleiben, bis dieses Bit gesetzt ist und danach lasse ich ihn wieder schlafen!
Tommy wrote: > > Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich > das gelesen habe gibt es kein Bit welches angibt, das der komplette > String übertragen ist, ... Ich würde das ungefähr so machen: . uart_send() trägt das Zeichen in den Puffer ein und aktiviert den UDRE-Interrupt . dieser Interrupt triggert sofort, die ISR schiebt das erste Zeichen aus dem Puffer nach UDR; wenn sie feststellt, dass dieses Zeichen das letzte im Puffer ist, dann aktiviert sie den TXE-Interrupt . wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das Nachfüttern des UDR . wenn der TXE-Interrupt dann zuschlägt, sind keine Zeichen mehr im Software-Puffer, und die CPU kann sich (wenn sonst auch nichts mehr zu tun ist) schlafen legen.
> Nur so wie ich > das gelesen habe gibt es kein Bit welches angibt, das der komplette > String übertragen ist, Dafür hast Du sogar einen Interrupt... (TX-Complete) Im UDRE-Int schaufelst Du das nächste Zeichen des Strings, bei Erreichen der Ende-Kennung hörst Du damit auf (es gibt ja kein nächstes Zeichen). UART schiebt nun das letzte Zeichen raus, wenn das draußen ist, schlägt der TXC-Interrupt zu und Du kannst UART deaktivieren (oder auch nicht) und den Sleep-Mode (der jetzt Idle war) auf Tiefschlaf umschalten. KH
Vielen Dank, muss jetzt allerdings schluß machen, werde mich morgen wieder dran setzen. Vielleicht kann dann der Lehrgang ja wieder weitergehen ;)
Es gibt nur noch eine Sache die ich noch nicht verstehe! Wieso hat das nicht mit dem delay geklappt? Wenn ich nach der uart_puts() Funktion mehrere Millisekunden warte oder sogar eine Sekunde, dann hatte der Mc doch genug Zeit den String rauszuschicken wieso hat das denn nicht geklappt?
Dein delay hat doch geklappt, oder? Bei 9600 Bd braucht ein Zeichen halt rund eine Millisekunde. Daher kannst du mit 5 ms delay halt zwar "test" raussenden, nicht aber "hello, world!".
Das stimmt aber ich hatte das delay nachher erhoeht aber dann kam gar nichts mehr ueber die Uart
Wenn ich mit sleep_cpu() den MC schlafen lasse, gibt es eigentlich ein Flag, welches erkennt, dass der MC wieder wach geworden und wieder bereit ist Daten über die UART zu schicken?
Hallo Jörg du hattest recht mit dem delay! Es lag noch an etwas anderem das er nichts rübergeschickt hat. Erhöht man das delay kommen mehr zeichen über die UART. Versuche jetzt eine bessere Programmieriung mit HIlfe vom TXC Flag
Jörg Wunsch wrote: > . wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt > wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das > Nachfüttern des UDR Diesen Schritt kann man sich nach meiner Erfahrung sparen, da der UDRE-Interrupt immer vor dem TXE kommt. Es wird also rechtzeitig "nachgelegt", so dass der TXE erst auftritt, wenn der UDRE-Interrupt keine weiteren Zeichen mehr abschicken konnte, weil keine mehr da sind. Gruss Andreas
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.