以下是基于STM32标准库(以STM32F103为例)实现PWM输入模式(自动双沿捕获)的完整代码,通过配置定时器的PWM输入模式,可自动捕获外部PWM信号的周期(频率)和占空比,无需手动切换边沿。
🛠 完整代码实现
1. 硬件配置
- 引脚:PA6(TIM3_CH1,支持PWM输入模式)
- 定时器:TIM3(支持从模式复位)
- 功能:自动捕获上升沿(周期)和下降沿(占空比)
#include "stm32f10x.h"// 全局变量存储PWM参数
volatile uint32_t PWM_Period = 0; // 周期值(频率计算用)
volatile uint32_t PWM_Duty = 0; // 占空比值// 初始化函数
void PWM_Input_Init(void)
{// 1. 开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// 2. 配置PA6为浮空输入(复用功能)GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStruct);// 3. 定时器基础配置(1MHz计数频率,72MHz主频分频后)TIM_TimeBaseInitTypeDef TIM_BaseStruct;TIM_BaseStruct.TIM_Period = 0xFFFF; // ARR = 65535(最大计数)TIM_BaseStruct.TIM_Prescaler = 72 - 1; // 72分频 → 1MHz计数TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_BaseStruct);// 4. 配置PWM输入模式(关键!)TIM_ICInitTypeDef TIM_ICStruct;TIM_ICStruct.TIM_Channel = TIM_Channel_1; // 通道1(PA6)TIM_ICStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿触发TIM_ICStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入TIM_ICStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频TIM_ICStruct.TIM_ICFilter = 0x0; // 无滤波TIM_PWMIConfig(TIM3, &TIM_ICStruct); // 自动配置双通道// 5. 从模式配置(复位模式)TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); // TI1作为触发源TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // 触发时复位计数器// 6. 开启捕获中断TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE); // 使能通道1&2中断// 7. 配置NVICNVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);// 8. 启动定时器TIM_Cmd(TIM3, ENABLE);
}// 中断服务函数
void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) { // 通道1中断(周期捕获)PWM_Period = TIM_GetCapture1(TIM3); // 读取周期值(上升沿时间)TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除标志}if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET) { // 通道2中断(占空比捕获)PWM_Duty = TIM_GetCapture2(TIM3); // 读取占空比值(下降沿时间)TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);}
}// 计算频率(Hz)和占空比(%)
float PWM_Get_Frequency(void)
{return 1000000.0f / (PWM_Period ); // 1MHz计数 → 单位:Hz
}float PWM_Get_DutyCycle(void)
{return (PWM_Duty + 0.0f) / (PWM_Period + 0.0f) * 100.0f; // 占空比百分比
}
⚙️ 工作原理
PWM输入模式
- 使用
TIM_PWMIConfig()
自动配置两个通道:- 通道1(直接输入):捕获上升沿 → 周期值存入CCR1。
- 通道2(间接输入):捕获下降沿 → 占空比值存入CCR2。
- 无需手动切换极性,硬件自动完成双沿捕获。
- 使用
从模式复位
- 触发源设为
TI1FP1
(通道1的滤波后信号)。 - 从模式设为
Reset
:上升沿时复位计数器,确保每次捕获的周期值准确。
- 触发源设为
参数计算
- 频率:
Freq = 1MHz / (CCR1 + 1)
- 占空比:
Duty = (CCR2 + 1) / (CCR1 + 1) * 100%
- 频率:
⚠️ 关键注意事项
- 引脚复用
- 确保PA6映射到TIM3_CH1(参考芯片手册)。
- 时钟配置
- 默认使用72MHz系统时钟,需在
SystemInit()
中正确配置。
- 默认使用72MHz系统时钟,需在
- 抗干扰设计
- 若信号抖动大,增加
TIM_ICStruct.TIM_ICFilter
(0-15)滤波。
- 若信号抖动大,增加
- 测量范围
- 最大频率受限于定时器计数频率(1MHz下最高约500kHz)。
🔍 扩展应用
- 多通道捕获:若需同时捕获多路PWM,使用不同定时器(如TIM2/TIM4)。
- 高精度需求:减少预分频值(如
TIM_Prescaler = 0
)可提升计时分辨率。 - 实时处理:在中断中直接计算频率/占空比,避免全局变量延迟。
此代码通过STM32硬件自动完成双沿捕获,显著降低CPU负载,适用于电机控制、遥控信号解析等场景。实际使用时需根据信号特性调整滤波参数,并注意定时器溢出处理(如信号周期 > 65ms)