STM32C552定时器输入捕获:精确测量PWM频率与占空比实战

发布时间:2026/7/25 14:52:41
STM32C552定时器输入捕获:精确测量PWM频率与占空比实战 在嵌入式开发中频率测量是一个常见需求特别是在电机控制、电源管理、通信协议分析等场景。STM32C552系列微控制器提供了强大的定时器功能其中输入捕获模式能够精确测量外部信号的频率和占空比。本文将详细介绍如何使用STM32C552的TIM15定时器配置输入捕获功能实现对TIM1生成的PWM信号进行频率和占空比测量。1. 输入捕获技术原理1.1 输入捕获基本概念输入捕获是定时器的重要功能之一主要用于测量外部信号的脉冲宽度、周期和频率。当检测到指定的边沿上升沿或下降沿时定时器会立即将当前计数器的值保存到捕获寄存器中并产生中断请求。STM32C552的定时器输入捕获功能具有以下特点支持上升沿、下降沿或双边沿触发可配置输入滤波消除噪声干扰支持直接模式或间接模式可与其他定时器功能协同工作1.2 频率测量原理通过输入捕获测量频率的基本原理是捕获两个连续上升沿的时间间隔这个时间间隔就是信号的周期其倒数即为频率。计算公式频率 定时器计数频率 / 捕获的周期计数值 占空比 高电平时间 / 周期 × 100%在STM32C552中TIM15定时器的时钟频率为144MHz通过合理设置预分频器可以获得适合的计数频率来进行精确测量。2. 硬件环境准备2.1 开发板配置本实验使用STM32C552CET6或STM32C552CEU6作为主控芯片。需要准备以下硬件STM32C552开发板USB转串口模块用于调试输出杜邦线若干逻辑分析仪可选用于验证结果2.2 引脚连接方案信号连接如下TIM1_CH3 (PA5) → PWM信号输出TIM15_CH1 (PA7) → 输入捕获引脚USART1 (PA9/PA10) → 串口调试使用杜邦线将PA5与PA7连接实现PWM信号的自发自收测量。2.3 开发环境IDE: STM32CubeIDE 或 Keil MDK固件库: HAL库调试工具: ST-Link调试器串口工具: 波特率1152003. STM32CubeMX工程配置3.1 时钟树配置首先配置系统时钟确保TIM15获得正确的时钟源HSE频率根据实际晶振设置通常8MHz系统时钟144MHzAPB1定时器时钟144MHzAPB2定时器时钟144MHz3.2 TIM1 PWM输出配置TIM1配置为PWM输出模式参数设置如下// TIM1基础参数 TIM1_CLK 144 MHz ARR 9999 PSC 14399 // 用于生成1Hz PWM在CubeMX中配置TIM1Mode: PWM Generation CH3Channel: CH3 (PA5)Prescaler: 14399Counter Period: 9999Pulse: 2500 (25%占空比)或7500 (75%占空比)Clock Division: NoneAutoReload Preload: Enable3.3 TIM15输入捕获配置TIM15配置为输入捕获模式关键参数设置// 在CubeMX中配置TIM15 TIM15 Clock 144 MHz Channel: CH1 (PA7) Mode: Input Capture direct mode Polarity: Both Edges Prescaler: 1 (不分频) Filter: No filter具体配置步骤在Timers中选择TIM15选择Channel1为Input Capture direct mode设置IC Polarity为Rising Edge实际代码中改为双边沿配置Prescaler为0即1分频Counter Period设置为6553516位最大值使能TIM15全局中断3.4 USART1串口配置用于输出测量结果Mode: AsynchronousBaud Rate: 115200Word Length: 8 BitsParity: NoneStop Bits: 1GPIO: PA9(TX), PA10(RX)4. 代码实现详解4.1 工程头文件包含在main.c文件中添加必要的头文件#include main.h #include mx_usart1.h #include mx_tim15.h #include stdio.h #include string.h // 定时器计数频率配置 #define TIM15_CLK_HZ 144000000UL #define TIM15_COUNTER_HZ 1000000UL #define TIM15_PRINT_INTERVAL 50U // 预分频器计算 #define TIM15_PSC ((TIM15_CLK_HZ / TIM15_COUNTER_HZ) - 1U) #define TIM15_ARR 0xFFFFU4.2 printf重定向实现重写_write函数使printf输出到串口// 文件main.c int _write(int file, char *ptr, int len) { hal_uart_handle_t *huart1 mx_usart1_uart_gethandle(); if (huart1 ! NULL) { HAL_UART_Transmit(huart1, (uint8_t*)ptr, len, 1000); } return len; }4.3 输入捕获变量定义定义用于存储捕获数据的全局变量// 输入捕获相关变量 static volatile uint32_t tim15_rise_last 0; static volatile uint32_t tim15_rise_now 0; static volatile uint32_t tim15_fall_now 0; static volatile uint32_t tim15_period_count 0; static volatile uint32_t tim15_high_count 0; static volatile uint32_t tim15_print_freq 0; static volatile uint32_t tim15_print_duty_x10 0; static volatile uint32_t tim15_print_period 0; static volatile uint32_t tim15_print_high 0; static volatile uint8_t tim15_print_flag 0; static volatile uint8_t tim15_rise_valid 0; static volatile uint8_t tim15_fall_valid 0; static volatile uint32_t tim15_measure_count 0;4.4 计数器溢出处理函数由于TIM15是16位计数器需要处理溢出情况static uint32_t TIM15_GetDiff(uint32_t start, uint32_t end) { if (end start) { return end - start; } else { return (TIM15_ARR 1U - start) end; } }4.5 定时器动态配置函数在代码中动态配置TIM15参数提高灵活性static hal_status_t TIM15_IC_SetPSC_ARR(uint32_t psc, uint32_t arr) { hal_tim_handle_t* htim15 mx_tim15_gethandle(); if (htim15 NULL) { return HAL_ERROR; } if (HAL_TIM_SetPrescaler(htim15, psc) ! HAL_OK) { return HAL_ERROR; } if (HAL_TIM_SetPeriod(htim15, arr) ! HAL_OK) { return HAL_ERROR; } if (HAL_TIM_SetCounter(htim15, 0) ! HAL_OK) { return HAL_ERROR; } return HAL_OK; }4.6 输入捕获启动函数启动TIM15输入捕获功能static hal_status_t TIM15_IC_Start(void) { hal_tim_handle_t* htim15 mx_tim15_gethandle(); if (htim15 NULL) { return HAL_ERROR; } // 启动TIM15_CH1输入捕获中断 if (HAL_TIM_IC_StartChannel_IT(htim15, HAL_TIM_CHANNEL_1) ! HAL_OK) { return HAL_ERROR; } // 启动TIM15计数器 if (HAL_TIM_Start(htim15) ! HAL_OK) { return HAL_ERROR; } return HAL_OK; }4.7 输入捕获回调函数这是核心的测量逻辑处理上升沿和下降沿捕获void HAL_TIM_InputCaptureCallback(hal_tim_handle_t* htim, hal_tim_channel_t channel) { uint32_t capture_value; uint32_t period; uint32_t high; uint32_t freq; uint32_t duty_x10; uint32_t pin_level; // 检查是否为TIM15_CH1的中断 if ((htim ! mx_tim15_gethandle()) || (channel ! HAL_TIM_CHANNEL_1)) { return; } // 读取捕获值 capture_value HAL_TIM_IC_ReadChannelCapturedValue(htim, HAL_TIM_CHANNEL_1); // 读取PA7当前电平判断边沿类型 pin_level (GPIOA-IDR GPIO_IDR_ID7); if (pin_level ! 0U) { // 上升沿处理 if (tim15_rise_valid 0U) { tim15_rise_last capture_value; tim15_rise_valid 1U; tim15_fall_valid 0U; } else { tim15_rise_now capture_value; period TIM15_GetDiff(tim15_rise_last, tim15_rise_now); if ((period ! 0U) (tim15_fall_valid ! 0U)) { high TIM15_GetDiff(tim15_rise_last, tim15_fall_now); if (high period) { // 计算频率和占空比 freq TIM15_COUNTER_HZ / period; duty_x10 (high * 1000U) / period; // 放大10倍便于显示小数 tim15_period_count period; tim15_high_count high; tim15_measure_count; // 累计一定次数后准备打印 if (tim15_measure_count TIM15_PRINT_INTERVAL) { tim15_measure_count 0; tim15_print_freq freq; tim15_print_duty_x10 duty_x10; tim15_print_period period; tim15_print_high high; tim15_print_flag 1; } } } tim15_rise_last tim15_rise_now; tim15_fall_valid 0U; } } else { // 下降沿处理 if (tim15_rise_valid ! 0U) { tim15_fall_now capture_value; tim15_fall_valid 1U; } } }4.8 主函数实现主函数中初始化各个模块并启动测量int main(void) { // 系统初始化 if (mx_system_init() ! SYSTEM_OK) { return (-1); } printf(STM32C552 TIM15 Input Capture Frequency Measurement\r\n); printf(TIM1 PWM Output: 1000Hz, CH375%% Duty Cycle\r\n); // 配置TIM1生成PWM信号 TIM1_SetFrequency(1000); // 设置1kHz频率 TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3, TIM1_PWM_ARR, 75); // 75%占空比 TIM1_PWM_Start_CH1_CH3(); // 启动PWM输出 // 配置并启动TIM15输入捕获 if (TIM15_IC_SetPSC_ARR(TIM15_PSC, TIM15_ARR) ! HAL_OK) { printf(TIM15 IC set PSC/ARR failed\r\n); } if (TIM15_IC_Start() ! HAL_OK) { printf(TIM15 IC start failed\r\n); } printf(Input Capture Started...\r\n); // 主循环 while (1) { if (tim15_print_flag) { uint32_t freq; uint32_t duty_x10; uint32_t period; uint32_t high; // 关中断读取变量避免数据不一致 __disable_irq(); tim15_print_flag 0; freq tim15_print_freq; duty_x10 tim15_print_duty_x10; period tim15_print_period; high tim15_print_high; __enable_irq(); // 打印测量结果 printf(TIM15 Measured: freq%lu Hz, duty%lu.%lu%%, period%lu, high%lu\r\n, freq, duty_x10 / 10U, duty_x10 % 10U, period, high); } HAL_Delay(100); // 短暂延时减少CPU负载 } }5. 测量结果分析与验证5.1 理论计算与实测对比对于1000Hz、75%占空比的PWM信号理论周期1000us理论高电平时间750usTIM15计数频率1MHz1计数1us理论测量值周期计数值1000高电平计数值750频率1000Hz占空比75.0%5.2 实际测量结果在实际测试中可能会观察到以下结果TIM15 Measured: freq1025 Hz, duty75.3%, period975, high734这种微小差异可能由以下因素引起时钟源精度误差信号传输延迟中断响应时间计数器量化误差5.3 精度优化策略提高测量精度的几种方法提高定时器计数频率在允许范围内提高TIM15的计数频率多次测量取平均对多次测量结果进行平均计算信号调理添加硬件滤波电路减少噪声时钟校准使用更高精度的外部晶振6. 常见问题与解决方案6.1 测量值不稳定或跳变问题现象测量结果在不同值之间跳变无法稳定可能原因信号存在噪声干扰中断处理时间过长计数器溢出处理不当解决方案// 启用输入捕获滤波 // 在CubeMX中设置IC Filter为合适的值如0x4 // 优化中断处理函数减少执行时间 void HAL_TIM_InputCaptureCallback(hal_tim_handle_t* htim, hal_tim_channel_t channel) { // 尽快读取捕获值 uint32_t capture_value HAL_TIM_IC_ReadChannelCapturedValue(htim, channel); // 将复杂计算移到主循环中处理 // 中断中只做必要的数据采集 }6.2 无法进入输入捕获中断问题现象程序运行但无法进入捕获回调函数可能原因定时器中断未使能GPIO引脚配置错误时钟配置不正确排查步骤检查CubeMX中TIM15全局中断是否使能验证PA7引脚配置为Alternate Function模式确认TIM15时钟源已正确配置检查NVIC中断优先级设置6.3 测量频率范围受限问题现象高频信号测量不准确或低频信号响应慢解决方案// 动态调整预分频器适应不同频率范围 void Adjust_Capture_Range(uint32_t expected_freq) { uint32_t new_psc; if (expected_freq 10000) { // 高频信号提高计数频率 new_psc (TIM15_CLK_HZ / 10000000UL) - 1; // 10MHz计数 } else if (expected_freq 100) { // 低频信号降低计数频率延长测量范围 new_psc (TIM15_CLK_HZ / 1000UL) - 1; // 1kHz计数 } else { // 中频信号 new_psc (TIM15_CLK_HZ / 1000000UL) - 1; // 1MHz计数 } TIM15_IC_SetPSC_ARR(new_psc, TIM15_ARR); }7. 性能优化与进阶应用7.1 DMA辅助测量对于高频信号测量可以使用DMA减少CPU干预// 配置DMA进行连续捕获 HAL_TIM_IC_Start_DMA(htim, Channel, pData, Length);7.2 多通道同步测量同时测量多个信号的频率和相位关系// 配置TIM15的多个输入捕获通道 HAL_TIM_IC_StartChannel_IT(htim15, HAL_TIM_CHANNEL_1); HAL_TIM_IC_StartChannel_IT(htim15, HAL_TIM_CHANNEL_2);7.3 高精度时间戳应用结合RTC实现长时间戳记录// 获取精确的时间戳 uint32_t timestamp HAL_GetTick(); uint32_t capture_value HAL_TIM_IC_ReadChannelCapturedValue(htim, channel);7.4 自动量程切换实现自适应不同频率范围的智能测量void Auto_Range_Measurement(void) { uint32_t measured_freq Get_Current_Frequency(); if (measured_freq current_range_max) { Switch_To_Higher_Range(); } else if (measured_freq current_range_min) { Switch_To_Lower_Range(); } }通过本文介绍的STM32C552定时器输入捕获测量频率方法开发者可以快速实现精确的频率测量功能。这种技术在实际项目中具有广泛的应用价值特别是在需要监控电机转速、电源频率、传感器信号等场景中。