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);
}
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.
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.
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.
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
volatileunsignedlongtiAkt=0;
2
unsignedlongtiLED1=0;
3
unsignedlongtiLED2=0;
4
unsignedlongtiLED3=0;
5
6
ISR-->zähltjedemsdietiAktum1hoch
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