从 0 到 1 学 STM32 定时器:CubeMX 配置 + HAL 库代码完善,实现点灯与串口通信双功能
从 0 到 1 学 STM32 定时器:CubeMX 配置 + HAL 库代码完善,实现点灯与串口通信双功能
文章目录
一、定时器理论知识
STM32 定时器介绍
定时器(Timer)是嵌入式系统的 “时间管理核心”,本质是通过硬件计数器实现高精度时间间隔测量与周期性事件触发的模块,无需 CPU 持续参与,能极大降低处理器资源占用。STM32 微控制器不仅提供了覆盖 “基础定时 - 通用功能 - 高级控制” 的多类定时器,还集成了 PWM 生成、频率 / 占空比测量、死区控制、DMA 同步等丰富功能,可直接满足定时中断、电机驱动、信号采集、功率控制等多样化场景需求,是实现系统精准时序控制的关键硬件。
1. 定时器概念
定时器是一种可以根据设定时间间隔自动执行某些操作的硬件功能模块。常见的功能包括:
- 产生周期性事件
- 计算时间间隔
- 产生精确的PWM波形
- 测量输入信号的时间
- 生成中断
定时器通常由计数器、预分频器、比较寄存器、自动重载寄存器等部分组成。当计数器的值达到设定的值时,会触发中断或更新事件。
2. STM32定时器分类
STM32微控制器提供了多个类型的定时器,主要可分为以下几类:
(1) 基本定时器(Basic Timers)
这些定时器通常用于产生简单的定时中断。常见的基本定时器包括:
- TIM6 和 TIM7:这两个定时器是基本定时器,主要用于定时中断,无法产生PWM信号。
(2) 通用定时器(General Purpose Timers)
这些定时器功能较强,既可以产生PWM波形,又可以测量外部输入信号的频率等。常见的通用定时器有:
- TIM2、TIM3、TIM4 和 TIM5:支持多种工作模式,如计数器模式、PWM输出、输入捕获等。
(3) 高级定时器(Advanced Control Timers)
这些定时器用于控制复杂的应用场景,如电机控制和精密的PWM波形输出。常见的高级定时器有:
- TIM1 和 TIM8:支持高分辨率的PWM输出,适用于复杂的电机控制和精密应用。
(4) 看门狗定时器(Watchdog Timers)
看门狗定时器用于监控系统的运行状态,防止系统进入死锁状态。常见的看门狗定时器有:
- 独立看门狗定时器(IWDG)
- 窗口看门狗定时器(WWDG)

