STM32F103 通用定时器实战:从PWM到脉冲计数的核心应用

张开发
2026/4/17 7:54:11 15 分钟阅读

分享文章

STM32F103 通用定时器实战:从PWM到脉冲计数的核心应用
1. STM32F103通用定时器基础认知第一次接触STM32F103的通用定时器时我完全被它丰富的功能震撼到了。相比基础定时器通用定时器就像瑞士军刀一样全能。在STM32F103C8T6这类中容量产品里内置了4个通用定时器TIM2-TIM5每个都支持PWM生成、输入捕获、输出比较等实用功能。记得当时为了调试电机转速我整整两天没合眼最后发现是定时器分频系数设错了——这种踩坑经历反而让我对定时器的理解更加深刻。通用定时器的核心是16位自动重装载计数器配合可编程预分频器能实现从几微秒到几分钟的定时范围。实际项目中我经常用TIM3控制电机TIM4处理编码器信号TIM2做系统心跳计时。关键要掌握三个寄存器CNT计数器当前值、ARR自动重载值、PSC预分频系数。举个例子当系统时钟72MHz时设置PSC71ARR999产生的定时频率就是72MHz/(711)/(9991)1kHz。最让我惊喜的是通道复用设计。每个定时器有4个独立通道通过GPIO重映射功能可以灵活配置引脚。去年做机械臂项目时TIM3的四个通道分别控制四个关节电机只用单个定时器就完成了所有PWM输出。这里有个实用技巧使用CubeMX配置时记得勾选TIMx remap选项否则信号可能无法从预期引脚输出。2. PWM电机控制实战解析2.1 PWM基础配置要点让TIM3产生PWM控制智能小车电机时我总结出几个关键参数配置经验。首先是频率选择——普通直流电机常用1-20kHz。频率太低会听到线圈啸叫太高会导致MOS管过热。实测12V电机用8kHz效果最佳对应的ARR值计算公式为ARR (定时器时钟频率) / (PSC1) / PWM频率 - 1假设使用72MHz时钟PSC设为8分频则ARR72MHz/(81)/8kHz-1999占空比调节是另一个重点。通过修改CCR寄存器值改变脉宽我习惯用百分比转换公式实际占空比 (CCRx 1) / (ARR 1) * 100%比如要实现75%占空比当ARR999时CCRx应该设为749。这里有个坑部分开发板默认PWM模式是模式1还是模式2不同会导致占空比计算逻辑相反。有次调试时电机死活不转最后发现是PWM极性设错了。2.2 高级PWM技巧分享进阶应用中死区时间配置是H桥驱动的关键。TIM1/TIM8高级定时器支持硬件死区控制但通用定时器需要软件模拟。我的做法是用两个互补通道通过延迟开启实现死区TIM_OC_InitTypeDef sConfigOC { .OCMode TIM_OCMODE_PWM1, .Pulse 300, // 主通道脉宽 .OCPolarity TIM_OCPOLARITY_HIGH, .OCFastMode TIM_OCFAST_DISABLE }; HAL_TIM_PWM_ConfigChannel(htim, sConfigOC, TIM_CHANNEL_1); // 延迟100ns后开启互补通道 __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, 320);呼吸灯效果是测试PWM的好方法。我优化过的渐变算法包含加速曲线void PWM_Breathing(TIM_HandleTypeDef *htim, uint32_t channel) { static uint16_t duty 0; static int8_t step 1; duty step * (duty / 50 1); // 非线性递增 if(duty 1000) step -1; if(duty 0) step 1; __HAL_TIM_SET_COMPARE(htim, channel, duty); HAL_Delay(10); }这个算法在低亮度区域变化缓慢高亮度区域变化迅速更符合人眼感知特性。3. 编码器测速技术揭秘3.1 输入捕获配置陷阱用TIM5测量电机转速时我掉进过不少坑。首先是信号滤波设置——编码器输出的脉冲常带有毛刺。TIMx_CCMR1寄存器的ICF位可以设置数字滤波器我的经验公式是滤波器带宽 定时器时钟 / (2 * (ICF 1))对于1MHz计数频率设置ICF3可过滤小于8μs的干扰脉冲。但要注意过度滤波会导致丢失真实信号有次我把ICF设为15结果低速时完全检测不到脉冲。另一个关键点是捕获极性选择。霍尔传感器输出通常是开漏信号需要配置为TIM_IC_InitTypeDef sConfigIC { .ICPolarity TIM_ICPOLARITY_BOTHEDGE, // 双沿捕获 .ICSelection TIM_ICSELECTION_DIRECTTI, .ICPrescaler TIM_ICPSC_DIV1, .ICFilter 0x3 };我曾误设为仅上升沿捕获导致转速测量值只有实际的一半。3.2 高精度测速算法标准测速方法存在低速盲区。当脉冲间隔超过ARR值时常规方法会漏计溢出次数。我的改进方案是结合输入捕获和定时器溢出中断volatile uint32_t capture_count 0; volatile uint8_t overflow_flag 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { static uint32_t last_capture 0; uint32_t current_capture HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); if(overflow_flag) { capture_count 0xFFFFFFFF - last_capture current_capture; } else { capture_count current_capture - last_capture; } last_capture current_capture; overflow_flag 0; } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { overflow_flag 1; }这个算法在1MHz计数频率下既能测量高达500kHz的信号也能检测低至0.5Hz的脉冲。4. 多定时器协同工作实战4.1 资源分配策略在智能小车项目中我是这样分配定时器资源的TIM2编码器脉冲计数正交解码模式TIM3左电机PWM输出TIM4右电机PWM输出TIM5超声波测距输入捕获关键是要避免中断冲突。通过NVIC设置优先级HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); // 编码器计数最高优先级 HAL_NVIC_SetPriority(TIM5_IRQn, 1, 0); // 超声波次之 HAL_NVIC_SetPriority(TIM3_IRQn, 2, 0); // PWM更新中断最低4.2 脉冲计数进阶技巧标准脉冲计数受限于16位计数器我的扩展方案是结合从模式与中断uint32_t read_encoder_count(TIM_HandleTypeDef *htim) { uint32_t count; __disable_irq(); // 关中断保证原子操作 count overflow_count * 65536 __HAL_TIM_GET_COUNTER(htim); __enable_irq(); return count; } void TIM2_IRQHandler(void) { if(__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE)) { overflow_count; __HAL_TIM_CLEAR_IT(htim2, TIM_IT_UPDATE); } }这个方案在智能车比赛中实测可稳定计数超过100万脉冲误差小于±2。

更多文章