前言
在STM32嵌入式开发里,定时器是十分核心的外设,LED闪烁、按键消抖、电机控制、实时调度等场景都需要它。基础定时器(Basic Timer)是定时器家族里最简单的一员,也是学习通用、高级定时器的基础。
不少初学者只会调用HAL库做延时,看不懂时基单元、影子寄存器和时钟通路,学到PWM、输入捕获时容易混淆,本质就是基础定时器底层原理没有学透。
本文基于STM32F1,从基础定时器的作用出发,拆解时基单元、时钟链路、影子寄存器,讲解中断、DMA联动原理、配置和适用场景,附带可直接运行的HAL库代码与面试考点。读完可以掌握定时器底层逻辑,为后续学习打下根基。
一、什么是基础定时器?我们为什么需要它?
1.1 基础定时器的硬件定位
下图列出STM32F103系列不同型号内置定时器资源差异,区分高级、通用、基础定时器:

下图为STM32F1系统架构框图,体现定时器、总线矩阵、APB1/APB2总线之间的连接关系:

下图示意Cortex‑M3内核与基础定时器TIM6、TIM7在MCU内部的布局:

在STM32F103系列中,TIM6、TIM7 是两款标准的基础定时器。它们是定时器家族中结构最简、资源最少的型号:
-
仅包含16位自动重装载计数器,仅支持向上计数一种模式
-
没有外部IO引脚,不支持输入捕获、输出比较、PWM生成等功能
-
核心能力:定时计数、产生更新事件、触发中断、触发DMA、触发DAC转换
简单说:基础定时器就是一个"纯硬件计数器",只专注于定时这件事,不做任何和IO相关的工作。
1.2 为什么不直接用SysTick?
很多人会有疑问:Cortex-M内核自带了SysTick滴答定时器,也能实现定时,为什么还要设计基础定时器?核心原因有三点:
-
硬件独立性
SysTick属于内核外设,和CPU强绑定;而基础定时器是独立的片上外设,可完全脱离CPU工作。配合DMA可以实现"零CPU干预"的定时触发,CPU可以休眠或处理其他任务。
-
外设联动能力
基础定时器的更新事件可以直接触发DAC转换、ADC采集、DMA传输,是模拟外设、数据传输的硬件时基,这是SysTick做不到的。
-
数量与扩展性
一颗芯片上可以有多个基础定时器(F103有TIM6、TIM7两个),各自独立工作;而SysTick只有一个,通常被操作系统占用作为系统时基,用户无法随意使用。
1.3 典型应用场景
-
简单的定时任务:按键扫描、LED闪烁、数据周期采集
-
DAC波形生成:定时触发DAC输出,生成正弦波、方波等模拟信号
-
DMA周期搬运:定时触发DMA,自动完成内存到外设、内存到内存的数据搬运
-
操作系统辅助:作为独立的硬件时基,辅助任务调度
二、STM32定时器全分类与特点对比
STM32的定时器按功能从简到繁分为三类,基础定时器是整个体系的基石,所有定时器的时基单元原理完全一致。
表格

核心规律:所有定时器的时基单元原理完全相同。搞懂基础定时器的时基,后续学通用、高级定时器只需要额外学习通道功能即可。
三、基础定时器核心原理:时基单元全拆解
基础定时器的核心是时基单元(Time-Base Unit),它是定时器实现计数的硬件核心,由三个关键寄存器+时钟通路组成。

3.1 时基单元的组成
时基单元包含三个核心寄存器,全部为16位:
-
预分频寄存器(PSC)

对输入的定时器时钟进行分频,输出计数器工作时钟CK_CNT。
写入值范围:0 ~ 65535,实际分频系数 = 写入值 + 1。
例如写入71,分频系数就是72,72MHz输入时钟会变成1MHz输出。
-
计数器寄存器(CNT)

真正执行计数的寄存器,在CK_CNT时钟驱动下累加。
基础定时器仅支持向上计数:从0开始,每个时钟加1,计数到目标值后溢出。
-
自动重装载寄存器(ARR)

