PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Verständnisfrage bei Timer-(Interrupt)



zetgun
10.07.2010, 14:23
Hi,
ich habe erst neu mit Microcontroller / Robotik angefangen und arbeite mich nun langsam durch den Stoff und hier durchs Forum :).
Nun hatte ich ein Problem, konnte dieses zwar Lösen, verstehe aber nicht warum nicht beides funktioniert.

Funktion:
Es soll LED3 (PB3) blinken. Nach ca. einer Minute soll die LED0 (PB0) leuchten, nach einer weiteren Minute dann LED0 aus und LED1 (PB1) an usw.
Ergebnis Code1:
LED3 blinkt wie gewünscht, aber die andere LEDs blinken von Anfang an auch ganz wild und unvorhersehbar.
Ergebnis Code2:
Nach etwas probieren (Interrupts während den IF-Anweisungen ausschalten usw.) hab ichs dann wie in Code2 geändert und nun tut es.


Nun hoffe ich, dass mir jemand von euch sagen kann warum. Es müsste doch egal sein, ob ich die Sekunden in der Hauptschleife oder dem Interrupt hochzähl, hauptsache ich deklariere die Variable als volatile, damit der Compiler das richtig umsetzt.

Wenn es jemand Testen will, ich arbeite mit einem NiboBee.

(Die stellen, welche sich unterscheiden sind mit " // <<----- Unterschied" markiert)
Code1 (funktioniert nicht):

#include <avr/io.h> // allgemeine Headerdatei
#include <stdint.h> // für standardisierten Typen z.B. int8_t
#include <avr/interrupt.h> // fuer sei() => Aktivieren, cli() => ausschalten und ISR():

// Prototypen
void Timer0_init(void);

// Globale Variable
volatile uint16_t blink_hz = 0;
volatile uint8_t blink = 0;
// <<----- Unterschied
int main(void)
{
// IO's init
Timer0_init();
DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3); // LED's

// Variable
volatile uint8_t sek = 0; // <<----- Unterschied

// Globale Interrupts erlauben
sei();

while(1)
{
if(blink)
{
PORTB |= (1<<PB3);
sek++; // <<----- Unterschied
}
else PORTB &= ~(1<<PB3);
if (sek > 59 && sek < 119) PORTB |= (1<<PB0);
else PORTB &= ~(1<<PB0);
}
return 0;
}


/*************
Funktionen
**************/
// Timer 1ms
void Timer0_init(void)
{
TCCR0 = (1<<WGM01); // Timer im CTC-Modus
TCCR0 |= (1<<CS00) | (1<<CS01); // Prescaler auf 64
OCR0 = 234; // um ca. 1ms zu erreichen
TIMSK |= (1<<OCIE0); // Interrupt "TIMER0_COMP_vect" aktivieren
}

/*************
Interrupts
**************/

ISR (TIMER0_COMP_vect)
{
blink_hz++;

switch (blink_hz)
{
case 499: blink = 1; break; // <<----- Unterschied
case 999: blink = 0; blink_hz = 0; break;
default: break;
}

}


Code2 (funktioniert):


#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
// Prototypen
void Timer0_init(void);

// Globale Variable
volatile uint16_t blink_hz = 0;
volatile uint8_t blink = 0;
volatile uint8_t sek = 0; // <<----- Unterschied

int main(void)
{
// IO's init
Timer0_init();
DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3); // LED's

// Globale Interrupts erlauben
sei();

while(1)
{
if(blink)
{
PORTB |= (1<<PB3);
// <<----- Unterschied
}
else PORTB &= ~(1<<PB3);
if (sek > 59 && sek < 119) PORTB |= (1<<PB0);
else PORTB &= ~(1<<PB0);
}
return 0;
}


/*************
Funktionen
**************/
// Timer 1ms
void Timer0_init(void)
{
TCCR0 = (1<<WGM01); // Timer im CTC-Modus
TCCR0 |= (1<<CS00) | (1<<CS01); // Prescaler auf 64
OCR0 = 234; // um ca. 1ms zu erreichen
TIMSK |= (1<<OCIE0); // Interrupt "TIMER0_COMP_vect" aktivieren
}

/*************
Interrupts
**************/

ISR (TIMER0_COMP_vect)
{
blink_hz++;

switch (blink_hz)
{
case 499: blink = 1; sek++; break; // <<----- Unterschied
case 999: blink = 0; blink_hz = 0; break;
default: break;
}

}


greez

p_mork
10.07.2010, 15:37
In Deinem ersten Listing wird sek in der 1-Phase von blink ständig, d.h. bei jedem Durchlauf der while-Schleife inkrementiert. Da die Schleife mit einigen 100 kHz läuft, dauert es keine Millisekunde, bis sek die 120 überschritten hat, sobald blink auf 1 ist.

In Deinem zweiten Listing wird sek erst bei jedem 1000sten Timer0 Compare Interrupt inkrementiert, also alle 1000 ms, was ja auch richtig ist.

MfG Mark

PS: nur die Variablen, die sowohl im Hauptprogramm als auch in ISRs verwendet werden, müssen volatile sein. In allen anderen Fällen ist es nur Verschwendung von Laufzeit.

zetgun
10.07.2010, 16:05
ah...stimmt. Hatte das im ISR erst anders gelöst und es deshalb wahrscheinlich übersehen. Danke für die Hilfe.
Hab den Wald vor Bäumen nicht gesehen :)

Und danke für den Tipp mit volatile.
Gibt es eine Möglichkeit eine Variable auch erst im ISR zu deklarieren und diese dann hochzählen (hier dann "blink_hz") oder muss ich diese als globale Variable speichern?

greez

Bernhard667
10.07.2010, 20:39
Hallo,
Variablen die als static definiert werden überleben das Verlassen und Wiedereintreten in die Funktion.
Das wäre für blink_hz die saubere Lösung

Bernhard