当前位置: 首页 > news >正文

DAY 2 TIM定时器

通过TIM内部时钟(Update溢出更新事件进行计数,在预期的目标数值进行内部更新

通过TIM外围输入设备的时钟(监听外部时钟CNT计数变换)进行计数,在预期的目标数值进行触发中断PWM波形输出(OC输出比较)


一、配套必懂单词缩写

TIM = Timer 定时器

作用:实现定时中断、PWM 波形输出、外部脉冲计数、输入捕获测信号等功能。


PSC = Prescaler 预分频器

作用:对输入的高频时钟做分频降频,把快速时钟放慢,调整最小计时刻度。

注意:分频倍数 = PSC + 1,PSC 写 0 就是不分频


CK_PSC = Clock of Prescaler 预分频器输入时钟

作用:送入预分频器的原始时钟信号,是定时器最上游的时钟源。


CK_CNT = Clock of Counter 计数器工作时钟

作用:经过 PSC 分频之后、专门给计数器 CNT 使用的时钟,每来一个脉冲 CNT 数值 + 1。


CNT = Counter Register 计数器寄存器

作用:存放当前计数值,从 0 开始随 CK_CNT 脉冲不断累加。

读取TIMx当前CNT计数值 num = TIM_GetCounter(TIMx);

不管 UIF 标志是 1 还是 0、有没有清标志、开没开中断,CNT 都会一直循环计数


ARR= Auto-Reload Register 自动重装载寄存器(自动重装载值)

作用:设定计数器计数上限;CNT 计数到 ARR 数值后,立刻清零重新从头计数,同时可触发定时中断 / 更新事件。

注意:CNT 从 0 数到 ARR,一共走 ARR+1 个时基单位


基本功能概念

UPD = Update Event 更新事件

作用:CNT 计满 ARR 归零的瞬间产生的信号,用来触发定时中断。

PWM = Pulse Width Modulation 脉冲宽度调制

作用:通过控制高电平时间占比输出可调平均电压,控制电机、LED 亮度。

CCR= Capture/Compare Register 捕获比较寄存器

作用:PWM、输入捕获核心寄存器,存放对比计数值。

APB= Advanced Peripheral Bus 高级外设总线

作用:STM32 挂载定时器、串口等外设的系统总线,提供 CK_PSC 时钟来源。

IRQ= Interrupt Request 中断请求

作用:定时器计时完成后向 CPU 发送的中断信号。

ITStatus IT = Interrupt 中断;Status = 状态

作用:中断状态(库函数返回值类型),TIM_GetITStatus,代码用TIM_GetITStatus(返回 ITStatus)读取标志,判断定时完成。

  • 不开 TIM_IT_Update:只产生 Update 事件、只置 UIF 标志,不会进中断服务函数;
  • 开启 TIM_IT_Update 中断:每次 Update 发生,UIF 置 1,向 CPU 发起中断请求,进入中断函数。
  • TIM_GetITStatus(TIMx, TIM_IT_Update) == SET发生溢出
  • TIM_GetITStatus(TIMx, TIM_IT_Update) == RESET未溢出

UIF = Update Interrupt Flag 更新中断标志位(更新事件标志)

硬件溢出:只负责把标志拉成 SET

你的清除代码:只负责把标志拉回 RESET 如果不写清除函数:一旦溢出置 1 后,永远保持 SET,不会变回 RESET

IRQHandler = Interrupt Request Handler 中断请求处理


高级功能概念

OC = Output Compare 输出比较

CNT 计数器按时基不断往上数,硬件实时对比CNTCCR里存的数值; 两者相等时,通道引脚电平自动翻转 / 置高 / 置低,用来输出方波、可调占空比 PWM,控制 LED、电机。 它只是借用定时器的时基计数时钟,但不改变时基计算公式。

  • CCR:Capture/Compare Register 捕获比较寄存器
  • CH:Channel 通道(TIM1_CH1、TIM2_CH2 等)
  • PWM:Pulse Width Modulation 脉冲宽度调制(OC 最常用场景)

IC = Input Capture 输入捕获

外部引脚来了上升 / 下降沿信号,立刻锁存当前 CNT 计数值到 CCR, 通过前后两次捕获的计数值差 × 时基单位,算出外部信号周期、高电平时长、频率。

二、细节概念

1.时基单位

时基单位 = 定时器最小的计时刻度 / 最小时间格子定时器计时,就像尺子:

  • 尺子最小一格是 1mm → 1mm 就是尺子的基单位
  • STM32 定时器最小一格是多少 ns/μs/ms → 这个值就是时基单位

时间单位缩写

s

ms毫秒 1/1000 秒 一个负千位

μs微秒 1/1000000 秒两个负千位

ns纳秒 1/1000000000 秒

1kHz → 周期 0.001 s=1 ms(毫秒) 一个负千位

1MHz → 周期 0.000001 s=1 μs(微秒)两个负千位

1GHz → 周期 1×10−9 s=1 ns(纳秒)三个负千位

定时周期 = 多格加起来,完整一轮计时总时长。

2.PWM频率(Frequency

一个周期T,信号从高电平(上拉)→低电平(下拉)→回到高电平(上拉)。

工程单位1。在1分钟内,能执行几个周期,则为频率 f = 1 / T。

如果20ms执行一个周期,一分钟可执行50次PWM周期。

3.占空比(DutyCycle

在一个PWM脉冲周期,通过高电平的时间与整个周期时间的比例。

对输出脉冲的宽度时间(高电平时间)进行调制,可以控制电压变化,如0~3.3V。

应用于:电机转速和转矩、LED亮度、音频信号、电源信号、温度控制

如果周期是10ms,脉宽是8ms,低电平2ms,则占空比是80%

三:参数设置:时基&定时总时间

TIMx_CNT计数器寄存器、TIMx_PSC预分频器寄存器、TIMx_ARR自动装载寄存器

举个实例(最常见 F103,APB1=72MHz)

时基单位计算公式:时基单位 =(预分频数值PSC + 1)÷ 定时器输入原始时钟CK_PSC

CK_PSC = 72MHz,设置 PSC=71 (分频系数 = PSC+1=72)

CK_CNT = 72MHz ÷ 72 = 1MHz

计数频率:CK_CNT = CK_PSC / ( PSC + 1 )

时基单位 = 1/1MHz = 1μs → 此时定时器最小一格就是 1 微秒,时基单位 = 1μs

定时总时间计算公式:定时总时间 = 时基单位 × (ARR + 1)。

定时总时间时基单位:沿用上面例子:时基 1μs,ARR=999 总定时 = 1μs × 1000 = 1000μs = 1ms,每 1ms 进一次中断

结论:时基 1μs(更小)→ 频率 1MHz(更高); 时基 10μs(更大)→ 频率 0.1MHz(更低)

计数时钟频率 = fCK_PSC ÷ (PSC + 1)

时基单位 T = (PSC + 1) ÷ fCK_PSC

定时周期 T 总 = T × (ARR + 1)

1. 时钟流程

外部时钟 → APB 总线 →CK_PSC(定时器原始时钟)→ PSC 预分频 →CK_CNT(计数时钟)CK_CNT 每跳 1 次,代表 1 个时基单位

2. 更新事件完整逻辑

CNT 不断按时基单位累加 → CNT == ARR → 产生更新事件 UPDATE→ 硬件自动把UIF 标志置 1

3. 新手混淆:区分更新事件与中断

真正的 “中断执行” 是什么?

开启中断 + 配置 NVIC + 写TIMx_IRQHandler中断服务函数CNT一溢出,硬件自动跳转到 IRQHandler 里执行代码,不用 while 循环一直查询,这才是中断。

Update 更新事件:程序要主动调用

  • CNT 数到 ARR → 自动溢出、CNT 清零、硬件置 UIF 标志,只要定时器在计数,必然产生,和开不开中断无关。 这是 “溢出” 的本体。
  • 你不用中断服务函数,只是在 while (1) 里循环读取这个标志,属于轮询查询
  • 触发根源:Update 溢出事件
  • 运行机制:主循环不停查询,CPU 空转等待不属于中断运行
  • while(1) { // 读取更新中断标志 if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { my_custom_func(); // 自定义函数 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清中断标志 } }

IRQ 中断请求:底层代码自动调用

  • 使用TIM_GetITStatus(..., TIM_IT_Update),必须提前开启更新中断使能:TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE)
  • 硬件溢出后置起中断挂起标志,这个标志专门给中断体系用;
  • 触发根源:同样是Update 溢出事件
  • 运行机制:硬件自动打断主循环,优先执行,这才是中断。
/** * 函 数:TIM2中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断 { Num ++; //Num变量自增,用于测试定时中断 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位 //中断标志位必须清除 //否则中断将连续不断地触发,导致主程序卡死 } }

中断向量表 + 启动文件 (.s)

1)启动文件 startup_stm32f10x.s(汇编文件)

里面有一张巨大的表格,叫中断向量表 Vector table。 表格每一行格式:DCD 函数名,含义:

某个中断触发时,CPU 要跳转到这个函数的内存地址执行。当你写了同名函数,编译器会把这个函数的内存地址填充到向量表里对应的位置。弱定义 WEAK机制,如果你 C 语言里重写同名函数:优先用你写的函数地址;

规则:表格里写死了固定函数名,名字不能随便改,必须严格对应TIM2_IRQHandler

节选定时器中断示例:

DCD TIM2_IRQHandler ; TIM2全局中断入口地址 DCD TIM3_IRQHandler ; TIM3全局中断入口地址 DCD TIM4_IRQHandler ; TIM4全局中断入口地址

四、定时器类型

所拥有功能数量从上到下兼容:高级定时器TIM1 | TIM8(总线APB2)、通用定时器TIM2~5(总线APB1)、基本定时器TIM6~7(总线APB1)

外设支持部分定时器,如STM32F103C8T6定时器资源:TIM1(高级)、TIM2~4(通用)

五、底层代码初始化多触发一次的更新

TIM_TimeBaseInit函数末尾,手动产生了更新事件,一定要设置中断输出配置,清除定时器更新标志位:TIM_ClearFlag(TIM2, TIM_FLAG_Update);

解决OLED计数的NUM从1开始,清除之后从0开始。

六、模块化

#include "stm32f10x.h" // Device header /** * 函 数:定时中断初始化 * 参 数:无 * 返 回 值:无 */ void Timer_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟 /*配置时钟源*/ TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟 /*时基单元初始化*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元 /*中断输出配置*/ TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位 //TIM_TimeBaseInit函数末尾,手动产生了更新事件 //若不清除此标志位,则开启中断后,会立刻进入一次中断 //如果不介意此问题,则不清除此标志位也可 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断 /*NVIC中断分组*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2 //即抢占优先级范围:0~3,响应优先级范围:0~3 //此分组配置在整个工程中仅需调用一次 //若有多个中断,可以把此代码放在main函数内,while循环之前 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置 /*NVIC配置*/ NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 /*TIM使能*/ TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行 } /* 定时器中断函数,可以复制到使用它的地方 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } */

#ifndef __TIMER_H #define __TIMER_H void Timer_Init(void); #endif

1、独立输出模式

完整周期由 (PSC+1)*(ARR+1) 共同决定,ARR自动重装在值固定100,所以公式要除以100。

720*1000是1Mhz

720是1Khz

2、从模式:

ARR设满量程65535,计数器由外部脉冲强制清零,周期不由ARR决定,因此计算频率不用除以ARR。

TIM是16位计数器,最大计数上限65535,设置满量程,最大化可测量脉冲周期范围。

  • 最大可测周期:
  • 对应最小可测频率:

72是1Mhz

  • 普通模式:CNT加到ARR自动清零;
  • Reset从模式:外部TIx触发立刻清零,ARR仅作为计数上限保护

外部脉冲 → PA6(GPIO) → 滤波器 → 边沿检测 → TI1FP1(通道1滤波触发信号)

工作流程:

1. 第一个脉冲上升沿:

- TI1FP1触发输入捕获,把当前CNT值存入CCR1;

- 同时从模式Reset立刻将计数器CNT清零;

2. 第二个脉冲上升沿:

- 再次捕获CNT(此时CNT是两个脉冲间隔的计数值);

- 再次清零CNT;

核心逻辑

- CCR寄存器存储两个相邻脉冲之间的计数值N;

- 脉冲周期

- 脉冲频率

①输入捕获模式测频率GetFreq

②PWMI模式测频率占空比GetDuty

http://www.jsqmd.com/news/1098774/

相关文章:

  • 移动语义与容器极致优化,emplace/push底层差异、对象复用、std::allocator原理、自定义STL分配器实战
  • 对称加密算法的扩散层(P盒)密码学指标详细介绍
  • C++开发者如何学好汇编(上)
  • 不要把CNC机内测头当成三坐标
  • PCL 垂距法实现点云精简
  • 深入解析Hermes Agent:从Skill驱动架构到实战部署的AI Agent框架指南
  • 东莞翻译机构 韩语审计报告重点
  • Kimi LeetCode 3425. 最长特殊路径 Java实现
  • 从入门到精通:Python OpenPyXL完整教程
  • 3个突破性技巧:如何用SRWE实现Windows窗口的实时魔法编辑
  • 6个月小白逆袭AI初级工程师:收藏这份保姆级学习路线,从零基础到实战大模型!
  • 嵌入式音频开发实战:AU-60 全功能 DSP 语音模组一站式开发指南
  • 揭秘AI写教材黑科技!低查重的AI教材生成,为教学助力
  • MCP 协议传输层进化:从 stdio 到 Streamable HTTP,我的踩坑实录
  • 5分钟免费解锁英雄联盟所有皮肤:R3nzSkin国服特供版完整指南
  • How To: Create A Word Document In Powershell – Part 1 – Opening The Document, Writing Some Text, Usi
  • Kimi LeetCode 3425. 最长特殊路径 Python3实现
  • 低查重AI写教材攻略:精选5款AI工具,轻松搞定教材写作难题!
  • GNSS数据处理新手必看:手把手教你读懂RINEX 3.04钟差文件(CLK)里的关键信息
  • django文件对象是什么?
  • Highcharts有版权吗?
  • 对称加密算法的混淆层(S盒)密码学指标详细介绍
  • TVA在具身智能全栈能力体系中的关键作用(6)
  • 限峰功率最大熵定理的理论推导和MATLAB仿真实现(P124302075刘家隆)
  • php里直接塞CSS代码?别傻了,这招让加载快如闪电
  • VMware虚拟机安装Ubuntu Linux:从零搭建开发环境的完整指南
  • TVA:连接数字与物理世界的智能底座(3)
  • 北方高寒矿区专网通信搭建要点,适配低温、粉尘、防爆严苛工况
  • 基于YOLOv8的船舶检测分类系统:从模型训练到部署的完整实践
  • 第十六篇:商业模式重塑——告别数据垄断,拥抱能力订阅