定时器 Timer
基于 STM32F103 · 零基础讲解定时器原理、计数模式、输入捕获、输出比较、PWM 生成。
🎯学习目标
- 了解定时器的基本概念、用途和工作原理;
- 理解 STM32F103 定时器的分类(高级控制、通用、基本)及各自特点;
- 掌握定时器的时基单元组成(PSC、ARR、CNT)和三种计数模式;
- 理解定时器定时时间的计算方法,能根据需求配置 PSC 和 ARR;
- 掌握输入捕获的原理,能测量外部信号的频率和脉宽;
- 掌握输出比较和 PWM 的原理,能生成指定占空比的 PWM 波形;
- 熟悉定时器相关中断的配置和处理;
- 掌握定时器的标准库和 HAL 库编程方法。
1定时器概述
定时器(Timer)是微控制器中用于定时、计数、PWM 生成、输入捕获等功能的外设。它的本质是一个可编程的计数器,按照设定的频率自动计数,计数到特定值时触发事件。
定时:每隔一段时间做一件事。比如每 1ms 刷新一次数码管、每 500ms 读一次传感器、每 1s 串口发送一次数据。定时器就像一个精准的秒表,到时间就自动通知 CPU。
计数:数外部脉冲的个数。比如编码器转了多少圈、流水线上过了多少个产品、按键按了多少次。定时器就像一个计数器,每来一个脉冲就加 1。
PWM 输出:输出可调占空比的方波。比如控制 LED 亮度(占空比大=亮,占空比小=暗)、控制电机转速(占空比大=快,占空比小=慢)、控制舵机角度。定时器就像一个可调宽度的脉冲发生器。
输入捕获:测量外部信号的频率和脉宽。比如红外遥控解码、超声波测距、测量 PWM 信号的频率。定时器就像一个精准的测量仪,记录信号变化的时刻。
想象一个水桶接水的模型:
① 时钟源(水龙头):提供稳定的脉冲信号,就像水龙头以固定流量出水。
② 预分频器 PSC(水管粗细):对时钟进行分频,降低计数频率。比如 72MHz 时钟,PSC=71,则计数频率 = 72MHz/(71+1) = 1MHz(每 1μs 计数一次)。
③ 计数器 CNT(水桶):每个脉冲加 1,就像水桶里的水位不断上升。
④ 自动重装载值 ARR(水桶容量):计数器计到 ARR 就"溢出",清零重新开始,同时产生一个"更新事件"(水桶满了倒掉重来)。
定时时间 = 水桶装满的时间 = (ARR+1) × (PSC+1) / 时钟频率。
2定时器分类
STM32F103 拥有 8 个定时器,按功能强弱分为三类:
📋 三类定时器对比
| 特性 | 高级控制定时器 | 通用定时器 | 基本定时器 |
|---|---|---|---|
| 型号 | TIM1、TIM8 | TIM2~TIM5 | TIM6、TIM7 |
| 位数 | 16 位 | 16 位 | 16 位 |
| 计数模式 | 向上/向下/中央对齐 | 向上/向下/中央对齐 | 仅向上计数 |
| 输入捕获 | ✅ 4 路 | ✅ 4 路 | ❌ |
| 输出比较 | ✅ 4 路 | ✅ 4 路 | ❌ |
| PWM 输出 | ✅ 4 路 | ✅ 4 路 | ❌ |
| 互补输出 | ✅(带死区控制) | ❌ | ❌ |
| DMA 触发 | ✅ | ✅ | ✅ |
| 时钟来源 | APB2 | APB1 | APB1 |
| 典型用途 | 电机控制、逆变器 | 通用定时、PWM、捕获 | DAC 触发、基本定时 |
互补输出:TIM1/TIM8 可以输出一对互补的 PWM 信号(CHx 和 CHxN),用于驱动 H 桥电路控制电机正反转。
死区控制:在互补输出之间插入一段"死区时间",防止上下桥臂同时导通导致短路。这是电机控制的关键安全机制。
刹车功能:当检测到异常(如过流)时,自动将输出置为安全状态,保护电路。
基本定时器(TIM6/7)就像一个简单的闹钟——只能定时,到时间就响。功能最少,但够用。
通用定时器(TIM2~5)就像一个瑞士军刀——既能定时,又能计数,还能输出 PWM、测量信号。功能全面,最常用。
高级定时器(TIM1/8)就像一个专业电机控制器——在通用定时器的基础上,增加了互补输出、死区控制、刹车功能,专门为电机控制设计。
3计数模式
定时器有三种计数模式,决定了计数器 CNT 的计数方向和行为:
向上计数
Up向下计数
Down中央对齐
Center-Aligned📐 定时时间计算
定时时间 = (ARR + 1) × (PSC + 1) / 定时器时钟频率
计数频率 = 定时器时钟频率 / (PSC + 1)
计数次数 = ARR + 1
例1:定时器时钟 72MHz,需要 1ms 定时。
设 PSC=71,则计数频率 = 72MHz/(71+1) = 1MHz(每 1μs 计数一次)。
需要 1ms = 1000μs,所以 ARR = 1000-1 = 999。
验证:定时时间 = (999+1)×(71+1)/72MHz = 1000×72/72000000 = 0.001s = 1ms ✅
例2:定时器时钟 72MHz,需要 500ms 定时。
设 PSC=7199,则计数频率 = 72MHz/(7199+1) = 10kHz(每 0.1ms 计数一次)。
需要 500ms = 5000×0.1ms,所以 ARR = 5000-1 = 4999。
验证:定时时间 = (4999+1)×(7199+1)/72MHz = 5000×7200/72000000 = 0.5s ✅
4定时器配置流程
使用定时器的基本步骤(以定时器中断为例):
- 使能定时器时钟:TIM2~TIM7 在 APB1,TIM1/TIM8 在 APB2;
- 配置时基单元:设置预分频器(PSC)、自动重装载值(ARR)、计数模式;
- 使能更新中断:如果需要定时中断,使能 TIM_IT_Update;
- 配置 NVIC:设置中断优先级并使能中断通道;
- 使能定时器:调用 TIM_Cmd() 启动计数;
- 编写中断服务函数:检测更新标志位,清除标志位,执行用户代码。
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 2. 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // ARR = 999
TIM_TimeBaseStructure.TIM_Prescaler = 71; // PSC = 71
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 3. 使能更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 4. 配置 NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 5. 启动定时器
TIM_Cmd(TIM2, ENABLE);
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除标志位
// 用户代码:如 LED 翻转、计数器累加等
GPIO_TogglePin(GPIOA, GPIO_Pin_0);
}
}
// CubeMX 自动生成的初始化代码
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim2);
}
// main() 中启动定时器中断
HAL_TIM_Base_Start_IT(&htim2);
// 回调函数(HAL 库自动调用)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
}
}
① 必须先使能时钟,否则配置无效;
② PSC 和 ARR 的值都是 0~65535(16 位),实际值 = 设定值 + 1;
③ 中断服务函数中必须清除标志位,否则会反复进入中断;
④ HAL 库使用回调函数机制,不需要手动判断和清除标志位。
5输入捕获
输入捕获(Input Capture)用于测量外部信号的频率和脉宽。当外部信号的边沿(上升沿或下降沿)到来时,定时器自动将当前计数器的值锁存到捕获寄存器(CCR)中。
输入捕获就像一个秒表:当信号从低变高(上升沿)时,你按下"开始";当信号从高变低(下降沿)时,你按下"停止"。两次按键的时间差就是脉宽。
如果连续两个上升沿之间的时间差就是信号的周期,取倒数就是频率。
📏 测量频率
① 配置输入捕获为上升沿触发;
② 第一个上升沿到来时,记录 CNT 值为 T1;
③ 第二个上升沿到来时,记录 CNT 值为 T2;
④ 周期 T = (T2 - T1) × (PSC+1) / 时钟频率;
⑤ 频率 f = 1 / T。
📏 测量脉宽(PWM 输入模式)
① 配置两个通道:通道 1 上升沿触发,通道 2 下降沿触发;
② 上升沿到来时,记录 CNT 值为 T1(周期起点);
③ 下降沿到来时,记录 CNT 值为 T2(高电平结束);
④ 高电平时间 = (T2 - T1) × (PSC+1) / 时钟频率;
⑤ 占空比 = 高电平时间 / 周期。
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置 GPIO(PA6 作为 TIM3_CH1 输入)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置输入捕获
TIM_ICInitTypeDef TIM_ICInitStructure;
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 = 0x00;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
// 4. 使能捕获中断
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
TIM_Cmd(TIM3, ENABLE);
6输出比较与 PWM
输出比较(Output Compare)
输出比较将计数器 CNT 的值与比较寄存器 CCR 的值进行比较,当两者相等时,输出引脚电平发生翻转、置位或复位。
PWM(脉冲宽度调制)⭐
PWM(Pulse Width Modulation,脉冲宽度调制)是一种通过改变方波的高电平持续时间(脉宽)来等效控制模拟信号的技术。
想象一个快速开关的灯:如果灯每秒开关 1000 次(频率 1kHz),每次亮 0.5ms 灭 0.5ms,人眼看到的就是"半亮";如果亮 0.8ms 灭 0.2ms,人眼看到的就是"很亮"。
PWM 就是这样一种技术——用数字信号(高低电平)模拟出模拟信号的效果。高电平时间占整个周期的比例叫占空比。
频率:PWM 信号每秒重复的次数。频率太低会闪烁(LED),太高会增加开关损耗(电机)。
占空比:高电平时间占整个周期的百分比。
占空比 = CCR / (ARR + 1) × 100%
其中 CCR 是比较寄存器值,ARR 是自动重装载值。
🔧 PWM 的常见应用场景
LED 调光
占空比大=亮,占空比小=暗。频率 1kHz 以上人眼无闪烁。
电机调速
占空比大=快,占空比小=慢。频率 10~20kHz。
舵机控制
脉宽 0.5~2.5ms 对应 0°~180°。频率 50Hz(周期 20ms)。
蜂鸣器
不同频率产生不同音调,占空比控制音量。
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置 GPIO(PA0 复用推挽输出)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置定时器时基
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // ARR = 999
TIM_TimeBaseStructure.TIM_Prescaler = 71; // PSC = 71
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 4. 配置 PWM 模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // CCR = 500,占空比 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
// 5. 启动 PWM
TIM_Cmd(TIM2, ENABLE);
// 动态修改占空比
TIM_SetCompare1(TIM2, 750); // 占空比变为 75%
模式 1:CNT < CCR 时输出有效电平(高电平),CNT ≥ CCR 时输出无效电平。最常用。
模式 2:CNT < CCR 时输出无效电平,CNT ≥ CCR 时输出有效电平。与模式 1 相反。
7标准库函数
常用定时器标准库函数(源码在 stm32f10x_tim.c):
📋 时基配置函数
| 函数 | 功能 | 参数说明 |
|---|---|---|
TIM_TimeBaseInit() | 初始化时基单元 | 参数:定时器指针 + TIM_TimeBaseInitTypeDef 结构体 |
TIM_Cmd() | 使能/禁用定时器 | 参数:定时器 + NewState(ENABLE/DISABLE) |
TIM_ITConfig() | 配置定时器中断 | 参数:定时器 + 中断源 + NewState |
TIM_GetITStatus() | 获取中断状态 | 返回 SET 或 RESET |
TIM_ClearITPendingBit() | 清除中断挂起位 | 必须在中断服务函数中调用 |
📋 PWM / 输出比较函数
| 函数 | 功能 |
|---|---|
TIM_OC1Init()~TIM_OC4Init() | 配置通道 1~4 的输出比较模式 |
TIM_SetCompare1()~TIM_SetCompare4() | 设置比较寄存器值(动态修改 PWM 占空比) |
TIM_CtrlPWMOutputs() | 使能 PWM 输出(高级定时器必须调用) |
TIM_OC1PreloadConfig() | 使能/禁用预装载(影子寄存器) |
📋 输入捕获函数
| 函数 | 功能 |
|---|---|
TIM_ICInit() | 配置输入捕获模式 |
TIM_GetCapture1()~TIM_GetCapture4() | 读取捕获寄存器值 |
TIM_PWMIConfig() | 配置 PWM 输入模式(双通道) |
📋 其他常用函数
| 函数 | 功能 |
|---|---|
TIM_SetCounter() | 直接设置计数器值 |
TIM_GetCounter() | 读取当前计数器值 |
TIM_SetPrescaler() | 动态修改预分频值 |
TIM_SetAutoreload() | 动态修改自动重装载值 |
TIM_GenerateEvent() | 软件触发更新事件 |
8HAL 库函数
HAL 库中定时器相关函数(更抽象,跨系列可移植):
📋 基本定时器函数
| 函数 | 功能 |
|---|---|
HAL_TIM_Base_Init() | 初始化定时器基本功能 |
HAL_TIM_Base_Start() | 启动定时器(轮询模式) |
HAL_TIM_Base_Start_IT() | 启动定时器(中断模式) |
HAL_TIM_Base_Start_DMA() | 启动定时器(DMA 模式) |
HAL_TIM_Base_Stop() | 停止定时器 |
📋 PWM 函数
| 函数 | 功能 |
|---|---|
HAL_TIM_PWM_Init() | 初始化 PWM 模式 |
HAL_TIM_PWM_ConfigChannel() | 配置 PWM 通道 |
HAL_TIM_PWM_Start() | 启动 PWM 输出 |
HAL_TIM_PWM_Stop() | 停止 PWM 输出 |
__HAL_TIM_SET_COMPARE() | 动态修改 PWM 占空比(宏) |
📋 输入捕获函数
| 函数 | 功能 |
|---|---|
HAL_TIM_IC_Init() | 初始化输入捕获 |
HAL_TIM_IC_ConfigChannel() | 配置捕获通道 |
HAL_TIM_IC_Start_IT() | 启动输入捕获(中断模式) |
HAL_TIM_IC_Stop_IT() | 停止输入捕获 |
📋 回调函数(中断处理)
| 回调函数 | 触发时机 |
|---|---|
HAL_TIM_PeriodElapsedCallback() | 定时器溢出(更新事件) |
HAL_TIM_IC_CaptureCallback() | 输入捕获完成 |
HAL_TIM_PWM_PulseFinishedCallback() | PWM 脉冲完成 |
| 对比项 | 标准库 | HAL 库 |
|---|---|---|
| 初始化 | 手写结构体配置 | CubeMX 图形化生成 + HAL_TIM_Base_Init() |
| 启动 PWM | TIM_Cmd() + TIM_CtrlPWMOutputs() | HAL_TIM_PWM_Start() 一行搞定 |
| 修改占空比 | TIM_SetCompare1() | __HAL_TIM_SET_COMPARE() |
| 中断处理 | 手动判断标志位 + 清除 | 重写回调函数,自动清除 |
| 可移植性 | 仅限 STM32F1 | 跨 STM32 系列可移植 |
⭐重点例题
// PSC=7199, ARR=4999
// 定时时间 = (7199+1)×(4999+1)/72MHz = 7200×5000/72000000 = 0.5s
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置 GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 4999;
TIM_TimeBaseStructure.TIM_Prescaler = 7199;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 4. 使能中断 + NVIC
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 5. 启动定时器
TIM_Cmd(TIM3, ENABLE);
// 中断服务函数
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
GPIO_WriteBit(GPIOA, GPIO_Pin_0,
(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0)));
}
}
关键点:① 定时时间 = (PSC+1)×(ARR+1)/时钟频率;② 中断中必须清除标志位;③ 翻转引脚用读-取反-写操作。
// 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置 GPIO(复用推挽)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置定时器时基(PWM 频率 = 72MHz/(71+1)/(999+1) = 1kHz)
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 配置 PWM 模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
// 主循环:呼吸灯
uint16_t pwm = 0;
int8_t dir = 1;
while(1)
{
pwm += dir * 5;
if (pwm >= 995) dir = -1;
if (pwm <= 5) dir = 1;
TIM_SetCompare1(TIM2, pwm);
delay_ms(10);
}
关键点:① GPIO 必须配置为复用推挽输出;② PWM 频率 = 时钟/((PSC+1)×(ARR+1));③ 动态修改 TIM_SetCompare1() 改变占空比。
volatile uint16_t IC_Value1 = 0, IC_Value2 = 0;
volatile uint16_t IC_Duty = 0;
volatile uint8_t IC_Flag = 0;
// 配置输入捕获(省略时钟和 GPIO 配置)
TIM_ICInitTypeDef TIM_ICInitStructure;
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 = 0;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
TIM_Cmd(TIM3, ENABLE);
// 中断服务函数
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if (IC_Flag == 0)
{
IC_Value1 = TIM_GetCapture1(TIM3); // 第一次捕获
IC_Flag = 1;
}
else
{
IC_Value2 = TIM_GetCapture1(TIM3); // 第二次捕获
IC_Duty = IC_Value2 - IC_Value1; // 周期(计数值)
IC_Flag = 0;
}
}
}
// 频率 = 时钟频率 / ((PSC+1) × IC_Duty)
// 占空比需要配合下降沿捕获计算
关键点:① 输入捕获会自动锁存 CNT 到 CCR;② 需要至少两次捕获才能计算周期;③ 溢出处理要考虑 16 位计数器回绕。
🎯自测(点击展开)
STM32 定时器分几类?各有哪些?
定时器的三种计数模式是什么?
定时时间如何计算?写出公式。
PWM 占空比如何计算?
PWM 输出时 GPIO 应配置为什么模式?为什么?
高级定时器输出 PWM 时为什么必须调用 TIM_CtrlPWMOutputs()?
输入捕获的原理是什么?
定时器时钟来自哪个总线?
基本定时器 TIM6/TIM7 有什么功能限制?
HAL 库中如何动态修改 PWM 占空比?
PWM 模式 1 和模式 2 有什么区别?
中断服务函数中为什么必须清除标志位?
📝强化题库
选择题点选即时判分;填空题输入后"检查"或"显示答案"。