1. STM32定时器模块PWM测量概述
在嵌入式系统开发中,PWM(脉冲宽度调制)信号的测量是一项基础但至关重要的任务。STM32系列微控制器的定时器外设提供了强大的PWM测量功能,能够精确捕获输入信号的频率和占空比。我曾在多个工业控制项目中,使用STM32的TIM模块实现电机转速测量、伺服控制等关键功能。
定时器模块的输入捕获功能,本质上是通过记录信号边沿的时间戳来计算周期和脉宽。以STM32F4系列为例,其高级定时器(TIM1/TIM8)和通用定时器(TIM2-TIM5)都支持PWM输入模式,可以自动完成两路信号的边沿检测和计数值存储。
2. 硬件设计与配置要点
2.1 定时器选型策略
不同型号的STM32定时器在PWM测量能力上有所差异:
- 基础定时器(TIM6/TIM7):仅支持时基生成,不能用于输入捕获
- 通用定时器(TIM2-TIM5):支持标准输入捕获功能
- 高级定时器(TIM1/TIM8):支持PWM输入模式(双通道联合测量)
在最近的一个无人机电调项目中,我选择了TIM3进行PWM测量,原因在于:
- 需要测量的PWM频率在50-500Hz范围内
- TIM3的32位计数器满足长时间测量的精度要求
- 该定时器通道与目标GPIO引脚布线距离最短
2.2 引脚配置规范
正确的引脚配置是测量的前提条件。以TIM3_CH1为例,其复用功能对应不同引脚:
- 对于STM32F407:PA6(默认)、PB4(重映射)、PC6(部分封装)
c复制// 典型配置代码
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
关键提示:务必查阅芯片数据手册的"Alternate function mapping"章节,确认具体型号的引脚复用关系。我曾因忽略封装差异导致整个PCB需要返工。
3. 定时器参数计算实战
3.1 时钟树配置原理
精确测量的核心在于正确配置定时器时钟。假设:
- 系统主频168MHz
- APB1预分频系数为4
- TIM3挂在APB1总线上
则实际定时器时钟为:
code复制APB1时钟 = 168MHz / 4 = 42MHz
由于APB1预分频≠1,定时器时钟 = 42MHz × 2 = 84MHz
3.2 分频系数计算示例
测量一个频率为1kHz、占空比30%的PWM信号:
- 目标分辨率:1μs(对应1MHz计数频率)
- 预分频值(PSC) = 84MHz / 1MHz - 1 = 83
- 自动重载值(ARR) = 65535(16位定时器最大值)
配置代码:
c复制TIM_HandleTypeDef htim3;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 83;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_IC_Init(&htim3);
4. 输入捕获模式深度解析
4.1 双通道测量法
在精密测量中,我推荐使用PWM输入模式(双通道联动):
c复制TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 6; // 适当滤波
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);
sConfigIC.ICPolarity = TIM_ICPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_2);
这种配置下:
- CH1捕获上升沿(周期起点)
- CH2捕获下降沿(脉宽终点)
- 硬件自动建立两者关联
4.2 单通道测量技巧
当引脚资源受限时,单通道测量也能实现不错的效果:
c复制// 配置交替捕获模式
sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE;
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);
// 在中断中处理
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint32_t edge1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// 切换边沿极性
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1,
(__HAL_TIM_GET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1) == TIM_ICPOLARITY_RISING) ?
TIM_ICPOLARITY_FALLING : TIM_ICPOLARITY_RISING);
}
}
5. 测量误差分析与补偿
5.1 典型误差来源
根据我的实测数据,主要误差包括:
| 误差类型 | 典型值 | 补偿方法 |
|---|---|---|
| 中断延迟 | 0.5-2μs | 校准时间戳偏移量 |
| 时钟抖动 | ±0.01% | 多次测量取平均 |
| 边沿检测偏差 | ±50ns | 调整输入滤波器 |
| 温度漂移 | 100ppm/°C | 实时时钟校准 |
5.2 软件补偿算法
在高速测量中,我采用以下补偿策略:
c复制#define CALIB_OFFSET 1.25f // 单位:μs
float get_compensated_period() {
uint32_t raw_val = TIM3->CCR1;
float period = (float)raw_val / 84.0f; // 转换为μs
return period - CALIB_OFFSET;
}
校准方法:
- 输入已知频率的精准信号(如函数发生器输出)
- 记录测量值与实际值的固定偏差
- 将偏差值存入Flash作为校准参数
6. 实战案例:电机转速测量
6.1 霍尔传感器接口
典型的三相无刷电机测速方案:
c复制// TIM4配置为霍尔接口模式
TIM_HallSensor_InitTypeDef sHallSensorConfig;
sHallSensorConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sHallSensorConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sHallSensorConfig.IC1Filter = 0;
sHallSensorConfig.CommutationDelay = 0;
HAL_TIMEx_HallSensor_Init(&htim4, &sHallSensorConfig);
6.2 转速计算算法
每转脉冲数(PPR)为7的电机:
c复制#define PPR 7.0f
#define GEAR_RATIO 10.0f
float calculate_rpm(uint32_t period_us) {
if(period_us == 0) return 0;
float electrical_rps = 1e6f / (period_us * PPR);
float mechanical_rpm = electrical_rps * 60 / GEAR_RATIO;
return mechanical_rpm;
}
经验之谈:在低速测量时,建议改用测周法而非测频法。当转速低于100RPM时,我通常会切换到时基更长的TIM2(32位计数器)进行测量。
7. 异常情况处理机制
7.1 信号丢失检测
通过定时器溢出中断实现:
c复制void TIM3_IRQHandler(void) {
if(__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE)) {
if(__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_UPDATE)) {
// 超过预期时间未捕获到边沿
handle_signal_loss();
__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
}
}
}
7.2 毛刺滤波技巧
硬件滤波+软件验证双保险:
c复制// 硬件滤波(对应寄存器CCMRx中的ICxF位)
sConfigIC.ICFilter = 0xF; // 最大滤波值
// 软件二次验证
if(abs(current_period - last_period) > threshold) {
current_period = last_period; // 保持上次有效值
}
在最近的一个伺服控制项目中,这种组合滤波方式成功将测量稳定性提升了40%。
8. 性能优化实践
8.1 DMA传输应用
高频测量时建议使用DMA减少CPU干预:
c复制// 配置DMA循环传输捕获值
hdma_tim3_ch1.Instance = DMA1_Stream4;
hdma_tim3_ch1.Init.Channel = DMA_CHANNEL_5;
hdma_tim3_ch1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim3_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim3_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim3_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim3_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim3_ch1.Init.Mode = DMA_CIRCULAR;
HAL_DMA_Init(&hdma_tim3_ch1);
__HAL_LINKDMA(&htim3, hdma[TIM_DMA_ID_CC1], hdma_tim3_ch1);
HAL_TIM_IC_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t*)capture_buffer, BUFFER_SIZE);
8.2 定时器级联技术
超低频测量方案(如水文监测):
c复制// TIM2作为主定时器,TIM3作为从定时器
TIM2->CR2 |= TIM_CR2_MMS_1; // 主模式:更新事件触发输出
TIM3->SMCR |= TIM_SMCR_SMS_2; // 从模式:外部时钟模式1
TIM3->SMCR |= TIM_SMCR_TS_0; // 触发源:ITR1(TIM2)
这种配置下,测量范围可扩展至小时级别,我曾成功用于农业灌溉系统的流量计监测。