Question

I have an atmega168a chip. I use Counter 0 to toggle PORTC by using ISR(TIMER0_COMPA_vect) and ISR(TIMERB_COMPA_vect) interrupt sub-routines. I would like to activate the 16-bit timer when if condition is true. So, I use TIMSK1 = (1<<OCIE1A), but this line calls ISR(TIMER1_COMPA_vect) interrupt instantly where I want 16 bit timer to be interrupted only when the counter reaches to OCR1A value. How can I activate the 16-bit timer on the run-time without causing an instant interrupt?

here is my code:

#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

volatile uint8_t counter;

int main (void){
    DDRC = 0xFF;  //sets PORTC as output
    PORTC = 0xFF; //initial output value
    /*COUNTER0 settings*/
    TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B));  // Enable Interrupt TimerCounter0 Compare Match A & B
    TCCR0A = (1<<WGM01);  // Mode = CTC
    TCCR0B = (1<<CS01) | (1<<CS00);   // Clock/64, 1/(8000000/64)= 0.000008 seconds per tick
    OCR0A = 200;      //   0.000008 *230 = 1.6 ms
    OCR0B = 100;      //     0.8 ms

    /*16bit timer - counter1 settings*/
    TIMSK1 &= ~(1<<OCIE1A); // Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
    TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12));    // Clock/1024, 1/(8000000/1024) = 0.000128 seconds per tick, Mode=CTC
    OCR1A = 40;                       // 0.000128*40 ~= 5.12 milliseconds

    sei(); //interrupts are globally switched on
    counter =0;
    while(1){
        if(counter >= 4){
            TCNT1 = 0; // clear the counter 1
            TIMSK1 = (1<<OCIE1A);// Enables the interrupt for Counter 1,(TIMER1_CMPA_vect)
            TIMSK0 &= ~((1<<OCIE0A) | (1<<OCIE0B)); //disables the Counter 0's interrupts
            counter = 0;
        }
    }
    return 0;
}

ISR(TIMER0_COMPA_vect){ //1.6ms
    PORTC = 0xFF;
    counter++;
}

ISR(TIMER0_COMPB_vect){  //0.8 ms
    PORTC = ~PORTC;
}

ISR(TIMER1_COMPA_vect){ // 5.2 milisecond interrupt
    PORTC = 0x00;
    TCNT0 = 0; //clear the counter of counter0
//  TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B)); //Enable the Counter 0 interrupts
//  TIMSK1 &= ~(1<<OCIE1A);// Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
}

here is an oscilloscope output that shows why I don't want the interrupt to be set instantly, because it set the signal 0 instantly. enter image description here

Était-ce utile?

La solution

I think the problem might be that in CTC mode, interrupt is generated when OCF1A flag is set (in TIFR). Since your timer is always running, just not generating interrupts, it sets OCF1A flag, which never gets cleared. On page 142 in the datasheet it says:

OCF1B is automatically cleared when the Output Compare Match B Interrupt Vector is executed. Alternatively, OCF1B can be cleared by writing a logic one to its bit location.

This means that when you set up timer 1, you also need to clear OCF1A:

TIFR1 &= ~(1<<OCF1A)

However, I think you can do better. You could just stop the timer when not needed, and start it when you do, instead of twiddling the TIMSK and having timer 1 run always. If you set TCCR1B to zero, that clears CS12, CS11, and CS10, which, according to the datasheet means "Timer stopped." Then, when your counter reaches 4 you can turn on timer1 as you have it above:

TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12));

If you do this, you shouldn't need to turn timer 1 interrupts on and off: just leave them on, and only turn the counting on when you need it.

Also I am wondering if it is actually necessary to fire off two interrupts to toggle pins on PORTC? Are you not using PWM for that because it doesn't give you the pulse lengths precisely enough?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top