
做嵌入式这些年我见过太多人在 STM32 定时器上翻车其中十个里有八个是栽在 PSC、ARR 和时钟源这三个参数上。明明公式背得滚瓜烂熟PWM 频率一上示波器就是差一倍定时中断要么太快要么太慢查半天发现是 APB 预分频没算明白。这篇文章就把这三个最容易算错的地方掰开揉碎讲清楚附带完整的计算过程、标准库和 CubeMX 的配置实操以及我调试时踩过的一堆坑。不管你是在学定时器中断、输出 PWM、做输入捕获测频率还是在调 GD32 这类国产兼容芯片这篇都值得收藏。1. PSC 预分频器为什么分频总是差一拍1.1 寄存器值和实际分频系数的“减一”陷阱PSC 的中文名叫预分频器作用是把定时器输入时钟降频给计数器提供一个合适的计数时钟。它是个 16 位寄存器取值范围 0 到 65535但这里有个新手最容易踩的坑寄存器写入的值和实际分频系数不是一回事实际分频系数是 PSC 1。为什么因为 PSC 0 时这不是“不分频”而是“1 分频”也就是输入时钟原样通过一个计数脉冲都不少。PSC 1 是 2 分频PSC 71 是 72 分频。所以当你要把 72MHz 的系统时钟降成 1MHz 计数时钟时需要 72 分频PSC 应该写 71而不是 72更不是 73。这个“减一”逻辑特别容易在手动配置寄存器或检查别人代码时翻车。我见过有人拿着一个频率恰好差一点点的波形在那边东查西查最后发现就是 PSC 多写了 1。假设系统时钟 72MHz目标是 1MHz 计数时钟PSC 72 时实际分频 73计数时钟变成 72MHz / 73 ≈ 986.3kHz算出来的溢出频率偏离目标值将近 1.4%。单看一次中断可能感觉不明显累计 1000 次中断时间误差就能到十几毫秒做通信协议定时的时候就非常难受了。顺便说一句PSC 的最大分频能力是 65536 倍PSC 65535最小是 1 倍PSC 0。16 位寄存器看起来范围很大但在某些场景下还是不够用。比如你想用 72MHz 定时器时钟产生一个周期很长的低频中断PSC 不够大就得靠 ARR 一起扛这一点后面讲 ARR 时会再提到。1.2 改了 PSC 但频率没变影子寄存器在作怪很多人在调试时碰到过这种情况程序里改了 PSC 的值理论上输出频率应该变但示波器上的波形纹丝不动。这不是你改错了而是 PSC 有影子寄存器机制。所谓影子寄存器就是你在代码里写入的并不是真正参与计数工作的那个寄存器而是先写进一个“预备区”要等特定时刻才会被装载到实际工作寄存器里。STM32 定时器的 PSC 就采用了这种机制新的预分频值要等到一次更新事件发生后才会真正生效这个更新事件可以是计数器溢出、软件触发或者外部触发。所以调试时如果你想“我改完 PSC 立刻看到变化”正确的做法是手动产生一次更新事件把影子寄存器里的值加载进去。标准库里有现成接口TIM_GenerateEvent(TIMx, TIM_EventSource_Update)连续调用通常能强制装载并清零计数器。如果用的寄存器操作直接置TIMx-EGR | TIM_EGR_UG就行。还有一种情况特别容易误导人你在中断服务函数里动态改 PSC改完后当前这个计数周期还没结束新值要等下一个溢出才生效。如果此时你拿调试器看寄存器会看到你写的值已经在寄存器里了但计数器行为还是旧的。我一开始也以为是芯片有问题后来翻参考手册才明白是分频器的装载延迟。这个“改了一个数但现象没变”的坑本质上是没理解时基结构非常值得记在心里。1.3 PSC 选得太大精度会悄悄丢失PSC 不仅是分频它还直接决定定时器的分辨率。所谓分辨率就是计数器每加 1 代表真实世界里的多长时间。计数时钟越慢每个计数脉冲的时间就越长分辨率就越低。举例来说你在做一个输入捕获测频率的模块信号周期约 1ms如果计数时钟是 1MHz那一个周期约对应 1000 个计数误差理论上是 1us 级别如果计数时钟是 1kHz那一个周期只对应 1 个计数根本没法用。所以我做捕获类应用时PSC 通常不会一开始就设成 7199 这种大分频值而是优先保证边沿分辨率再通过 ARR 和溢出计数来扩展测量范围。但在编码器模式或者某些 PWM 输出场景里情况又反过来。编码器模式需要尽可能高的计数分辨率PSC 直接设 0 都用不够因为编码器信号本身频率可能很高你再分频就丢步了。所以 PSC 到底选多大取决于你要优先保什么。我习惯先列三个数输入时钟频率、想要的分辨率、计数器溢出周期然后反过来推 PSC 的范围。千万别一上来就套现成的 PSC 7199那是别人针对特定时钟算出来的照搬到你的芯片上只会得到一堆莫名其妙的波形。2. ARR 自动重装载值周期不是你想的那个数2.1 ARR 同样存在“加一”逻辑ARR 是自动重装载寄存器它决定计数器计到多少才产生溢出更新事件。很多新手把 ARR 当成“周期”写 1000 就以为是 1000 个计数脉冲实际上计数器是从 0 开始往上计的计到 ARR 才溢出所以一整个周期包含的是 ARR 1 个计数脉冲。拿最常见的场景举例STM32F103 系统时钟 72MHzAPB1 和定时器时钟的事先放到下一节讲先假设定时器时钟已经是 72MHz。设置 PSC 71 得到 1MHz 计数时钟也就是每个计数脉冲 1us。如果你想要 1ms 的定时中断需要 1000 个计数脉冲那么 ARR 应该写 999而不是 1000。写 1000 的话实际周期是 1.001ms在要求严格的场合这个误差不能忍。同理标准的 PWM 频率计算公式是PWM频率 定时器时钟 / ((PSC 1) * (ARR 1))公式里 PSC 和 ARR 都必须加 1两个加一叠加起来出错概率翻倍。我给你的建议是第一次算的时候把所有中间量都写出来比如定时器时钟是多少、分频后计数时钟是多少、目标周期对应多少计数脉冲、ARR 就是计数脉冲减 1。只要你把“计数脉冲数”和“寄存器值”分开理解就再也不会搞混。2.2 中心对齐模式周期减半的坑如果你用的是中心对齐模式同样的 PSC 和 ARR输出频率会变成边沿对齐模式的一半。这一点特别容易在电机控制、逆变电路里翻车因为很多人从边沿对齐的例程改到中心对齐模式只改了TIM_CounterMode其他参数原封不动结果 PWM 频率平白无故掉了一半半天找不出原因。原理很简单边沿对齐的计数器是从 0 往上计数到 ARR然后直接跳回 0一次循环只走 ARR 1 个计数脉冲。中心对齐模式下计数器要先从 0 往上数到 ARR再掉头向下数回 0一个完整的 PWM 周期要经历一次“上山”一次“下山”走的距离约等于边沿对齐的两倍。所以同样配置下中心对齐模式 PWM 输出频率大约是边沿对齐的一半。这里的“约等于”背后还有个细节让人头疼某些芯片资料里把更新事件定义在 ARR 位置有些则定义在 ARR - 1 位置加上 F1 系列中心对齐还分模式 1、模式 2、模式 3更新中断产生的位置还不同——模式 1 在向下计数时更新模式 2 在向上计数时更新模式 3 在上下计数时都更新。所以我不建议死记硬背一个“中心对齐频率公式”而是直接用逻辑推演一个完整来回需要多少个计数脉冲。只要想明白计数器的走向公式随手就能推出来而且不会记错。2.3 动态修改 ARR预装载和缓存问题ARR 同样存在影子寄存器问题而且专门有个控制位叫 ARPEAuto-reload preload enable。当 ARPE 1 时你写 ARR 时写入的是影子寄存器要等更新事件才会拷贝到工作寄存器ARPE 0 时写进去立即使当前计数器使用新值。听起来 ARPE 0 更直接但在输出 PWM 时千万别这么干。如果你在计数器已经数到一半的时候直接改 ARR当前周期可能会被强行拉长或缩短产生一个不完整的畸形波形。我用逻辑分析仪抓过这种波形一帧里会出现一个宽度明显异常的脉冲后级电路如果对这个脉冲敏感系统就莫名奇妙地出问题。正确的做法是把 ARPE 置 1让 ARR 等更新事件再统一装载这样每个 PWM 周期都能平滑过渡。如果需要动态调频我一般会先算好新的 ARR写入寄存器后等一次更新事件再更新 CCR 占空比寄存器。这里要特别提醒F1 标准库的TIM_TimeBaseInit会默认把 ARPE 置 1但如果你直接操作寄存器或者从某个精简代码里拷了段初始化很可能 ARPE 被清零了动态调参时就会踩到缓存失效的坑。3. 时钟源APB 分频和定时器时钟的“隐形倍数”3.1 APB1/APB2 预分频不等于定时器时钟这一节是很多人从头到尾都没搞明白的地方。STM32 内部有一个复杂的时钟树系统时钟经过 AHB 预分频后得到 HCLKHCLK 再经过 APB1 和 APB2 预分频得到 PCLK1 和 PCLK2。定时器到底挂在哪个总线决定了它的输入时钟是多少。关键点来了当 APB1 或 APB2 的预分频系数为 1也就是不分频时定时器时钟等于对应 APB 时钟当 APB 预分频系数大于 1 时定时器时钟是 APB 时钟的两倍。以最经典的 STM32F103 为例系统时钟 72MHzAPB1 最大只能跑到 36MHz所以必须 2 分频。这时候挂在 APB1 上的 TIM2、TIM3、TIM4、TIM5 等它们的输入时钟并不是 36MHz而是自动倍频成 72MHz。你要是拿着 36MHz 去算 PSC 和 ARR出来的定时时长直接翻倍。这也是为什么 CubeMX 里你看到 APB1 36MHz但 Timer Clock 一栏显示的是 72MHz。不要觉得这是软件自己胡填的它是硬件就是这么设计的——APB 为了外设低功耗降低了频率但定时器这种对时间敏感的外设需要较高时钟于是芯片内部自动帮忙乘了个 2。所以配置定时器之前第一步永远是确认定时器时钟到底是多少。我自己的习惯是打开 CubeMX 的 Clock Configuration 页面看一眼或者直接查参考手册的时钟树把对应总线的 APB 预分频因子记下来再决定要不要乘 2。国产芯片更要小心比如 GD32F450 的 RCU 里就有专门的定时器时钟预分频配置RCU_TIMER_PSC_MUL2这类参数必须显式处理不能默认它和 STM32 完全一样。3.2 内部时钟、外部时钟模式 1、模式 2 别再混了STM32 定时器的时钟源不止内部时钟一种还包括外部时钟模式 1、外部时钟模式 2以及内部触发时钟。很多人一听“外部时钟”就以为是某个引脚直接提供时钟结果配置半天没反应。内部时钟就是默认的时钟树所有不额外配置的定时器都用它。外部时钟模式 1 是把某个输入通道上的信号比如 TI1、TI2 经过滤波、极性选择、分频之后作为计数器的时钟来源。这个模式下外部信号每来一个边沿计数器就加 1。外部时钟模式 2 用的是专门的 ETR 引脚配置后同样可以实现外部脉冲直接驱动计数器。两者的区别很多人不清楚外部时钟模式 1 的信号要先经过触发输入选择器并且还要满足采样同步的要求外部时钟模式 2 走的是 ETR 专用路径少了部分同步环节响应更快适合更高频率的外部脉冲。具体用哪个取决于你的外部信号接到哪个引脚。如果你只是简单计数一个传感器输出的脉冲用外部时钟模式 1 就够如果信号频率很高优先考虑带 ETR 引脚的定时器配外部时钟模式 2。用外部时钟时还有个硬性注意点外部脉冲频率不能太高。STM32 内部有同步逻辑外部信号通常不能超过定时器内部时钟的一半否则会出现计数不稳、丢脉冲的现象。我之前做转速测量时就吃过这个亏外部编码器输出几百 kHz 的脉冲定时器时钟才 1MHz结果计数器像喝醉了一样乱跳。后来把定时器时钟拉高问题立刻消失。做硬件之前一定要估算好外部信号的最高频率别让定时器工作在极限边缘。3.3 定时器级联和 ITRx从模式不是摆设内部触发时钟ITRx是另一个容易被人忽略的时钟源。它的作用是让一个定时器跟随另一个定时器的特定事件运行实现定时器级联。比如你有一个主定时器输出触发信号另一个定时器以这个触发信号作为时钟这样就能做出很长的定时范围或者让多个定时器严格同步启动。实际项目中我常用定时器级联做高精度长周期测量。一个定时器负责捕获高频信号另一个定时器配合溢出计数把测量量程扩展出去。如果没有 ITRx你得在中断服务函数里软件联动延迟和抖动都大精度根本没法保证。配置定时器级联时关键是从模式的设置。标准库里有TIM_SelectInputTrigger(TIMx, TIM_TS_xxx)和TIM_SelectSlaveMode(TIMx, TIM_SlaveMode_xxx)两个接口。比如你想让 TIM2 跟着 TIM1 的更新事件跑就得把 TIM2 的触发输入选为 ITR0从模式选为外部时钟模式。这个配置顺序也有讲究先配主定时器、再配从定时器最后同时启动否则容易漏掉第一个事件。4. 实操从零配置一个 1kHz PWM 和 1 秒定时中断4.1 算子先行把 PSC、ARR、CCR 一次算对纸上谈兵不如直接做一遍。现在以 STM32F103 为例系统时钟 72MHzAPB1 2 分频后 PCLK1 36MHz所以挂在 APB1 上的 TIM2 时钟是 72MHz。目标一产生 1kHz 的 PWM初始占空比 50%。第一步选 PSC。我习惯让计数时钟落在 1MHz 左右这样 ARR 不用太大频率调节步进也够细。72MHz / 1MHz 72所以分频系数是 72PSC 71。第二步算 ARR1kHz PWM 意味着每秒 1000 个周期每个周期对应计数脉冲数 1MHz / 1kHz 1000所以 ARR 999。第三步算 CCR50% 占空比比较值大约是计数周期的一半CCR 499。目标二产生 1 秒一次的定时中断。如果还用 1MHz 计数时钟1 秒需要 100 万个计数脉冲ARR 最大只能到 65535不够用。这时候要么把 PSC 加大要么换 32 位定时器。F103 的 TIM2 和 TIM5 是 32 位定时器ARR 最大可达 4294967295你可以直接 PSC 71、ARR 999999但为了通用性我演示用 16 位方案。PSC 7199分频系数 7200计数时钟 72MHz / 7200 10kHz。1 秒对应 10000 个计数脉冲ARR 9999。这样 PSC 和 ARR 都没超 16 位范围。我把这两个场景整理成表格方便你对照目标PSC实际分频计数时钟ARR实际溢出频率1kHz PWM71721MHz9991000Hz1秒中断7199720010kHz99991Hz100Hz PWM71721MHz9999100Hz4.2 标准库代码完整实现基于标准库写一个输出 1kHz PWM 的初始化代码。注意 TIM2 挂在 APB1 上要先开 APB1 时钟GPIO 要复用推挽输出RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; // TIM2_CH1 PA0 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure);然后是定时器时基初始化TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler 71; // 72MHz / 72 1MHz TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 999; // 1MHz / 1000 1kHz TIM_TimeBaseStructure.TIM_ClockDivision 0; 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 499; // 50% 占空比 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE);这里每一步都有讲究。TIM_Prescaler传入的是 71而不是 72原因是“分频系数减一”。TIM_Period传入 999而不是 1000原因是 ARR 从 0 开始计数。TIM_Pulse是 CCR 值PWM 模式 1 下计数器小于 CCR 时输出有效电平CCR 499 时一个周期 1000 个计数里有 499 个有效、500 个无效占空比约 50%——严格说是 49.9%差别来自计数从 0 开始的边界效应实际应用通常可以忽略。如果你用的是 TIM1 或者 TIM8 这种高级定时器PWM 配置完后还要额外调用TIM_CtrlPWMOutputs(TIM1, ENABLE)否则主输出使能位 MOE 没置位引脚上什么都出不来。这个问题在我刚接触高级定时器时浪费过一下午拿出来重点提醒。定时中断的配置类似只是不用配置输出比较而是开更新中断和 NVICNVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler 7199; // 72MHz / 7200 10kHz TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 9999; // 10kHz / 10000 1Hz TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE);中断服务函数里别忘了两件事清中断标志位以及不要在中断里做耗时操作。很多人定时不准不是因为参数算错而是中断服务函数太啰嗦程序还没执行完下一次更新事件就到了现象就是中断偶尔丢。4.3 CubeMX 配置验证时钟树里的猫腻用 CubeMX 配置的时候很多人直接在 Timers 页面填 PSC、ARR却忽略了左侧时钟树。CubeMX 会根据你在 Clock Configuration 里设置的时钟自动算出定时器时钟但这个值并不总是你心里想的那个数。我建议首次配置时一定切到 Clock Configuration 页面确认 TIM2 的时钟输入是多少。比如 F103 工程里 APB1 显示 36MHzTimer Clock 显示 72MHz这就是前面说的“自动乘 2”。如果你把 APB1 改成不分频Timer Clock 就会变成 36MHz这时候同样的 PSC 71、ARR 999输出频率就不是 1kHz 而是 500Hz 左右公式一算就露馅。CubeMX 生成代码后PSC 和 ARR 是写死在MspInit之外的主函数初始化里的修改时钟树后这些值不会自动重新计算。很多人改完 HSE 频率或者 APB 分频后忘了重新核算 PSC 和 ARR结果程序跑起来频率完全不对。CubeMX 不是万能的它只负责生成代码不负责给你算业务参数。验证阶段我强烈建议用示波器或逻辑分析仪抓一下 PWM 引脚而不是只看代码。你算出的 1kHz示波器上应看到 1ms 周期占空比 50%。如果看到 2ms先别怀疑占空比设置优先检查定时器时钟是不是多乘或少乘了 2。5. 调试中的典型问题与排查技巧5.1 中断频率是计算值的一半或两倍如果你发现定时中断频率和计算值差了一半首先查计数模式。中心对齐模式下更新事件产生的频率天然比边沿对齐低这是最容易被忽略的。其次是查定时器时钟确认 APB 预分频是否导致了“自动乘 2”。F103 的 APB1 如果设置成 2 分频、4 分频、8 分频对应定时器时钟都是 PCLK 的 2 倍唯独 1 分频时不是。F4 系列也类似APB1 4 分频时定时器时钟是 PCLK1 的 2 倍。这一步判断错了后面的 PSC、ARR 全白算。排查时我习惯按这个顺序来先确认RCC-CFGR里 PPRE1/PPRE2 的值算出定时器真实输入时钟再确认计数模式是向上、向下还是中心对齐最后再核对 PSC 和 ARR 的“加一”逻辑。这三步走完80% 的频率不对问题都能定位。5.2 输入捕获测频率数据不稳定用输入捕获测外部信号频率是常见应用但很多人测出来的数据跳来跳去。原因主要有三个PSC 分频太大导致分辨率不足比如待测信号 100Hz计数时钟只有 1kHz一个周期就 10 个计数稍有抖动误差就是 10%输入滤波器配置不当毛刺被当成有效边沿计数结果被污染还有计数器溢出没处理捕获到的值不是本周期完整计数。解决方法是把计数时钟尽量拉高并配合溢出计数扩展量程。具体做法是在更新中断里累加一个变量volatile uint32_t overflow_cnt 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); overflow_cnt; } }捕获中断里再把捕获值和溢出次数组合起来得到一个完整的时间戳。比如配置 TIM3 捕获上升沿捕获中断里读取TIM_GetCapture1(TIM3)同时通过溢出中断记录计数器回绕次数实际捕获时间 溢出次数 × (ARR 1) 当前捕获值再除以计数时钟频率就是信号周期。这个方法特别适合测低频信号能有效突破 16 位定时器的量程限制。5.3 PWM 占空比设为 100% 却输出异常“我要输出 100% 占空比”很多人直接把 CCR 设置成 ARR结果示波器上看到的却是接近满量程但仍有微小缺口甚至有时候输出完全不对。这是因为 PWM 模式 1 下计数器值小于 CCR 时才输出有效电平。当 CCR ARR 时计数器只有计数到 ARR 的那一个瞬间才不小于 CCR所以输出接近 100% 但并不是纯高电平。要得到真正 100% 占空比有两个办法一是把 CCR 设置成大于 ARR 的值比如 ARR 1 甚至更大这样计数器在整个周期里都满足小于 CCR 的条件输出恒为有效电平二是直接控制输出极性或者用强制输出功能把通道锁定在高电平。另外OC 极性配置也容易踩坑。TIM_OCPolarity_High和TIM_OCPolarity_Low决定了有效电平是高还是低如果你理解反了看波形时会把占空比 50% 看成 50%但 0% 和 100% 会完全颠倒。我之前调试时就遇到过CCR 499 显示 50% 正常CCR 0 时反而一直输出高电平就是有效电平定义反了。5.4 刹车后 PWM 无法恢复高级定时器 TIM1/TIM8 带刹车功能用于电机控制等安全场合。刹车信号一旦有效输出会被强制拉到安全状态同时 MOE 主输出使能位会被硬件清零。最坑的是即使刹车信号恢复MOE 也不会自动重新置位。如果你在调试电机驱动时发现 PWM 突然消失检查刹车引脚状态是第一步第二步就是检查 MOE 位。恢复方法是调用TIM_CtrlPWMOutputs(TIM1, ENABLE)有些国产芯片可能还需要额外操作。同时注意刹车极性刹车引脚默认高电平有效还是低电平有效取决于你的硬件设计。如果你把极性配反了可能出现一上电 PWM 就被锁死完全不受控制的现象。我在产品设计里习惯把刹车极性设计成失效安全型即“断线或传感器异常就触发刹车”这样万一硬件故障系统能保证处于安全状态。5.5 在线调试时修改 PSC、ARR 不生效这个问题前面在影子寄存器部分已经解释过。在线调试时你通过 watch 窗口写入 PSC 或 ARR如果当前计数器没有发生更新事件写入值不会真正参与计数。解决办法是让定时器停止后修改或者先置 UG 位产生一次软件更新事件。很多教程里说“直接写寄存器就能生效”严格说是在某些条件下但你不能指望它永远生效。另外国产兼容芯片和 STM32 之间在寄存器行为上不一定完全一致。比如 GD32 有些型号的定时器时钟树需要单独配置 RCU 寄存器如果你把 STM32 的工程直接移植过去其他地方能跑但定时器就是不对很可能就是这些“隐形配置”没跟上。碰到这种问题先翻对应芯片的数据手册和参考手册按它的时钟树重新走一遍。最后分享一个我的自查顺序到现在为止PSC、ARR 和时钟源这三个容易算错的地方已经讲完了。最后分享一个我在实际调试中固定使用的自查顺序希望能帮你少走弯路第一步确认定时器时钟。看 CubeMX 时钟树或者查 RCC_CFGR明确 TIMx 的输入时钟到底是 PCLK 还是 PCLK×2这一步错了后面全废。第二步把 PSC 和 ARR 的“1”写在纸面上。分频系数写 PSC1计数周期写 ARR1别偷懒心算两位数还好碰到 7199 和 9999 这种心算很容易漏掉加一。第三步代码烧进去后立刻抓波形或者数秒表别只看计算结果。示波器显示 1kHz 但程序里算出来是 1000Hz说明参数是对的示波器显示 500Hz十有八九是时钟源或者计数模式问题。定时器这东西看着简单真正用起来坑都在细节里。希望这篇东西能帮你把最容易错的三块地方彻底理清下次配置定时器时一次点亮。