STM32定时器详解
详细讲解STM32定时器的所有功能
一、基本概念讲解
1.1 定时器分类
STM32 把片上定时器按功能复杂度分成 3 大类。分别为:基本定时器、通用定时器和高级定时器。
口诀:“基本最简单,通用最常用,高级最强大”。
| 类别 | 典型编号(以 F1/F4 为例) | 位数 | 计数方向 | 外部 IO | 特色功能 | 常见用途 |
|---|---|---|---|---|---|---|
| 基本定时器 | TIM6、TIM7 | 16 位 | 只能向上 | 无 | 单纯定时、触发 DAC、DMA | 延时、周期性中断 |
| 通用定时器 | TIM2~TIM5,TIM9~TIM14 | 16/32 位 | 向上/向下/中心对齐 | 有 | 输入捕获、输出比较、PWM、编码器接口、主从触发 | 测频率、PWM 灯/电机、脉冲计数 |
| 高级定时器 | TIM1、TIM8 | 16 位 | 向上/向下/中心对齐 | 有(最多 8 路) | 通用全部功能 + 互补 PWM + 死区 + 刹车 | 三相电机、数字电源、逆变器 |
对于外部IO的讲解:
基本定时器:没有外部通道引脚,只能内部计数或触发 DAC/DMA,所以不能把定时器的功能做到芯片的某个 GPIO 引脚上。(TIM6、TIM7)
通用定时器:定时器有 1~4 个通道引脚,可以接外部信号。(TIM2~TIM5、TIM9~TIM14)
高级定时器:TIM1/TIM8 有 4 个通道,每个通道又能输出互补信号 CHx/CHxN,所以一共 8 路 PWM。(TIM1_CH1、TIM1_CH1N … TIM1_CH4N)
补充说明:
总线与时钟:TIM1/8/10/11 挂在 APB2;其余多在 APB1。
计数器位宽:TIM2/5 在 F4 系列是 32 位,其余多为 16 位。
系统定时器:SysTick 是内核 24 位递减定时器,独立于上述外设,用于系统节拍或延时
下面使用STM32F103C8T6做讲解:
STM32F103C8T6 一共 4 个 16 位定时器,按复杂度分为 基本型、通用型、高级型 三类。
| 定时器 | 类型 | 通道数 | 主要功能 | 典型时钟源 | 简单一句话 |
|---|---|---|---|---|---|
| TIM1 | 高级 | 4 CH(可互补) | 三相 PWM、死区、刹车 | APB2 @ 72 MHz | 电机/逆变器专用 |
| TIM2 | 通用 | 4 CH | 输入捕获、输出比较、PWM、编码器 | APB1 @ 72 MHz | 最常用“万能定时器” |
| TIM3 | 通用 | 4 CH | 同上 | APB1 @ 72 MHz | LED 闪烁/测量脉冲用 |
| TIM4 | 通用 | 4 CH | 同上 | APB1 @ 72 MHz | 与 TIM3 功能相同 |
APB1 对定时器有 ×2 倍频,所以挂在 APB1 的 TIM2/3/4 实际计数时钟 = 72 MHz。TIM6/TIM7 不存在 于 F103C8T6,因此没有“基本定时器”。
1.2 定时器内部模式分类
1.2.1 定时器定时中断
基本原理
定时器通过内部时钟或外部信号计数,当计数器达到设定值时触发中断,实现精准定时。
关键组成部分:
-
计数器(CNT):向上/向下计数,每个时钟周期递增或递减。
-
预分频器(PSC):降低时钟频率(
定时器时钟 = 主时钟 / (PSC + 1))。 -
自动重载寄存器(ARR):定义计数上限,决定定时周期。
-
中断标志:更新事件(UEV)触发中断。
让计数器从 0 数到“设定值”,数到头就自动向 CPU 发一次中断,CPU 可以在这段中断里干任何想干的事。
定时器定时中断 = “硬件闹钟”:配置一次 → 计数器自己跑 → 到点叫 CPU 干活,CPU 其余时间爱干嘛干嘛。
| 参数 | 作用 | 示例值(1ms中断) |
|---|---|---|
| Prescaler (PSC) | 分频系数 | 72-1(72MHz→1MHz) |
| Counter Period (ARR) | 自动重载值 | 1000-1(1MHz→1kHz) |
| Counter Mode | 计数方向(Up/Down) | Up(向上计数) |
计算公式:定时周期 = (PSC + 1) × (ARR + 1) / 定时器时钟频率
1.2.2 内部其它模式
| 功能 | 原理 | 典型场景 |
|---|---|---|
| 触发 DAC 转换 | 更新事件(UEV)直接连到 DAC 的触发输入,无需 CPU 介入 | 音频波形发生器、任意函数发生器 |
| 触发 ADC 采样 | 把更新事件作为 ADC 的外部触发源,实现固定频率采样 | 等间隔数据采集 |
| DMA 请求源 | 每次溢出产生 DMA 请求,搬运数据/控制块 | 连续缓冲区填充、无 CPU 数据流 |
| 主从级联时基 | 自身作为“主定时器”,用 TRGO 去触发/同步其它定时器 | 多定时器同步启动、级联计数 |
上面的功能全部在 MCU 内部完成,不要求任何 GPIO 引脚参与,只需通过内部信号线(TRGO、UEV、DMA 请求等)即可完成
1.3 定时器输出模式分类
STM32 定时器“定时输出模式”可以按功能/用途粗分为 4 大类 8 种。记住下面这张表,选型时直接对照即可。
| 大类 | 具体模式 | 中文名 | 关键特点 | 典型应用 |
|---|---|---|---|---|
| PWM 输出 | PWM1 | 边沿对齐 PWM1 | CNT<CCR 时高,≥CCR 时低 | LED 调光、电机调速 |
| PWM2 | 边沿对齐 PWM2 | 与 PWM1 电平逻辑相反 | 同上,仅极性差异 | |
| Center-aligned PWM | 中心对齐 PWM | 上下计数各翻转一次,谐波更低 | BLDC、逆变器 | |
| 输出比较 | Output Compare (OC) | 输出比较 | 匹配时翻转/置位/清零/无动作 | 产生单次脉冲、频率分频 |
| One-Pulse Mode (OPM) | 单脉冲模式 | 产生一个精确宽度脉冲后自动停止 | 触发采样、激光测距 | |
| GPIO 翻转 | 中断/软件翻转 | 软件定时翻转 | 定时中断里手动写 GPIO | 简单方波、蜂鸣器 |
| 互补输出 | Complementary PWM | 互补 PWM + 死区 | 高级定时器专用,带死区、刹车 | 三相电机驱动 |
如何选模式?
-
只要连续占空比可调波形 → PWM1 / PWM2 / Center-aligned
-
需要精确时间点翻转一次 → 输出比较 OC
-
只想要一个脉冲 → One-Pulse Mode
-
三相桥/逆变器 → 互补 PWM + 死区(TIM1/TIM8)
1.3.1 输出比较简介
输出比较(Output Compare)是 STM32 定时器的重要功能,主要用于精确控制输出信号的电平变化,典型应用包括 PWM 生成、单脉冲输出和定时触发等。其核心原理是通过比较定时器计数器(CNT)和捕获/比较寄存器(CCRx)的值,控制输出引脚的电平状态。