保存计数的上限值。当CNT的值等于ARR的值时,计数器溢出,产生更新事件(UEV),同时CNT自动清零,开启下一轮计数。
3.2 完整计数工作流程
-
总线时钟经过RCC分频后,输入到定时器的预分频器PSC
-
PSC分频后得到计数器时钟CK_CNT,驱动CNT寄存器从0开始向上累加
-
当CNT计数值 == ARR值时,计数器溢出,硬件自动产生更新事件UEV
-
UEV事件会做三件事:
-
自动将CNT清零,重新开始计数
-
将预装载寄存器的值加载到影子寄存器(下一节详解)
-
可触发更新中断、DMA请求、DAC转换
- 重复上述过程,产生周期性的定时信号
3.3 定时时间计算公式
定时周期(秒)= (PSC + 1) × (ARR + 1) / 定时器时钟频率
-
加1的原因:PSC和ARR都是从0开始计数,写入0代表不分频/计数1个周期
-
定时器时钟频率:F1系列基础定时器挂载在APB1总线,默认72MHz
举例计算(72MHz时钟)
-
1ms定时:PSC=71,ARR=999
周期 = 72 × 1000 / 72000000 = 0.001s = 1ms
-
1秒定时:PSC=7199,ARR=9999
周期 = 7200 × 10000 / 72000000 = 1s
3.4 RCC时钟源与时基单元的关系
下图是STM32F103的时钟树框图,图中标注了APB预分频与定时器时钟的倍频逻辑:

下图示意在STM32F103中,由RCC时钟树输出TIMxCLK,驱动基础定时器工作:

这是初学者最容易搞错的知识点:基础定时器的时钟不是直接等于APB1总线时钟。
STM32F1的时钟规则:
-
如果APB1预分频系数 = 1,定时器时钟 = APB1总线时钟
-
如果APB1预分频系数 > 1,定时器时钟 = APB1总线时钟 × 2
F1系列默认配置:APB1总线最大36MHz,预分频系数为2,系统主频72MHz。
因此:基础定时器的输入时钟 = 36MHz × 2 = 72MHz
关键结论:不要直接拿APB1总线频率算定时周期,一定要记得定时器有2倍频机制。使用前必须在RCC中开启对应定时器的时钟,否则定时器不会工作。
四、重点难点:影子寄存器(Shadow Register)深度解析
影子寄存器是STM32定时器最经典的设计,也是面试必考知识点,很多人学了很久定时器都没真正搞懂。
4.1 什么是影子寄存器?
以ARR寄存器为例,它在硬件上并不是一个寄存器,而是成对存在:

-
预装载寄存器(Preload Register):我们通过代码读写的就是它。你写的数值会先存在这里,不会直接影响计数器。
-
影子寄存器(Shadow Register):真正和CNT做比较的寄存器,它的值才决定计数周期。
简单概括:预装载寄存器是"缓存",影子寄存器是"运行内存"。我们改的是缓存,不会立刻生效,只有特定时机才会同步到运行内存。
4.2 为什么要设计影子寄存器?
核心目的:保证每一个计数周期的完整性,避免中途改参数导致时序混乱。
举个场景:当前ARR=999,周期1ms。当CNT计数到500的时候,你在代码里把ARR改成1999。
-
如果没有影子寄存器:计数器立刻以新的ARR=1999为上限,当前这个周期会从500数到1999,周期变长,时序被打乱。
-
如果有影子寄存器:当前周期依然以999为上限,数到999正常溢出;等下一个周期开始时,才会加载新值1999。每个周期都是完整的,时序完全稳定。
对于工业控制、电机驱动等对时序精度要求高的场景,影子寄存器是保障时序稳定的关键设计。
4.3 ARPE位:影子寄存器的开关
下图是TIMx_CR1寄存器,ARPE位控制ARR预装载缓冲:

通过TIMx_CR1寄存器的ARPE位(自动重装载预装载使能) 可以控制影子寄存器是否生效:
-
ARPE = 0 :关闭预装载功能。修改ARR的值后,立即直接写入影子寄存器,立刻生效,无缓冲。
-
ARPE = 1 :开启预装载功能。修改ARR的值仅写入预装载寄存器,等待下一次更新事件UEV发生时,才加载到影子寄存器,下一个周期生效。
4.4 拓展:PSC也有影子寄存器

