STM32: прерывание по таймеру срабатывает немедленно

Это код таймера в моем проекте на 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");
   }
  }
 }

И после запуска timerReset()функции в середине моей программы прерывание начинается не через несколько секунд, а почти сразу. Я попробовал несколько других таймеров, чтобы проверить, нет ли проблем с оборудованием, но нет, это не так.

Я предлагаю вам явно очистить флаг прерывания таймера в вашей функции timerReset().
После добавления между DeInit и Init __HAL_TIM_CLEAR_FLAG(&RS485Timer, TIM_FLAG_UPDATE); и __HAL_TIM_CLEAR_FLAG(&Таймер RS485, TIM_IT_UPDATE); ничего нового не происходит.

Ответы (6)

Я столкнулся с этим с STM32F105. Функции стандартной периферийной библиотеки STM32F1xx немного отличаются от того, что вы используете, но идея должна быть той же.

Выдача TIM_TimeBaseInit()функции привела к установке флага TIM_SR_UIF. Я еще не вернулся, чтобы понять, почему. Как только этот бит установлен, прерывание сработает, как только оно будет разрешено.

Чтобы это исправить, после звонка TIM_TimeBaseInit()я сразу позвонил TIM_ClearITPendingBit(). Затем я бы включил прерывание с помощью TIM_ITConfig(). Это решило проблему.

Моя полная процедура инициализации выглядит так:

// 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);
Та же проблема на STM32L151 с использованием библиотек HAL. Обходной путь (например, для TIM6):__HAL_TIM_CLEAR_FLAG(&htim6, TIM_SR_UIF);
Комментарий в новом драйвере HAL объясняет, почему: они делают это, чтобы принудительно обновить значение PSC при инициализации, поскольку оно фактически загружается в SR->PSC только после события обновления.
Отлично, @Galaxy, спасибо за информацию.

Поскольку у меня была аналогичная проблема, и я не нашел ответов, я делюсь своим опытом в надежде помочь другим людям.

Я считаю, что в вашем случае установка URS (Источник запроса на обновление) перед инициализацией таймера также решает проблему.

В моем случае я использую низкоуровневые драйверы, поэтому пример кода будет таким:

//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);

Проблема в том, что я использовал функции LL_TIM_SetPrescaler(TIM16, 7999)и LL_TIM_SetAutoReload(TIM16, 2999)для настройки базы времени и обнаружил, что при использовании этих функций значения не обновлялись, поэтому мне пришлось сгенерировать событие для обновления значений с помощью LL_TIM_GenerateEvent_UPDATE(TIM16).

Затем вы можете либо очистить флаг события с помощью LL_TIM_ClearFlag_UPDATE(TIM16)перед включением прерывания, либо использовать LL_TIM_SetUpdateSource(TIM16, LL_TIM_UPDATESOURCE_COUNTER)перед генерацией события.

На основе решения отprogramrsought.com

Сбрасывать TIM_SR_UIFфлаг перед каждым HAL_TIM_Base_Start_IT()вызовом

#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); 
}

Протестировано на плате STM32F407 Discovery.

При отладке помните, что по умолчанию периферийные устройства не останавливаются при остановке ядра, поэтому таймеры будут продолжать запускать прерывание, пока вы остановлены. У меня также была приятная головная боль некоторое время назад, пока я не обнаружил биты для зависания периферийных устройств в режиме отладки.

Вы можете включить его при запуске main следующим образом:

void main(void){

   __HAL_DBGMCU_FREEZE_TIM1();    // Enable Timer1 Freeze on Debug

   HAL_Init();

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

У вас есть все определения в начале stm32f4xx_hal.h.

У меня была похожая проблема в моде One Pulse, и я нашел решение для библиотеки HAL. Когда я управлял флагами таймера в функции «TIM2_IRQHandler», я видел, что установлен «флаг сравнения захвата 1». Поэтому я очистил «флаг сравнения захвата 1». Но на этот раз я увидел, что установлен «флаг сравнения захвата 2». Поэтому я очистил все флаги сравнения (от 1 до 4) в моей функции «TIM2_IRQHandler», используя следующие коды.

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 */
}

Та же проблема с TIM_TimeBaseInit() и STM32F0xx. Последняя строка этой функции:

  TIMx->EGR = TIM_PSCReloadMode_Immediate;

Он устанавливает событие обновления в регистре генерации событий. Вот почему я поставил галочку на обработчик IRQ:

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