cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F103 TIM2 counting properly but won't interrupt

RPasc.2
Associate III

For a simple LED Blink program using interrupts, TIM2 is counting correctly, but its IRQs are not happening. As expected, there are about 1440 TIM2 "count-outs" per 1.0 seconds.

Over the past few years several CubeMX timer IRQ bugs have surfaced, but I'd expect they would have been fixed by now. Are there any left to be resolved ?

Most probably I have missed some MX configuration, but I have no idea where that would be. Any ideas and pertinent experience will be appreciated.

Here are the two MX TIM2 configuration screen shots:

(I hope you see the full-size images and not the tumbnails I see.) This message editor's file-attachment button is grayed-out!)

0693W000006HKb5QAG.png 

0693W000006HKbKQAW.pngHere's the entire program code:

/*  2020-12-30 10:50:07 PM
    main.c    C:\Users\HTPC\STM32CubeIDE\workspace_1.5.0\BluePill_Timer_NonBlocking\Core\Src
    "STM32 Timers and Timer Interrupts"
    https://www.digikey.be/en/maker/projects/getting-started-with-stm32-timers-and-timer-interrupts/d08e6493cefa486fb1e79c43c0b08cc6
    Example 3: Interrupt-driven LED timer to blink the BluePill User LED.
*/
...
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart2;
 
/* CubeMX-generated System Function Prototypes
   System Function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM2_Init(void);
 
int main(void)
{
    /* USER CODE BEGIN 1 */
    // {null}
 
    /* MCU Configuration--------------------------------------------------------
       Reset of all peripherals, init the Flash ixface and the System clock rate. */
    HAL_Init();
 
    /* Configure the system clock */
    SystemClock_Config();
 
    /* USER CODE BEGIN SysInit */
    // {null}
 
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    // Configure LED GPIO pin Output Level = Off/'1'; LED is a current source.
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);  // Init: LED=Off
    //
    MX_USART2_UART_Init();
    MX_TIM2_Init();
 
    // For serial TX. RX unused but configured for use.
    char uart_buf[50];
    int uart_buf_len;
    uint16_t timer_start_cnt, timer_delta_cnt;
 
    // "Start-of-Program" header for terminal display..
    uart_buf_len = sprintf(uart_buf, "\n\rProgram Start:\n\r");
    HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 100);
 
    /* USER CODE BEGIN 2
       Start the timer. */
    HAL_TIM_Base_Start_IT(&htim2);
 
    while (1)   // MAIN LOOP
    {
        /* USER CODE BEGIN 3 */
        timer_start_cnt = __HAL_TIM_GET_COUNTER(&htim2);
        HAL_Delay(1000);
 
        // Get end count and calc the elapsed.
        timer_delta_cnt = __HAL_TIM_GET_COUNTER(&htim2) - timer_start_cnt;
 
        // Send the elapsed count for the display.
        uart_buf_len = sprintf(uart_buf, "%u ticks\n\r", timer_delta_cnt);
        HAL_UART_Transmit(&huart2, (uint8_t *)uart_buf, uart_buf_len, 100);
    }
}  // end Main
 
/* System Clock Configuration */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
    /* Initializes the RCC Oscillators according to the s
       pecified parameters in the RCC_OscInitTypeDef structure.
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }
 
    /* Initializes the CPU, AHB and APB buses clocks. */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler();
    }
}
 
/* TIM2 Init Function */
static void MX_TIM2_Init(void)
{
    /* USER CODE BEGIN TIM2_Init 0 */
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
 
    /* USER CODE BEGIN TIM2_Init 1
    BluePill APB2 input system clock to timer = 72 MHz
    Let TIM2_Prescaler = 50,000 --> F_TIMER2 = 1,440 Hz (F_LED = 0.5 Hz)
 
    Use a static variable to count timer overflow events.
    Set the counter period to 770 for a 0.500 sec interrupt rate to toggle the LED.
    */
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 50000;   // F_TIM2CLK --> 1440 Hz toggle rate
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 32768;      // 50% of 2**16
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
    {
        Error_Handler();
    }
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    {
        Error_Handler();
    }
 
    /* USER CODE BEGIN TIM2_Init 2 */
    // {null}
}
...
 