很多教程只讲ARR有影子寄存器,实际上预分频器PSC同样有影子寄存器,原理完全一致。
修改PSC的值不会立刻改变分频系数,会等到下一次更新事件时才生效,保证当前计数周期的时钟稳定。
4.5 软件触发更新事件(UG位)
下图是TIMx_EGR寄存器,UG位用于软件手动触发更新事件:

TIMx_EGR寄存器的UG位可以软件强制产生一次更新事件。
它的作用就是:初始化定时器后,手动触发一次UEV,把PSC和ARR的预装载值立刻加载到影子寄存器里,让第一个周期就按设定值运行。
HAL库的初始化函数内部默认会置位UG位,完成影子寄存器的初始化。
五、深度详解:基础定时器和中断与DMA联动
基础定时器的核心价值,在于通过更新事件UEV联动中断和DMA,实现定时任务的硬件自动化。这也是基础定时器区别于SysTick的核心优势。
5.1 更新中断(Update Interrupt)机制全解
5.1.1 中断的产生与标志位
下图为TIMx_DIER寄存器,用来开启定时器更新中断与DMA请求:
下图是TIMx_SR寄存器,UIF标记更新事件标志:

当计数器溢出产生更新事件UEV时,如果TIMx_DIER寄存器的UIE位(更新中断使能) 置1,就会向NVIC发起中断请求。
-
中断标志位:TIMx_SR寄存器的UIF位,中断触发后硬件自动置1
-
必须在中断服务函数中软件清除UIF标志,否则会反复进入中断
5.1.2 HAL库中断处理完整流程
-
初始化阶段:配置定时器参数,开启定时器时钟,配置NVIC中断优先级
-
启动阶段:调用 HAL_TIM_Base_Start_IT() ,硬件自动置位UIE位,开启更新中断
-
中断触发:计数器溢出,产生中断请求,CPU跳转到TIM6_IRQHandler中断入口
-
HAL库中断处理:
-
进入 HAL_TIM_IRQHandler() 通用处理函数
-
检查UIF标志位,确认是更新中断
-
硬件清除UIF标志位
-
调用回调函数 HAL_TIM_PeriodElapsedCallback()
- 用户逻辑:在回调函数中编写定时任务代码
5.1.3 中断使用注意事项
-
中断服务函数快进快出:不要在回调里执行延时、串口打印等耗时操作,避免阻塞其他中断和主程序
-
优先级配置:根据任务紧急程度配置中断抢占优先级和子优先级,高优先级任务可以打断低优先级
-
避免重入问题:如果回调中执行时间超过定时周期,会出现中断重入,导致逻辑混乱;必要时可在进入回调后关闭中断,退出前再开启
-
适用场景:任务逻辑简单、执行时间短、需要CPU参与判断的定时场景,比如按键扫描、标志位翻转、短周期数据采集
5.2 DMA硬件触发联动原理与实战
DMA联动是基础定时器的高阶用法,真正实现"零CPU占用"的定时自动化。
5.2.1 硬件通路与通道对应
基础定时器的更新事件可以直接作为DMA的硬件触发信号,无需CPU干预。
-
STM32F103中,TIM6更新事件对应DMA1的通道3,TIM7更新事件对应DMA1的通道4
-
触发方向:通常是内存→外设(比如把内存里的波形数据搬到DAC寄存器),也可以外设→内存、内存→内存
-
硬件自动完成:定时器溢出→触发DMA传输→传输完成可选DMA中断,全程CPU不参与
5.2.2 完整工作流程
-
配置DMA:设置源地址、目标地址、传输数据量、数据宽度,开启定时器触发模式
-
配置定时器:设置定时周期,开启DMA触发使能(TIMx_DIER寄存器的UDE位)
-
启动定时器与DMA:定时器开始计数
-
每产生一次更新事件,硬件自动触发DMA完成一次数据传输
-
传输完成后,可选择产生DMA传输完成中断,通知CPU处理
5.2.3 典型应用:定时器+DMA+DAC生成任意波形
这是基础定时器最经典的工业应用:
-
把正弦波/方波的采样点存在数组里(内存)
-
TIM6定时产生更新事件,触发DMA把一个采样点搬到DAC_DATA寄存器
-
DAC输出对应的模拟电压,最终输出连续的波形
-
整个过程CPU只需要初始化一次,之后完全硬件自动运行,CPU可以休眠
5.2.4 HAL库核心配置代码片段

