Hallo hab da ein kleines Problem, und zwar will ich meinen Timer in eine Endlosschleife einfügen, aber egal wo ich die Schleife setze, irgendwie klappt es nicht. Entweder werden die Timer nicht abgearbeitet oder aber die LED welche an Port B0 angeschlossen ist blinkt im falschen takt und das Programm wird nur einmal abgearbeitet. Hoffe jemand kann mir helfen. Gruß Paul #include <avr/io.h> #include <avr/interrupt.h> //Variablen für die Zeit volatile unsigned int millisekunden; volatile unsigned int sekunde; volatile unsigned int minute; volatile unsigned int stunde; int main(void) { DDRB = 0xff; // Timer 0 konfigurieren TCCR0 = (1<<WGM01); // CTC Modus TCCR0 |= (1<<CS01); // Prescaler 8 // ((1000000/8)/1000) = 125 OCR0 = 125-1; // Compare Interrupt erlauben TIMSK |= (1<<OCIE0); // Global Interrupts aktivieren sei(); while(1) { /*Hier kann die aktuelle Zeit ausgeben werden*/ } } /* Der Compare Interrupt Handler wird aufgerufen, wenn TCNT0 = OCR0A = 125-1 ist (125 Schritte), d.h. genau alle 1 ms */ ISR (TIMER0_COMP_vect) { millisekunden++; if(millisekunden == 16000) { sekunde++; millisekunden = 0; } sekunde++; if(sekunde == 60) { minute++; sekunde = 0; } stunde++; if(minute == 1) { PORTB |= (1<<PB0); } if(minute == 1) { PORTB &= ~(1<<PB0); } } }
Pepe schrieb: > blinkt im falschen takt und das Programm wird > nur einmal abgearbeitet. Wie schnell blinkt sie? Woran erkennst du, das das Programm nur einmal abgearbeitet wird? :-)
Die LED blinkt einmal auf und anstelle einer Minute blinkt sie vielleicht 10 sekunden auf. Wenn ich die Schleife nicht drin habe so wie oben funktioniert es.
"sekunde" nur innerhalb des "if(millisekunden == 16000)" erhöhen, nicht unkonditional danach nochmal, und dasselbe für die anderen Variablen. Je nach Microcontroller musst du auch ein Interrupt-Statusregister auslesen und beschreiben, wenn der Interrupt ausgelöst wurde, um den Interruptzustand zurückzusetzen, da der sonst nicht nochmal ausgelöst wird (siehe Datenblatt).
Vielen Dank für die schnellen Antworten. Hast du das etwa so gemeint??? #include <avr/io.h> #include <avr/interrupt.h> //Variablen für die Zeit volatile unsigned int millisekunden; volatile unsigned int sekunde; volatile unsigned int minute; volatile unsigned int stunde; int main(void) { DDRB = 0xff; // Timer 0 konfigurieren TCCR0 = (1<<WGM01); // CTC Modus TCCR0 |= (1<<CS01); // Prescaler 8 // ((1000000/8)/1000) = 125 OCR0 = 125-1; // Compare Interrupt erlauben TIMSK |= (1<<OCIE0); // Global Interrupts aktivieren sei(); while(1) { /*Hier kann die aktuelle Zeit ausgeben werden*/ } } /* Der Compare Interrupt Handler wird aufgerufen, wenn TCNT0 = OCR0A = 125-1 ist (125 Schritte), d.h. genau alle 1 ms */ ISR (TIMER0_COMP_vect) { millisekunden++; if(millisekunden == 16000) { sekunde++; millisekunden = 0; } if(sekunde == 60) { minute++; sekunde = 0; } if(minute == 1) { PORTB |= (1<<PB0); } if(minute == 1) { PORTB &= ~(1<<PB0); } } Und wo müßte jetzt korrekterweise die Endlosschleife rein? Nach: ISR (TIMER0_COMP_vect) { ??? Wie kann man einen Interruptzustand zurücksetzen? Gruß Pepe
Pepe schrieb: > Vielen Dank für die schnellen Antworten. > > Hast du das etwa so gemeint??? Im Grunde ja. Aber überleg mal: > ISR (TIMER0_COMP_vect) > { > > millisekunden++; > > if(millisekunden == 16000) > { > sekunde++; > millisekunden = 0; > } > > if(sekunde == 60) Wann kann den sekunde überhaupt 60 werden? Doch wohl nur dann, wenn vorher millisekunden 16000 war und daher sekunde um 1 hochgezählt wurde. Wenn das nicht der Fall war, dann kann sekunde auch nicht 60 sein. > { > minute++; > sekunde = 0; > } > > if(minute == 1) > { > PORTB |= (1<<PB0); > } wenn minute gleich 1 ist, dann schaltest du die LED EIN > > if(minute == 1) > { > PORTB &= ~(1<<PB0); > } wenn minute gleich 1 ist, dann schaltest du die LED AUS In summe ergibt das: möglichst komplizert: wenn Minute gleich 1 ist, dann die LED einschalten und gleich darauf wieder ausschalten. Und du denkst ernsthaft, dass du das sehen kannst, wenn die LED ein paar Nanosekunden leuchtet? Der Rest stimmt schon. Die Endlosschleife ist immer in main Und den Interrupt brauchst du auch nicht zurücksetzen. Durch den Aufruf der ISR wird der automatisch zurückgesetzt.
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | //Variablen für die Zeit
|
5 | volatile unsigned int millisekunden; |
6 | volatile unsigned int sekunde; |
7 | volatile unsigned int minute; |
8 | volatile unsigned int stunde; |
9 | |
10 | int main(void) |
11 | {
|
12 | DDRB = (1<<PB0); // PB0 Ausgang |
13 | // PB0 anfangs (1. Minute) default LOW
|
14 | |
15 | // Timer 0 konfigurieren
|
16 | TCCR0 = (1<<WGM01); // CTC Modus |
17 | TCCR0 |= (1<<CS01); // Prescaler 8 |
18 | // ((1000000/8)/1000) = 125
|
19 | OCR0 = 125-1; |
20 | |
21 | // Compare Interrupt erlauben
|
22 | TIMSK |= (1<<OCIE0); |
23 | |
24 | // Global Interrupts aktivieren
|
25 | sei(); |
26 | |
27 | while(1) |
28 | {
|
29 | /*Hier kann die aktuelle Zeit
|
30 | ausgeben werden*/
|
31 | }
|
32 | }
|
33 | |
34 | /*
|
35 | Der Compare Interrupt Handler wird aufgerufen,
|
36 | wenn TCNT0 = OCR0A = 125-1 ist (125 Schritte),
|
37 | d.h. genau alle 1 ms bei 1 MHz
|
38 | */
|
39 | |
40 | ISR (TIMER0_COMP_vect) |
41 | {
|
42 | millisekunden++; |
43 | if(millisekunden == 16000) // F_CPU 16 MHz statt 1 MHz |
44 | {
|
45 | millisekunden = 0; |
46 | sekunde++; |
47 | if(sekunde == 60) |
48 | {
|
49 | sekunde = 0; |
50 | minute++; |
51 | PORTB ^= (1<<PB0); // PB0 beim Minutenwechsel toggeln |
52 | }
|
53 | if(minute == 60) |
54 | {
|
55 | minute = 0; |
56 | stunde++; |
57 | }
|
58 | if (stunde == 24) |
59 | {
|
60 | stunde = 0; |
61 | }
|
62 | }
|
63 | }
|
So hab es jetzt noch einmal ein bisschen verändert: Im Grunde genommen soll das ganze später eine Messung ergeben. Soll 4h messen und das für 1 min. Danach soll das ganze wieder von vorne beginnen. Aber der Counter zählt ja weiter oder? Kann man den irgendwie wieder reseten? Hier mal der Code:
1 | {
|
2 | DDRB = (1<<PB0); // PB0 Ausgang |
3 | // PB0 anfangs (1. Minute) default LOW
|
4 | |
5 | // Timer 0 konfigurieren
|
6 | TCCR0 = (1<<WGM01); // CTC Modus |
7 | TCCR0 |= (1<<CS01); // Prescaler 8 |
8 | // ((1000000/8)/1000) = 125
|
9 | OCR0 = 125-1; |
10 | |
11 | // Compare Interrupt erlauben
|
12 | TIMSK |= (1<<OCIE0); |
13 | |
14 | // Global Interrupts aktivieren
|
15 | sei(); |
16 | |
17 | while(1) |
18 | {
|
19 | /*Hier kann die aktuelle Zeit
|
20 | ausgeben werden*/
|
21 | }
|
22 | }
|
23 | |
24 | /*
|
25 | Der Compare Interrupt Handler wird aufgerufen,
|
26 | wenn TCNT0 = OCR0A = 125-1 ist (125 Schritte),
|
27 | d.h. genau alle 1 ms bei 1 MHz
|
28 | */
|
29 | |
30 | ISR (TIMER0_COMP_vect) |
31 | {
|
32 | while(1) |
33 | {
|
34 | while(1) |
35 | {
|
36 | millisekunden++; |
37 | |
38 | if(millisekunden == 16000) // F_CPU 16 MHz statt 1 MHz |
39 | {
|
40 | millisekunden = 0; |
41 | sekunde++; |
42 | }
|
43 | |
44 | if(sekunde == 60) |
45 | {
|
46 | sekunde = 0; |
47 | minute++; |
48 | }
|
49 | |
50 | if(minute == 60) |
51 | {
|
52 | minute = 0; |
53 | stunde++; |
54 | }
|
55 | |
56 | if (stunde == 24) |
57 | {
|
58 | stunde = 0; |
59 | }
|
60 | |
61 | if(minute == 240) |
62 | {
|
63 | PORTB |= (1<<PB0); |
64 | }
|
65 | |
66 | if(minute == 241) |
67 | {
|
68 | PORTB &= ~(1<<PB0); |
69 | }
|
70 | |
71 | break; |
72 | |
73 | }
|
74 | |
75 | }
|
76 | |
77 | }
|
Hab jetzt mal nen break drin in der Hoffnung das wemnn die zweite Schleife unterbrochen wird die erste wieder von 0 beginnt. Aber wahrscheinlich muß erst mal der Counter genullt werden oder??? Gruß Andi
Das Problem ist ich kann den ISR (TIMER0_COMP_vect) nicht in die Schleife mit einfügen dann bekomme ich vom AVR Studio ne Fehlermeldung bei der Schleife.
Wie soll das funktionieren, wenn die Minuten von 0 bis 59 laufen if(minute == 60) { minute = 0; stunde++; } und du sie hier auf 240 testen willst? if(minute == 240) { PORTB |= (1<<PB0); } Das geht so nicht. Wenn du so arbeiten willst, brauchst du durchlaufende Minuten, keine "klassischen" Minuten wie bei der Uhr (0..59). Logisch kannst du die vier Zeitvariablen nach der Messung zurücksetzen. Dabei daran denken, dass du auch den Hardware-Timercounter nullst.
Ok das ist einleuchtend. Und wie kann man diese Zeitvariablen zurücksetzen und den Counter?
Das macht so keinen Sinn mit der Schleife im Interrupt. Sinn des Interrupts ist es ja, daß der automatisch periodisch aufgerufen wird und du den möglichst schnell wieder verlässt, damit der in der nächsten Millisekunde wieder aufgerufen werden kann. Bei jedem Aufruf zählst du die nächste Millisekunde, falls der Kommentar stimmt, daß der Interrupt jede Millisekunde aufgerufen wird. In dem Fall auch "millisekunden == 1000" testen. Interruptzustand zurücksetzen hängt vom Chip und der Entwicklungsumgebung ab. Wo ich letztens einen Freescale-Chip getestet hatte, musst Bit 7 im RTCSC-Register setzen, damit der Interrupt erneut ausgelöst wurde. Kann aber sein, daß mit "ISR (TIMER0_COMP_vect)" das alles schon von alleine gemacht wird oder dein Microcontroller sowas nicht braucht. Daher Datenblatt zum Microcontroller lesen und die Erklärung zu "ISR" in der Compiler-Anleitung lesen.
So jetz noch einmal das ganze: So müßte doch das Problem mit dem zählen gelöst sein.
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | //Variablen für die Zeit
|
5 | volatile unsigned int millisekunden; |
6 | volatile unsigned int sekunde; |
7 | volatile unsigned int minute; |
8 | volatile unsigned int stunde; |
9 | |
10 | int main(void) |
11 | {
|
12 | DDRB = (1<<PB0); // PB0 Ausgang |
13 | // PB0 anfangs (1. Minute) default LOW
|
14 | |
15 | // Timer 0 konfigurieren
|
16 | TCCR0 = (1<<WGM01); // CTC Modus |
17 | TCCR0 |= (1<<CS01); // Prescaler 8 |
18 | // ((1000000/8)/1000) = 125
|
19 | OCR0 = 125-1; |
20 | |
21 | // Compare Interrupt erlauben
|
22 | TIMSK |= (1<<OCIE0); |
23 | |
24 | // Global Interrupts aktivieren
|
25 | sei(); |
26 | |
27 | while(1) |
28 | {
|
29 | /*Hier kann die aktuelle Zeit
|
30 | ausgeben werden*/
|
31 | }
|
32 | }
|
33 | |
34 | /*
|
35 | Der Compare Interrupt Handler wird aufgerufen,
|
36 | wenn TCNT0 = OCR0A = 125-1 ist (125 Schritte),
|
37 | d.h. genau alle 1 ms bei 1 MHz
|
38 | */
|
39 | |
40 | ISR (TIMER0_COMP_vect) |
41 | {
|
42 | while(1) |
43 | {
|
44 | while(1) |
45 | {
|
46 | millisekunden++; |
47 | |
48 | if(millisekunden == 16000) // F_CPU 16 MHz statt 1 MHz |
49 | {
|
50 | millisekunden = 0; |
51 | sekunde++; |
52 | }
|
53 | |
54 | if(sekunde == 60) |
55 | {
|
56 | sekunde = 0; |
57 | minute++; |
58 | }
|
59 | |
60 | if(minute == 60) |
61 | {
|
62 | minute = 0; |
63 | stunde++; |
64 | }
|
65 | |
66 | if (stunde == 24) |
67 | {
|
68 | stunde = 0; |
69 | }
|
70 | |
71 | if(stunde == 4) |
72 | {
|
73 | PORTB |= (1<<PB0); |
74 | |
75 | if(minute == 2) |
76 | {
|
77 | PORTB &= ~(1<<PB0); |
78 | }
|
79 | |
80 | }
|
81 | |
82 | |
83 | |
84 | break; |
85 | |
86 | }
|
87 | |
88 | }
|
89 | |
90 | }
|
Das mit der äußeren while-Schleife und dem break ist übrigens identisch, wie wenn du die äußere while-Schleife und das break einfach weglassen würdest. Man sollte es auch besser formatieren, also nach geschweifter Klammer eine Ebene einrücken und wieder zurück bei geschweifter Klammer zu, sodaß man die Struktur besser sehen kann. Bist du sicher, daß C-Programmierung das Richtige für dich ist? Hat normalerweise nicht was mit Versuch und Irrtum zu tun, sondern mit überlegtem und planvollem vorgehen :-)
Pepe, schmeiss alle while() in der ISR raus. Du musst und kannst ohne auskommen. Du hast ein while in der main(). Schreib dein Programm so, dass kein while in der ISR ist und dafür das while in der main() benutzt wird. Deine Zeitvariablen (minute&stunde) kannst du auch in main() benutzen! Die ISR ist dann nur für das Hochzählen der Zeitvariablen verantwortlich. Lies dir den Artikel Interrupt durch und beachte das Thema atomaren Datenzugriff mit der cli/sei Klammer.
1 | // Eine neue Hilfsvariable um die Wartezeit in Minuten zu zählen
|
2 | volatile unsigned char gesamtminuten; // max. 255 Minuten! |
3 | |
4 | // Eine Funktion um nach der Messung die Wartezeit zu resetten
|
5 | void timer_neustart(void) |
6 | {
|
7 | TIMSK &= ~(1<<OCIE0); // Timer aus |
8 | millisekunden = 0; sekunde = 0; minute = 0; |
9 | gesamtminuten = 0; stunde = 0; TCNT0 = 0; |
10 | TIMSK |= (1<<OCIE0); // Timer an |
11 | }
|
12 | |
13 | // ... dann in main() die Arbeitsroutine
|
14 | |
15 | while(1) |
16 | {
|
17 | // ggf. stunde:minute:sekunde anzeigen
|
18 | |
19 | if (gesamtminuten == 240) |
20 | {
|
21 | PORTB |= (1<<PB0); |
22 | }
|
23 | |
24 | if (gesamtminuten == 241) |
25 | {
|
26 | PORTB &= ~(1<<PB0); |
27 | timer_neustart(); |
28 | }
|
29 | }
|
30 | |
31 | // ... und in der ISR() neu das Zählen der Wartezeit
|
32 | |
33 | if(sekunde == 60) |
34 | {
|
35 | sekunde = 0; |
36 | minute++; |
37 | gesamtminuten++; |
38 | }
|
Ich würde an deiner Stelle auch darüber nachdenken, die Minuten nicht raufzuzählen sondern runterzuzählen, so wie bei einem Countdowntimer. In main() setzt du die minuten auf die Zeit, die es abzuwarten gilt. UNd dann wird einfach in main() ständig überprüft, ob die Minuten wieder auf 0 heruntergezählt wurden. Die Stunden brauchst du nicht wirklich. 4 Stunden sind 240 Minuten und das geht mit einer 8 Bit Variablen noch gut.
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.