cancel
Showing results for 
Search instead for 
Did you mean: 

What's the behaviour of the timer if I set `TIMx_ARR` register to be less than `TIMx_CNT`?

chenlijun1999
Associate II

I know that it's straightforward to just try, but currently I don't have the board at hand...

This doubt has arisen while I was unit testing some corner cases of my timer functions on the development machine (with mocks and so on).

For the common case, the reference manual is quite clear

0690X00000DXObEQAX.png

But, let's say that Counter register has value 30, what happens if I set TIMxARR to 30?

I would expect that a fraction of tick later, the update event is triggered.

And what happens if I set TIMxARR to 29?

I've skimmed through reference manual, datasheet and timer cookbook (application note), but I didn't find anything in this regard.

I have kinda the feeling that to someone with some experience in implementing hardware timers with FPGA/ASIC, this should be quite obvious. But I'm not sure.

Is there any "hidden knowledge" I'm not aware of, for which the behaviour in this case is so obvious that doesn't merit to be documented?

1 ACCEPTED SOLUTION

Accepted Solutions

> If the behaviour is not documented

It's not not documented. If (in upcounter) CNT matches ARR, in the next count (i.e. with the clock which rolls over the prescaler) CNT is zeroed and that generates the update.

> But, let's say that Counter register has value 30, what happens if I set TIMxARR to 30?

> I would expect that a fraction of tick later, the update event is triggered.

Not a fraction of tick, but as I wrote above, when the input clock rolls over the prescaler. In this experiment, TIM2 is clocked in external clock mode from TIM3 which rolls over once in 10 seconds. I've set ARR to match CNT and then waited (erased some of the printout for clarity):

(gdb)
$1433 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134ba,
...
}
(gdb) set TIM2->ARR=0x1134be
(gdb) p /x *TIM2
$1434 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134be,
...
}
 
[...]
 
(gdb)
$1444 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134be,
...
}
(gdb)
$1445 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x5f,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x0,
  PSC = 0x0,
  ARR = 0x1134be,
}

Note the UIF flag change in SR.

> And what happens if I set TIMxARR to 29?

CNT won't match ARR until it runs up to its maximum, wraps over(*) and "meets" ARR again.

This is the very reason why the timers have ARR preloading.

JW

(*) Technically, this is what's not documented. The CNT might stop at its maximum, or the chip might explode and the world as we know it, end. But it's very unlikely any of this would happen. What's perhaps more dubious, is the behaviour of update at such overflow - experiment shows, there's no update there; but you can doubt that will always be the case, quite reasonably.

View solution in original post

4 REPLIES 4
berendi
Principal

If the behaviour is not documented, you should treat it as undocumented, i.e. it could work some way on one MCU, and another way on the next batch that goes into production tomorrow, and a third way on every Sunday.

> If the behaviour is not documented

It's not not documented. If (in upcounter) CNT matches ARR, in the next count (i.e. with the clock which rolls over the prescaler) CNT is zeroed and that generates the update.

> But, let's say that Counter register has value 30, what happens if I set TIMxARR to 30?

> I would expect that a fraction of tick later, the update event is triggered.

Not a fraction of tick, but as I wrote above, when the input clock rolls over the prescaler. In this experiment, TIM2 is clocked in external clock mode from TIM3 which rolls over once in 10 seconds. I've set ARR to match CNT and then waited (erased some of the printout for clarity):

(gdb)
$1433 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134ba,
...
}
(gdb) set TIM2->ARR=0x1134be
(gdb) p /x *TIM2
$1434 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134be,
...
}
 
[...]
 
(gdb)
$1444 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x40,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x1134be,
  PSC = 0x0,
  ARR = 0x1134be,
...
}
(gdb)
$1445 = {
  CR1 = 0x1,
  CR2 = 0x0,
  SMCR = 0x27,
  DIER = 0x0,
  SR = 0x5f,
  EGR = 0x0,
  CCMR1 = 0x0,
  CCMR2 = 0x0,
  CCER = 0x0,
  CNT = 0x0,
  PSC = 0x0,
  ARR = 0x1134be,
}

Note the UIF flag change in SR.

> And what happens if I set TIMxARR to 29?

CNT won't match ARR until it runs up to its maximum, wraps over(*) and "meets" ARR again.

This is the very reason why the timers have ARR preloading.

JW

(*) Technically, this is what's not documented. The CNT might stop at its maximum, or the chip might explode and the world as we know it, end. But it's very unlikely any of this would happen. What's perhaps more dubious, is the behaviour of update at such overflow - experiment shows, there's no update there; but you can doubt that will always be the case, quite reasonably.

Thank you for your thorough explanation and demonstration!

> CNT won't match ARR until it runs up to its maximum, wraps over(*) and "meets" ARR again.

> This is the very reason why the timers have ARR preloading.

Yeah. I've asked by boss (which knows very well FPGA/ASIC design) and the behaviour that he'd expect coincides with the one you described. I guess it's something relatively "standard" when designing timers.

Everything could have been resolved by using a downcounter and modifying TIMx_CNT instead. Sadly, this time all the timers that supported dow-counting were already occupied.

The only safe work-around I've been able to devise for this edge case is to decrement TIMx_CNT and then set TIMx_ARR as needed, all in a critical section. The assumption is that the time the core takes to execute these two assignment is far less than the time after which the TIMx_CNT is incremented again. Fortunately, in my case this assumption is valid.

Of course decrementing CNT, or setting it straight to 0 would work. Beware of unintended pulses if it drives a PWM output, trigger, DMA etc, and the compare value/range is matched again.

There are other safe ways around this.

  • Set TIM_CR1_ARPE beforehand, it is actually invented for safely changing ARR at the end of the cycle. Optionally, you can then set TIM_EGR_UG to force loading ARR, and resetting CNT, but it requires some thought to avoid a double update event.

  • Clear TIM_CR1_CEN to stop the counter, make all the changes you want, ensure that CNT < ARR afterwards, and reenable CEN.

  • Use the timer in downcounting mode (TIM_CR1_DIR=1), this way you can be sure that the update event happens when CNT reaches 0.