5.3 中断模式 vs DMA模式:如何选型
表格

六、代码实战:HAL库1ms定时中断(可直接复用)
下面基于STM32F103C8T6,给出完整的1ms定时中断工程代码,包含CubeMX配置步骤和核心逻辑。
6.1 CubeMX配置步骤
-
配置系统时钟:外部高速晶振HSE,配置主频72MHz
-
进入Timers → TIM6配置:
-
Activated:勾选,激活定时器
-
Prescaler (PSC):71
-
Counter Mode:Up(向上计数)
-
Counter Period (ARR):999
-
auto-reload preload:Enable(开启影子寄存器,ARPE=1)
-
进入NVIC Settings:勾选TIM6 global interrupt,开启定时器中断
-
生成工程代码
6.2 核心代码解析

6.3 启动定时器与中断回调
在main函数中启动定时器中断:

编写定时回调函数,实现1ms执行一次任务:

6.4 影子寄存器验证实验(选做)
你可以亲手验证影子寄存器的效果:
-
开启ARPE,在主循环中延时修改ARR值为1999
-
观察LED:会在当前1ms周期结束后,才变成2ms闪烁
-
关闭ARPE,重复实验:LED闪烁频率会立刻变化
七、面试高频考点汇总
基础定时器是嵌入式岗位面试的入门必考题,以下是最高频的8个问题及标准答案:
- 基础定时器和SysTick有什么区别?
-
SysTick是Cortex-M内核自带的24位递减计数器,通常用作系统时基,功能单一,和CPU绑定。
-
基础定时器是独立片上外设,16位计数器,可触发中断、DMA、DAC,多个定时器可独立工作,不占用内核资源,外设联动能力强。
- 什么是影子寄存器?它的作用是什么?
影子寄存器是定时器中ARR、PSC的缓冲机制。寄存器分为预装载寄存器和影子寄存器两层,我们操作的是预装载寄存器,只有更新事件发生时,值才会加载到影子寄存器中真正生效。
作用:保证每个计数周期的完整性,避免中途修改参数导致计数周期混乱、时序异常,提升定时精度和稳定性。
- 定时器的定时周期如何计算?
定时周期 = (PSC预分频值 + 1) × (ARR自动重装载值 + 1) ÷ 定时器输入时钟频率。
加1是因为PSC和ARR都从0开始计数。
- 基础定时器可以输出PWM吗?
不可以。基础定时器仅包含时基单元,没有输入输出通道,不能产生PWM波形,只能实现定时计数、触发中断/DMA、触发DAC。
- ARPE位的功能是什么?
自动重装载预装载使能位,控制ARR影子寄存器是否生效。
-
置1:修改ARR的值仅写入预装载寄存器,下一次更新事件时生效;
-
置0:修改ARR的值立即生效,无缓冲。
- STM32F1的定时器时钟和APB1时钟是什么关系?
当APB1预分频系数为1时,定时器时钟等于APB1时钟;
当APB1预分频系数大于1时,定时器时钟为APB1时钟的2倍。
F1默认APB1分频系数为2,36MHz总线频率,因此定时器时钟为72MHz。
- 定时器触发DMA和定时器中断有什么区别?
-
中断需要CPU进入中断服务函数处理数据,占用CPU资源,适合需要逻辑处理的场景;
-
DMA触发由硬件自动完成数据传输,CPU零占用,适合高速、纯数据搬运的场景,精度更高、响应更快。
- 基础定时器TIM6、TIM7有什么专属用途?
可以直接触发DAC转换,是DAC输出波形的常用硬件时基;同时可以触发DMA传输,实现无CPU干预的自动数据搬运。
结语
基础定时器虽结构简单,却是整个STM32定时器体系的核心根基。时基单元的计数逻辑、影子寄存器的时序设计、中断与DMA的双模式联动,本质上都是嵌入式硬件时序控制思想的具象体现。吃透这部分内容,不止是掌握了一个外设,更是建立硬件级开发思维的关键一步,后续学习通用定时器、高级定时器都会事半功倍。
如果本文对你有所帮助,欢迎点赞、收藏、转发支持,后续会持续更新STM32外设开发、嵌入式编程、电赛实战相关的技术干货,和大家一起从入门进阶到精通。