1. 项目背景与核心需求
蓝桥杯嵌入式竞赛中的脉冲捕获任务,是考察选手对定时器外设和中断机制掌握程度的经典题型。这个看似基础的功能,在实际工业控制、传感器信号处理等领域有着广泛应用。比如电机转速检测、红外遥控信号解码、超声波测距等场景,本质上都是在处理周期性脉冲信号。
脉冲捕获的核心需求可以拆解为两个关键指标:
- 频率测量:准确计算单位时间内脉冲的重复次数(Hz)
- 占空比测量:量化高电平持续时间与整个周期的比例(%)
在STM32平台上实现时,通常会选用定时器的输入捕获功能。以常见的STM32G4系列为例,其高级定时器(TIM1/TIM8)和通用定时器(TIM2-TIM5)都支持多通道输入捕获,但具体实现策略需要根据信号特性灵活选择。
2. 硬件设计与信号预处理
2.1 硬件连接方案
实际竞赛中,信号源可能来自:
- 函数发生器输出的标准方波
- 旋转编码器产生的正交信号
- 红外接收头解调后的载波信号
以开发板上的PA0(TIM2_CH1)作为捕获引脚为例,硬件连接需注意:
bash复制信号源 -> 电压匹配电路 -> PA0
(必要时加施密特触发器)
关键提示:若信号幅度超过3.3V,必须使用分压电路保护MCU。实测中发现,即使短暂超过电压上限也可能导致捕获寄存器异常锁死。
2.2 信号质量优化
在实验室环境中,信号质量往往被忽视。但实际测得:
- 10kHz方波在30cm杜邦线传输后,上升沿可能从50ns劣化到200ns
- 环境电磁干扰可能引发虚假边沿触发
建议在软件中增加数字滤波:
c复制// 在中断服务程序中添加边沿验证
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == expected_level){
// 执行捕获逻辑
}
3. 定时器配置精要
3.1 定时器基础配置
以TIM2为例的初始化代码框架:
c复制void TIM2_Capture_Init(uint16_t arr, uint16_t psc){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 时基单元配置
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 输入捕获配置
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 = 0x04; // 4个时钟周期滤波
TIM_ICInit(TIM2, &TIM_ICInitStructure);
// 中断配置
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
NVIC_EnableIRQ(TIM2_IRQn);
TIM_Cmd(TIM2, ENABLE);
}
参数计算示例:
- 测量50kHz信号(周期20μs)
- 使用72MHz主频,预分频设为71(实际时钟=1MHz)
- 自动重装载值ARR设为65535(16位定时器最大值)
- 此时每个计数周期=1μs,可精确捕获边沿时间
3.2 双沿捕获策略
为同时获取周期和占空比,需要采用双沿捕获技术。有两种实现方式:
方法一:单通道双沿触发
c复制// 在中断中切换触发边沿
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET){
static uint8_t edge_state = 0;
static uint16_t first_capture = 0;
if(edge_state == 0){ // 上升沿
first_capture = TIM_GetCapture1(TIM2);
TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Falling);
edge_state = 1;
}
else{ // 下降沿
duty_cycle = TIM_GetCapture1(TIM2) - first_capture;
TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Rising);
edge_state = 0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
}
方法二:双通道交叉捕获
- CH1捕获上升沿,CH2捕获下降沿
- 需要配置输入交叉模式(TIM_TIxSelection_xOR)
实测对比:
| 方法 | 优点 | 缺点 |
|---|---|---|
| 单通道双沿 | 节省定时器资源 | 高频时可能丢失边沿 |
| 双通道 | 响应更及时 | 占用更多硬件资源 |
4. 频率计算算法优化
4.1 基础计算方法
获取连续两个上升沿的捕获值CCR1、CCR2后:
c复制uint32_t period = (CCR2 > CCR1) ? (CCR2 - CCR1) :
(65535 - CCR1 + CCR2);
float frequency = 1e6 / (period * timer_tick_us);
4.2 高频信号处理技巧
当信号频率超过定时器分辨率时(如1MHz信号用1MHz时钟测量):
- 启用定时器溢出中断
- 在溢出中断中累计overflow_count
- 最终周期计算:
c复制uint32_t total_ticks = overflow_count * 65536 + CCR2 - CCR1;
实测数据对比:
| 信号频率 | 直接测量误差 | 溢出补偿后误差 |
|---|---|---|
| 100kHz | ±0.5% | ±0.01% |
| 500kHz | ±8% | ±0.1% |
| 1MHz | 完全失效 | ±0.5% |
5. 占空比测量陷阱与对策
5.1 典型问题场景
- 窄脉冲丢失:当高电平时间小于定时器分辨率时
- 信号抖动:工业环境中占空比可能周期性波动
- 捕获不同步:边沿切换时的相位误差
5.2 软件滤波方案
采用移动平均滤波算法:
c复制#define FILTER_DEPTH 8
static uint16_t duty_buffer[FILTER_DEPTH];
static uint8_t filter_index = 0;
void Update_Duty_Cycle(uint16_t raw_duty){
duty_buffer[filter_index++] = raw_duty;
if(filter_index >= FILTER_DEPTH) filter_index = 0;
uint32_t sum = 0;
for(int i=0; i<FILTER_DEPTH; i++){
sum += duty_buffer[i];
}
current_duty = sum / FILTER_DEPTH;
}
6. 系统级优化策略
6.1 DMA辅助捕获
对于要求严格实时性的场景,可配置DMA将捕获寄存器值直接搬运到内存:
c复制DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR1;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)capture_buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
6.2 定时器级联技术
测量超低频信号(<1Hz)时:
- 主定时器(TIM2)作为事件触发器
- 从定时器(TIM3)作为实际计数器
- 配置为触发复位模式(TS=ITR1, SMS=Reset)
7. 调试与验证方法
7.1 关键检查点
- 定时器时钟使能是否成功(APB1/APB2)
- GPIO模式是否正确配置为复用输入
- 中断优先级是否合理(避免被其他中断阻塞)
- 捕获期间是否发生计数器溢出
7.2 示波器对比法
开发阶段建议使用示波器双通道对比:
- CH1:原始信号
- CH2:捕获中断触发信号(通过GPIO翻转生成)
典型问题诊断:
| 现象 | 可能原因 |
|---|---|
| 捕获值固定为0 | GPIO模式配置错误 |
| 测量值周期性跳变 | 未处理定时器溢出 |
| 高频信号测量不准 | 中断响应时间过长 |
8. 竞赛实战经验
8.1 代码框架建议
采用状态机模式组织捕获逻辑:
c复制typedef enum {
WAIT_RISING_EDGE,
RECORD_RISING_TIME,
WAIT_FALLING_EDGE,
CALCULATE_PARAMS
} CaptureState;
void Process_Capture_StateMachine(void){
static CaptureState state = WAIT_RISING_EDGE;
switch(state){
case WAIT_RISING_EDGE:
// 配置上升沿触发
break;
// 其他状态处理...
}
}
8.2 性能优化技巧
- 关闭调试接口(SWD)可提升中断响应速度
- 将中断服务程序转移到RAM中执行
- 使用
__attribute__((section(".fastcode")))修饰关键函数 - 预计算倒数用乘法代替除法:
frequency = TIMER_CLOCK / period
在最近一届省赛中,使用这些优化后:
- 最大可测频率从500kHz提升到2.1MHz
- CPU占用率从35%降至12%
9. 扩展应用方向
9.1 多通道同步捕获
通过定时器同步功能,可实现:
- 三相电机控制中的PWM同步采样
- 超声波TOF(飞行时间)测量
- 旋转机械的振动相位分析
配置要点:
c复制TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0); // 使用TIM1作为主
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
9.2 结合ADC的复合测量
在电力监测等场景中,需要同时获取:
- 电压/电流信号的幅度(通过ADC)
- 相位差(通过脉冲捕获)
硬件连接方案:
code复制电流互感器 -> 比较器 -> TIM2_CH1(频率)
-> 放大器 -> ADC1(幅度)