Timer/Counter register updated while interrupts disabled ?

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When using timers, e.g. the 16-bit Atmega328p Timer1, is the counter/timer register (TCNT1) updated while interrupts are disabled ?

I assume it's not updated in this case but I want to be sure and I can't find anything about this in the datasheet.

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
When using timers, e.g. the 16-bit Atmega328p Timer1, is the counter/timer register (TCNT1) updated while interrupts are disabled ?
Yes it is updated. It cares not that the interrupts are disabled.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Mon. Sep 17, 2012 - 11:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I assume it's not updated in this case but I want to be sure and I can't find anything about this in the datasheet.

Why would you assume that? Why would timer/counters act any differently whether interrupts are enabled or disabled? (Well, other than the obvious actual trigger and entry of ISRs.)

You can use (nearly?) all timer features, including interrupt flags, whether global interrupts are enabled or not.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When the prescaler (TCCRx) register is set the timer starts to count from zero to top,there is no need to set interrupts,or any TCNTx value for the timer.
The timer overflow can catched by polling TOVx bit in TIFR register and then clear it by writing one on it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought the timer/counter register available to us was a copy of the actual counter ? It would make sense if this copy was not updated while interrupts were disabled.

So, the case is that the value available to us is updated while interrupts are disabled. This makes it very hard to keep track of time with high resolution, at least the way I am doing it.

I'll illustrate with some code, that based on what you guys say will not be reliable.

static volatile uint8_t tick = 0;

ISR(TIMER1_OVF_vect)
{
    if (tick == 49)
    {
        tick = 0;
    }
    else
    {
        ++tick;
    }
}

unsigned getMs()
{
    cli();
    unsigned temp = TCNT1 / 1000 + tick * 20;
    sei();
    return temp;
}

The timer is set up to clear on TCNT1 == 20000, one tick per us.

The use of cli()/sei() is obviously pointless, I left them in to show what I thought I could do.

So how would you go about this ?

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ChaunceyGardiner wrote:
So how would you go about this ?

cli
temp = TCNT
if (Overflow_Interrupt_Flag_Is_Set && temp < TimerMax/2)
    temp = temp / 1000 + (ticks+1) * 20
else
    temp = temp / 1000 + ticks * 20
sei

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would rearrange that to this:

cli
temp = TCNT;
tempTick = ticks;
sei;
if (Overflow_Interrupt_Flag_Is_Set && temp < TimerMax/2)
    tempTick++;
temp = temp / 1000 + tempTick * 20;

It simplifies the code so that the calculation is done only in one place. It also puts the calculation after the sei so that it has less of a chance of missing an interrupt.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Tue. Sep 18, 2012 - 12:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, guys. I'll probably do it like that.

Meanwhile I came up with another idea:

unsigned getMs()
{
    cli();
    uint8_t tcntl = TCNT1L,
            t     = tick,
            tcnth = TCNT1H;
    sei();
    return ((tcnth << 8) + tcntl) / 1000 + t * 20;
}

Would that work ?

(I am still more likely to do it the way you suggested, I just want to know.)

Sid

Life... is a state of mind

Last Edited: Tue. Sep 18, 2012 - 12:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ChaunceyGardiner wrote:
Would that work ?
No. What if a timer overflow happens between these two lines?

    cli();
    uint8_t tcntl = TCNT1L, 

BTW: In Steve's code the sei must be moved behind the if.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Important point of information: there are no "register copies". You access the real thing in real time.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet indicates that there is some buffering going on. It just doesn't apply in this case.

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are a few cases with buffering - such as UART.

There is also buffering when you read a 16-bit register. Only one byte can be read at a time by code. So, the CPU copies both, simultaneously, to a temp register. If you read in the right order, everything is fine. avr-libc handles this automatically so you almost don't have to worry. There IS the remote but finite probability that an interrupt can hit between the two byte reads of the temp register, so if it CAN be messed up in an ISR (example: also reads the same 16-bit register), then you do need to manage interrupt enables.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet says that only the high byte is put in a temporary register (when you or the MCU read the low byte), and that this temporary register is shared between all the 16-bit timer registers.

It also states that some registers are double-buffered, e.g. OCR1A/OCR1B.

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chauncey, I gather you've set the timer to CTC mode in order to get the reload at 20,000. If this is the case, should the isr be compareA rather than overflow?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
I gather you've set the timer to CTC mode in order to get the reload at 20,000. If this is the case, should the isr be compareA rather than overflow?

I'm using mode 14 (fast PWM) as I am using the timer for PWM output as well as keeping track of time.

I think the compare is used to set the signal low in this mode.

What is the difference between compare and overflow in the CTC mode ?

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I'm using mode 14 (fast PWM) as I am using the timer for PWM output as well as keeping track of time.

I think the compare is used to set the signal low in this mode.


Mode14 is like PWM with CTC. The ICR1 registers sets TOP so it counts 0 to TOP and then generates the OVF interrupt. Meanwhile OCR1A and OCR1B are matched at some point to change state of the PWM outputs. If required they could each generate compare interrupts when/(if) that match occurs.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The ICR1 registers sets TOP so it counts 0 to TOP and then generates the OVF interrupt
Isn't it ICFx that's set? (and both ICFx and OVFx if ICRx == 0xFFFF?)

mega1284p datasheet wrote:
• Bit 5 – ICF1: Timer/Counter1, Input Capture Flag
This flag is set when a capture event occurs on the ICP1 pin. When the Input Capture Register (ICR1) is set by the WGMn3:0 to be used as the TOP value, the ICF1 Flag is set when the counter reaches the TOP value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

snigelen,

My reading is from the WGM table that says for Mode14 "TOP=ICR1" and "TOV1 Flag set on TOP". Also the official definition of TOP is given as:

Quote:
The counter reaches the TOP when it becomes equal to the highest value in the count sequence. The TOP value can be assigned to be one of the fixed values: 0x00FF, 0x01FF, or 0x03FF, or to the value stored in the OCR1A or ICR1 Register. The assignment is dependent of the mode of operation

(the ICR1 clause applies in this case).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're correct. And me too (except my parenthesis above). Both ISR's are called (OVF 4.7 us before CAPT, F_CPU=8e6).

#include 
#include 

ISR(TIMER1_OVF_vect)
{
    PINB |= 1; // Toggle PB0
}

ISR(TIMER1_CAPT_vect)
{
    PINB |= 2; // Toggle PB1
}

int main(void)
{
    // Leds on PB0,1, active low
    DDRB = 3; PORTB = 3;

    // Timer1 mode 14, TOP=ICR1. No OCR active, presc 1:64
    ICR1 = 40000;
    TCCR1A = (1<<WGM11);
    TCCR1B = (1<<WGM13)|(1<<WGM12) | (1<<CS11)|(1<<CS10);
    // OVF and ICR interrupt
    TIMSK1 = (1<<ICIE1) | (1<<TOIE1);

    sei();

    while(1) {
    }
}