Friday, February 10, 2012

8bit timer issue on Proteus for ATmega169

I have noticed a glitch on behaviour of VSM Proteus with Timer0 and Timer2 (only 8 bit timers) of devices such as Atmega169 Atmega169P Atmega169PA Atmega329 and so on.
When a CTC (clear timer on compare match) is enabled the corresponding IRQ is called two times: once at the compare match and once one timer cycle (tick) later.
For example using WinAVR GCC compiler, I set up the following instructions:
// Normal Mode, CTC, 111= 1/1024 (page 163 of DS)
TCCR2A = 0b00001111;       
OCR2A = 0x7F;   //1024*(0x7f+1)/8Mhz = 16.384ms
bset(TIMSK2, OCIE2A); //Enable IRQ on compare A


Then on the IRQ I simply toggle one pin:

ISR(TIMER2_COMP_vect){  //16.384ms
    if(toggle){
        clr(DEBUG_PIN);           
    }else{
        set(DEBUG_PIN);
    }
    toggle = 1-toggle;
}


This is what I SHOULD obtain (confirmed by AVRstudio 4 Simulator and, above all, the REAL Hardware): A square wave with 16.384ms half period.

And this is what I obtain in Proteus:

Interrupt occurs when TCNT2 == OCR2A (then it is automatically cleared) and when TCNT2 == 1, thus the waveform will be almost always high or low (and the Interrupt routine will be executed twice in the same time interval).

WORKAROUND
Since the Interrupt Request is erroneously triggered when TCNT2 goes 1 outside the Interrupt routine, it is simply necessary to wait for it to go 1 INSIDE the Interrupt routine itself.
Just end up your routine as follows:

ISR(TIMER2_COMP_vect){  //16.384ms
    if(toggle){
        clr(DEBUG_PIN);           
    }else{
        set(DEBUG_PIN);
    }
    toggle = 1-toggle;


    while(TCNT2 < 1);
}


If the instructions in the routine take some time, there is a good chance that the last instruction is tested but not executed (and not much extra time is wasted). If the routine is short (as in the example) there is one timer tick waste at the end of the routine itself, but at least it will work correctly on emulator AND on real hardware.

There is no need for such a workaround for timer overflow interrupts.

I hope that this will be useful for someone. It took me a few hours to investigate and solve...
(and of course this is a test routine: if I wanted just to toggle one pin I would use the OC2A output without using the IRQ...)

No comments: