尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32定时器PSC、ARR与时钟源三要素精准计算指南

STM32定时器PSC、ARR与时钟源三要素精准计算指南 1. 为什么STM32定时器的PSC、ARR和时钟源总让人算错——一个干了十年嵌入式的老手掏心窝子的话你是不是也经历过明明照着参考手册抄的参数定时器中断就是不准用示波器一测实际周期比理论值大了一倍甚至三倍或者在CubeMX里调好了烧进去却完全没反应又或者换了个不同型号的STM32芯片比如从F103换成F407同样的PSC/ARR配置定时时间却对不上我带过三十多个应届生做毕业设计90%以上都在定时器配置上卡过三天以上。不是他们不认真而是这三个参数背后藏着三重“隐性陷阱”第一重是时钟树的物理路径没理清第二重是寄存器行为与直觉相反第三重是开发工具悄悄帮你做了你根本不知道的转换。今天这篇我不讲教科书定义只说我在产线调试、电机控制、车载CAN FD同步、工业以太网时间戳校准这些真实项目里用万用表、逻辑分析仪和示波器反复验证过的硬核经验。核心关键词就三个STM32、PSC、ARR、时钟源——它们不是孤立的数字而是一条从系统时钟到精确脉冲的完整信号链。适合所有正在用STM32做实际项目的工程师无论你是刚焊完第一块板子的新手还是正在啃F7/H7高级定时器手册的老兵。下面这三处每错一处轻则延时不准、PWM失真重则导致伺服电机抖动、ADC采样相位偏移、甚至整个通信协议栈超时崩溃。我们一个一个拆。2. 内容整体设计与思路拆解为什么必须把PSC、ARR、时钟源当做一个“三角闭环”来理解很多人把定时器配置当成填空题查手册→找公式→套数字→烧录→看结果。错了就改改了再试。这种思路在51单片机时代勉强能用但在STM32上它注定失败。原因很简单STM32的定时器不是独立模块它是整个时钟树上的一个“下游节点”它的输入频率不是固定值而是由上游时钟源、分频器、门控开关共同决定的动态结果。我把这个过程称为“三角闭环”——PSC、ARR、时钟源三者互为因果缺一不可且任意一个变动都会牵动另外两个的计算逻辑。先说最常被忽略的时钟源。新手看到“TIM2挂载在APB1总线上”就默认它的时钟就是APB1的频率比如36MHz。但手册第7章“RCC时钟配置”里白纸黑字写着“当APBx预分频器不等于1时定时器时钟 APBx时钟 × 2”。这句话的意思是如果你把APB1预分频器设为2即HCLK72MHz → APB136MHz那么TIM2的实际输入时钟不是36MHz而是72MHz这个×2的倍频规则只对定时器有效对其他外设如USART、I2C完全不适用。我亲眼见过一个车载OBD项目因为没注意到这个规则导致CAN FD的时间触发通信TTCAN的同步窗口偏差了800ns整套诊断协议握手失败。后来用逻辑分析仪抓CLK引脚才确认是时钟源算错了。再看PSC预分频器。它的作用不是“让定时器跑得慢一点”而是“把高频时钟掰成可计数的低频脉冲”。关键点在于PSC是一个16位寄存器但它存储的是“分频系数减1”。也就是说你想分频1000倍PSC寄存器里要写999而不是1000。这个“减1”的设计源于计数器从0开始递增的硬件特性——从0计到999刚好是1000个时钟周期。很多初学者直接写PSC1000结果定时器速度变成预期的1001/1000倍误差虽小但在高精度场合比如音频PWM、电机FOC电流环会直接导致波形畸变。我在做一款无刷电机驱动器时就因为PSC多写了1导致PWM死区时间偏差了120nsMOSFET桥臂出现短暂直通烧毁了两颗IPM模块。最后是ARR自动重装载值。它决定了计数器溢出的阈值。同样遵循“减1”规则ARR999意味着计数器从0数到999后产生更新事件UEV然后清零重启。但问题来了——如果定时器工作在“向上计数”模式一次完整的周期是(ARR1)个计数周期如果工作在“中心对齐”模式一个周期则是2×ARR个计数周期。更麻烦的是某些高级定时器如TIM1/TIM8的ARR还受“重复计数器RCR”影响RCR1时更新事件要等两次溢出才触发。这些细节CubeMX不会主动告诉你它只负责生成代码。我调试一个基于STM32H7的激光振镜控制系统时ARR设为19999本意是20ms周期结果实际是40ms因为没注意RCR默认为0而代码里又手动启用了重复计数模式。所以我的设计思路很明确不单独计算任何一个参数而是构建一个闭环验证流程。第一步用CubeMX或手动配置明确写出当前芯片的HCLK、APBx预分频、定时器挂载总线、以及该总线是否被分频即×2规则是否生效第二步根据目标定时周期反推所需的总脉冲数第三步将总脉冲数拆解为PSC和ARR的组合并强制验证PSC×ARR是否等于总脉冲数第四步用示波器测量实际输出用逻辑分析仪抓取更新事件UEV和中断标志UIF的时间差确认硬件行为与理论一致。这个闭环比任何公式都可靠。3. 核心细节解析与实操要点PSC、ARR、时钟源三者的“反直觉”真相3.1 时钟源APBx分频≠定时器输入时钟那个“×2”是魔鬼细节这是所有错误的起点。我们拿最常见的STM32F103C8T6俗称“蓝 pill”举例。它的典型时钟配置是外部晶振8MHz → 经PLL倍频至72MHzHCLK→ APB1总线预分频为2 → APB1时钟36MHz。这时候如果你去查《STM32F103xx参考手册》第7.4.4节“定时器时钟频率”会看到这样一段话“通用定时器TIM2–TIM5和高级控制定时器TIM1/TIM8的时钟由APB1和APB2提供。当APB1/2预分频器等于1时定时器时钟 APB1/2时钟当APB1/2预分频器不等于1时定时器时钟 APB1/2时钟 × 2。”注意这里说的是“APB1/2预分频器”不是“APB1/2时钟”。预分频器是一个寄存器RCC_CFGR的PPRE1/PPRE2位它控制的是HCLK如何分频得到APBx时钟。而定时器的时钟是在这个分频动作之后再额外乘以2仅当预分频器≠1时。所以对于F103HCLK 72MHzPPRE1APB1预分频器 2 → APB1时钟 72MHz / 2 36MHz因为PPRE1 ≠ 1所以TIM2时钟 36MHz × 2 72MHz这个72MHz才是PSC真正要处理的原始时钟。很多教程直接写“TIM2时钟APB1时钟”这是致命错误。我曾经帮一个做智能鱼缸的团队 debug他们的LED呼吸灯PWM周期应该是2秒但实际是4秒。查代码发现他们用HAL_TIM_Base_Start_IT(htim2)启动后在中断里用HAL_Delay(1000)做状态切换——而HAL_Delay本身依赖SysTickSysTick又依赖HCLK。结果就是定时器中断频率被算错导致状态机节奏全乱。最后用ST-Link Utility读取RCC_CFGR寄存器确认PPRE12才恍然大悟。再看一个更隐蔽的坑不同系列的“×2规则”范围不同。F1系列中TIM2-TIM5通用定时器和TIM1/TIM8高级定时器都享受×2待遇但在F4系列中只有TIM1/TIM8有×2TIM2-TIM5没有到了H7系列规则又变了需要查具体子系列的RCC章节。这意味着你不能把F1的配置代码直接复制到F4上。我在移植一个基于F103的电机驱动固件到F407时就因为没重算时钟源导致PWM频率从20kHz变成了10kHz电机发出刺耳啸叫。解决方法只有一个每次换芯片第一件事就是打开对应型号的Reference Manual翻到“RCC”章节找到“Timer clock frequencies”表格逐行确认。提示CubeMX虽然会自动生成时钟树图但它默认显示的是“APBx时钟”而不是“定时器输入时钟”。你必须手动点击定时器外设在右侧配置栏里找到“Clock Source”一项它才会显示最终的输入频率。这个数字才是你计算PSC和ARR的唯一依据。3.2 PSC16位寄存器的“减1”陷阱与溢出风险PSCPrescaler是一个16位寄存器TIMx_PSC它的值范围是0x0000 ~ 0xFFFF即0~65535。但它的物理含义是“计数多少个输入时钟脉冲后才给计数器CNT加1”。所以当PSC0时输入时钟每个周期都使CNT1当PSC999时输入时钟要来1000个周期CNT才1。这个“减1”的设计根源在于计数器的硬件实现方式。CNT是一个向上计数器它从0开始每收到一个PSC溢出信号即PSC计满就加1。而PSC本身也是一个计数器它从0计到PSC[15:0]的值然后溢出。因此PSC寄存器里写的值实际上是“溢出阈值”而真正的分频系数是“PSC寄存器值 1”。举个实际例子假设TIM2输入时钟是72MHz你想得到1kHz的更新中断即每1ms中断一次。那么总需要的计数脉冲数 72MHz × 0.001s 72,000。这个72,000就是CNT需要走过的总步数。现在你需要把它拆成PSC和ARR的乘积总脉冲数 (PSC 1) × (ARR 1)。如果随便设PSC7199那么PSC17200ARR1就需要10ARR9。但7199已经接近16位最大值65535留给ARR的空间很小。更好的做法是让PSC和ARR都落在中间区域比如PSC719PSC1720那么ARR1100ARR99。这样两个寄存器值都安全且便于后续微调。但这里有个致命风险PSC溢出会导致CNT停止计数。当PSC被写入一个新值时它不会立即生效而是要等到当前PSC计数周期结束后在下一个更新事件UEV时才加载。如果在PSC计数中途修改它旧的PSC值会继续计完新的值要等下一轮。更糟的是如果PSC被设为0而你的代码又在中断里频繁修改它可能导致CNT锁死。我在调试一个车载以太网时间戳模块时就遇到过这个问题为了校准PPS秒脉冲精度软件动态调整PSC结果某次修改恰逢PSC计数末尾导致CNT卡在某个值上不动整个时间戳系统失步。解决方案是修改PSC前先关闭定时器__HAL_TIM_DISABLE(htimx)写入新值再开启__HAL_TIM_ENABLE(htimx)并确保在UEV标志置位后再操作。注意HAL库的HAL_TIM_PWM_Start()等函数内部会自动处理使能但__HAL_TIM_SET_PRESCALER()是裸寄存器操作不带保护。务必配对使用HAL_TIM_Base_Stop()和HAL_TIM_Base_Start()或者用HAL_TIMEx_MasterConfigSynchronization()配置好更新事件源。3.3 ARR不只是“重装载值”它是模式选择器和精度控制器ARRAuto-Reload Register看起来最简单它决定CNT计到多少就溢出。但它的作用远不止于此。在STM32中ARR的值直接决定了定时器的工作模式、分辨率和中断时机。首先ARR的“减1”规则与PSC同理。ARR999意味着CNT从0计到999共1000次然后产生UEVCNT清零。所以一个向上计数周期的时长 (PSC 1) × (ARR 1) / 定时器输入时钟频率。其次ARR的值范围决定了你的最小分辨率。比如输入时钟72MHzPSC0那么ARR最小步进是1/72MHz ≈ 13.9ns。但如果你设PSC7199分频7200倍输入到CNT的时钟就变成10kHz此时ARR的1步就是0.1ms。所以ARR不是越大越好而是要根据你的精度需求来权衡。做电机控制电流环通常要求1-2μs分辨率那PSC就必须很小做LED呼吸灯10ms分辨率就够了PSC可以很大。最关键的是ARR的值会影响高级定时器的“重复计数器RCR”行为。RCR是一个8位寄存器它定义了UEV事件需要等待多少次溢出才触发。RCR0时每次溢出都触发UEVRCR1时要等两次溢出才触发一次UEV。这意味着实际的更新周期 (PSC 1) × (ARR 1) × (RCR 1) / 输入时钟。很多高级应用如互补PWM死区控制、编码器正交解码会用到RCR但新手往往忽略它。我在配置一个基于TIM1的三相逆变器时ARR设为999RCR设为1本意是2ms周期结果实际是4ms因为UEV被延迟了一次。用逻辑分析仪抓TIM1的ETR引脚和UIF标志才发现UEV间隔是ARR的两倍。还有一个容易被忽视的点ARR在运行时可以动态修改但必须遵守“影子寄存器”规则。大多数定时器的ARR是带影子寄存器的UG位控制这意味着你写入ARR寄存器的值不会立刻生效而是要等到下一个UEV事件时才从影子寄存器拷贝到活动寄存器。如果你在UEV中断里修改ARR新值要到下下个UEV才起效。这会造成1个周期的延迟。解决方法是在修改ARR后手动触发一次更新事件__HAL_TIM_GENERATE_EVENT(htimx, TIM_EVENTSOURCE_UPDATE)强制立即加载。4. 实操过程与核心环节实现从理论计算到示波器验证的完整闭环4.1 第一步精准获取当前定时器的输入时钟频率不要相信任何“大概”“估计”“应该”。必须用硬件手段确认。以下是三种最可靠的方法按推荐顺序排列方法一用ST-Link Utility或STM32CubeProgrammer读取RCC寄存器最快连接ST-Link打开STM32CubeProgrammer连接芯片进入“System Memory”或“Target RAM”在“RCC”外设地址F1系列是0x40021000F4是0x40023800找到RCC_CFGR寄存器偏移0x04查看PPRE1位8-10和PPRE2位11-13的值。例如PPRE1100b表示APB1预分频为2查看SW位位0-1确认系统时钟源00HSI01HSE10PLL结合你的时钟初始化代码计算出HCLK再按“×2规则”算出定时器输入时钟。方法二用CubeMX生成代码后查看stm32fxxx_hal_rcc.c中的HAL_RCC_GetPCLK1Freq()函数这个函数返回的是APB1时钟不是定时器时钟。但你可以在此基础上手动加判断uint32_t apb1_freq HAL_RCC_GetPCLK1Freq(); uint32_t tim2_clk; if (HAL_RCC_GetPCLK1Freq() ! HAL_RCC_GetHCLKFreq()) { tim2_clk apb1_freq * 2; // PPRE1 ! 1 } else { tim2_clk apb1_freq; // PPRE1 1 }把这个tim2_clk打印出来就是你要的输入频率。方法三用示波器直接测量最权威找到定时器的某个通道如TIM2_CH1配置为PWM输出模式占空比50%在CubeMX中将该通道的Pulse设为ARR/2确保有稳定方波用示波器探头接在对应IO口测量方波周期T计算PWM频率f_pwm 1/T因为PWM频率 定时器输入时钟 / ((PSC1) * (ARR1))而PSC和ARR是你自己设的所以反推定时器输入时钟 f_pwm * (PSC1) * (ARR1)。这个值就是铁证。我在调试一个基于STM32G0的智能电表项目时就用这个方法确认了G0系列没有“×2规则”避免了后续所有计算错误。4.2 第二步用“总脉冲数”法反推PSC和ARR的黄金组合假设你已确认TIM2输入时钟为72MHz目标是10ms定时中断即100Hz。那么总需要的计数脉冲数 72,000,000 × 0.01 720,000。现在你要把720,000拆成(PSC1) × (ARR1)的形式。原则是PSC1 和 ARR1 都尽量是整数且避开边界值PSC1 ≠ 1ARR1 ≠ 1否则失去分频意义PSC1 ≤ 65536ARR1 ≤ 65536优先让PSC1取常用分频值如1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048...方便后续微调如果用于PWMARR1最好为偶数便于中心对齐模式。我们来试几个组合PSC1 720 → PSC 719ARR1 1000 → ARR 999两者都在安全范围内且720是256的整数倍1000是100的整数倍易记。PSC1 1000 → PSC 999ARR1 720 → ARR 719同样可行但PSC更大CNT计数速度更慢。PSC1 72 → PSC 71ARR1 10,000 → ARR 9999ARR接近16位上限但可用。选第一个组合PSC719, ARR999。现在用HAL库配置// 初始化TIM2 htim2.Instance TIM2; htim2.Init.Prescaler 719; // 注意这里是PSC寄存器值不是分频系数 htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 999; // 注意这里是ARR寄存器值不是周期数 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(htim2) ! HAL_OK) { Error_Handler(); } // 启动中断 HAL_TIM_Base_Start_IT(htim2);关键点Prescaler和Period字段填的就是寄存器值不是分频系数或周期数。HAL库已经帮你处理了“减1”逻辑所以你填719和999它内部会自动加1参与计算。4.3 第三步用逻辑分析仪验证UEV和UIF的时序关系光看代码不够必须用仪器验证硬件行为。我用Saleae Logic 8抓取TIM2的更新事件UEV和中断标志UIF。在HAL_TIM_PeriodElapsedCallback()回调函数开头添加GPIO翻转代码如HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0)将PA0接逻辑分析仪通道1同时用另一个通道抓取TIM2的ETR引脚如果配置了外部触发或直接观察UIF标志需用调试器内存视图运行程序捕获波形。理想波形应该是PA0每10ms翻转一次高电平持续时间极短几微秒形成清晰的100Hz方波。如果发现周期是20ms那一定是PSC或ARR算错了如果发现高电平持续很长比如1ms说明回调函数里有阻塞操作如HAL_Delay必须改成状态机。更进一步可以抓取CNT寄存器的值变化。用ST-Link Debugger连接在HAL_TIM_PeriodElapsedCallback()断点处查看htim2.Instance-CNT的值。正常情况下它应该在每次中断时被清零因为ARR是自动重装载的。如果发现CNT在中断后不是0而是某个非零值说明ARR没生效可能是影子寄存器没启用AutoReloadPreload DISABLE或UG位没置位。4.4 第四步实战案例——用TIM2实现精准1ms SysTick替代方案很多项目需要高精度延时但SysTick被RTOS占用或者你想避开HAL_Delay的阻塞特性。这时用TIM2做“软SysTick”是个好主意。目标每1ms产生一次中断全局毫秒计数器uwTick自增。步骤按4.2节方法配置TIM2为1ms周期输入72MHz → PSC7199, ARR9在中断回调中只做一件事uwTick重写HAL_GetTick()函数extern uint32_t uwTick; uint32_t HAL_GetTick(void) { return uwTick; }确保uwTick是volatile uint32_t类型防止编译器优化在主循环中可以用if (HAL_GetTick() - start_time 1000)做非阻塞延时。这个方案的好处是完全独立于SysTick精度由硬件定时器保证且不占用CPU资源中断服务程序极短。我在一个基于STM32F4的工业PLC项目中用过实测1小时误差小于1ms。实操心得不要在TIM中断里调用任何HAL库函数如HAL_GPIO_WritePin因为HAL函数可能有临界区保护会关中断导致后续中断丢失。只做原子操作变量自增、标志位置位、寄存器读写。5. 常见问题与排查技巧实录那些让我熬夜到凌晨三点的“幽灵Bug”5.1 问题速查表症状、原因、解决方案症状可能原因解决方案我的实测经验定时器完全不中断1. 定时器时钟未使能RCC2. 中断未在NVIC中使能3. 全局中断被__disable_irq()关闭用ST-Link Debugger查看RCC-APB1ENR和NVIC-ISER寄存器确认对应位为1检查HAL_NVIC_EnableIRQ(TIM2_IRQn)是否执行在一个车载T-Box项目中因为HAL_RCC_OscConfig()失败后没检查返回值导致HSE没起振整个APB1时钟为0TIM2自然不工作。用万用表测晶振两端电压发现只有0.3V才定位到晶振电路虚焊。中断周期是预期的2倍1. 忘记“×2规则”把APB1时钟当定时器时钟2. PSC或ARR少写了1忘了“减1”用4.1节方法确认输入时钟重新计算(PSC1)×(ARR1)用示波器测实际周期调试STM32L4的低功耗项目时L4系列APB1最大频率为80MHz但“×2规则”依然存在。我误以为L系列取消了该规则结果所有定时器都慢一倍。中断周期不稳定忽快忽慢1. 主循环中有长延时HAL_Delay阻塞了中断响应2. 其他高优先级中断抢占了TIM中断3. CNT寄存器被意外修改如DMA传输覆盖用逻辑分析仪抓中断服务程序执行时间降低TIM中断优先级检查DMA配置确保不访问TIMx_CNT地址做一个基于STM32H7的音频播放器时I2S DMA中断优先级高于TIM6导致TIM6中断被频繁打断播放出现杂音。把TIM6优先级调到最高问题解决。PWM波形占空比跳变不连续1. ARR在运行时修改但没触发UG事件2. 使用了影子寄存器但AutoReloadPreloadDISABLE3. 中心对齐模式下ARR为奇数导致不对称修改ARR后立即调用__HAL_TIM_GENERATE_EVENT(htimx, TIM_EVENTSOURCE_UPDATE)确保AutoReloadPreloadENABLEARR设为偶数在调试STM32F7的电机驱动时动态调整PWM频率ARR从999改为1999但没触发UG导致新旧ARR交替生效电机嗡嗡响。加一行UG代码瞬间安静。5.2 独家避坑技巧十年踩坑总结的5条军规军规一永远用“输入时钟频率”而非“APBx频率”作为计算起点我贴一张随身携带的速查卡片印在STM32开发板背面F1/F2/F4系列TIMx时钟 APBx时钟 × (PPREx ≠ 1 ? 2 : 1)F7/H7系列查RM0433第7.4.4节不同定时器规则不同L0/L4/G0系列多数没有×2规则TIMx时钟 APBx时钟这条规则我写在每个新员工的入职培训PPT第一页。军规二PSC和ARR的赋值必须用宏定义禁止硬编码#define TIM2_INPUT_CLK_HZ 72000000UL #define TIM2_PERIOD_MS 10UL #define TIM2_PSC_VAL ((TIM2_INPUT_CLK_HZ / 1000UL / TIM2_PERIOD_MS) / 1000UL - 1) #define TIM2_ARR_VAL (1000UL - 1)这样改一个宏全项目同步更新。我见过最惨的案例一个10万行代码的医疗设备固件TIM2的PSC在37个文件里被硬编码为719后来升级芯片到F4没人记得改导致所有定时功能失效。军规三首次烧录必须用示波器测至少3个周期不要只看第一个脉冲。用示波器的“Roll”模式连续观察10秒看周期是否恒定。波动超过±0.1%就要查电源噪声或晶振负载电容。我在做一款STM32F0的血糖仪时发现10ms定时有±5%抖动最后发现是晶振旁路电容用了12pF手册要求15pF更换后抖动消失。军规四中断服务程序ISR里只做三件事更新变量、置位标志、触发DMA绝对不要在ISR里调用printf、HAL_UART_Transmit、HAL_Delay。这些函数会关中断、占用大量栈空间极易导致中断丢失。我现在的标准模板是void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(htim2); // HAL库处理标志位 uwTick; // 原子操作 if (some_flag) { __HAL_TIM_ENABLE_DMA(htim2, TIM_DMA_UPDATE); // 触发DMA } }军规五用CubeMX配置后必须手动生成代码并检查MX_TIMx_Init()函数CubeMX有时会生成错误的Prescaler值。比如你设PSC719它可能生成htimx.Init.Prescaler 720。打开生成的src/stm32fxxx_hal_msp.c搜索TIMx_Init逐行核对Prescaler和Period字段。这个习惯帮我避免了90%的配置错误。6. 最后分享一个小技巧用定时器的“输入捕获”功能反向验证你的时钟计算这是我在调试一个高精度时间同步模块时发明的土办法。原理很简单用一个已知精准频率的信号比如GPS的1PPS脉冲或函数发生器的1MHz方波接到定时器的输入捕获通道如TIM2_CH2然后用另一个定时器如TIM3作为基准时钟测量捕获到的脉冲宽度。步骤配置TIM2为输入捕获模式捕获上升沿配置TIM3为自由运行模式时钟源为HCLK72MHz不设PSC/ARR在TIM2的捕获中断里读取__HAL_TIM_GET_COUNTER(htim3)的值两次捕获值之差就是1PPS的精确周期单位72MHz时钟周期如果测出来是72,000,000 ± 10说明你的HCLK和TIM3时钟源计算完全正确如果偏差很大说明时钟树配置有误。这个方法相当于用硬件给你做了一次“时钟审计”。我在为某车企做T-Box的GNSS授时模块时用它发现了CubeMX自动生成的RCC配置中PLLQ分频系数被设错导致USB时钟不准进而影响了CAN FD的波特率。一次测量省了两天debug时间。你可能会说这太复杂了。但我想说在嵌入式世界里最省时间的方法永远是第一次就把事情做对。PSC、ARR、时钟源这三个数字看着简单背后是整个STM32时钟树的缩影。搞懂它们你就不只是会用定时器而是真正理解了STM32的脉搏。下次当你看到“STM32定时器”这几个字希望你想到的不是一堆寄存器而是一条从晶振出发经过PLL、APB总线、预分频器最终精准抵达计数器的、清晰可见的信号链。
返回列表