Hallo, ich habe mir eben das Beispiel vom Timer und Zähler (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR) durchgelesen, da ich eine LED die ganze Zeit während der Controller (ATmega8) läuft blinken lassen möchte, unabhängig vom Hauptprogramm. Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm lässt sich nicht mehr ausführen. Welche ISR benötige ich genau für so etwas? #define F_CPU 1000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> int main(void) { // Timer 0 konfigurieren TCCR0 = (1<<CS01); // Prescaler 8 // Overflow Interrupt erlauben TIMSK |= (1<<TOIE0); // Global Interrupts aktivieren sei(); while(1) { /* Sonstige Aktionen */ } } /* Der Overflow Interrupt Handler wird aufgerufen, wenn TCNT0 von 255 auf 0 wechselt (256 Schritte), d.h. ca. alle 2 ms */ #ifndef TIMER0_OVF_vect // Für ältere WinAVR Versionen z.B. WinAVR-20071221 #define TIMER0_OVF_vect TIMER0_OVF0_vect #endif ISR (TIMER0_OVF_vect) { // Led blinken lassen ... } Kann mir dabei jemand weiterhelfen? Wäre super! MfG Cuadrado
Cuadrado schrieb: > Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode > gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm > lässt sich nicht mehr ausführen. Hast du das schon ausprobiert, oder ist das eine Vermutung?
Daniel V. schrieb: > Cuadrado schrieb: >> Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode >> gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm >> lässt sich nicht mehr ausführen. > > Hast du das schon ausprobiert, oder ist das eine Vermutung? Ja, wie wurde das getestet? Dein Hauptprogramm ist leer und somit passiert dort auch nicht viel
Also, während in der ISR eine LED immer blinkt, soll im Hauptprogramm einfach mal eine andere LED etwas schneller blinken. Dieses Programm habe ich ausprobiert. Die ISR LED hat funktioniert, an PD1 lagen aber immer +5V an. #define F_CPU 1000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> int main(void) { DDRD = 0xFF; // Timer 0 konfigurieren TCCR0 = (1<<CS01); // Prescaler 8 // Overflow Interrupt erlauben TIMSK |= (1<<TOIE0); // Global Interrupts aktivieren sei(); while(1) { PORTD |= (1<<PD1); _delay_ms(100); PORTD &= ~(1<<PD1); _delay_ms(100); } } ISR (TIMER0_OVF_vect) { PORTD |= (1<<PD0); _delay_ms(500); PORTD &= ~(1<<PD0); _delay_ms(500); }
Cuadrado schrieb: > ISR (TIMER0_OVF_vect) > { > PORTD |= (1<<PD0); > _delay_ms(500); > PORTD &= ~(1<<PD0); > _delay_ms(500); > > } Scherzkeks! Niemals in ISR ein delay() verwenden! Überleg mal warum.
Kann ich mir gut vorstellen, der ist ja in der ISR ausschließlich mit Warten beschäftigt und kommt gar nicht zum Hauptprogramm. Und nach dem Verlassen der ISR macht er einen Takt im Hauptprogramm und springt dann sofort wieder in die ISR. Ich vermute, wenn du lange genug wartest, wird sich auch im Hauptprogramm was tun. Das dauert aber lange (1 Takt pro Sekunde). Lösung: Delay raus aus der ISR und das ganze mit Timern machen.
Weil die An/Aus-Zeit, in diesem Fall 1s länger ist als die Zeit bis die nächste ISR ausgelöst wird?
Cuadrado schrieb: > Weil die An/Aus-Zeit, in diesem Fall 1s länger ist als die Zeit bis die > nächste ISR ausgelöst wird? Hmm... Eine ISR ist ASAP wieder zu verlassen, da sie das Hauptptogramm unterbricht. Wie in der Anleitung vermerkt wurde, wird TIMER0_OVF alle 2ms aufgerufen. Wenn du da ein delay von 500ms machst, dann kannst du den Timer auch gleich weglassen (du häbgst die ganze Zeit im Interrupt fest). Da gehört ein Zähler rein, der auf 500ms abgefragt wird, dann toggelst du den Pin und setzt den Zähler zurück. Ach ja, wenn du schon ein Tutorial liest, solltest du auch alles lesen. So könnte man es machen: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR#Timer2_im_Asynchron_Mode
Ok, danke für die Tipps! Also so funktioniert schonmal was, allerdings muss die LED in der ISR sehr schnell blinken und die andere auch. Naja, da muss ich mich wohl noch weiter einlesen, aber das Prinzip ist jetzt klarer geworden. :D #define F_CPU 1000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> int main(void) { DDRD = 0xFF; // Timer 0 konfigurieren TCCR0 = (1<<CS02); // Prescaler 256 // Overflow Interrupt erlauben TIMSK |= (1<<TOIE0); // Global Interrupts aktivieren sei(); while(1) { PORTD |= (1<<PD1); _delay_ms(200); PORTD &= ~(1<<PD1); _delay_ms(200); } } ISR (TIMER0_OVF_vect) { int zeit; for (zeit=0; zeit<1000; zeit++) { PORTD |= (1<<PD0); } PORTD &= ~(1<<PD0); }
Cuadrado schrieb: > ISR (TIMER0_OVF_vect) > { > > int zeit; > > for (zeit=0; zeit<1000; zeit++) > { > PORTD |= (1<<PD0); > } > > PORTD &= ~(1<<PD0); > > } Du wartest doch schon wieder in der ISR! Mach die Zeit mit dem Timer. Und in der ISR wird dann nur noch der Port gesetzt und dann gehts wieder raus.
>ISR (TIMER0_OVF_vect) >{ > >int zeit; > > for (zeit=0; zeit<1000; zeit++) > { > PORTD |= (1<<PD0); > } > > PORTD &= ~(1<<PD0); > >} Das ist doch der gleiche Käse wie mit dem delay! ISR (TIMER0_OVF_vect) { zeit++; if (zeit >= 1000) { PORTD |= (1<<PD0); zeit = 0; } else { PORTD &= ~(1<<PD0); } Sowas in der Art müsste gehen, da wird in der ISR nur eine Abfrage gemacht und keine Zeit verplempert. }
Cuadrado schrieb: > > > ISR (TIMER0_OVF_vect) > { > PORTD |= (1<<PD0); > _delay_ms(500); > PORTD &= ~(1<<PD0); > _delay_ms(500); > > } AUTSCH!!! das warten übernimmt doch schon der timer für dich stelle ihn so ein, dass die isr alle 500ms aufgerufen wuird und toggle den pin... gut is
Du kannst dir ausrechnen, wie lange ein Zählschritt bei einer Taktfrequenz von 1 MHz und einem Prescaler von 256 dauert. Da der Zähler ein 8-Bit-Zähler ist, dauert es 256 Zählschritte, bis der Timer überläuft (ca. 65 ms). Wenn das kürzer ist als die Zeit, die zwischen den Umschaltvorgängen vergehen soll, musst du in der Interruptroutine eine Variable hochzählen und per "if" prüfen, ob der gewünschte Wert schon erreicht ist. Wenn ja, kannst du den Pinzustand entweder mittels XOR oder durch Beschreiben des PIN-Registers umschalten.
1 | int main (void) { |
2 | volatile uint8_t zeit = 0; |
3 | ...
|
4 | }
|
5 | |
6 | |
7 | ISR (TIMER0_OVF_vect) { |
8 | zeit++; |
9 | |
10 | if (zeit > 8) { |
11 | PORTD ^= (1<<PD0); |
12 | zeit = 0; |
13 | }
|
14 | }
|
Die Variable "zeit" muss außerhalb von main stehen. Das volatile ist in diesem Fall nicht nötig, da sie ja nur in der ISR verwendet wird.
Hi
Wie immer sitzt ein Fehler vor dem µC. Merke: Ein Controller macht genau
das, was du ihm sagst und nicht das, was du glaubst, ihm gesagt zu
haben. Das ist der Unterschied zwischen Mensch und Maschine. Ich kann
leider kein C und deshalb auch nicht mit Code weiterhelfen, aber merk
dir eines: Eine ISR soll etwas ganz schnell zwischendurch erledigen, das
keinen Aufschub erlaubt. Wenn ein Postbote an der Tür klingelt und du
der Meinung bist, das es ja Zeit hat darauf zu reagieren, ist er wieder
weg. Also, alles liegen lassen, die Post holen und dann weitermachen.
Lesen kannst du dann in Ruhe. Ich glaub, die wenigsten Menschen lesen im
Beisein des Postboten erst mal die Post, bevor sie wieder zurück in die
Wohnung gehen. Es könnt sonst sein, das des Essen verkokelt oder das
Bügeleisen ein neues Emblem auf das Hemd gezaubert hat.
MAch folgendes:
In deiner ISR, die alle 2mSek. aufgerufen wird, zählst du erst mal bis
50. Das entspricht der Zeit von 0.1 Sek. In einem beliebigen Byte setzt
du dann ein Bit. dann zählst du bei einem Überlauf bei 50 noch mal bis
10. Das entspricht der Sekunde.
Etwas so:
ISR:
INC Zähler1
Zähler1<50 dann ende_ISR
Set Bit_100mSek
INC Zähler2
Zähler2<10 dann ende_ISR
Set Bit_Sek
ende_ISR
Das ist eine überschaubare ISR. Im Hauptprogramm fragst du diese Bits
ab:
If Bit_100mSek dann
.....
Reset Bit100mSek
endIf
If Bit_Sek dann
...
Reset Bit_Sek
EndIF
Sorry, besser kann ich es nicht beschreiben, aber diesen Pseudocode
sollte jeder in seine Sprache übersetzen können.
Gruß oldmax
Bastler schrieb: > > > ISR (TIMER0_OVF_vect) > { > > zeit++; > > if (zeit >= 1000) > { > PORTD |= (1<<PD0); > zeit = 0; > } > else > { > PORTD &= ~(1<<PD0); > } > > } um mit Deinen Worten zu antworten: Das ist doch der gleiche Käse wie mit dem delay! Da kann man die ISR auch gleich weglassen. > Sowas in der Art müsste gehen, nein, die blitzt höchstens ab und zu mal.
Cuadrado schrieb:1 | ISR (TIMER0_OVF_vect) // Led blinken lassen ... |
2 | {
|
3 | PORTD ^= (1<<PD0); // LED togeln |
4 | }
|
Ich würde es mal mit "toggeln" (s.o.) versuchen; d.h. bei jedem Aufruf der ISR wird der aktuelle Zustand der LED invertiert. Über den Timer stellst Du ein, in welchem Rhythmus das passiert und ob es passiert.
Cuadrado schrieb: > Also, während in der ISR eine LED immer blinkt, soll im Hauptprogramm > einfach mal eine andere LED etwas schneller blinken. Dieses Programm > habe ich ausprobiert. Die ISR LED hat funktioniert, an PD1 lagen aber > immer +5V an. Warum muss denn eine LED in der ISR blinken? Meine wichtigste ISR in jedem Programm stellt nur einen Zähler zur Verfügung, der pro ms um 1 hochgezählt wird. Und der Ganze Rest läuft in der Hauptschleife. Hier mal 3 LEDs, die "gleichzeitig" mit unterschiedlicher Frequenz blinken:
1 | volatile unsigned long tiAkt=0; |
2 | unsigned long tiLED1=0; |
3 | unsigned long tiLED2=0; |
4 | unsigned long tiLED3=0; |
5 | |
6 | ISR --> zählt jede ms die tiAkt um 1 hoch |
7 | |
8 | main () { |
9 | |
10 | while(1) { |
11 | if (tiAkt>tiLED1) { // Zeit für LED1 abgelaufen |
12 | tiLED1+=300; // nächsten Zeitpunkt ausrechnen |
13 | LED1=~LED1; // Led toggeln |
14 | }
|
15 | if (tiAkt>tiLED2) { // Zeit für LED2 abgelaufen |
16 | tiLED2+=500; // nächsten Zeitpunkt ausrechnen |
17 | LED2=~LED2; // Led toggeln |
18 | }
|
19 | if (tiAkt>tiLED3) { // Zeit für LED3 abgelaufen |
20 | tiLED3+=700; // nächsten Zeitpunkt ausrechnen |
21 | LED3=~LED3; // Led toggeln |
22 | }
|
23 | |
24 | // Und hier das tun, was sonst noch zu tun ist.
|
25 | // Aber nichts, womit unnötig Zeit verplempert wird!
|
26 | // Also kein delay() oder nischtnutzige Zählschleifen.
|
27 | // Als Faustregel: die Hauptschleife muss in maximal 10ms
|
28 | // einmal komplett durchlaufen werden.
|
29 | }
|
30 | }
|
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.