Hi Stefan,
vielen Dank für Deinen Hinweis! Ich habe nun zwei Varianten getestet, um das beschriebene Problem zu verhindern.
1. Möglichkeit: Ich lösche das Timer0 Overflow Flag manuell, indem ich eine 1 ins TOV0 schreibe. Das funzt wunderbar! Hier der Code zur Dokumentation:
Mit diesem Code läuft der Timer0 durchgehend. Ich habe mal das Delay zwischen Eintreffen des externen Interrupts und dem Hochziehen von PA1 mitm Oszi gemessen. Das Delay beträgt ca. 2µs. Siehe Bild:Code:/ #define F_CPU 16000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <stdio.h> #include <stdlib.h> void InitTimer0() { //TCCR0 |= (1<<WGM01) | (1<<WGM00); //Fast PWM TCCR0 |= (1<<CS01) | (1<<CS00) | (1<<COM00) ; //Setup timer0 in normal mode with a prescaler of 64; enable OVF interrupt in main loop to start interrupt generation DDRB |= (1<<PB3); //set OC0 as output //TIMSK |= (1<<TOIE0); //enable timer0 OVF interrupt; } void StopTimer0() { TCCR0 &= ~(1<<COM00); //Set pin mode to normal operation; if this is not done, the pin will stay at the last logical level when stoppting timer TCCR0 &= ~(1<<CS02); //Stops timer according to datasheet TIMSK &= ~(1<<TOIE0); //disable timer0 OVF interrupt; } int main (void) { MCUCR |= (1<<ISC00); //interrupt on any logical change on Int0 "PD2" GICR |= (1<<INT0); //enable INT0 interrupt DDRA |= (1<<PA1); //Pin PA1 as output InitTimer0(); //start timer 0 sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed while(1) { //nothing here except interrupts } } ISR(INT0_vect) //ISR when logical change on INT0 "PD2" { GICR &= ~(1<<INT0); //disable this IRF; enable again when the timer0 OVF ISR fires TIFR |= (1<<TOV0); //clear Timer 0 Overflow flag by writing one to the register! TIMSK |= (1<<TOIE0); //enable timer0 OVF interrupt; will fire about 1ms after this ISR has completed TCNT0 = 250; //Write x to timer 0 to restart counting; any 8 bits work to adjust delay PORTA |= (1<<PA1); //Set PA1 high when INT0 fires //SFIOR |= (1<<PSR10); //Reset prescaler } ISR(TIMER0_OVF_vect) { TIMSK &= ~(1<<TOIE0); //disable timer0 OVF interrupt -> enable again in next INTO ISR //StopTimer0(); //Stop timer0 GICR |= (1<<INT0); //turn on INT0 IRF again PORTA &= ~(1<<PA1); //Set PA1 low again when Timer0 OVF ISR fires -> High pulse after logic level change on INT0 pin }
![]()
Falls man den Timer0 für andere Sachen benötigt, während der externe Interrupt abgearbeitet ist, kann man Folgendes machen...
2. Möglichkeit: Ich starte und stoppe den Timer, indem ich den Prescaler an- bzw. ausschalte (siehe Datenblatt). Um das Timer Overflow Flag und das entsprechende Enable Bit braucht man sich nicht mehr zu kümmern. Hier der Code zur Dokumentation:
Am Oszi schaut das dann so aus:Code:#define F_CPU 16000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <stdio.h> #include <stdlib.h> /* void InitTimer0() { //TCCR0 |= (1<<WGM01) | (1<<WGM00); //Fast PWM TCCR0 |= (1<<CS01) | (1<<CS00) | (1<<COM00) ; //Setup timer0 in normal mode with a prescaler of 64; enable OVF interrupt in main loop to start interrupt generation DDRB |= (1<<PB3); //set OC0 as output //TIMSK |= (1<<TOIE0); //enable timer0 OVF interrupt; } void StopTimer0() { TCCR0 &= ~(1<<COM00); //Set pin mode to normal operation; if this is not done, the pin will stay at the last logical level when stopping timer TCCR0 &= ~(1<<CS01) | ~(1<<CS00); //Stops timer according to data sheet TIMSK &= ~(1<<TOIE0); //disable timer0 OVF interrupt; } */ int main (void) { MCUCR |= (1<<ISC00); //interrupt on any logical change on Int0 "PD2" GICR |= (1<<INT0); //enable INT0 interrupt DDRA |= (1<<PA1); //Pin PA1 as output /*This will init timer0 without starting it*/ DDRB |= (1<<PB3); //set OC0 as output TCCR0 |= (1<<COM00); //Setup timer0 in normal mode TIMSK |= (1<<TOIE0); //enable timer0 OVF interrupt; sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed while(1) { //nothing here except interrupts } } ISR(INT0_vect) //ISR when logical change on INT0 "PD2" { GICR &= ~(1<<INT0); //disable this IRF; enable again when the timer0 OVF ISR fires TCCR0 |= (1<<CS01) | (1<<CS00); //start Timer0 by setting prescaler (here 64)! TCNT0 = 250; //Write x to timer 0 to restart counting; any 8 bits work to adjust delay PORTA |= (1<<PA1); //Set PA1 high when INT0 fires } ISR(TIMER0_OVF_vect) { TCCR0 &= ~(1<<CS01) | ~(1<<CS00); //Stops timer according to data sheet (unset prescaler) GICR |= (1<<INT0); //turn on INT0 IRF again PORTA &= ~(1<<PA1); //Set PA1 low again when Timer0 OVF ISR fires -> High pulse after logic level change on INT0 pin }
![]()
Es ist zu erkennen, dass der Interrupt schneller verarbeitet wird als bei Möglichkeit 1 (ca. 200ns schneller). Das ist nicht viel, aber man kann den Timer nun in den Pausen für was anderes hernehmen. Ich werd's also so machen.
Vielleicht helfen diese Codeschnippsel ja jemand. Danke nochmals für die Hilfe!
Schönen Abend noch,
Christian







Zitieren


Lesezeichen