STM32: la interrupción del temporizador funciona inmediatamente

Este es el código para el temporizador en mi proyecto en STM32F429:

//timer initialization
 void timerInit()
 {
  uwPrescalerValue2 = (uint32_t) ((SystemCoreClock / 2) / 100000) - 1;
  RS485Timer.Instance = TIM5;
  RS485Timer.Init.Period = 67400000; // high value to notice interrupt even without debugging
  RS485Timer.Init.Prescaler = 400000;
  RS485Timer.Init.ClockDivision = 0;
  RS485Timer.Init.CounterMode = TIM_COUNTERMODE_UP;
  HAL_TIM_Base_Init(&RS485Timer);
 }

 void timerReset()
 {
 HAL_TIM_Base_Stop_IT(&RS485Timer);
 HAL_TIM_Base_DeInit(&RS485Timer);
 HAL_TIM_Base_Init(&RS485Timer);
 HAL_TIM_Base_Start_IT(&RS485Timer);
 printf("%d timer reset\n", countereset);
 countereset++;
 } 

 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
 {
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* TIMx Peripheral clock enable */
  __TIM5_CLK_ENABLE();

  /*##-2- Configure the NVIC for TIMx #########################################*/
  /* Set the TIMx priority */
  HAL_NVIC_SetPriority(TIM5_IRQn, 7, 1);

  /* Enable the TIMx global Interrupt */
  HAL_NVIC_EnableIRQ(TIM5_IRQn);
 }

 void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim)
 {
  __TIM5_FORCE_RESET();
  __TIM5_RELEASE_RESET();

  HAL_NVIC_DisableIRQ(TIM5_IRQn);
 }

 void TIM5_IRQHandler(void)
 {
  if (__HAL_TIM_GET_FLAG(&RS485Timer, TIM_FLAG_UPDATE) != RESET)      //In case other interrupts are also running
  {
   if (__HAL_TIM_GET_ITSTATUS(&RS485Timer, TIM_IT_UPDATE) != RESET)
   {
    __HAL_TIM_CLEAR_FLAG(&RS485Timer, TIM_FLAG_UPDATE);
    HAL_TIM_IRQHandler(&RS485Timer);
    printf("timer interrupt\n");
   }
  }
 }

Y después de ejecutar timerReset()la función en medio de mi programa, la interrupción comienza no pocos segundos después, sino casi de inmediato. Probé algunos otros temporizadores para verificar si no hay ningún problema de hardware, pero no, no lo es.

Le sugiero que borre explícitamente el indicador de interrupción del temporizador en su función timerReset().
Después de agregar entre DeInit e Init __HAL_TIM_CLEAR_FLAG(&RS485Timer, TIM_FLAG_UPDATE); y __HAL_TIM_CLEAR_FLAG(&RS485Timer, TIM_IT_UPDATE); no pasa nada nuevo.

Respuestas (6)

Me encontré con esto con un STM32F105. Las funciones de la biblioteca de periféricos estándar STM32F1xx son un poco diferentes a las que está utilizando, pero la idea debería ser la misma.

La emisión de la TIM_TimeBaseInit()función hizo que se estableciera el indicador TIM_SR_UIF. Todavía no he vuelto para averiguar por qué. Una vez que se establece este bit, la interrupción se activará tan pronto como se habilite.

Para arreglarlo, después de llamar TIM_TimeBaseInit(), llamé inmediatamente TIM_ClearITPendingBit(). Entonces habilitaría la interrupción con TIM_ITConfig(). Esto solucionó el problema.

Mi rutina de inicialización completa se ve así:

// Enable the peripheral clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

// Configure the timebase
TIM_TimeBaseInitStructure.TIM_Prescaler = 1;
TIM_TimeBaseInitStructure.TIM_Period = 35999;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure);

// That last function caused the UIF flag to get set. Clear it.
TIM_ClearITPendingBit(TIM5, TIM_IT_Update);

// Configure so that the interrupt flag is only set upon overflow
TIM_UpdateRequestConfig(TIM5, TIM_UpdateSource_Regular);

// Enable the TIM5 Update Interrupt type
TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE);
Mismo problema en STM32L151 usando las bibliotecas HAL. Solución alternativa (por ejemplo, para TIM6):__HAL_TIM_CLEAR_FLAG(&htim6, TIM_SR_UIF);
Un comentario en el nuevo controlador HAL explica por qué: hacen esto para forzar que el valor PSC se actualice en la inicialización, ya que solo se carga en el SR->PSC después de un evento de actualización.
Genial, @Galaxy, gracias por la información.

