一、STM32G431有多少个定时器?
STM32G431RBT6是蓝桥杯嵌入式竞赛的指定芯片,它的定时器资源非常丰富,共有10个定时器,分为四种类型:
| 类型 | 定时器编号 | 数量 | 特点 |
|---|---|---|---|
| 高级控制定时器 | TIM1、TIM8 | 2 | 16位,支持互补输出、死区插入、编码器接口,常用于电机控制 |
| 全功能通用定时器 | TIM2、TIM3、TIM4 | 3 | 16位,支持输入捕获、输出比较、PWM、编码器接口 |
| 通用定时器(简化版) | TIM15、TIM16、TIM17 | 3 | 只有1~2个通道,功能较少 |
| 基本定时器 | TIM6、TIM7 | 2 | 16位,只能做定时中断和触发DAC,没有输入输出通道 |
另外还有SysTick(内核滴答定时器)、看门狗定时器和RTC等,但这些不属于通用定时器范畴。
💡 蓝桥杯常用选择:TIM2、TIM3、TIM4这三个全功能通用定时器最常用,功能齐全且配置简单。TIM6/TIM7只能做定时中断,如果只需要简单的定时功能也可以用。
二、定时器的内部结构
STM32定时器的核心是一个16位计数器,围绕三个关键寄存器工作:
时钟源(CK_INT) → 预分频器(PSC) → 计数器(CNT) → 与ARR比较 → 触发更新事件
1. 预分频器 PSC
预分频器对输入的时钟信号进行分频。PSC是一个16位寄存器,可设置0~65535。实际分频系数 = PSC + 1。比如PSC设为7999,就是把时钟除以8000。
2. 计数器 CNT
计数器根据分频后的时钟脉冲进行计数。每来一个脉冲,CNT加1(向上计数模式)。
3. 自动重装载寄存器 ARR
ARR决定了计数器的上限。当CNT的值等于ARR时,产生更新事件,CNT自动清零,重新开始计数。同时可以触发中断、DMA请求等。
4. 影子寄存器
ARR和PSC都有影子寄存器(也叫预装载寄存器)。修改ARR的值时,新值不会立即生效,而是在下一次更新事件时才被载入影子寄存器。这样保证定时器在运行过程中修改参数不会产生毛刺。
💡 通俗理解:把定时器想象成一个沙漏。PSC决定沙子流的速度(分频),CNT是已经流下的沙子量,ARR是沙漏的总容量。沙子流满(CNT=ARR)就翻转一次,产生一个"更新事件"。
三、定时器的模式简介(本实验只用到一种)
定时器有很多种"玩法",但按大类分其实就两类:
- 时基模式 :不占用通道,只利用计数器本身。包括向上计数、向下计数、中央对齐三种。本实验用的就是向上计数模式,配合更新中断实现固定周期的定时。
- 通道模式:必须绑定通道才能用。包括输出比较、PWM输出、输入捕获、编码器模式等。这些模式用于输出波形或测量外部信号,本实验暂时用不到,知道名字即可,后续用到再查。
所以间隔闪烁LED只需要最基础的向上计数模式 + 更新中断,其他模式不用管。CubeMX里默认就是向上计数,不用额外修改。
四、频率计算方法
核心公式:
定时器计数频率 = 定时器时钟源频率 / (PSC + 1)
中断周期 = (ARR + 1) / 计数频率
合并成一个公式:
中断周期 = (PSC + 1) × (ARR + 1) / 定时器时钟源频率
时钟源从哪里来?
蓝桥杯开发板的主频配置为80MHz。定时器的时钟源来自APB总线:
- TIM1、TIM8、TIM15、TIM16、TIM17 挂在 APB2 总线上
- TIM2、TIM3、TIM4、TIM6、TIM7 挂在 APB1 总线上
当APB预分频器为1时,定时器时钟 = APB总线时钟 = 80MHz。
1秒定时怎么算?
假设使用TIM3,时钟源80MHz,要产生1秒的定时中断:
方案一:PSC = 8000 - 1 = 7999,ARR = 10000 - 1 = 9999
计数频率 = 80MHz / 8000 = 10kHz
中断周期 = 10000 / 10kHz = 1秒 ✓
方案二:PSC = 800 - 1 = 799,ARR = 100000 - 1 = 99999
计数频率 = 80MHz / 800 = 100kHz
中断周期 = 100000 / 100kHz = 1秒 ✓
两个方案都能实现1秒定时,区别在于计数频率不同。方案一计数慢一些,适合低功耗场景;方案二精度更高。蓝桥杯比赛中,常用 PSC=7999、ARR=9999 这组参数。
⚠️ 注意:ARR的值不能超过65535(16位寄存器的上限),所以如果PSC设得比较小,ARR可能放不下。这时候需要增大PSC。
五、CubeMX配置步骤
这边工程创建和led配置就不讲不了有需要的可以去看看嵌入式蓝桥杯从零点亮第一个LED
1. 选择定时器
在CubeMX的Pinout & Configuration界面,左侧找到Timers,展开后选择TIM1(或其他定时器)。