输出比较原理:通过比较定时器的计数器(CNT)和捕获/比较寄存器(CCRx)的值,控制输出引脚(如TIMx_CHx)的电平状态。
关键点如下:
| 比较结果 | 输出行为(以PWM模式1为例) | 典型应用 |
|---|---|---|
| CNT < CCRx | 输出高电平(或低电平,取决于极性配置) | PWM生成、单脉冲输出 |
| CNT ≥ CCRx | 输出低电平(或高电平) | 控制占空比或精确时间触发 |
| CNT == ARR(自动重载值) | 计数器复位,重新从0开始计数 | 决定PWM周期 |
自动重载(ARR):
- 当CNT计数到ARR值时,计数器复位(CNT=0),并产生更新事件(UEV)。
- 决定PWM的周期(
周期 = (ARR + 1) × 计数周期)。
输出比较(CCRx):
- 仅比较CNT与CCRx的值,控制输出引脚电平,不影响计数器复位。
- 决定PWM的占空比(
占空比 = CCRx / (ARR + 1))。
总结:
在捕获/比较寄存器设置了一个比较值,它一直都在和定时器CNT计数器的值进行比较,当CNT的值和自动重装寄存器值相同时,CNT就会复位从零重新开启计数。在寄存器和计数器进行比较的过程中,会出现寄存器里的值小于/大于计数器的值,这时会输出低/高电平。
1.4 定时器输入模式分类
STM32 通用/高级定时器的“输入模式”大体可以分成 4 类 8 种,全部围绕 TIMx_CHx 引脚 做文章,核心目的只有一个:把外部信号的“时间特征”搬进 CCR 寄存器。
| 输入模式 | 中文名 | 典型用途 | 关键特点 |
|---|---|---|---|
| Input Capture Direct | 直接输入捕获 | 测脉宽、测周期、测频率 | 指定边沿→锁存 CNT→产生中断/DMA |
| Input Capture Indirect | 间接输入捕获 | 与通道 1 组成“差分”测占空比 | 同一引脚映射到两个通道,极性相反 |
| PWM Input Mode | PWM 输入模式 | 一次捕获同时得周期 + 占空比 | 通道 1 捕获上升沿,通道 2 捕获下降沿,硬件自动复位计数器 |
| Encoder Interface Mode | 编码器接口 | 正交/方向编码器计数 | 两相边沿计数,自动加减,无需软件 |
外部时钟模式虽然也用外部引脚,但本质是“把引脚当计数时钟”,不算严格意义的“输入捕获”,故未列入。
1.4.1 输入捕获简介
输入捕获的主要作用是用来测量输入信号,如测量波形的频率或者占空比。其核心原理是通过捕获外部信号的边沿(上升沿/下降沿),记录定时器计数器的值,从而计算时间差。
当外部信号发生边沿跳变(如上升沿或下降沿)时,定时器会立即锁存当前计数器的值(CNT),并存储到捕获/比较寄存器(CCRx)中。通过比较两次捕获的值,可以计算出:
-
信号的周期(两次上升沿之间的时间差)
-
占空比(上升沿和下降沿之间的时间差与周期的比值)
STM32的定时器(TIMx)通常有多个输入捕获通道(如TIM2有4个通道,TIM3有4个通道等),每个通道可独立配置:
-
输入引脚:如TIM2_CH1对应PA0,TIM3_CH1对应PA6(具体参考芯片手册)。
-
触发边沿:可配置为上升沿、下降沿或双边沿触发。
-
滤波:可设置数字滤波器(ICF)消除噪声干扰。
由于输入捕获和输出比较都是公用一个CCR寄存器的。所以同一定时器的同一个通道不能同时使用输入捕获和输出比较功能

