2
\$\begingroup\$

I'm new to the world of embedded and I'm implementing a short, long and two-click on a button logic in my project on a stm32f051

I'm having a problem with the timer interrupt, I want it to generate the interrupt when the Counter Value (TIM14->CNT) resets ONLY by overflow.

When I click on the button, it enters the external interrupt (EXTI2_3_IRQHandler) which then starts Timer14, if the button is released, it falls back to the interrupt which will then stop the counter and reset the count. My logic is implemented like this:

void EXTI2_3_IRQHandler(void)
{
    HAL_NVIC_ClearPendingIRQ(EXTI2_3_IRQn); //Clear nvic interrupt flag
    HAL_GPIO_EXTI_IRQHandler(SWITCH_Pin); //Clear gpio interrupt flag
    if (HAL_GPIO_ReadPin(SWITCH_GPIO_Port, SWITCH_Pin) != GPIO_PIN_SET) //high level indicates switch is not being pressed
    {
        HAL_TIM_Base_Start_IT(&htim14); //Initializes the 3s timer count
    }else{
        HAL_TIM_Base_Stop_IT(&htim14);
        TIM14->CNT = 0;
    }
}

I noticed a "bug" that when I just click a short click, just the first time, it falls on the external interrupt and starts the timer, when the external interrupt ends it goes straight to the timer interrupt, which then goes to the callback where I increment the counter that the button was held down for 3 seconds. After this bug it works as expected, it only increments the counter if it was pressed for more than 3 seconds.

Reviewing the reference manual (https://www.st.com/resource/en/reference_manual/dm00031936-stm32f0x1stm32f0x2stm32f0x8-advanced-armbased-32bit-mcus-stmicroelectronics.pdf), page 459, I found the following information:

"Interrupt generation on the following events: – Update: counter overflow, counter initialization (by software)"

More details about my TIM14 setup (HCLK = 48Mhz) and callback function. Initialization TIM14:

static void MX_TIM14_Init(void) {
  /* USER CODE BEGIN TIM14_Init 0 */

  /* USER CODE END TIM14_Init 0 */

  /* USER CODE BEGIN TIM14_Init 1 */

  /* USER CODE END TIM14_Init 1 */
  htim14.Instance = TIM14;
  htim14.Init.Prescaler = 8000;
  htim14.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim14.Init.Period = 18000;
  htim14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim14) != HAL_OK) {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM14_Init 2 */

  /* USER CODE END TIM14_Init 2 */
}

And the timer callback function:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM16) {
    HAL_IncTick();
    if(Get_ButtonPressedValue() == TRUE || Get_ButtonPressedState() != NO_PRESS)
    {
        FSM_Button_Pressed();
    }
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == TIM14) {
      if (HAL_GPIO_ReadPin(SWITCH_GPIO_Port, SWITCH_Pin) != GPIO_PIN_SET) //high level indicates switch is not being pressed
      {
          ui16CounterTIM14++;
          Set_FlagTim14CntFinished(TRUE);
      }
  }
  /* USER CODE END Callback 1 */
}

I've tried to put a breakpoint on the start of the timer and check the registers that are changed and find the ones that make sense to enter the timer interrupt by counter initialization. I've tried to change the values ​​to zero, in the SFRs tab of the STM32F0x1 (Special Function Registers), since the register has write permission (Access permission: RW) and even so after the short click it registers as if it were a click from 3 seconds.

Before HAL_TIM_Base_Start_IT(&htim14) I only have the SR->UIF (update interrupt flag) at 1. And the PSC and ARR values ​​configured in Init.

After the HAL_TIM_Base_Start_IT(&htim14) it has a value of CNT (>2000), the PSC and ARR values ​​configured in Init, and the following reg in value 1:

  • CR1->CEN (Counter enable)
  • DIER->UIE (Update interrupt enable)
  • SR->UIF (Update interrupt flag)

What should I do to not fall into this counter initialization interrupt?

Inspired by: https://stackoverflow.com/questions/66823397/implementing-a-single-press-long-press-and-a-double-press-function-in-hal-for-s

\$\endgroup\$
4
  • \$\begingroup\$ I think the logical place for counter reset is before you start it, not after you stopped it, when the counter value does not matter anymore. Also, what is that weird code for TIM16 and why does it call HAL_IncTick? The function looks like user override for weak implementation but it has Cube-specific user code wrappers that are not supposed to be in user code. \$\endgroup\$
    – Maple
    Dec 17, 2021 at 16:20
  • \$\begingroup\$ It goes into the external interrupt both when I press and release the button, since I set it to rising/falling edge. It seems to make more sense to stop the counter when it is no longer under pressure. I tested it and realized that what you said falls into the same logic as mine, putting it before checking if the button is being pressed, it does not need that ELSE. My TIM16 is being used as Timebase Source for SYS, I configured it in CUBEMX. I just didn't mention. \$\endgroup\$ Dec 17, 2021 at 17:04
  • \$\begingroup\$ AFAIK, when you configure sys tick Cube adds all necessary calls by itself on HAL side of the code (usually from SysTick_Handler). Your HAL_TIM_PeriodElapsedCallback seems to be user implementation, so it does not need to make a call to HAL_IncTick. I might be wrong, of course, since there is a lot of code and configuration specific for your case that I don't know about. \$\endgroup\$
    – Maple
    Dec 17, 2021 at 17:17
  • \$\begingroup\$ Its bad juju to use external interrupts on a switch input. Use the systick to generate a 10ms tick and poll, debounce and count time is the systick isr. You’ll most likely need a timebase for other things as well. \$\endgroup\$
    – Kartman
    Dec 17, 2021 at 22:37

1 Answer 1

0
\$\begingroup\$

HAL_TIM_Base_Init() updates the interrupt flag, and we want the interrupt flag to be fired only when the button is pressed. To do this just clear the UIF bit of the SR:

TIM14->SR &= ~TIM_SR_UIF;

static void MX_TIM14_Init(void) {
   /* USER CODE BEGIN TIM14_Init 0 */

   /* USER CODE END TIM14_Init 0 */

   /* USER CODE BEGIN TIM14_Init 1 */

   /* USER CODE END TIM14_Init 1 */
   htim14.Instance = TIM14;
   htim14.Init.Prescaler = 8000;
   htim14.Init.CounterMode = TIM_COUNTERMODE_UP;
   htim14.Init.Period = 18000;
   htim14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
   htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
   if (HAL_TIM_Base_Init(&htim14) != HAL_OK) {
     Error_Handler();
   }
   TIM14->SR &= ~TIM_SR_UIF;
   /* USER CODE BEGIN TIM14_Init 2 */

   /* USER CODE END TIM14_Init 2 */
}
\$\endgroup\$
1
  • \$\begingroup\$ I am glad you found a solution to your problem, but do you understand the reason for Cube-generated user code wrappers in there? This is the same question I asked about HAL_TIM_PeriodElapsedCallback earlier \$\endgroup\$
    – Maple
    Dec 17, 2021 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.