一、通用定时器输入捕获框图

1. 下图是输入捕获在整体框图上的位置

在这里插入图片描述

2. 输入捕获框图

以通道1为例,输入部分框图如下:
在这里插入图片描述
根据信号方向 , 从左向右对模块解析如下:

(1) 输入信号与滤波(Filter)

  • 输入源:左侧的 TI1 是外部引脚输入(如定时器通道 1 的引脚),fDTS 是定时器的数字滤波时钟(由 TIMx_CR1DTS 位配置)。
  • 滤波模块filter down-counter数字滤波器,通过 TIMx_CCMR1ICF[3:0] 位配置(设置滤波采样次数和频率,用于消抖或滤除高频噪声)。滤波后输出 TI1F(Filtered TI1 信号)。

(2)边缘检测(Edge Detector)

  • 检测 TI1F上升沿(TI1F_rising)下降沿(TI1F_falling),区分信号的跳变方向。

(3)极性控制与多路选择(上半部分)

  • 多路选择器(MUX1):由 TIMx_CCERCC1P 位控制(极性选择):
    • 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_CCMR1CC1S[1:0] 位配置,选择 IC1 的输入源:
    • CC1S=00:禁用输入捕获(通道作为输出比较);
    • CC1S=01:选择 TI1FP1(通道 1 滤波+边缘检测后的信号);
    • CC1S=10:选择 TI2FP1(通道 2 滤波+边缘检测后的信号,交叉捕获);
    • CC1S=11:选择 TRC(从模式控制器的信号,用于同步或触发)。
  • 分频器(Divider):由 TIMx_CCMR1ICPS[1:0] 位配置,对选中的信号进行 1/2/4/8 分频,输出 IC1PS(分频后的捕获信号),用于控制捕获事件的频率。

(6)使能控制(CC1E)

  • TIMx_CCERCC1E 位是 输入捕获使能位,置 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注入信号并打印捕获结果:
在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