滤波器的作用:滤波器可以过滤掉信号的抖动干扰,固定时钟频率下进行采样。如果连续N个采样都为了相同的电平,那就代表信号稳定了。采样频率f和采样点数N都是滤波器的参数,频率越低,采样点越多,那滤波效果越好。
输入捕获原理:当检测到输入信号发生边沿跳变时(上升沿、下降沿或双边沿),CNT计数器当前的计数值会保存到捕获/比较寄存器中
1.4.2 频率测量简介

1、测频法
在指定砸闸门时间T(一般是1秒)内,对边沿跳变时几次,得到N。所以它的频率为:fx = N / T
它的思想是取上升沿然后做平均值,一般用于测高频信号,时间闸门T越大误差越小。结果是一段时间内的平均值。得到的结果比较满需要一个闸门时间。——UP:野生绿波电龙
2、测周法
图中的①和②分别保存了两次边沿跳变时的CNT值,两次CNT值的差值就是次数N,频率fc是根据定时器的时钟源和预分频器(PSC)值确定的,所以它的频率为:fx = fc / N

具体来说: 假设配置TIM3进行测周法
htim3.Instance = TIM3;
htim3.Init.Prescaler = 72 - 1; // PSC=71
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
由于fc为定时器时钟源分频后的时钟频率,比如APB1定时器时钟为72MHZ,对他进行72分频,fc的值就是1MHZ。
而N的值就是一个周期内计数次数,取值为:|CCR1_2 - CCR1_1|。
已知标准频率fc,那么计一次的时间为1 / fc,在一个周期T内计数N次,一共用时T = N / fc。所以频率:fx = 1 / T = 1 / (N / fc) = fc / N