2. 配置时钟源
将 Clock Source 设置为 Internal Clock ,表示使用内部时钟。

3. 配置参数
在Parameter Settings选项卡中设置:
- Prescaler (PSC):7999(即8000分频)
- Counter Mode:Up(向上计数,本实验就用这个模式)
- Counter Period (ARR):9999
- 其他保持默认

4. 使能中断
切换到 NVIC Settings 选项卡,勾选 TIM1 global interrupt ,这样定时器就配置好了

5. 配置RCC和SYS
- 在左侧
Categories分类栏,点击展开 System Core(第一个红框)。 - 在 System Core 的子列表里,选中 RCC(第二个红框),页面右侧会弹出 RCC 配置面板。
- 在右侧
RCC Mode and Configuration区域找到 High Speed Clock (HSE) 下拉菜单。 - 点击下拉框,选择 Crystal/Ceramic Resonator 。

- 在右侧
SYS Mode and Configuration区域找到 Debug 下拉选项。 - 点击下拉框,选择 Serial Wire(上方红框)。
- led配置点击前面发的链接

6. 配置时钟树
- 点击顶部标签栏 Clock Configuration(上方红色框),切换到时钟树配置页面。
- 在时钟树框图找到 HCLK (MHz) (右侧红色框),此处为 AHB 总线时钟,当前配置为 80MHz,芯片最大支持 170MHz。

7. 设置工程名称与开发工具链
- 点击顶部标签栏 Project Manager(上方红框),切换到工程管理页面。
- 在
Project Settings区域找到 Project Name ,输入工程名:TIM1(左上红框)。 - 找到 Toolchain / IDE 下拉菜单,点击展开,选择 MDK-ARM (下方红框,Keil MDK 开发环境)。

8. 配置代码生成选项
切换到 Project Manager 页面下的 Code Generator 选项卡,勾选Generate peripheral initialization as a pair of '.c/.h' files per peripheral,使每个外设生成独立的初始化源文件与头文件。

9. 生成代码
确保工程使用的库是 LL库 (在Project Manager → Advanced Settings中将TIM3的库选择改为LL)。

六、实验:间隔闪烁LED
6.1 实验目标
用TIM3每1秒产生一次中断,在中断中翻转LED的电平,实现LED每隔1秒闪烁一次。
6.2 CubeMX生成的初始化代码
c
// tim.c 中自动生成
void MX_TIM3_Init(void)
{
LL_TIM_InitTypeDef TIM_InitStruct = {0};
// 使能TIM3时钟
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
//配置中断
NVIC_SetPriority(TIM1_UP_TIM16_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
// 配置时基参数
TIM_InitStruct.Prescaler = 7999;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 9999;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_SetTriggerOutput2(TIM1, LL_TIM_TRGO2_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
//下面这二行要自己加进去
LL_TIM_EnableIT_UPDATE(TIM1);//使能中断更新
LL_TIM_EnableCounter(TIM1);//使能计数
}
6.3 中断服务函数
在 stm32g4xx_it.c 中编写TIM1的中断处理函数:
c
void TIM1_UP_TIM16_IRQHandler(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM1) == 1 )//读取更新中断标志位
{
ucled ^= 0X01; // 翻转变量
LL_TIM_ClearFlag_UPDATE(TIM1); // 清除中断标志
}
}
⚠️ LL库的关键区别 :与HAL库不同,LL库的中断服务函数必须手动清除标志位
LL_TIM_ClearFlag_UPDATE()。如果忘记清除,中断会反复触发,程序会卡死。
6.4 main函数
c
int main(void)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),15, 0));
LL_PWR_DisableUCPDDeadBattery();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
while (1)
{
/* USER CODE END WHILE */
Led_Disp(ucled);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
6.5 实验效果
LED每隔1秒 翻转一次状态,亮1秒、灭1秒,形成周期为2秒的闪烁。如果发现闪烁太快或太慢,检查PSC和ARR的计算是否正确,以及定时器的时钟源是否真的是80MHz。
需要完整工程文档的可以点击下载STM32G431 TIM1 定时器 LL 库工程(蓝桥杯嵌入式)
七、总结
STM32G431有10个定时器,蓝桥杯常用TIM2/3/4。定时器的模式很多,但本实验只需要最简单的向上计数模式 + 更新中断 。定时器本质上是一个"分频计数器",PSC决定计数速度,ARR决定计数上限,两者配合就能产生任意周期的定时中断。用LL库开发的关键三步是:CubeMX配置参数 → 使能中断 → 中断中手动清标志位。把这三点搞清楚,定时器的基础应用就不成问题了。其他模式(PWM、输入捕获等)等需要时再查即可。