STM32F303VC TIM2单脉冲模式首次启用即完成异常排查
解决STM32F303 TIM2单脉冲模式(OPM)首次启动立即完成的问题
这个问题我之前调试STM32定时器OPM模式时也碰到过,根源其实是定时器的预分频器(PSC)和自动重载寄存器(ARR)的影子寄存器没有完成同步,导致首次启动时触发了意外的更新事件,让你误以为定时器“瞬间完成”。
问题分析
STM32定时器的PSC和ARR寄存器默认带有影子寄存器,只有当发生更新事件时,你写入的数值才会被加载到影子寄存器中生效。你在初始化时直接写入了PSC和ARR,但没有主动触发更新事件,首次启动定时器时,硬件可能自动触发了一次更新(或是残留了初始化阶段的更新标志),直接把UIF位置位,导致轮询逻辑立刻退出。
修复方案
在配置完PSC和ARR之后,手动触发一次更新事件,同时提前清除UIF标志,确保首次启动时定时器处于正确的初始状态。修改后的代码如下:
use cortex_m_rt::entry; use stm32f3xx_hal::pac; #[entry] fn main( ) -> ! { let p = pac::Peripherals::take( ).unwrap( ); p.RCC.apb1enr.modify( | _, w | w.tim2en( ).set_bit( ) ); p.TIM2.cr1.write( | w | w .urs( ).set_bit( ) .opm( ).set_bit( ) .cen( ).clear_bit( ) ); // 基于8MHz时钟设置预分频器 p.TIM2.psc.write( | w | w.psc( ).bits( 7999 ) ); // 初始化ARR寄存器 p.TIM2.arr.write( | w | w.arr( ).bits( 3999 ) ); // --- 新增关键代码 --- // 手动触发更新事件,同步PSC和ARR的影子寄存器 p.TIM2.egr.write(|w| w.ug().set_bit()); // 清除触发UG后自动置位的UIF标志 p.TIM2.sr.write(|w| w.uif().clear_bit()); // --- 新增结束 --- // 此处初始化LED(GPIOE),为简化示例已移除相关代码 let delay = | duration | { p.TIM2.arr.write( | w | w.arr( ).bits( duration ) ); // 每次修改ARR后,同步影子寄存器并清除标志 p.TIM2.egr.write(|w| w.ug().set_bit()); p.TIM2.sr.write(|w| w.uif().clear_bit()); p.TIM2.cr1.modify( | _, w | w.cen( ).set_bit( ) ); while p.TIM2.sr.read( ).uif( ).bit_is_clear( ) { } p.TIM2.sr.write( | w | w.uif( ).clear_bit( ) ); }; // 点亮LED,现在会正确等待4秒 delay( 3999 ); // 熄灭LED loop { } }
关键细节解释
- 触发更新事件(UG位):设置
TIM2_EGR.UG位会强制定时器生成一次更新事件,把PSC和ARR的当前值加载到影子寄存器中,同时自动重置计数器CNT为0(默认TIM_CR1.UDIS位未置位,此行为生效)。 - 清除UIF标志:触发UG位会自动置位
TIM2_SR.UIF,所以必须在启动定时器前手动清除这个标志,避免轮询逻辑直接跳出。 - 每次修改ARR后同步:在
delay函数中修改ARR后,同样需要触发更新事件,确保新的重载值立即生效,避免出现计时偏差。
为什么后续调用正常?
第一次调用delay后,定时器完成了一次完整的更新事件,影子寄存器已经完成同步,后续启动时硬件会按照正常的计数流程工作,不会再触发意外的更新,所以计时逻辑恢复正常。
内容的提问来源于stack exchange,提问作者WBuck
相关产品推荐
相关产品推荐

