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
:
Bearbeitet durch User
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 | }
|
:
Bearbeitet durch User
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 | }
|
:
Bearbeitet durch Moderator
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.