1. 项目概述
在嵌入式系统开发中,PWM(脉冲宽度调制)信号的测量是一个基础但极其重要的技能。作为一名在工业自动化领域摸爬滚打多年的工程师,我经常需要精确测量各种PWM信号的参数。今天要分享的这个项目,就是利用STM32的定时器触发输入和从模式功能来实现高精度的PWM占空比测量。
这个方案特别适合需要非侵入式测量PWM信号的场景。相比传统的输入捕获方法,触发输入+从模式的组合能更精确地捕捉信号边沿,尤其适合测量高频PWM信号。我在多个工业控制项目中都采用过这种方案,实测精度可以达到纳秒级。
2. 核心原理解析
2.1 PWM信号测量基础
PWM信号有三个关键参数:周期(Frequency)、占空比(Duty Cycle)和极性(Polarity)。占空比是指高电平时间占整个周期的百分比,计算公式为:
code复制占空比 = (高电平时间 / 周期时间) × 100%
传统测量方法通常使用输入捕获功能,在上升沿和下降沿分别触发中断来记录时间戳。但这种方法有两个明显缺点:
- 高频信号下中断响应延迟会影响测量精度
- CPU需要频繁处理中断,影响系统整体性能
2.2 STM32定时器的从模式
STM32的定时器从模式(Slave Mode)允许一个定时器被另一个定时器或外部信号控制。我们主要使用两种从模式:
- 复位模式(Reset Mode):触发信号上升沿时,定时器计数器复位
- 门控模式(Gated Mode):触发信号高电平期间,定时器计数器运行
本项目中使用复位模式,配合触发输入功能实现精确的边沿检测。
2.3 硬件触发输入
STM32的定时器可以将特定引脚配置为触发输入(Trigger Input),当该引脚检测到边沿信号时,可以触发定时器的特定行为。我们使用TIMx_ETR引脚作为PWM信号输入,通过配置滤波器减少噪声干扰。
3. 硬件设计与配置
3.1 硬件连接
典型的连接方式如下:
code复制PWM信号源 --> STM32的TIMx_ETR引脚(如PA0对应TIM2_ETR)
注意:不同STM32型号的ETR引脚可能不同,需查阅具体芯片的参考手册确认。
3.2 定时器配置步骤
以下是使用STM32CubeMX配置定时器的关键步骤:
-
主定时器配置:
- 时钟源:内部时钟
- 预分频器:根据信号频率设置(保持计数器频率足够高)
- 计数模式:向上计数
- 自动重装载值:最大(0xFFFF)
-
从模式配置:
- 触发源:ETRF(外部触发输入)
- 从模式:复位模式(Reset Mode)
- 触发极性:根据PWM极性选择上升沿或下降沿
- 输入滤波器:适当设置(通常4-8个时钟周期)
-
ETR输入配置:
- ETR预分频器:禁用(不分频)
- ETR极性:与PWM信号极性匹配
- ETR滤波器:根据信号质量设置(噪声大时增加)
3.3 寄存器级配置(不使用HAL库)
对于追求极致性能的场景,可以直接操作寄存器:
c复制// 以TIM2为例
TIM2->SMCR |= TIM_SMCR_SMS_2; // 复位模式
TIM2->SMCR |= TIM_SMCR_TS_2; // ETRF作为触发源
TIM2->SMCR |= TIM_SMCR_ETF_0 | TIM_SMCR_ETF_1; // 滤波器设置
TIM2->CCMR1 |= TIM_CCMR1_CC1S_0; // CC1通道输入
4. 软件实现与测量逻辑
4.1 测量流程设计
整个测量过程分为三个阶段:
-
周期测量:
- 配置定时器在PWM上升沿复位
- 记录两次复位之间的时间差即为周期
-
高电平时间测量:
- 配置定时器在PWM下降沿复位
- 记录两次复位之间的时间差即为高电平时间
-
占空比计算:
- 根据测得的高电平时间和周期计算占空比
4.2 关键代码实现
c复制// 初始化定时器
void TIM_Config(void) {
// 主定时器基础配置
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
// 从模式配置
TIM_SlaveConfigTypeDef sSlaveConfig;
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_ETRF;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 4;
HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig);
// 启动定时器
HAL_TIM_Base_Start(&htim2);
}
// 测量周期
uint32_t Measure_Period(void) {
static uint32_t last_count = 0;
uint32_t current_count = TIM2->CNT;
uint32_t period = current_count - last_count;
last_count = current_count;
return period;
}
4.3 测量精度优化技巧
-
时钟源选择:
- 使用最高精度的时钟源(如外部晶振)
- 在CubeMX中配置正确的时钟树,确保定时器时钟最大化
-
滤波器设置:
- 对于干净信号,滤波器可以设置较小(1-2个时钟周期)
- 对于噪声较大信号,适当增加滤波器值(但会引入延迟)
-
中断处理:
- 虽然本方案减少了中断使用,但必要时可以使用DMA传输测量结果
- 避免在中断服务程序中做复杂计算
5. 实际应用中的问题与解决方案
5.1 常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 测量值不稳定 | 信号噪声大 | 增加ETR滤波器值,检查硬件连接 |
| 测量值为零 | 触发极性错误 | 检查TIM_SMCR_ETP和TIM_SMCR_ETF设置 |
| 测量值偏小 | 定时器溢出 | 减小预分频器,提高计数器频率 |
| 无测量结果 | 引脚配置错误 | 确认ETR引脚映射正确 |
5.2 性能优化经验
-
多定时器协作:
- 使用一个定时器专门测量周期
- 另一个定时器测量高电平时间
- 通过定时器同步实现并行测量
-
动态调整策略:
c复制// 根据信号频率动态调整预分频器 if(measured_period < 1000) { htim2.Init.Prescaler = 0; // 高频信号,不分频 } else { htim2.Init.Prescaler = SystemCoreClock / 1000000 - 1; // 1MHz计数 } HAL_TIM_Base_Init(&htim2); -
抗干扰设计:
- 在ETR引脚添加适当RC滤波
- PCB布局时缩短信号走线
- 必要时使用差分信号传输
6. 进阶应用与扩展
6.1 多通道PWM测量
通过配置多个定时器或使用定时器的多个输入通道,可以实现多路PWM同时测量:
c复制// 配置TIM2和TIM3分别测量不同信号
void MultiChannel_Config(void) {
// TIM2测量通道1
htim2.Instance = TIM2;
// ... TIM2配置
// TIM3测量通道2
htim3.Instance = TIM3;
// ... TIM3配置
}
6.2 与DMA结合实现无CPU干预测量
对于需要连续测量的应用,可以结合DMA:
- 配置定时器在特定事件(如更新事件)触发DMA
- DMA将计数器值传输到内存缓冲区
- 后台处理数据,计算占空比
c复制// DMA配置示例
hdma_tim2_up.Instance = DMA1_Channel2;
hdma_tim2_up.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim2_up.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim2_up.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim2_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim2_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
HAL_DMA_Init(&hdma_tim2_up);
6.3 低功耗模式下的PWM测量
对于电池供电设备,可以配置定时器在低功耗模式下工作:
- 使用LPTIM(低功耗定时器)
- 配置唤醒中断只在边沿触发时激活
- 测量完成后立即返回睡眠模式
c复制// 低功耗配置示例
void Enter_LowPowerMode(void) {
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后重新初始化定时器
TIM_Config();
}
在实际项目中,这种测量方法已经成功应用于:
- 工业电机转速监测
- LED调光系统反馈控制
- 电源管理芯片PWM信号分析
- 遥控接收信号解码
经过多次项目验证,这种方法的典型测量精度可以达到0.1%以内(在72MHz系统时钟下),完全满足大多数工业应用需求。最关键的是,它几乎不占用CPU资源,使得系统可以同时处理其他重要任务。