cancel
Showing results for 
Search instead for 
Did you mean: 

TIM16 incorrect, random period

avidroneg
Associate II

This is for an STM32F303.

I've reduced my MX configuration down as much as possible so that it only has SWD and a button on GPIO PA8 configured and the incorrect behaviour still occurs. The intent is for PA8 EXTI falling edge to start a timer, and for that timer to in turn interrupt on expiry. I do get an interrupt (for both EXTI and the timer), but the duration of the timer is always wrong (sometimes instant, sometimes quite slow), and when on interrupt I check instance->CNT it is always a random value.

avidroneg_0-1759763131044.png

avidroneg_1-1759763166203.png

Following https://community.st.com/t5/stm32cubeide-mcus/hal-tim-periodelapsedcallback-firing-very-prematurely/m-p/588486 I am issuing

hbuttonDebounceTimer->Instance->SR &= ~TIM_SR_UIF;

during USER CODE 2 (after init, before the main loop), and it has no effect.

My ISRs look like

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim == buttonDebounceTimer) {
        HAL_TIM_Base_Stop_IT(htim);
    }
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    switch (GPIO_Pin) {
    case nBUTTON_Pin:
        HAL_TIM_Base_Start_IT(buttonDebounceTimer);
        return;
    }
}
1 ACCEPTED SOLUTION

Accepted Solutions

@TDK wrote:

what makes you think it's not successful?

Because __HAL_TIM_GET_AUTORELOAD returns an unexpected value that does not make sense for my configuration an unsigned 16-bit value that didn't perform sign extension to 32 bits as I was originally assuming it would, and the effective delay is not what I expect.

 


TIM16 is a 16-bit counter. You cannot set its period to anything above 65535. Timer counters are positive. You cannot set it to -10000.


I know that the timer counter is positive; but writing in a negative value casts to an unsigned integer that has the same effect as if it were signed for the purposes of overflow behaviour. I know that TIM16 is a 16-bit counter, but in TIM_TypeDef both ARR and CNT are 32-bit.

 


If you want it to fire after 10000 ticks, set ARR (period) to 10000, set the counter to 0, generate an update event to load the ARR value, clear the UIF flag, and start the timer.


Is that true? I would only expect that to work if it were a down-counter, but it's an up-counter. The docs claim that the update interrupt is generated not on ARR comparison, but on overflow:


> Interrupt/DMA generation on the following events:
>   – Update: counter overflow
>   – Input capture
>   – Output compare
>   – Break input

 

At this point I have a working solution, but I'm still not sure where __HAL_TIM_GET_AUTORELOAD is getting its value from.

I have most of my answer: the HAL uses a 32-bit unsigned value for ARR, and doesn't perform sign extension when converting from any 16-bit value written in the MX UI.

View solution in original post

10 REPLIES 10
gbm
Principal

1. You should disable the button EXTI interrupt in its ISR.

2. Timer UIF flag should be cleared this way: 

hbuttonDebounceTimer->Instance->SR = ~TIM_SR_UIF;

- don't use AND operation on SR.

3. It's quite a bad idea to use EXTI and a dedicated timer for button handling. You already have SysTick running / check the buitton state every, say, 20 ms in SysTick ISR.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

...What? If SR maps to TIM1_SR, then setting SR to ~TIM_SR_UIF means that you're going to be setting all of CC6IF through CC1UF, the 14 other active bits in that register.

TDK
Super User

When you start the timer, set its counter to 0 first.

 

Look in the reference manual for how to clear the UIF bit. You will find that @gbm is correct. The SR register is not a word in memory but a register interface to the peripheral which behaves as described in the RM.

If you feel a post has answered your question, please click "Accept as Solution".

Definitely NOT. Read The Fine Manual - SR register description. You should never use bitwise AND on TIM SR.

For a multichannel timer, using AND will lead to skipping/loosing the interrupt requests.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

@TDK wrote:

When you start the timer, set its counter to 0 first.


It's an up-counter, and so what I actually want to do is set it to -10000. When I do that it works well. That raises a (perhaps even stranger) question: why is the following not successful?

__HAL_TIM_SET_COUNTER(htim, __HAL_TIM_GET_AUTORELOAD(htim);

When I check the value of __HAL_TIM_GET_AUTORELOAD I see 65527, which is neither 0x1_0000  - 10000 nor 0x1_0000_0000 - 10000; so I don't know where it's coming from. My mistake; configuration de-sync issue.

ARR is a 32-bit field in TIM_TypeDef.

Otherwise,


Look in the reference manual for how to clear the UIF bit. 


I'm sorry, but the obvious search terms within RM0316 have not turned anything up. Can you give a hint as to where it's written?

TDK
Super User

> why is the following not successful?

> __HAL_TIM_SET_COUNTER(htim, __HAL_TIM_GET_AUTORELOAD(htim);

For one, parentheses are not balanced so it can't even compile.

Barring that, what makes you think it's not successful?

 

TIM16 is a 16-bit counter. You cannot set its period to anything above 65535.

 

Timer counters are positive. You cannot set it to -10000.

If you want it to fire after 10000 ticks, set ARR (period) to 10000, set the counter to 0, generate an update event to load the ARR value, clear the UIF flag, and start the timer.

Or, if you leave the default period at 65535, you can set the counter to 65535-10000 and start it.

If you feel a post has answered your question, please click "Accept as Solution".

@TDK wrote:

what makes you think it's not successful?

Because __HAL_TIM_GET_AUTORELOAD returns an unexpected value that does not make sense for my configuration an unsigned 16-bit value that didn't perform sign extension to 32 bits as I was originally assuming it would, and the effective delay is not what I expect.

 


TIM16 is a 16-bit counter. You cannot set its period to anything above 65535. Timer counters are positive. You cannot set it to -10000.


I know that the timer counter is positive; but writing in a negative value casts to an unsigned integer that has the same effect as if it were signed for the purposes of overflow behaviour. I know that TIM16 is a 16-bit counter, but in TIM_TypeDef both ARR and CNT are 32-bit.

 


If you want it to fire after 10000 ticks, set ARR (period) to 10000, set the counter to 0, generate an update event to load the ARR value, clear the UIF flag, and start the timer.


Is that true? I would only expect that to work if it were a down-counter, but it's an up-counter. The docs claim that the update interrupt is generated not on ARR comparison, but on overflow:


> Interrupt/DMA generation on the following events:
>   – Update: counter overflow
>   – Input capture
>   – Output compare
>   – Break input

 

At this point I have a working solution, but I'm still not sure where __HAL_TIM_GET_AUTORELOAD is getting its value from.

I have most of my answer: the HAL uses a 32-bit unsigned value for ARR, and doesn't perform sign extension when converting from any 16-bit value written in the MX UI.

You wrote "0x10000 - 10000" in the MX UI. This is an unsigned value equal to 55536, so that's what it uses. You're not writing a negative value.

If you feel a post has answered your question, please click "Accept as Solution".

@TDK wrote:

You wrote "0x10000 - 10000" in the MX UI. This is an unsigned value equal to 55536, so that's what it uses. You're not writing a negative value.


Not that it matters much, but for my purposes you're mostly wrong. Two's complement doesn't really care about signedness at the hardware level, and it's perfectly valid to interpret a number close to the field capacity to be negative. Indeed, outside of MX, I'm literally loading -10000 and it works as expected.

The only other thing I'll add to this thread for posterity is about safe UIF clearing. The best explainer I found for this is in STM32 gotchas.