STM32 HAL库开发学习15. 通用定时器输入捕获
STM32 HAL开发 通用定时器实现输入捕获
STM32 HAL库开发学习15. 通用定时器输入捕获
一、通用定时器输入捕获框图
1. 下图是输入捕获在整体框图上的位置

2. 输入捕获框图
以通道1为例,输入部分框图如下:
根据信号方向 , 从左向右对模块解析如下:
(1) 输入信号与滤波(Filter)
- 输入源:左侧的
TI1是外部引脚输入(如定时器通道 1 的引脚),fDTS是定时器的数字滤波时钟(由TIMx_CR1的DTS位配置)。 - 滤波模块:
filter down-counter是 数字滤波器,通过TIMx_CCMR1的ICF[3:0]位配置(设置滤波采样次数和频率,用于消抖或滤除高频噪声)。滤波后输出TI1F(Filtered TI1 信号)。
(2)边缘检测(Edge Detector)
- 检测
TI1F的 上升沿(TI1F_rising) 和 下降沿(TI1F_falling),区分信号的跳变方向。
(3)极性控制与多路选择(上半部分)
- 多路选择器(MUX1):由
TIMx_CCER的CC1P位控制(极性选择):CC1P=0:选择 上升沿(TI1F_rising);CC1P=1:选择 下降沿(TI1F_falling)(信号反相,检测下降沿)。
- 输出
TI1F_ED(Edge Detected TI1 信号),送往 从模式控制器(Slave Mode Controller),用于触发定时器的启动、停止或复位(如门控模式、触发模式等)。
(4)跨通道信号与从模式信号(下半部分)
- 通道 2 信号:
TI2F_rising/TI2F_falling是定时器通道 2 滤波后的边缘信号,可通过另一路 MUX 引入(用于 交叉触发,如通道 1 捕获通道 2 的信号)。 - 从模式信号(TRC):来自从模式控制器的内部触发信号(如定时器同步、外部触发的反馈)。
(5)输入源选择与分频(IC1 路径)
- 多路选择器(IC1 MUX):由
TIMx_CCMR1的CC1S[1:0]位配置,选择 IC1 的输入源:CC1S=00:禁用输入捕获(通道作为输出比较);CC1S=01:选择 TI1FP1(通道 1 滤波+边缘检测后的信号);CC1S=10:选择 TI2FP1(通道 2 滤波+边缘检测后的信号,交叉捕获);CC1S=11:选择 TRC(从模式控制器的信号,用于同步或触发)。
- 分频器(Divider):由
TIMx_CCMR1的ICPS[1:0]位配置,对选中的信号进行 1/2/4/8 分频,输出IC1PS(分频后的捕获信号),用于控制捕获事件的频率。
(6)使能控制(CC1E)
TIMx_CCER的CC1E位是 输入捕获使能位,置 1 时才会激活上述整个输入捕获链路。
3. 捕获/比较通道1主电路