通常我们使用的都是通用定时器,其特点:
-
位于ABP1低速总线上
-
16位向下,向上/向下(中心对齐模式)计数模式,自动重装载计数器(TIMx_CNT)
-
16位可编程(可以实现修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535任意数值
-
四个独立通道(TIMx_CH1~4),通道用来支持:
- 输入捕获
- 输出比较
- PWM生成
- 单脉冲模式输出
-
可使用外部信号(TIM_ETR)控制定时器和定时器互连的同步电路
3. 计数器模式
STM32定时器支持多种计数器模式,常见的模式包括:
(1) 上升计数模式(Up Counting)
计数器从0开始,逐渐增加直到达最大值,然后重置为0重新开始。
(2) 下降计数模式(Down Counting)
计数器从最大值开始逐渐减少到0,然后重置为最大值重新开始。
(3) 上下计数模式(Up/Down Counting)
计数器从0开始上升到最大值,然后从最大值下降到0,反复进行。此模式通常用于对称的PWM输出。
(4) 单脉冲模式(One Pulse Mode)
计数器仅计数一次,从0计数到设定值后停止。

4. 定时时钟计算方法
定时器的时钟通常由系统时钟或外部时钟源驱动。STM32定时器的时钟频率通常由系统时钟(例如HCLK、PCLK)和定时器的预分频器(Prescaler)来决定。
定时器的周期时间(Period)可以通过自动重载寄存器(ARR)来设置。
Tout = ((arr+1)(psc+1))/Tclk
其中:
Tclk:定时器的输入时钟频率(单位MHZ)
Tout:定时器溢出时间(单位为us)
arr: 计数装载值
psc: 时钟分频系数
5. 定时器工作过程
STM32定时器的工作过程主要涉及以下几个步骤:
(1) 初始化定时器
在初始化阶段,需要设置定时器的工作模式、计数方向、预分频器、自动重载值(ARR)等参数。
(2) 计数启动
定时器在启动后开始计数,根据设定的计数频率(由时钟和预分频器决定),定时器的计数器不断增加或减少。
(3) 更新事件
当定时器计数器的值达到自动重载寄存器(ARR)的值时,会产生一个“更新事件”。此时可以触发中断,执行相关操作。
(4) 定时器中断
定时器可配置为产生中断,当计数器达到设定值时,会产生中断请求。可以在中断服务程序中处理相关逻辑,如切换PWM输出或记录时间。
(5) 停止计数
当定时器完成一次计数后,根据设定的工作模式,可以停止计数或重新计数。例如,PWM信号会继续周期性地输出,直到定时器被关闭或重新配置。
总结
STM32的定时器非常强大,支持多种模式,能够适应不同的应用场景。通过配置合适的时钟源、预分频器、自动重载值等,可以实现精准的时间控制和各种功能,如PWM输出、定时中断、频率测量等。掌握定时器的使用对于嵌入式开发非常重要,能够帮助开发者更好地控制时间和事件。
二、工程建立
为了让实验过程更简单、原理更易懂,本次所有实验要求均基于 HAL 库实现。另外,STM32CubeMX 软件的详细介绍,我已整理在之前的博客中,有需要的朋友可自行查阅。
1. 创建新项目
在STMCubeMX主界面,创建新项目,点击ACCEE TO MCU SELECTOR。

在part name里选择自己的芯片(一般选择直接搜索所需芯片),本文采用STM32F103C8T6点击信息栏中的具体芯片信息选中,点击start project。

2. RCC配置
点System Cor,选择RCC,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator。

3. SYS配置
选择调试接口,点System Cor,选择SYS。,在右侧弹出的菜单栏中选Serial Wire。
4. GPIO配置
这里我们选择PA6作为LED灯的输出,将其选为GPIO-OUT,这里我们只使用一个灯,做演示用。

5. 配置定时器
这里我们使用定时器 2 和定时器 3 来实现定时的功能。如图所示,依次点击位置1,选中定时器 2 ;位置2,配置定时器 2 的时钟源为内部时钟;位置3,分频系数为71,向上计数模式,计数周期为5000,使能自动重载模式。

同上,配置定时器 3 即可。
注意;分频系数那里虽然写的是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟我们一般会配置为72MHZ,所以72分频后得到1MHZ的时钟。1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒。也就是每隔0.005秒定时器2会产生一次定时中断。
6. 配置中断
如下图所示,开启定时器2和定时器3的中断。
如下图所示,生成定时器2和定时器3中断优先级配置代码。
7. 配置USART
选择Connectivity,点开USART1,Mode选择异步通信Asynchronous。

波特率为115200,1位停止位,无校验位(这里不需要改,默认就是这样)。
8. 配置时钟树
如下图所示,更改配置即可。

9. 配置项目设置
如下图所示,更改配置即可。

设置项目名称、路径(不能有中文)、编译器及版本。
三、代码完善
1. 启动定时器
放在main函数中
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
该函数表示启动相应的定时器,“h”表示HAL库,“tim2”表示定时器2。所以这行代码的意思就是启动定时器2和定时器3。
2. 串口通信
在main.c中定义STM32需要给上位机发送的消息。
uint8_t hello[20]="hello windows! ";
并将其放入后续的定时器中断回调函数。
HAL_UART_Transmit(&huart1,hello,20,100000);
3. 定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time_cnt =0;
static uint32_t time_cnt3 =0;
if(htim->Instance == TIM2)
{
if(++time_cnt >= 400)
{
time_cnt =0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_6);
}
}
if(htim->Instance == TIM3)
{
if(++time_cnt3 >= 1000)
{
time_cnt3 =0;
HAL_UART_Transmit(&huart1,hello,20,100000);
}
}
}
**定时器 TIM2:**每当发生 400 次溢出时,切换 GPIOA_PIN_1 的电平,用于控制一个 LED 实现定时闪烁。一次定时中断的时间是0.005秒,所以400次中断的时间是0.005400=2秒。也就是说每隔2秒,LED的状态翻转一次。
定时器 TIM3:每当发生 1000 次溢出时,通过串口 huart1 发送一个包含 20 字节数据的 hello 字符串。
HAL_TIM_PeriodElapsedCallback 是定时器的中断回调函数,用于处理中断事件。生1000次中断,才会让串口发一次消息。0.0051000=5秒,5s发送一次字符串。
通过定时器的溢出次数 (time_cnt 和 time_cnt3),可以实现周期性任务。
四、结果展示
LED灯(2s一次变换)
串口(5s接收一次消息)
timer
五、心得体会
本次项目让我对 STM32 定时器的应用有了更深入的实践认知,从理论到实操的过程,也沉淀了不少开发经验。
在定时器参数配置时,我切实感受到细节对时序精度的影响。按文档设置 TIM2 预分频系数 71、自动重载值 5000 时,起初不解为何是 72 分频,后来才明白系统会自动对预分频值加 1。结合 72MHz 系统时钟,最终得到 1MHz 计数时钟,算出 0.005 秒中断间隔,这让我意识到定时器参数需紧扣计算逻辑,不能有丝毫偏差。
中断回调函数的编写,让我体会到非阻塞任务调度的优势。项目中 TIM2 回调控制 LED 翻转,TIM3 回调实现串口发数,无需 CPU 轮询。通过静态变量累计中断次数,轻松实现 LED 每 2 秒翻转、串口每 5 秒发数,这种 “硬件触发 + 软件计数” 的模式,让我明白定时器中断能有效为 CPU 减负。
工程配置环节,我认识到模块化配置的重要性。从 STM32CubeMX 的 RCC 时钟、SYS 调试接口设置,到 GPIO 分配、USART 参数配置,每个环节环环相扣。比如未正确配置 RCC 外部晶振,会导致定时器计数频率偏差;USART 参数不匹配会引发串口乱码,这让我学会从系统视角规划开发。
调试过程中,我养成了追溯原理的习惯。初期 LED 翻转异常,回溯时钟计算逻辑发现预分频值理解偏差;串口数据不稳定,排查出是中断优先级配置问题。这让我明白,嵌入式问题多源于原理认知疏漏,需从根源解决。
这次项目不仅让我掌握了定时器与串口、GPIO 的协同使用,更建立了硬件驱动精准控制的思维,为后续复杂项目积累了宝贵经验。
六、仓库地址
https://gitee.com/zhang-kaikaikaikai/study-timer-and-pwm.git
七、参考博客
https://blog.csdn.net/weixin_64559251/article/details/127561710
更多推荐


所有评论(0)