STM32定时器时基解析:从时钟树到PSC/ARR实战配置

发布时间:2026/10/3 6:46:57
STM32定时器时基解析:从时钟树到PSC/ARR实战配置 定时器几乎是每一个写STM32的人都会碰到的外设但要是一分钟之内说出“定时器到底在数什么”还真不一定答得上来。很多人用CubeMX点两下鼠标定时器就能跑中断也能进可真要问“这个时间基准是怎么来的”就发现脑子里全是浆糊。这篇文章就从计数本质、时钟链路到实战配置把STM32定时器的“时基”从头到尾捋干净。1. 定时器不是在计时是在数脉冲先抛开ST的官方手册用一个特别朴素的视角看定时器STM32的定时器本质上是一个计数器它在数一种非常规律的脉冲这个脉冲叫做“时钟源”。每个时钟脉冲到来一次计数器的值就加一或者减一。当计数器的值撞到某个预设的数字时硬件就会自动触发一些动作比如产生中断、翻转引脚、触发ADC采样。这就像小学时玩过的“报数”游戏老师喊一声学生报一个数。老师就是时钟源学生就是计数器。老师喊得越快学生报数就越快报完一轮所需的时间就越短。所以在定时器的世界里从根本上决定“时间基准”的是时钟源本身的频率。STM32内部最常用的定时器时钟源来自RCCReset and Clock Control复位与时钟控制模块。MCU上电之后内部有一个高速内部振荡器HSIHigh-Speed Internal oscillator频率一般是8MHz。这个8MHz信号经过一系列倍频、分频的处理最终成为定时器外设的驱动时钟。也就是说不管是8MHz的HSI、16MHz的LSE还是经过PLL锁相环倍频出来的72MHz/168MHz/240MHz主频最后都要通过RCC内部的一套时钟树配置出一个“定时器专用时钟”TIMxCLK。这里有一个很关键的点定时器时钟不等于主频。很多刚从51单片机转过来的人习惯性地认为“主频72MHz定时器计数频率就是72MHz”放在STM32上不一定对。在F1系列中APB1预分频器系数为1时定时器时钟等于APB1时钟但APB1预分频器系数大于1时定时器时钟会强制变为APB1的两倍。这个细节如果不看芯片参考手册光靠经验猜很容易在延时时间上差出整整一倍。做产品误差这么大基本就等于废了。理解了“定时器数的是时钟脉冲”后面所有关于预分频、自动重装载、捕获比较的计算都是在这个基础上展开的。与其说定时器是“计时器”不如说它是一个“脉冲计数器”更准确。只是因为这些脉冲有固定的频率所以数脉冲就等同于计量时间。2. 时间基准从哪来把时钟树走一遍既然定时器数的是时钟脉冲那“时间基准”的源头就落到了时钟树配置上。STM32的时钟树不像51单片机那么一根筋它给开发者留了很多组合选择外部高速晶振HSE、内部高速振荡器HSI、外部低速晶振LSE、内部低速振荡器LSI、PLL锁相环倍频……不同的组合直接影响定时器计数时钟的频率和精度。我以一个典型的STM32F103项目为例把整条链路走一遍。系统上电后默认使用HSI 8MHz作为系统时钟源。如果项目里接了8MHz外部晶振RCC可以通过PLL把HSE倍频到72MHz这个72MHz就是SYSCLK。SYSCLK经过AHB预分频器得到HCLKHCLK再经过APB1预分频器得到PCLK1APB2预分频器得到PCLK2。而挂在APB1总线上的TIM2、TIM3、TIM4、TIM5它们的时钟源TIMxCLK不是直接等于PCLK1而是看APB1分频系数如果分频系数为1TIMxCLK PCLK1如果分频系数大于1TIMxCLK 2 × PCLK1。举一个具体数字设HSE 8MHzPLL倍频9倍SYSCLK 72MHz。AHB不分频HCLK 72MHz。APB1预分频2PCLK1 36MHz。因为APB1分频系数是2大于1所以TIM2的时钟TIM2CLK 2 × 36MHz 72MHz。也就是说我定时器最终数的是72MHz的脉冲数72,000,000次正好用时1秒。如果把预分频器PSC设为7199那么定时器计数频率变成72MHz / 7200 10kHz计数器从0数到4999就是0.5秒。这套配置过程在CubeMX图形化界面里看是一目了然的。它会直接显示APB1分频系数、APB2分频系数还会在时钟树页面标出每个总线的最终频率。不过如果只用寄存器操作或者标准库就必须要自己关心这些分频系数了。我见过不少人把APB1设成4分频之后定时器溢出中断间隔比预期慢了一倍就是因为没这样思考过“2倍频”逻辑。对时间基准的精读至少还包括另一个维度精度。HSE外部晶振的精度一般在几十ppm量级HSI内部RC振荡器的精度在室温下通常是1%左右但温漂大在一些对时序敏感的场景下如串口波特率、USB时钟、PWM载波频率很容易出问题。如果PWM频率本身是20kHz误差1%就是200Hz对电机控制来说这个偏差足以让FOC算法的电流环表现异常。所以关于时间基准的选择我自己的原则是能用HSE就不用HSI能用PLL锁相环就用PLL锁相环除非是成本敏感、时序要求不高的消费类产品。3. 定时器基元拆解PSC、CNT、ARR到底怎么配合把时间基准的源头搞清楚后下一步就看定时器内部的三个核心寄存器预分频寄存器PSC、计数器寄存器CNT、自动重装载寄存器ARR。这三个家伙组合起来决定了定时器一个完整的计数周期有多长。先讲PSCPrescaler。PSC做的是“降频”工作。假设定时器时钟是72MHz如果不分频CNT每计一个数只需1/72,000,000秒也就是约13.89纳秒。这个时间粒度太细了直接拿来做0.5秒的延时CNT得数到3600万次计数器的一大半行程都耗在这上面不够灵活。PSC出场后把72MHz降成10kHz计一个数变成0.1毫秒数5000次就是0.5秒就非常适合做常见的毫秒级时基。PSC的实际取值逻辑是PSC 目标计数频率的分频系数 - 1。例如目标计数频率为10kHz分频比为72MHz / 10kHz 7200PSC 7199。这里“减1”是因为PSC从0开始计数写成寄存器0x1C1F就是7200-1的十六进制。很多人第一次拿标准库写TIM_TimeBaseStructure.TIM_Prescaler 7199;恰恰写的就是7200分频。如果直接用72去除就会踩坑。接下来是CNT。CNT就是那个“同学报数”时学生嘴里喊出来的数字。向上计数模式下CNT从0开始递增每来一个时钟脉冲加1达到ARR后清0或者翻转为0取决于选择的是连续计数还是中心对齐计数。向下计数模式则反过来从ARR减到0。CNT是可以在运行中被程序读出来的这是实现输入捕获测频率、编码器计数、超声波脉宽测量的基础。ARRAuto-Reload Register是计数终点。定时器溢出中断的周期公式是T (PSC 1) × (ARR 1) / TIMxCLK这个公式要能一眼看懂并默写。PSC 1是预分频分频比ARR 1是计数周期数两者相乘是总脉冲数再除以时钟频率就是时间。设TIMxCLK 72MHzPSC 7199ARR 4999那么T 7200 × 5000 / 72,000,000 0.5秒。这个计算凡是做定时器应用的都得滚瓜烂熟。值得注意的是STM32的定时器寄存器都是16位的PSC最大只能到65535ARR最大也只能到65535。如果要产生一个特别长的周期比如1秒仅靠PSC和ARR可能不够这时就要做“定时器级联”或者“溢出次数累加”比如在中断服务函数里加一个软件计数器靠多次溢出凑出目标时间。这是非常常见的手法也是嵌入式开发中的常识性操作。对PSC、CNT、ARR三者的关系我用一句特别生活化的类比来理解PSC决定了时钟的“流速”ARR决定了“路程”CNT就是“当前走到哪”。流速乘时间得到路程所以溢出周期天然等于“两个数字乘积除以时钟频率”。有了这层直觉读任何一款MCU的定时器手册都会觉得很顺因为几乎所有芯片的通用定时器都长这个样。4. 从时基到功能定时中断、PWM与输入捕获的实操之路有了时基设置定时器核心功能就能逐个展开了。最基础的是定时中断。拿STM32F103 HAL库举例如果要做一个1毫秒的定时中断第一步用RCC把定时器时钟打开第二步设置PSC和ARR第三步使能更新中断第四步启动定时器。HAL库函数在CubeMX生成的代码里就会帮你完成前两步你自己要做的就是开中断回调。PSC和ARR的设置有个值得反复确认的细节标准库中TIM_Prescaler和TIM_Period都是以“寄存器值”方式写入也就是说要写减1后的值而HAL库的__HAL_TIM_SET_AUTORELOAD又是直接写ARR寄存器值。很多人混用标准库和HAL库的思维把周期数当成目标数值直接填进去结果定时周期成倍偏移。例如本意是计数5000次标准库写TIM_Period 5000实际芯片是从0数到5000周期其实是5001次。虽然差1次在宏观时间上几乎无感但在高精度PWM输出时占空比的精度和载波频率就会受影响。PWM占空比的分辨率就是PSC、ARR的位数以及时钟频率的实时关系尤其在FOC这类需要精确波形控制的场合这种误差是不能无视的。再说PWM输出。使用定时器输出PWM步奏是配置定时器时基PSC/ARR配置输出比较通道CCR、极性、模式启动计数器和PWM输出。以20kHz载波、50%占空比为例若定时器时钟72MHz那PSC0ARR 72000000 / 20000 - 1 3599。CCR设成1800输出就是50%占空比。如果载波频率要调到24kHz只需把ARR改到2999其他不用动。这个操作方式决定了PWM的分辨率和实时调整能力改变ARR就可以调频率改变CCR就可以调占空比。热词里还有一个高频场景叫“输入捕获测频率”。它的原理是配置定时器工作在输入捕获模式不断捕捉外部信号的两个相邻上升沿计算两次捕获时CNT的差值再根据计数频率反推外部信号频率。如果外部信号是1kHz方波定时器计数频率10kHz那么相邻两个上升沿之间大概差10次计数。这个差值的倒数乘上时钟周期就是信号周期。捕获中断中读CCR的值是很多人踩坑的地方因为读CCR寄存器前必须确认中断产生了否则拿到的是上一次的值。还有一点如果被测信号频率过高导致CNT溢出那捕获差值就得叠加一次ARR的周期来修正否则测出来的频率会偏差好几倍。超声波测距也依赖输入捕获。测距原理是发射超声波后开始计时遇到障碍物反射回来时停止计时时间差乘以声速再除以二就是距离。STM32实现的常见方式是用定时器的一个PWM通道输出40kHz的脉冲串另一个通道配置输入捕获捕获回波引脚从低电平到高电平的跳变从中断中读取当前CNT即可算出时间。这个项目里最核心的误差来源就是开始计时和捕获到位的延迟单次捕获的延迟大约1-2个定时器周期选一个计数频率高一点的定时器比如20MHz、40MHz能明显提升测距分辨率。5. 那些让定时器“失灵”的坑实测排查记录写定时器应用时新老手都会遇到一些奇奇怪怪的现象。这里整理几个我亲手踩过、也帮别人排查过的典型问题作为速查参考。现象一定时器中断频率完全不对或者干脆不进中断。优先检查时钟树配置。我用标准库时曾把RCC_PCLK1Config(RCC_HCLK_Div2)写成RCC_HCLK_Div4结果定时器时钟变成APB1两倍中断周期直接翻倍。还有不少人初始化GPIO时用了GPIO_PinRemapConfig把定时器引脚重映射了导致PWM输出到错误的引脚上找不到信号就以为定时器坏了。建议排查顺序是先看是否有时钟再看NVIC有没有使能再看中断标志位是否真的置位最后检查引脚映射。现象二delay消抖时程序卡死。热词里有“stm32延时函数delay卡死”。我遇到的最常见原因是Systick中断函数里做了一堆耗时操作或者初始化顺序错了Systick优先级设成和定时器中断一样导致优先级翻转和死锁。特别是使用HAL库时HAL_Delay依赖Systick中断如果Systick中断被更高优先级任务打断或者Systick中断没有清标志位就非常容易卡死。解决方法是延时函数只用Systick做到短小精悍不要让位检查全局中断是否误关。现象三CAN通信突然连不上。这个和定时器看似无关实际却经常一起出现。不少人把CAN初始化和定时器以及PWM放在一起做当PWM频率和CAN波特率共享同一个时钟树中的PLL后如果改了PLL分频系数而忘了重新初始化CAN波特率CAN就彻底瘫痪。另外定时器中断里喂狗或者频繁做耗时运算导致CAN中断被延迟也会让总线上出现大量错误帧表现为“突然连不上”。排查时要区分硬错误和软错误软错误多半是时序冲突。现象四捕获测频率结果飘忽不定。这种问题一般有两个原因一个是外部信号本身带了干扰毛刺定时器在两个相邻的上升沿之间被毛刺误触发导致捕获值忽大忽小。另一个原因是没有在中断里及时读取CCR等下一次捕获来了才取走上次的值计算时就产生了时间差。排查的时候用示波器看引脚波形是最直观的如果是毛刺造成的再加上一级RC滤波或者在代码里加一个“连续两次捕获值差小于阈值”的过滤逻辑。现象五PWM波形频率和预想差一倍。这个多半是PSC算错。比如想输出10kHz PWM定时器时钟72MHzPSC设成7199的话计数频率是10kHz但ARR设成7200忘了减1实际输出频率是10kHz / (7200 1) 1.39kHz完全不在预期。如果PSC和ARR都没问题那可能就是中心对齐模式选错了中心对齐模式下的PWM频率等于时钟频率除以2 × (PSC1) × (ARR1)比边沿对齐慢一倍。用示波器数一下波形就明白了。现象六频率校正不准。当你要让PWM频率精确到0.1%以内时时钟源本身的精度成为瓶颈。不少开发板上的HSI是8MHz但不同批次芯片之间容差超过±2%这时候输出20kHz的PWM可能变成19.6kHz或者20.4kHz。解决办法是把HSE外部晶振接上或者在量产时做频率校准写一个校准值到Flash。6. 定时器选型与模式的取舍经验STM32家族的定时器型号很多基本定时器、通用定时器、高级定时器之间有明确的分工。基本定时器TIM6、TIM7最简没有引脚输出适合做纯延时和时基。通用定时器TIM2、TIM3、TIM4、TIM5是主力具备PWM、输入捕获、输出比较、编码器接口等几乎全部功能。高级定时器TIM1、TIM8则在通用基础上增加了互补输出、死区控制、刹车输入专门用于电机控制、工业逆变器场景。选型时我通常按“引脚需求 功能需求”两个维度判断。如果只要一个1ms节拍TIM6就够了把通用定时器省给更复杂的PWM输出。如果要做FOC电机控制那必须上TIM1或TIM8这三个定时器的互补PWM输出通道自带死区插入可以避免上下桥臂直通烧MOS。如果只是控制一个LED呼吸灯用通用定时器的PWM就能胜任不需要高级定时器。模式选择上最容易被忽略的是“不连续计数模式”和“单脉冲模式”。单脉冲模式是TIM在一个触发信号到来后只产生一个PWM脉冲然后停止。在超声波测距中用单脉冲模式发出发射脉冲再切到输入捕获模式等回波整个过程非常优雅不需要频繁启停定时器。相比软件控制使能与关闭这个模式能省掉不少中断开销和时间抖动。DMA配合定时器也是提升效率的常见手段。比如用定时器更新事件触发ADC采样采样数据直接通过DMA搬运到内存整个过程CPU零干预。在这个架构里定时器不仅仅是“计时器”它扮演了硬件化调度中心的角色定时的同时触发一系列外设动作。这种思路对控制算法很重要比如FOC的电流环每到载波周期就要同步采样三相电流用定时器的更新事件和DMA做的同步硬件触发机制能保证每载波周期采样点一致不会发生软件抖动。7. 几个深入实践的细节与建议定时器数量、通道、映射、时钟源、中断优先级这些配置在CubeMX界面里看起来是填写表格实际上每个选项都对应一套完整的硬件事件链。理解了事件链之后代码调试就变成顺水推舟。第一件值得做的事是把“定时器时钟频率表”背熟或者做成速查表。以F103为例APB1最高36MHz分频系数大于1时定时器翻倍到72MHzAPB2最高72MHz分频系数大于1时定时器翻倍到72MHzF407的APB1定时器最高84MHzAPB2定时器最高168MHz。不同芯片的具体数值要在参考手册的Clock Tree图里查这是一小时就能掌握的确定性知识却是无数问题的根因。第二件值得做的事是调试时善用“计数器实时值”。在Keil的Watch窗口里直接添加CNT寄存器可以看到当前值从而判断PSC、ARR是否生效。尤其是输入捕获时把CCR实时值拉出来看能立刻发现是不是捕获中断多次触发、是不是读取顺序有问题。第三件值得做的事是在正式项目里为每个定时器定义一个“时基用途”注释。避免后来的人看到TIM3不知道它是做PWM输出还是输入捕获还是时基。代码上建议把时基配置放在独立函数里比如TIM3_Init(TIM3_CLK_DIV_1, 999, 7199)调用时直接传目标频率和分频系数。8. 从定时器到整个系统的时间观回到最初的问题“定时器到底在数什么”答案是它在数一个由RCC时钟树提供的、频率恒定的脉冲流。这个脉冲流的来源可能是内部RC振荡器、外部晶振也可能是PLL倍频后的产物。定时器内部用PSC降低计数频率用CNT记录当前脉冲数用ARR设定计数终点。当CNT撞到ARR就会产生更新事件从而引出定时中断、PWM翻转、DMA触发、ADC采样等一系列后续动作。想真正掌握STM32的定时器不能只背配置代码要把每一段配置和硬件链路对起来。PSC为什么减一ARR为什么减一APB1为什么要翻倍PLL的倍频系数为什么是9每一个问题背后都是某条具体芯片手册中的规则。把这些规则落实成数感、直觉和调试能力才算真的明白了“时间基准从哪来”。这个内容后续还可以这样扩展从定时器到输入捕获再到DMA一步步搭出一个完整的电机控制采样链。也可以把定时器与Systick做对比看它们各自的精度、中断开销和应用边界。定时器虽然只是一个外设但它背后串着芯片的时钟架构和整个系统的实时性设计。想通这一层你对嵌入式系统的理解会上一个台阶。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询