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:
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
}
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:
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:
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
}
Am Oszi schaut das dann so aus:
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
Lesezeichen