ST意法半导体
直播中

康大争

8年用户 2258经验值
私信 关注
[问答]

stm32H743定时器溢出的原因?怎么解决?

我利用定时器更新中断实现软件计时,定时时间是固定的按照{10ms, 20ms,650ms, 50ms,50ms}周期性进行的。由于下一次的计时时间是需要在定时器的中断处理函数中更新的,定时器没有使能自动重装载功能,而是在每次的定时器中断函数中先关闭定时器,再执行装载值更新,再启动定时器。但是,项目中在某个定时期间,进行了flash操作,导致了装载值没有及时更新,定时器计数到装载值后,定时器继续计数直到u32溢出,期间不再产生更新中断,个人尝试了将中断函数放在RAM中执行,防止flash操作期间无法执行中断,但是也无法避免,请问如何避免这种情况。

回帖(1)

Petc

2025-6-25 17:58:52

针对STM32H743定时器溢出导致中断丢失的问题,以下是原因分析和解决方案:


问题原因分析



  1. Flash操作阻塞总线:STM32在执行Flash擦写/编程操作时,内核会暂停执行(等待Flash操作完成),导致中断无法及时响应。

  2. 临界操作失效

    • 中断处理中 关闭定时器 → 更新ARR值 → 重启定时器 操作需要访问TIM寄存器

    • Flash操作期间总线阻塞,即使中断在RAM中,也无法及时完成寄存器操作


  3. 计数器溢出

    • 当ARR未及时更新时,计数器CNT从旧ARR值继续累加至0xFFFF FFFF溢出

    • 溢出后CNT=0重新计数,期间不会触发更新中断(因ARR值过大)





解决策略(按推荐顺序)


✅ 方案一:启用ARR预装载 + 单次模式(最优解)


原理:利用TIM硬件自动管理重装载,避免中断中关闭定时器。


// 初始化设置(在TIM配置中)
TIMx->CR1 &= ~TIM_CR1_ARPE;  // 确保预装功能关闭时配置
TIMx->ARR = initial_arr;     // 设置初始ARR
TIMx->CR1 |= TIM_CR1_OPM;    // 单次模式(计数到ARR后停止)
TIMx->CR1 |= TIM_CR1_ARPE;   // 启用ARR预装载缓冲
TIMx->DIER |= TIM_DIER_UIE;  // 使能更新中断
TIMx->CR1 |= TIM_CR1_CEN;    // 启动定时器

// 中断处理函数(在RAM中执行)
void __attribute__((section(".ramfunc"))) TIMx_IRQHandler(void) {
  if (TIMx->SR & TIM_SR_UIF) {
    TIMx->SR = ~TIM_SR_UIF;  // 清除中断标志

    // 1. 执行周期任务
    static uint32_t cycles[5] = {10, 20, 650, 50, 50}; // 单位ms
    static uint8_t idx = 0;
    Execute_Task(idx);        // 执行对应任务

    // 2. 更新下一个周期
    idx = (idx + 1) % 5;
    uint32_t next_arr = (SystemCoreClock / 1000) * cycles[idx] - 1;

    // 安全更新ARR(硬件自动在更新事件时装载)
    TIMx->ARR = next_arr;   // 预装载寄存器会被缓冲

    // 3. 重启定时器(单次模式需手动重启)
    TIMx->CR1 |= TIM_CR1_CEN;
  }
}

优势



  • 硬件保证ARR在下一个周期生效

  • 无需关闭定时器,避免总线阻塞导致操作失败

  • CNT从0开始计数,无溢出风险




✅ 方案二:主从定时器架构(分散任务)


// 主定时器(TIM2)配置为10ms周期中断
// 从定时器(TIM3)处理长周期任务

// TIM2中断(10ms周期)
void TIM2_IRQHandler() {
  static uint32_t counters[4] = {2, 65, 5, 5}; // 对应20/650/50/50ms
  for(int i=0; i<4; i++) {
    if(--counters[i] == 0) {
      Execute_Task(i+1);     // 执行任务
      counters[i] = ...;     // 重置计数器
    }
  }
}

// 650ms任务单独处理(避免被短任务阻塞)
void TIM3_IRQHandler() {
  Execute_Task(2);          // 执行650ms任务
  TIM3->SR = ~TIM_SR_UIF;
}



✅ 方案三:优化Flash操作时机


// 在短周期任务中插入Flash操作
void Execute_Task(uint8_t idx) {
  switch(idx) {
    case 0: /*10ms任务*/ break;
    case 1: /*20ms任务*/ break;
    case 2: // 650ms任务
      if(flash_operation_pending) {
        __disable_irq();     // 进入临界区
        FLASH_Program(...);  // 执行Flash操作
        __enable_irq();
        flash_operation_pending = 0;
      }
      break;
    ...
  }
}



关键补充措施



  1. 中断优先级配置
    HAL_NVIC_SetPriority(TIMx_IRQn, 0, 0); // 最高硬件优先级


  2. Flash操作优化


    // 使用ICache加速
    SCB_EnableICache();

    // 降低Flash延迟
    FLASH->LATENCY = FLASH_LATENCY_8;




? 结论建议

首选方案一(单次模式+预装载),从根本上解决总线阻塞导致的更新失效问题。结合以下措施:  



  1. 所有中断函数使用 __attribute__((section(".ramfunc")))  

  2. 避免在650ms任务期间执行Flash操作  

  3. 若必须长时操作,使用DMA传输数据减少CPU阻塞  



通过以上方案,可彻底解决因Flash操作导致的定时器溢出问题,确保周期性任务稳定运行。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分