3、中界频率
待测信号频率小于fm时使用测周法误差更小,待测信号频率大于fm时使用测频法误差更小
无论是测频法(在时间T内可能末端波形刚好在一半周期),还是测周法(末端计数CNT才计到一半它的上升沿就到了)都有正负一个计数的误差。——UP:野生绿波电龙
1.5 定时器模式讲解
1.5.1 主模式与从模式的关系
主模式:控制定时器如何向其他外设(如其他定时器、DAC、ADC)发送触发信号(TRGO)。
- 通过配置
TIMx_CR2寄存器的MMS位(Master Mode Selection)选择触发源(如更新事件、捕获事件等)。 - 主模式无需显式“启用”,只要配置了
TIMx_CR2寄存器的MMS位,定时器就会按主模式规则输出TRGO信号。 - 需单独配置,决定定时器如何输出TRGO信号。从模式选择Disable ≠ 主模式,二者是正交的功能。
- 主模式参数通常用于高级定时器(如TIM1)的同步,普通定时器可忽略。
从模式:控制定时器如何响应外部或内部触发信号(如复位、启动/停止计数等)。
- 通过配置
TIMx_SMCR寄存器的SMS位(Slave Mode Selection)选择模式(如禁用、复位、门控等)。
| 定时器类型 | 代表型号 | 主模式支持 | 特殊功能 |
|---|---|---|---|
| 基本定时器 | TIM6, TIM7 | 不支持TRGO输出 | 仅用于基本计时和DAC触发 |
| 通用定时器 | TIM2, TIM3, TIM4, TIM5 | 支持主模式(TRGO输出) | 可触发其他定时器或外设(如ADC) |
| 高级定时器 | TIM1, TIM8 | 支持更丰富的主模式事件(如刹车事件、COM事件) | 带死区控制的PWM、互补输出等 |
1.5.2 从模式选择

| 模式名称 | 功能描述 |
|---|---|
| Disable | 禁用从模式,定时器独立运行。 |
| External Clock Mode 1 | 通过外部引脚(如TI1、TI2)输入时钟信号驱动定时器计数。 |
| Reset Mode | 触发信号(如TI1上升沿)会复位计数器(CNT=0),重新开始计数。 |
| Gated Mode | 触发信号(如TI1高电平)使能计数,低电平时暂停计数。 |
| Trigger Mode | 触发信号启动定时器(类似复位模式,但可与其他从模式组合使用)。 |
1.5.3 触发源选择

