
上一篇搞定了串口这篇讲定时器。定时器是STM32最复杂也最强大的外设之一——延时、PWM、测频、编码器全靠它。这篇用大白话把定时器的核心用法讲透看完就能上手。一、定时器到底能干什么很多新手觉得定时器就是定时用的其实它能干的事远比你想的多功能应用场景定时中断1秒闪一次灯、周期性采样延时替代不精确的while(count--)PWM输出控制LED亮度、舵机角度、电机调速输入捕获测量脉冲宽度、频率测速、测距输出比较精确时间点触发动作计数器等于比较值时输出电平变化PWM就是用输出比较实现的编码器接口读电机编码器编码器安装在电机轴上的传感器输出脉冲信号反映转速和转向测速测位置一句话定时器是STM32的瑞士军刀搞不定它很多项目做不了。二、STM32定时器的分类STM32F1系列有8个定时器分三类┌─────────────────────────────────────┐ │ 高级定时器2个 │ │ TIM1、TIM8 │ │ 功能最全6路PWM互补输出、死区死区互补PWM两路输出之间的都关闭的短暂间隔防止电机驱动的上下桥臂同时导通导致短路新手暂时不用深究 │ │ 适用电机控制、电源 │ ├─────────────────────────────────────┤ │ 通用定时器4个 │ │ TIM2、TIM3、TIM4、TIM5 │ │ 功能中等4路PWM、输入捕获 │ │ 适用PWM调光、测频、舵机 │ ├─────────────────────────────────────┤ │ 基本定时器2个 │ │ TIM6、TIM7 │ │ 功能最简只能定时中断 │ │ 适用纯延时、DAC触发 │ └─────────────────────────────────────┘新手建议先学通用定时器TIM2-TIM5它覆盖了90%的常用场景。三、定时器的核心概念预分频和重装载这是理解定时器最关键的两个参数先搞懂它们后面的都好办。3.1 时钟源定时器的计数时钟来自哪里以STM32F10372MHz主频为例系统时钟 72MHz ↓ APB1分频通常/2 36MHz ↓ 定时器自动×2倍频 72MHz ↓ 预分频器(PSC) ↓ 计数器(CNT) ↓ 重装载值(ARR) ↓ 触发中断/事件坑点TIM2-TIM5挂在APB1最大36MHz但STM32硬件会自动×2倍频所以通用定时器的时钟也是72MHz。别以为APB1是36MHz定时器就只能36MHz。为什么APB1分频了又要倍频STM32设计上APB1总线最大只能跑36MHz为了保证总线上的外设稳定工作所以系统时钟72MHz要先÷2再给APB1。但定时器需要更高频率才能实现精细计时所以硬件在APB1分频系数≠1时自动把定时器时钟×2补偿回来让定时器能跑到72MHz。这不是多此一举而是总线速度和定时器精度的折中设计。3.2 预分频器PSC作用把72MHz分频成更慢的计数时钟。计数频率 72MHz / (PSC 1)为什么1因为PSC0表示不分频÷1PSC1表示÷2所以分频系数 PSC1。例要1MHz计数频率1μs计一个数PSC 72MHz / 1MHz - 1 713.3 重装载值ARR作用计数器计到这个值就清零并触发更新事件中断。中断周期 (ARR 1) / 计数频率同样1因为计数器从0计到ARR含ARR共ARR1个数。3.4 计算公式定时中断频率中断频率 72MHz / [(PSC 1) × (ARR 1)]例要1kHz中断1ms一次方案1PSC711MHz计数ARR9991ms触发 方案2PSC719910kHz计数ARR91ms触发建议PSC取小、ARR取大定时器精度更高。比如1ms中断用PSC71, ARR999比PSC7199, ARR9精度好。四、定时中断实现让LED每秒翻转以TIM2为例实现1秒定时中断。4.1 HAL库版CubeMX配置CubeMX配置Timers → TIM2 → 勾选 Internal Clock Prescaler (PSC): 7199 → 10kHz计数频率 Counter Period (ARR): 9999 → 1秒触发一次 NVIC Settings → TIM2 global interrupt → Enable代码/* main.c */MX_TIM2_Init();/* 初始化定时器 */HAL_TIM_Base_Start_IT(htim2);/* 启动定时中断 *//* 重写回调函数名字固定 */voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if(htim-InstanceTIM2){HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);/* 翻转LED */}}4.2 寄存器版voidtim2_init(void){RCC-APB1ENR|(10);/* 开TIM2时钟 */TIM2-PSC7199;/* 预分频72MHz/7200 10kHz */TIM2-ARR9999;/* 重装载10kHz/10000 1Hz */TIM2-DIER|(10);/* 允许更新中断 */TIM2-CR1|(10);/* 启动定时器 */NVIC_EnableIRQ(TIM2_IRQn);/* NVIC使能NVIC嵌套向量中断控制器STM32内部管理中断的硬件模块这里告诉它允许TIM2中断 */}/* 中断服务函数名字固定启动文件里定义 */voidTIM2_IRQHandler(void){if(TIM2-SR(10)){/* 检查更新中断标志 */TIM2-SR~(10);/* 清标志必须 */GPIOC-ODR^(113);/* 翻转PC13 */}}关键中断函数里必须清除中断标志否则会无限重入中断主程序根本跑不了。五、用定时器实现精确延时上一篇串口文章里延时用的是HAL_Delay()或while(count--)。前者精度毫秒级后者更不准。用定时器可以实现微秒级精确延时。5.1 微秒延时阻塞式利用CNT计数器1MHz计数频率下CNT每加1就是1μs/* 前提TIM配置为PSC711MHz计数ARR65535 */voiddelay_us(uint16_tus){__HAL_TIM_SET_COUNTER(htim2,0);/* 计数器清零 */while(__HAL_TIM_GET_COUNTER(htim2)us);/* 等到计数到us */}5.2 毫秒延时voiddelay_ms(uint16_tms){while(ms--){delay_us(1000);}}适用场景传感器初始化序列、时序敏感的通信协议。HAL_Delay()最小1ms做不了μs级延时。六、PWM输出调LED亮度/控制舵机PWM脉宽调制是定时器最常用的功能之一。6.1 PWM原理PWM通过改变高电平占整个周期的比例占空比来控制平均电压占空比 50% ┌──┐ ┌──┐ ┌──┐ │ │ │ │ │ │ ──┘ └──┘ └──┘ └── ←周期→ 占空比 20%LED暗 ┌─┐ ┌─┐ ┌─┐ │ │ │ │ │ │ ──┘ └────┘ └────┘ └─── 占空比 80%LED亮 ┌───┐ ┌───┐ ┌───┐ │ │ │ │ │ │ ──┘ └──┘ └──┘ └─平均电压 供电电压 × 占空比。占空比越大LED越亮。6.2 三个关键参数参数作用公式ARR决定PWM周期频率频率 72MHz / [(PSC1)×(ARR1)]CCR决定占空比占空比 CCR / (ARR1)PSC预分频和ARR一起决定频率6.3 PWM调光实现HAL库CubeMX配置TIM3 → Channel1 → PWM Generation CH1 Prescaler: 71 → 1MHz计数 Counter Period (ARR): 999 → 1kHz PWM频率 Pulse (CCR): 500 → 初始50%占空比代码/* 启动PWM */MX_TIM3_Init();HAL_TIM_PWM_Start(htim3,TIM_CHANNEL_1);/* 调节占空比0-1000对应0%-100% */__HAL_TIM_SET_COMPARE(htim3,TIM_CHANNEL_1,500);/* 50%亮度 *//* 呼吸灯效果 */uint16_tbrightness0;int8_tstep10;while(1){__HAL_TIM_SET_COMPARE(htim3,TIM_CHANNEL_1,brightness);brightnessstep;if(brightness1000||brightness0)step-step;HAL_Delay(20);}呼吸灯原理占空比从0渐变到100%再变回0循环往复LED看起来像在呼吸。6.4 PWM控制舵机舵机SG90控制信号是50Hz PWM占空比2.5%-12.5%对应0-180度。这些参数怎么来的舵机内部的控制电路期望接收50Hz周期20ms的PWM信号其中高电平持续时间0.5ms-2.5ms对应0-180度。换算成占空比0.5ms/20ms2.5%2.5ms/20ms12.5%。这些参数来自SG90的Datasheet数据手册不同舵机可能不同。/* 舵机配置50Hz PWM *//* PSC71(1MHz), ARR19999 → 1MHz/20000 50Hz *//* CCR 500-2500 对应 0-180度 */voidservo_set_angle(floatangle){uint16_tccr500(uint16_t)(angle*2000.0/180.0);__HAL_TIM_SET_COMPARE(htim3,TIM_CHANNEL_1,ccr);}/* 使用 */servo_set_angle(0);/* 0度 */HAL_Delay(1000);servo_set_angle(90);/* 90度 */HAL_Delay(1000);servo_set_angle(180);/* 180度 */七、输入捕获测量频率输入捕获可以测脉冲宽度、频率常用于转速测量、超声波测距。7.1 原理┌─────────┐ ┌─────────┐ │ │ │ │ ───────┘ └─────────┘ └── ↑ ↑ 捕获点1 捕获点2 (上升沿) (下降沿) 两次捕获的CNT差值 高电平时间定时器在引脚电平变化时上升沿/下降沿自动把当前CNT值存入CCR通过两次CCR差值就能算出脉冲宽度。7.2 测量频率代码HAL库uint32_tcapture_val10,capture_val20;uint8_tcapture_flag0;floatfrequency0;/* CubeMX: TIM4 CH1, 配置为上升沿捕获 */voidHAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef*htim){if(htim-InstanceTIM4){if(capture_flag0){capture_val1HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);capture_flag1;}else{capture_val2HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);/* 计算周期假设计数频率1MHz */uint32_tperiodcapture_val2-capture_val1;if(period0){frequency1000000.0/period;/* 1MHz / 周期 */}capture_flag0;}}}/* 启动 */HAL_TIM_IC_Start_IT(htim4,TIM_CHANNEL_1);应用测电机转速霍尔传感器输出脉冲、超声波HC-SR04回波时间、红外遥控解码。八、常见踩坑坑1定时中断不触发排查顺序开了时钟吗RCC-APB1ENR对应位开了中断吗TIMx-DIER的UIE位NVIC使能了吗NVIC_EnableIRQ()启动定时器了吗TIMx-CR1的CEN位清标志了吗中断函数里必须清SR的UIF位五步缺一不可少一步中断就不工作。坑2中断频率不对现象算的是1秒中断实际2秒或0.5秒。原因忘了1PSC和ARR都要1公式里是(PSC1)×(ARR1)时钟频率搞错APB1定时器倍频忘了以为36MHz实际72MHzCubeMX里的PSC值CubeMX里Prescaler栏填的就是PSC寄存器值比如填71寄存器就是71硬件会自动按PSC172来分频。你不需要手动加1填71就行。验证方法用示波器看LED翻转频率或串口打印计数验证。坑3PWM无输出排查引脚配置对了吗PWM引脚必须是复用功能AF不能是普通GPIO启动PWM了吗HAL_TIM_PWM_Start()必须调用CCR值合理吗CCR0时占空比0%常灭CCRARR时占空比100%常亮通道选对了吗TIM3_CH1对应PA6别接到PA7CH2上坑4输入捕获值跳动现象测出的频率忽大忽小。原因信号有毛刺触发误捕获 → 加硬件滤波计数器溢出了 → 增大ARR或用预分频降低计数频率两次捕获间隔过长CNT回绕 → 检测溢出并补偿九、总结要点内容定时器分类基本(TIM6/7) / 通用(TIM2-5) / 高级(TIM1/8)核心参数PSC预分频 ARR重装载公式频率72MHz/[(PSC1)(ARR1)]定时中断配PSC/ARR → 开中断 → 启动 → 中断里清标志精确延时1MHz计数频率下CNT每11μsPWM输出CCR决定占空比ARR决定频率输入捕获两次捕获CCR差值 脉冲宽度一句话总结定时器的核心就是PSCARR算频率CCR定占空比把这两个公式记牢定时器就拿下了一半。下一篇预告《STM32 ADC采样详解从单次转换到DMA连续采集》如果这篇文章对你有帮助点赞 收藏 关注这是我持续更新的动力有问题欢迎评论区交流我会逐条回复。作者嵌入式阿蔡