STM32通用定时器TIM2~5实战:输入捕获测量PWM频率与占空比(附代码)
在嵌入式系统开发中,精确测量外部PWM信号的频率和占空比是常见需求。无论是电机控制、LED调光还是传感器信号处理,都需要对PWM参数进行准确捕获。STM32的通用定时器TIM2~TIM5提供了强大的输入捕获功能,配合PWM输入模式(PWM Input Mode),仅需少量代码即可实现高精度测量。
1. 硬件架构与测量原理
STM32的通用定时器TIM2~TIM5每个都包含4个独立的输入捕获通道,采用三级流水线架构实现信号处理:
- 信号调理层:包含可编程数字滤波器和边沿检测器
- 通道映射层:支持输入通道与捕获通道的灵活交叉配置
- 捕获逻辑层:具有自动重装载和主从触发控制
测量PWM参数时,关键要理解双沿捕获机制:
- 频率测量:需要捕获两个相同极性边沿(如连续上升沿)的时间间隔
- 占空比测量:需要捕获相反极性边沿(如上升沿和后续下降沿)的时间间隔
// 典型PWM信号参数关系 周期T = 上升沿n到上升沿n+1的时间 占空比 = 高电平时间 / 周期T * 100%定时器工作在72MHz时,理论最小可检测脉宽约13.89ns(1/72MHz),实际精度受滤波器配置影响。
2. PWM输入模式配置详解
PWM输入模式是STM32提供的高级测量功能,其核心在于单输入双捕获架构:
| 特性 | 常规输入捕获模式 | PWM输入模式 |
|---|---|---|
| 所需通道数 | 2个 | 1个 |
| 测量方式 | 软件协调 | 硬件自动 |
| 触发逻辑 | 独立配置 | 主从联动 |
| 适用场景 | 简单信号 | 精确PWM |
配置步骤可分为硬件层和软件层:
2.1 硬件电路设计要点
- 信号接入:PWM信号应接入TIMx_CH1或TIMx_CH2引脚
- 滤波电路:高频干扰环境下建议增加RC硬件滤波(如100Ω+100pF)
- 电平匹配:确保输入信号电压在GPIO容忍范围内
注意:TIM2的CH1/CH2对应PA0/PA1,TIM3对应PA6/PA7,不同型号引脚可能不同,需查阅具体芯片数据手册。
2.2 软件配置流程
时钟使能:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO初始化:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // TIM3_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);时基配置:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // 1MHz计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);输入捕获配置:
TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0x0; TIM_ICInit(TIM3, &TIM_ICInitStructure);
3. 双通道捕获实战代码
以下完整代码展示了如何利用TIM3测量PWM频率和占空比:
#include "stm32f10x.h" volatile uint32_t IC3ReadValue1 = 0, IC3ReadValue2 = 0; volatile uint32_t DutyCycle = 0, Frequency = 0; void TIM3_Config(void) { TIM_ICInitTypeDef TIM_ICInitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 时钟使能 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* GPIO配置 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 时基配置 */ TIM_TimeBaseStructure.TIM_Period = 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // 1MHz TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); /* 输入捕获配置 - 通道1 */ TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0x0; TIM_ICInit(TIM3, &TIM_ICInitStructure); /* 输入捕获配置 - 通道2 */ TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; TIM_ICInit(TIM3, &TIM_ICInitStructure); /* PWM输入模式特定配置 */ TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* 中断配置 */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE); TIM_Cmd(TIM3, ENABLE); } void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) { IC3ReadValue1 = TIM_GetCapture1(TIM3); TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); } if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) { IC3ReadValue2 = TIM_GetCapture2(TIM3); DutyCycle = (IC3ReadValue2 * 100) / IC3ReadValue1; Frequency = 1000000 / IC3ReadValue1; // 单位Hz TIM_ClearITPendingBit(TIM3, TIM_IT_CC2); } } int main(void) { TIM3_Config(); while(1) { // 测量结果存储在全局变量中: // DutyCycle - 占空比(百分比) // Frequency - 频率(Hz) } }4. 性能优化与误差处理
实际应用中需要考虑以下关键因素来提升测量精度:
4.1 数字滤波器配置
TIMx_CCMRx寄存器中的ICxF位控制滤波参数:
| ICxF值 | 采样频率 | 采样次数 | 适用场景 |
|---|---|---|---|
| 0000 | 无滤波 | 1 | 清洁环境 |
| 0010 | fDTS/4 | 4 | 轻度干扰 |
| 0101 | fDTS/16 | 8 | 工业环境 |
| 1011 | fDTS/32 | 6 | 强干扰 |
// 设置通道1滤波器为8次采样 TIM3->CCMR1 |= (0x05 << 4);4.2 测量范围与精度平衡
通过调整预分频器实现不同量程:
| 预分频值 | 计时频率 | 最小分辨率 | 最大周期 |
|---|---|---|---|
| 0 | 72MHz | 13.89ns | 0.9ms |
| 71 | 1MHz | 1μs | 65.5ms |
| 719 | 100kHz | 10μs | 655ms |
4.3 常见问题排查
无中断触发:
- 检查GPIO模式应为浮空输入或上拉输入
- 验证TIMx_CCER寄存器CCxE位是否使能
- 确认NVIC中断控制器配置正确
测量值不稳定:
- 增加数字滤波器设置
- 检查电源稳定性
- 缩短信号走线长度
占空比计算异常:
- 确保两个通道极性配置相反
- 检查通道映射关系(直接/间接TI)
- 验证主从模式是否使能
提示:使用示波器对比实际信号与测量结果,可以快速定位硬件或软件问题。