/* USER CODE BEGIN 4 */
// Callback: timer has counted out.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    static int timer_ITR_ctr = 0;
 
    // Check if timer MX_TIM2 triggered this IRQ handler callback.
    if (htim == &htim2)
    {
        timer_ITR_ctr = timer_ITR_ctr + 1;  // Intentional simple increment.
        if (timer_ITR_ctr == 720)           // 0.5 secs elapsed: Toggle the LED output pin state.
        {
            HAL_GPIO_TogglePin(GPIOA, LED_Pin) ;
            //HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);   // Turn LED On.
 
            // 0.500 seconds has passed.
            timer_ITR_ctr = 0;  //Prepare to count another 720 count-outs.
        }
    }
}
...

16 REPLIES 16
TDK
Guru

> BluePill APB2 input system clock to timer = 72 MHz

> htim2.Init.Prescaler = 50000; // F_TIM2CLK --> 1440 Hz toggle rate

> htim2.Init.Period = 32768; // 50% of 2**16

Based on this (did not verify the timer clock), the timer overflows and generates an update at a rate of 72MHz / 50001 / 32769 = 0.043Hz, or every ~22.7 seconds. Code looks fine otherwise, you just need to redo your math.

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

I mis-documented: "htim2.Init.Prescaler = 50000; // F_TIM2CLK --> 1440 Hz toggle rate"

should have read: "htim2.Init.Prescaler = 50000; // F_TIM2CLK --> 1440 Hz TIM2 clock rate

If that were true the LED would toggle (turnon/turnoff) during the ISR callback routine. This isn't happening after 0.5 or 22 seconds. There must be something else wrong! I have verified I can toggle the LED with HAL IO Set/Reset calls.

Where can I find an explanation for setting the value for htim2.Init.Period ?

Is this how many timer clocks/counts there will be bewteen 2 autoreload events ?

No one ever mentions the concept of "duty cycle", yet that is what this is all about!

How is a 50% duty cycle set for a 1.0 second repieition ?

TDK
Guru

> Where can I find an explanation for setting the value for htim2.Init.Period ?

HAL is open source. You can go inspect the code to see what it does. F3 will jump to the definition of a function in CubeIDE.

htim2.Init.Period goes into the ARR register. There are (ARR + 1) timer ticks between update events in upcounting mode.

Don't really know where the issue is then. You use LED_GPIO_Port during initialization and GPIOA when toggling. There's no LED on any GPIOA pin on the bluepill board schematics I can find. Maybe this is the problem.

> HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // Init: LED=Off

> HAL_GPIO_TogglePin(GPIOA, LED_Pin) ;

Could be a HAL bug, but my money is on something else.

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

Isn't the LED on PC13 ?

At 1440 Hz (Prescaler = 50000-1), Period of 720-1 would be 2 Hz Update, and then you'd probably want to just toggle the LED, not faff about with a counter to make it even slower.

The TIM Update is 72 MHZ / (PSC+1) / (ARR+1)

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
RPasc.2
Associate III

The LED output pin really is C13, not A13 on the BluePill board (STM32F103C8T6). I've already made the LED turn on and off, so that's not it. Timer interrupts are simply not being generated using STM32CubeMX-generated code, else the callback function would have been called. I'm absolutely sure it's never called.

I can't find a full datasheet on the STM32F103C8T6, just short 6-page briefs. Where can I find the full timers write-ups ?

RPasc.2
Associate III

BTW, I've seen posts over the past few concerning HAL code bugs that failed to generate interrupts.

I may have to find a lower level code approach to configuring the timer for interrupting.

Michal Dudka
Senior III

Fastest way to get what is wrong is to use debugger and inspect TIMer registers. Take a look into CNT register if its value increasing (Timer counting...). Then look in DIER register if interrupt is enabled (UIE bit). Then look into SR (Status REgister) on UIF bit (update interrupt flag). Possibly you can observe also UG bit in EGR register. You can also go to .._it.c file and set breakpoint into Timer ISR.

There are two "datasheets" ... document called datasheet covers pinout, electrical parameters etc. Document called "Reference manual" covers peripherals etc.

RPasc.2
Associate III

The cause is really simple: the Cube-generated code has no call to HAL_TIM_IRQHandler !

I'll try switching micros to see where it's supposed to be.

TDK
Guru

The call would be in stm32f1xx_it.c.

There is a checkbox in CubeMX to enable this, in NVIC settings. And another checkbox to call HAL_TIM_IRQHandler within it.

0693W000006I7dtQAC.png 

Same setting is under the timer NVIC configuration as well.

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