1. 内部触发源(Internal Trigger)
| 触发源选项 | 功能描述 | 典型应用场景 |
|---|---|---|
| ITR0 | 内部连接至TIM1的TRGO输出(主定时器触发从定时器) | 多定时器同步(如TIM1触发TIM3) |
| ITR1 | 内部连接至TIM2的TRGO输出 | TIM2作为主定时器触发其他外设 |
| ITR2 | 内部连接至TIM3的TRGO输出(自身触发需谨慎) | 级联定时器或自同步 |
| ITR3 | 内部连接至TIM4的TRGO输出 | 复杂定时器联动(如TIM4触发TIM3) |
2. 外部引脚触发源(External Trigger)
| 触发源选项 | 功能描述 | 硬件连接要求 |
|---|---|---|
| TI1_ED | TI1引脚(如PA6/PB4)的双边沿触发(上升沿和下降沿均有效) | 需配置GPIO为Alternate Function |
| TI1FP1 | TI1引脚经过输入滤波和极性选择后的信号(仅上升沿或下降沿) | 适用于抗干扰环境 |
| TI2FP2 | TI2引脚(如PA7/PB5)经过滤波后的信号 | 同TI1FP1,但使用不同引脚 |
3. 特殊触发模式
| 触发源选项 | 功能描述 | 注意事项 |
|---|---|---|
| Disable | 禁用触发源,定时器独立运行 | 默认状态 |
| Reset Configuration | 触发信号会复位定时器计数器(CNT=0) | 需与Slave Mode的Reset Mode配合使用 |
1.5.4 通道模式选择

1.5.5 关键应用场景示例
场景1:PWM输入测量(PWMI模式)
-
配置:
-
Trigger Source =
TI1FP1(通过TI1引脚捕获PWM信号) -
Slave Mode =
Reset Mode
-
-
效果:每个TI1上升沿复位计数器,自动测量PWM周期和占空比。
场景2:定时器级联同步
-
配置:
-
主定时器(TIM1)设置TRGO输出(如更新事件触发)。
-
从定时器(TIM3)Trigger Source =
ITR0(TIM1作为触发源)。
-
-
效果:TIM3的计数与TIM1严格同步。
场景3:外部事件触发计数
-
配置:
-
Trigger Source =
TI2FP2(TI2引脚信号滤波后触发)。 -
Slave Mode =
Gated Mode(高电平时计数)。
-
-
效果:仅当TI2为高电平时,TIM3才计数,用于测量脉冲宽度。
二、定时器用例讲解
2.1 内部模式
2.1.1 定时器定时中断
2.2 输出模式
2.2.1 PWM输出
2.2.2 输出比较
2.2.3 GPIO翻转
2.3.4 互补输出
2.3输入模式
2.3.1 定时器外部时钟
2.3.2 直接输入捕获
直接模式:外部中断+定时器计数
测量原理
-
频率:通过外部中断检测上升沿,记录两个上升沿之间的时间(使用定时器计数)。
-
占空比:在中断中切换触发边沿(上升沿→下降沿→上升沿),计算高电平时间与周期的比值。
特点
-
优点:实现简单,无需专用硬件。
-
缺点:
-
依赖中断响应速度,高频信号下误差大。
-
CPU占用率高,不适合实时性要求高的场景。
-
代码片段
// 外部中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
static uint32_t last_time = 0;
uint32_t current_time = __HAL_TIM_GET_COUNTER(&htim2);
uint32_t period = current_time - last_time;
last_time = current_time;
}
2.3.3 间接输入捕获
间接模式:定时器输入捕获
测量原理
-
频率:捕获两个上升沿的时间差。
-
占空比:捕获上升沿和下降沿的时间差,计算占空比。
特点
-
优点:利用硬件捕获,精度高于直接模式。
-
缺点:需手动切换边沿触发,测量占空比需多次捕获。
配置步骤
-
定时器配置为输入捕获模式(如TIMx_CH1)。
-
交替捕获上升沿和下降沿。
代码片段
// 输入捕获回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
static uint32_t rise_time = 0;
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint32_t fall_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
uint32_t duty = fall_time - rise_time;
uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) - rise_time;
rise_time = 0;
}
}
2.3.4 PWM输入模式(PWMI)
PWMI模式:专用PWM输入捕获
1、测量原理
-
硬件自动测量:STM32的定时器(如TIMx)支持PWMI模式,可同时捕获周期和占空比。
-
通道1(TI1)捕获上升沿(周期)。
-
通道2(TI2)捕获下降沿(占空比)。
-
2、特点
-
优点:全硬件实现,无CPU干预,精度高;支持高频信号(最高定时器时钟频率)。
-
缺点:占用两个捕获通道。
2.3.4.1输入捕获测频率
F103C8T6配置步骤
时钟配置:配置RCC,开始外部高速时钟(HSE);时钟树配置AHB总线时钟(HCLK)为72MHZ

