用STM32F1的定时器玩点花的:PWM呼吸灯、编码器测速、输入捕获测频一站式搞定
STM32F1定时器高阶应用实战:PWM呼吸灯、编码器测速与输入捕获测频
1. 定时器技术概览与项目设计思路
STM32F1系列微控制器搭载了功能强大的定时器外设,其灵活性和可配置性为嵌入式开发者提供了丰富的设计可能性。本系列实验将聚焦通用定时器(TIMx)的三种典型应用场景,通过实际项目案例深入掌握定时器的高级功能。
定时器核心功能模块解析:
- 时基单元:由16位预分频器(PSC)和16位自动重装载寄存器(ARR)构成,决定计数频率和周期
- 输入捕获:精确测量脉冲宽度或信号频率
- 输出比较:生成PWM波形或驱动数字输出
- 编码器接口:直接处理正交编码器信号
// 定时器基本配置结构体示例 typedef struct { uint32_t Prescaler; // 预分频值 uint32_t CounterMode; // 计数模式(向上/向下/中央对齐) uint32_t Period; // 自动重载值 uint32_t ClockDivision; // 时钟分频 uint32_t RepetitionCounter; // 重复计数器(高级定时器) } TIM_TimeBaseInitTypeDef;提示:STM32F103C8T6包含3个通用定时器(TIM2-TIM4),每个定时器具有4个独立通道,可配置为输入捕获或输出比较模式。
2. PWM呼吸灯实现与调光技巧
2.1 PWM原理与硬件连接
脉宽调制(PWM)通过调节占空比实现对平均电压的控制,是LED调光的理想方案。呼吸灯效果本质上是占空比按特定规律周期性变化的结果。
硬件连接方案:
- LED阳极接VCC,阴极通过限流电阻接TIMx_CHy
- 或LED阴极接地,阳极通过限流电阻接TIMx_CHy(需配置输出极性)
- 典型限流电阻值:220Ω-1kΩ(根据LED参数调整)
2.2 定时器PWM模式配置
void PWM_Init(TIM_TypeDef* TIMx, uint32_t Channel, uint16_t arr, uint16_t psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_OCInitTypeDef TIM_OCInitStruct; // 时基单元配置 TIM_TimeBaseStruct.TIM_Prescaler = psc; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStruct.TIM_Period = arr; TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStruct); // 输出比较配置 TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStruct.TIM_Pulse = 0; // 初始占空比0% switch(Channel) { case TIM_Channel_1: TIM_OC1Init(TIMx, &TIM_OCInitStruct); TIM_OC1PreloadConfig(TIMx, TIM_OCPreload_Enable); break; // 其他通道配置类似... } TIM_Cmd(TIMx, ENABLE); }2.3 呼吸效果算法实现
指数渐变算法提供更符合人眼感知的亮度变化:
void Breath_LED_Update(TIM_TypeDef* TIMx, uint32_t Channel) { static uint8_t dir = 0; static uint16_t val = 0; static float factor = 1.05; // 渐变系数 if(!dir) { val = (uint16_t)(val * factor) + 1; if(val >= TIMx->ARR) { val = TIMx->ARR; dir = 1; } } else { val = (uint16_t)(val / factor); if(val <= 1) { val = 0; dir = 0; } } switch(Channel) { case TIM_Channel_1: TIMx->CCR1 = val; break; // 其他通道... } }注意:在main循环中定期调用Breath_LED_Update(),调用间隔影响呼吸节奏。可使用SysTick或另一个定时器实现精确时间控制。
3. 编码器接口与电机测速
3.1 编码器工作原理与接口配置
正交编码器输出两路相位差90°的方波信号,通过检测边沿和相位关系判断旋转方向和计数。
STM32编码器模式特点:
- 支持X2/X4计数模式(每个边沿计数)
- 自动识别方向(TI1/TI2相位关系)
- 16位向上/向下计数器
void Encoder_Config(TIM_TypeDef* TIMx) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_ICInitTypeDef TIM_ICInitStruct; // 时基配置(ARR设为最大值) TIM_TimeBaseStruct.TIM_Prescaler = 0; TIM_TimeBaseStruct.TIM_Period = 0xFFFF; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStruct); // 编码器接口配置 TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 输入捕获配置(滤波参数根据实际信号质量调整) TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; TIM_ICInitStruct.TIM_ICFilter = 0x6; TIM_ICInit(TIMx, &TIM_ICInitStruct); TIM_ICInitStruct.TIM_Channel = TIM_Channel_2; TIM_ICInitStruct.TIM_ICFilter = 0x6; TIM_ICInit(TIMx, &TIM_ICInitStruct); TIM_Cmd(TIMx, ENABLE); }3.2 速度计算与溢出处理
测速算法公式:
转速(RPM) = (ΔCount × 60) / (PPR × 采样周期(秒))其中PPR为编码器每转脉冲数
int32_t Get_Speed(TIM_TypeDef* TIMx, uint16_t PPR, float Sample_Period) { static int16_t last_count = 0; int16_t current_count = TIMx->CNT; int32_t delta = (int32_t)(current_count - last_count); // 处理计数器溢出(0xFFFF ↔ 0) if(delta > 0x7FFF) delta -= 0x10000; else if(delta < -0x7FFF) delta += 0x10000; last_count = current_count; return (delta * 60) / (PPR * Sample_Period); }优化技巧:
- 使用32位变量存储计数值差
- 定期重置计数器避免长期累积误差
- 添加软件滤波(移动平均或卡尔曼滤波)
4. 输入捕获测频与占空比测量
4.1 输入捕获模式配置
PWMI模式可同时测量频率和占空比,需要配置两个输入捕获通道:
void InputCapture_Config(TIM_TypeDef* TIMx) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_ICInitTypeDef TIM_ICInitStruct; // 时基配置(使用内部时钟) TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1; // 1MHz计数频率 TIM_TimeBaseStruct.TIM_Period = 0xFFFF; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStruct); // 通道1配置(上升沿捕获,用于周期测量) TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStruct.TIM_ICFilter = 0x4; TIM_ICInit(TIMx, &TIM_ICInitStruct); // 通道2配置(下降沿捕获,用于占空比测量) TIM_ICInitStruct.TIM_Channel = TIM_Channel_2; TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling; TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI; TIM_ICInit(TIMx, &TIM_ICInitStruct); // 从模式配置:复位模式 TIM_SelectSlaveMode(TIMx, TIM_SlaveMode_Reset); TIM_SelectInputTrigger(TIMx, TIM_TS_TI1FP1); TIM_Cmd(TIMx, ENABLE); }4.2 频率与占空比计算
测频法 vs 测周法对比:
| 方法 | 适用场景 | 精度影响因素 | 计算公式 |
|---|---|---|---|
| 测频法 | 高频信号(>1kHz) | 闸门时间精度 | f = N / T |
| 测周法 | 低频信号(<1kHz) | 参考时钟精度 | f = f_clk / N |
void Get_Freq_Duty(TIM_TypeDef* TIMx, float* freq, float* duty) { uint16_t ic1 = TIM_GetCapture1(TIMx); // 周期值 uint16_t ic2 = TIM_GetCapture2(TIMx); // 高电平时间 // 计算频率(假设时钟1MHz) *freq = 1000000.0f / ic1; // 计算占空比 *duty = (float)ic2 / ic1 * 100; }误差优化策略:
- 对于高频信号,采用输入捕获+定时器溢出计数
- 添加数字滤波(中值滤波或递推平均)
- 自动切换测频/测周法(中界频率自适应)
5. 工程整合与性能优化
5.1 资源冲突解决
当多个功能共用同一定时器时,需注意:
- 时基共享:所有通道共用同一PSC和ARR
- 中断管理:合理设置中断优先级
- DMA应用:高速数据传输时考虑使用DMA
推荐资源配置方案:
| 功能 | 推荐定时器 | 可用替代方案 |
|---|---|---|
| PWM呼吸灯 | TIM3_CH1 | TIM2_CH1/TIM4_CH1 |
| 编码器接口 | TIM2 | TIM3/TIM4 |
| 输入捕获测频 | TIM4_CH1 | TIM2_CH2/TIM3_CH2 |
5.2 实时性优化技巧
- 使用定时器硬件自动重装载(ARR预装载)
- 关键代码使用寄存器直接操作(如TIMx->CCR1)
- 中断服务函数精简,仅做标志位处理
- 主循环采用状态机设计
// 高效PWM更新示例(直接寄存器操作) #define PWM_SET_DUTY(TIMx, ch, duty) \ do { \ uint32_t ccr = (TIMx->ARR * (duty)) / 100; \ switch(ch) { \ case 1: TIMx->CCR1 = ccr; break; \ case 2: TIMx->CCR2 = ccr; break; \ /* 其他通道... */ \ } \ } while(0)5.3 调试与验证方法
调试技巧:
- 使用逻辑分析仪捕获PWM波形
- 通过串口实时输出测量数据
- 利用断点检查寄存器状态
常见问题排查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| PWM无输出 | GPIO未重映射/配置错误 | 检查AFIO配置和GPIO模式 |
| 编码器计数方向相反 | TI1/TI2相位接反 | 交换A/B相信号或修改极性配置 |
| 频率测量值跳动 | 信号噪声过大 | 增加输入捕获滤波器参数 |
| 占空比测量不准 | 边沿检测抖动 | 优化硬件电路(添加施密特触发器) |
通过这三个项目的实践,开发者可以全面掌握STM32定时器的高级应用技巧。在实际项目中,往往需要根据具体需求灵活组合这些功能,例如同时使用PWM驱动电机和编码器测速实现闭环控制,或者利用输入捕获监测传感器信号并同步生成控制PWM。