上图是通道1的寄存器数据流向与模式控制框图。捕获/比较通道采用双缓冲设计,由预装载寄存器供CPU读写,蚊子寄存器供硬件实时操作。具体地,按模块解析如下:
(1)核心结构:双缓冲寄存器(预装载 + 影子)
Capture/Compare Preload Register(预装载寄存器):
- CPU 直接读写的寄存器(通过 APB 总线),16 位寄存器分为高 8 位(CCR1H)和低 8 位(CCR1L)。
- 作用:隔离 CPU 读写与定时器硬件操作,避免实时运行时的冲突。
Capture/Compare Shadow Register(影子寄存器):
- 硬件实际使用的寄存器(与计数器、比较器直接交互),不可被 CPU 直接访问。
- 作用:确保波形生成或捕获操作的 原子性(避免中途被 CPU 改写数据)。
(2)模式切换:输入捕获 vs 输出比较(由 CC1S[1:0] 控制)
输入模式(捕获,CC1S≠00):
通道作为 输入捕获(如测量脉冲宽度、频率),此时:
- 外部信号经滤波、边缘检测后,触发 capture 信号。
- capture 信号将 计数器(CNT)的值 锁存到 影子寄存器,再通过 capture_transfer 传到 预装载寄存器(供 CPU 读取)。
输出模式(比较,CC1S=00):
通道作为 输出比较(如生成 PWM 波形),此时:
- CPU 将比较值写入 预装载寄存器,若 OC1PE=1(输出比较预装载使能,TIMx_CCMR1 位),则需等待 更新事件(UEV,来自时基单元) 触发 compare_transfer,将预装载值传到 影子寄存器。
- 影子寄存器的值与 计数器(CNT) 实时比较(comparator 模块),判断 CNT>CCR 或 CNT=CCR,触发输出电平变化。
(3)捕获触发条件(输入模式)
捕获操作由以下信号触发(逻辑或关系):
- ic1ps:前序输入捕获链路的分频后信号(如边缘检测后的触发信号)。
- CC1E:输入捕获使能位(TIMx_CCER 位,必须置 1 才允许捕获)。
- CC1G:软件触发位(TIMx_EGR 位,用于手动触发捕获,调试常用)。
(4)比较逻辑(输出模式)
比较器实时判断 计数器(CNT) 与 影子寄存器(CCR) 的关系:
- CNT > CCR:对应比较模式的 “上溢” 触发(如 PWM 模式的上升沿)。
- CNT = CCR:对应比较模式的 “匹配” 触发(如 PWM 模式的下降沿)。
根据配置(TIMx_CCMR1 的输出模式位),触发输出电平翻转、中断或 DMA 请求。
(5)寄存器读写流程
读操作(CPU 读 CCR):
触发 read_in_progress 时,从 预装载寄存器 读取数据(避免直接读影子寄存器时,硬件正在更新数据导致错误)。
写操作(CPU 写 CCR):
触发 write_in_progress 时,先写入 预装载寄存器:
- 若为 输出模式 且 OC1PE=1:需等待 更新事件(UEV) 触发 compare_transfer,才将预装载值传到影子寄存器。
- 若 OC1PE=0:预装载值会 立即 传到影子寄存器(无缓冲,实时生效,可能引发波形突变)。
二、通用定时器输入捕获脉宽测量原理
1. 核心原理
定时器持续计数(如按固定频率递增),当外部引脚(连接定时器通道)出现预设的电平跳变(如上升沿)时,立即将当前计数值锁存到 “捕获寄存器”。通过两次捕获的计数值之差,即可计算信号的时间特征。
2. 工作流程(以测量周期为例)
假设测量一个方波信号的周期:
第一次捕获(上升沿):当信号从低→高跳变时,捕获定时器值 t1。
第二次捕获(下一个上升沿):信号再次上升沿时,捕获定时器值 t2。
计算周期:周期 = t2 - t1(需考虑定时器溢出,需额外处理溢出标志)。
三、配置过程
1. 引脚与复用配置
将 GPIO 配置为 复用功能模式,连接到定时器的输入捕获通道(如 TIM3_CH1 对应 PA6 引脚)。
2. 定时器时基配置
- 计数频率:由定时器时钟(如 APB 总线时钟)和分频器决定(例:时钟 72MHz,分频器设为 71,则计数频率为 1MHz,即 1 个计数值对应 1μs)。
- 计数模式:通常用向上计数(从 0 递增到自动重装值后溢出)。
3. 输入捕获模式配置
- 触发边沿:通过寄存器选择上升沿、下降沿或两者(如TIMx_CCER的CCxP位设置极性)。
- 捕获预分频:可选 “每 1/2/4/8 个边沿捕获一次”(减少中断频率,如TIMx_CCMRx的ICxPSC位)。
- 捕获寄存器:如TIMx_CCR1,用于存储捕获的计数值。
4. 中断或 DMA 配置
- 捕获发生时可触发中断(如TIM_IT_CC1),在中断服务程序中读取TIMx_CCRx的值。
- 也可配置DMA直接传输捕获值,减少 CPU 负担。
四、CubeMX配置
1. 模式配置
这里选择TIM2:
- Clock Source:选 Internal Clock(内部时钟驱动定时器)。
- Channel 1:Mode 设为 Input Capture direct mode(直接输入捕获模式)。
- Polarity:先设为 Rising Edge(上升沿捕获,后续代码可切换为下降沿,若需测占空比)。
- Input Filter:根据信号噪声设置(如 0 表示无滤波,或 8 次采样滤波,滤除毛刺)。

2. 时基配置
- Prescaler:分频系数,如 71(使定时器时钟为 72MHz/(71+1) = 1MHz,即 1us/tick)。
- Counter Period:65535(16 位定时器最大计数值,溢出时间 65536us,若周期更长需处理溢出中断)。

3. NVIC 中断配置
勾选 TIM2 global interrupt,设置中断优先级。
4. 引脚复用设置

5. 时钟配置


五、核心代码
1. TIM2初始化
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 65535;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
2. 回调函数打印频率
// 输入捕获回调函数实现
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2) {
static uint32_t prev_value = 0;
static uint32_t period = 0;
static float freq_sum = 0.0f; // 存储频率测量值的总和
static uint16_t capture_count = 0; // 记录捕获次数
static uint8_t first_capture = 1; // 标记是否为首次捕获
uint32_t curr_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if (first_capture) {
prev_value = curr_value;
first_capture = 0;
} else {
// 计算周期(考虑计数器溢出)
period = (curr_value > prev_value) ? (curr_value - prev_value)
: (0xFFFF - prev_value + curr_value);
// 计算频率(单位:Hz)
float freq = 72000000.0f / (htim2.Init.Prescaler + 1) / period;
freq_sum += freq; // 累加频率值
capture_count++; // 捕获次数加 1
if (capture_count >= 500) {
float average_freq = freq_sum / 500.0f; // 计算平均频率
char buffer[50];
sprintf(buffer, "Average Frequency: %.2fHz\r\n", average_freq);
// 通过串口发送测量结果
if (HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY) != HAL_OK) {
// 处理发送失败的情况
Error_Handler();
}
// 重置总和和计数
freq_sum = 0.0f;
capture_count = 0;
}
prev_value = curr_value;
}
}
}
频率计算:
72000000.0f / (htim2.Init.Prescaler + 1) / period
通过PA0注入信号并打印捕获结果:
更多推荐



所有评论(0)