2018-05-01 06:43 PM
The TIM2 timer on an STM32F0 seems to not be resetting to 0 once the counter reaches the ARR value on occasion. I am periodically changing the ARR value, but the values are all in a small range. After a random amount of cycles, the counter will increment beyond that range without triggering an update event, resetting the counter back to 0. I have tried this with and without TIM_CR1_URS set. TIM_CR1_UDIS bit is in the reset state. Anyone else run into this problem?
Solved! Go to Solution.
2018-05-03 12:54 AM
Update occurs when CNT matches ARR (i.e. when they equal, CNT == ARR).
If, at a moment, CNT was below ARR (e.g. CNT = 10, ARR = 15), and at that moment you change ARR to a value below CNT (e.g. CNT = 11, ARR = 5), then the next match occurs only after CNT counts up to its limit, wraps around and reaches 5 again - which, given TIM2 is a 32-bit counter, will seem to be 'forever'.
The solution here is to use the ARR-preload, i.e. to set TIMx_CR1.ARPE. This splits the ARR into two physical registers, one which you access from the processor and other which is used for the comparison; and the newly written value to the former is copied into the latter only at the moment of update, (i.e. it becomes effective one cycle later). There are two diagrams in the timer chapter of RM, illustrating this, search for ' Update event when ARPE=0 (TIMx_ARR not preloaded)' and 'Update event when ARPE=1 (TIMx_ARR preloaded)'
JW
2018-05-02 05:15 PM
To add more information, I am only using Keil with CMSIS, no APIs. My program basically flows like what I have below. The timer generates an update event, assumingly loads the preload values, resets the TIM_CNT value, and calls the ISR for dozens of cycles. Then, randomly, I can see the TIM2 CNT register in the peripheral system viewer increment beyond the input value range that the ARR is set to, and never generates an update event to reset the CNT back to 0.
while(1){
if(newInput){
if(5 <= inputValue <= 2500){TIM2->ARR = inputValue;}
}
timer2ISR(void){count++;}
2018-05-03 12:54 AM
Update occurs when CNT matches ARR (i.e. when they equal, CNT == ARR).
If, at a moment, CNT was below ARR (e.g. CNT = 10, ARR = 15), and at that moment you change ARR to a value below CNT (e.g. CNT = 11, ARR = 5), then the next match occurs only after CNT counts up to its limit, wraps around and reaches 5 again - which, given TIM2 is a 32-bit counter, will seem to be 'forever'.
The solution here is to use the ARR-preload, i.e. to set TIMx_CR1.ARPE. This splits the ARR into two physical registers, one which you access from the processor and other which is used for the comparison; and the newly written value to the former is copied into the latter only at the moment of update, (i.e. it becomes effective one cycle later). There are two diagrams in the timer chapter of RM, illustrating this, search for ' Update event when ARPE=0 (TIMx_ARR not preloaded)' and 'Update event when ARPE=1 (TIMx_ARR preloaded)'
JW
2018-05-03 09:24 PM
I did previously have
TIM2->CR1 |= TIM_CR1_ARPE;
, and
I mention the values being preloaded, but there was another error in my code that caused that line to be skipped. I checked the peripheral system viewer in keil to make sure it was being set, after you described the behavior of the registers not being preloaded, and sure enough it was not being set. Thank you for breaking it down for me like that.