定时器配置生成待测波形:定时器2(TIM2)启用内部时钟(Internal Clock),开启定时器2的通道1的PWM模式(PWM Generation CH1),自动定位到PA0(GPIO_Pin_0)引脚。之后配置预分频系数(Prescaler)和自动重装值(Couter Period),以及设置初始化占空比的值为10。

PWM相关公式:
PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1) = 72,000,000 / 72 / 1000 = 1000
PWM占空比:Duty = CCR / (ARR + 1) = 10 / 1000 = 1%
PWM分辨率:Resc = 1 / (ARR + 1) = 1 / 1000 = 0.1%
配置输入捕获模式:启用内部时钟,配置从模式启用TL1FP1通道用于CNT计数清零,开启定时器通道1为输入捕获模式。

记得使能TIM3的全局中断

问题:自动重装值(ARR)是否必须设为最大值(65535)?
不是必须的。ARR的值取决于你的具体应用需求:
- 设为最大值(65535)通常是为了获得最大的计数范围,这在测量未知长度的脉冲或高频信号时有用。
- 如果你知道输入信号的预期频率范围,可以设置更小的ARR值以提高分辨率。
- 在输入捕获模式下,ARR主要影响定时器的溢出频率,不影响捕获功能本身。
输入捕获测频率代码片段:
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
int32_t freq;
uint32_t capture;
/* USER CODE END PV */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 设置比较寄存器CCR1的值 */
void PWM_SetCompare_1(uint16_t compare)
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, compare);
}
/* Reset模式测周法 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + 1;
// 动态计算定时器实际时钟(TIM3)
uint32_t pclk1 = HAL_RCC_GetPCLK1Freq(); // 获取APB1时钟(36MHz)
uint32_t tim_clk = pclk1 * 2; // 定时器时钟=72MHz(APB1分频≥2时自动×2)
uint32_t timer_ticks_per_second = tim_clk / (htim3.Instance->PSC + 1);
freq = timer_ticks_per_second / capture;
}
}
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 开启PWM输出
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开启输入捕获中断
PWM_SetCompare_1(500); // 设置占空比
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("Freq value: %d\n", freq);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
2.3.4.2输入捕获测占空比
在 PWMI(Pulse Width and Period Measurement Input,脉宽和周期测量输入) 模式下,CCR1 和 CCR2 分别用于捕获信号的 周期 和 脉宽,因此 占空比(Duty Cycle) 的计算公式为:

上升沿触发捕获周期,下降沿触发捕获占空比。捕获到两个下降沿的比值就是占空比 。
为什么占空比的值是 CCR2 / CCRR1?
CCR1 捕获周期(Period)
- 在 复位模式(Reset Mode) 下,定时器会在输入信号的 上升沿 自动复位
CNT(计数器)。 - 当 下一个上升沿 到来时,
CCR1会捕获CNT的值,这个值就是 信号的周期(因为CNT从 0 开始计数,直到下一个上升沿复位)。 - 例如:如果输入信号是 1kHz(周期 1ms),且定时器时钟为 1MHz(1 计数 = 1μs),则
CCR1会捕获1000(1ms = 1000μs)。
CCR2 捕获脉宽(Pulse Width)
- 在 输入捕获模式 下,
CCR2可以配置为在 下降沿 捕获CNT的值。 - 由于
CNT在上升沿复位,并在下降沿被CCR2捕获,因此CCR2的值就是 高电平时间(脉宽)。 - 例如:如果占空比是 30%,则
CCR2会捕获300(300μs)。
占空比计算
- 占空比 = 高电平时间 / 周期 =
CCR2 / CCR1 - 例如:
CCR1 = 1000(周期 1000μs),CCR2 = 300(脉宽 300μs),则占空比 =300/1000 = 30%。

F103C8T6配置步骤
在输入捕获测频率的定时器3配置的基础上,配置定时器3通道2为输入捕获间接模式。

配置通道1检测上升沿,通道2检测下降沿

输入捕获测量占空比 - 代码部分
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 设置比较寄存器CCR1的值 */
void PWM_SetCompare_1(uint16_t compare)
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, compare);
}
/* 设置预分频器的值:方便计数:72 * 10^n */
void PWM_SetPrescaler(uint32_t psc)
{
__HAL_TIM_SET_PRESCALER(&htim2, psc - 1);
}
/* 输入捕获Reset模式测周法,测量频率和占空比 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // 通道1 - 上升沿测频率
{
// 读取CCR捕获的值
capture1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + 1;
// 动态计算定时器实际时钟(TIM3)
uint32_t tim_clk = HAL_RCC_GetPCLK1Freq() * 2; // 定时器时钟=72MHz
uint32_t timer_ticks_per_second = tim_clk / (htim3.Instance->PSC + 1);
freq = timer_ticks_per_second / capture1; // 1000000 / capture1
}
else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) // 通道2 - 下降沿测占空比
{
// 读取CCR捕获的值
capture2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) + 1;
uint32_t duty_range = htim2.Instance->ARR + 1;
duty = capture2 * duty_range / capture1;
}
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 开启PWM输出
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开启输入捕获中断通道1
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2); // 开启输入捕获中断通道2
PWM_SetCompare_1(500); // 设置占空比
PWM_SetPrescaler(720); // 设置预分频器
HAL_Delay(100); // 等待定时器稳定 - 解决初始值不对情况
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("Freq value: %d\n", freq);
printf("Duty value: %d\n", duty);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
三种输入方式对比
结果一致性:在低频且信号稳定的情况下,三种方式测得的结果理论相同。高频时,直接模式因中断延迟会导致误差,而PWMI模式最精确。
如何选择?
- 低频调试:直接模式(简单快速)。
- 中等频率:间接模式(平衡性能与复杂度)。
- 高频/实时系统:PWMI模式(最优解)。
校准建议:使用示波器对比测量结果,尤其在高频时验证PWMI模式的准确性。
| 特性 | 直接模式 | 间接模式 | PWMI模式 |
|---|---|---|---|
| 硬件依赖 | 无 | 定时器捕获通道 | 定时器双捕获通道 |
| 测量精度 | 低(受中断延迟影响) | 中 | 高(全硬件) |
| CPU占用 | 高 | 中 | 低 |
| 适用频率范围 | 低频(<1kHz) | 中频(<10kHz) | 高频(可达定时器极限) |
| 占空比测量复杂度 | 需手动切换边沿 | 需手动切换边沿 | 自动测量 |
示例场景
-
舵机控制(50Hz):三种方式均可,推荐PWMI以减少CPU负载。
-
电机编码器(1kHz+):必须使用PWMI或间接模式。
-
超高频信号(>10kHz):仅PWMI模式可靠。
2.3.5 编码器接口
三、结尾
3.1 更新日志
Time: 2025/8/3 Info: PWMI输入捕获测频率原理
Time: 2025/8/4 Info: PWMI输入捕获测频率(代码)和占空比(原理+代码)
Time: 2025/8/3 Info:
3.2 声明
STM32的定时器还有很多功能和玩法,它也是MCU使用得比较多的功能。上面的文章会在我的项目经历下逐渐补充、完善。
由于博主入行嵌入式方向不到一年,文章内有错误和不足的地方,欢迎各位佬在评论区讨论说明。
你们的"一键三连"是主包更新的动力!!!
更多推荐





所有评论(0)