Como tuve un problema similar y no encontré respuestas, comparto mi experiencia con la esperanza de ayudar a otras personas.

Creo que en su caso, configurar el URS (fuente de solicitud de actualización) antes de inicializar el temporizador también resuelve el problema.

En mi caso, estoy usando los controladores de capa baja, por lo que un código de ejemplo sería:

//Enables APB1 TIM16 peripheral clock
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_TIM16);

//Sets update event source to counter overflows only
LL_TIM_SetUpdateSource(TIM16, LL_TIM_UPDATESOURCE_COUNTER);

//Configures the TIM16 time base
LL_TIM_InitTypeDef TIM_InitStruct;
TIM_InitStruct.Prescaler = 7999;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 2999;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM16, &TIM_InitStruct);

//Enables update interrupt
LL_TIM_EnableIT_UPDATE(TIM16);

//Enables timer counter
LL_TIM_EnableCounter(TIM16);

//Enables Interrupt
NVIC_EnableIRQ(TIM16_IRQn);

El problema es que estaba usando las funciones LL_TIM_SetPrescaler(TIM16, 7999)y LL_TIM_SetAutoReload(TIM16, 2999)para configurar la base de tiempo, y descubrí que al usar estas funciones no se actualizaban los valores, entonces tenía que generar un evento para actualizar los valores usando LL_TIM_GenerateEvent_UPDATE(TIM16).

Luego puede borrar el indicador de evento usando LL_TIM_ClearFlag_UPDATE(TIM16)antes de habilitar la interrupción, o usar LL_TIM_SetUpdateSource(TIM16, LL_TIM_UPDATESOURCE_COUNTER)antes de generar el evento.

Basado en la solución de programmersought.com

Borrar TIM_SR_UIFbandera antes de cada HAL_TIM_Base_Start_IT()llamada

#define FIX_TIMER_TRIGGER(handle_ptr) (__HAL_TIM_CLEAR_FLAG(handle_ptr, TIM_SR_UIF))

...

void myfunc(){
    FIX_TIMER_TRIGGER(&htim7);
    HAL_TIM_Base_Start_IT(&htim7); 
}

Probado en la placa Discovery STM32F407.

Si está depurando, recuerde que, de forma predeterminada, los periféricos no se detendrán en la detención del núcleo, por lo que los temporizadores seguirán activando la interrupción mientras usted está detenido. También tuve un buen dolor de cabeza hace un tiempo, hasta que descubrí los bits para congelar periféricos en modo de depuración.

Puede habilitarlo al inicio de main de esta manera:

void main(void){

   __HAL_DBGMCU_FREEZE_TIM1();    // Enable Timer1 Freeze on Debug

   HAL_Init();

   // blah
   // blah rest of the main code
   // blah
}

Tienes todas las definiciones al principio de stm32f4xx_hal.h

Tuve un problema similar en el mod One Pulse y encontré una solución para la biblioteca HAL. Cuando controlé los indicadores del temporizador en la función "TIM2_IRQHandler", vi que estaba configurado "capturar indicador de comparación 1". Así que borré "capturar comparar bandera 1". Pero esta vez vi que "capturar comparar el indicador 2" está configurado. Así que borré todos los indicadores de comparación (del 1 al 4) en mi función "TIM2_IRQHandler" usando los siguientes códigos.

void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */
  if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_CC1) != RESET)
  {
      timer2Proccess();
      __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_CC1 );
      __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_CC2 );
      __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_CC3 );
      __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_CC4 );
  }
  /* USER CODE END TIM2_IRQn 1 */
}

Mismo problema con TIM_TimeBaseInit() y STM32F0xx. La última cadena de esta función:

  TIMx->EGR = TIM_PSCReloadMode_Immediate;

Establece un evento de actualización en el registro de generación de eventos. Es por eso que puse check al IRQ Handler:

void TIM1_IRQHandler() {
if(TIM_GetFlagStatus(TIM1, TIM_FLAG_Update) == SET) {
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
    if((TIM1 -> CR1 & TIM_CR1_CEN) == 0) return; //Timer is not working
    //Interrupt code