第6章 基本定时器介绍及应用
第六章 基本定时器介绍及应用
1. 基本定时器简介
STM32F407 有众多的定时器,其中包括 2 个基本定时器(TIM6 和 TIM7)、 10 个通用定时器(TIM2~TIM5、 TIM9~TIM14)、 2 个高级控制定时器(TIM1 和 TIM8),这些定时器彼此完全独立,不共享任何资源。
STM32F407 有两个基本定时器 TIM6 和 TIM7,它们的功能完全相同,资源是完全独立的,可以同时使用。其主要特性如下: 16 位自动重载递增计数器,16 位可编程预分频器,预分频系数 1~65536,用于对计数器时钟频率进行分频,还可以触发 DAC 的同步电路,以及生成中断/DMA 请求。

1.1 时钟源
定时器的核心就是计算器,要实现计数功能,首先要给它一个时钟源。基本定时器时钟挂载在 APB1 总线,所以它的时钟来自于 APB1 总线,但是基本定时器时钟不是直接由 APB1 总线直接提供,而是先经过一个倍频器。当 APB1 的预分频器系数为 1 时,这个倍频器系数为 1,即定时器的时钟频率等于 APB1 总线时钟频率;当 APB1 的预分频器系数≥2 分频时,这个倍频 器系 数就 为 2 ,即 定时 器的 时钟频 率等 于 APB1 总 线时 钟频 率的两 倍。 我们 在sys_stm32_clock_init 时钟设置函数已经设置 APB1 总线时钟频率为 42Mhz, APB1 总线的预分频器分频系数是 2,所以挂载在 APB1 总线的定时器时钟频率为 84Mhz。
1.2 控制器
控制器除了控制定时器复位、使能、计数等功能之外,还可以用于触发 DAC 转换
1.3 时基单元
时基单元包括:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR) 。基本定时器的这三个寄存器都是 16 位有效数字,即可设置值范围是 0~65535。
时基单元中的预分频器 PSC,它有一个输入和一个输出。输入 CK_PSC 来源于控制器部分,实际上就是来自于内部时钟(CK_INT),即 2 倍的 APB1 总线时钟频率(84MHz)。输出 CK_CNT是分频后的时钟,它是计数器实际的计数时钟,通过设置预分频器寄存器(TIMx_PSC)的值可以得到不同频率 CK_CNT,计算公式如下:
fCK_CNT= fCK_PSC / (PSC[15:0]+1)
上式中, PSC[15:0]是写入预分频器寄存器(TIMx_PSC)的值
由上述可知,我们只要设置预分频寄存器和自动重载寄存器的值就可以控制定时器更新事件发生的时间。自动重载寄存器(TIMx_ARR)是用于存放一个与计数器作比较的值,当计数器的值等于自动重载寄存器的值时就会生成更新事件,硬件自动置位相关更新事件的标志位,如:更新中断标志位。
下面举个例子来学习如何设置预分频寄存器和自动重载寄存器的值来得到我们想要的定时器上溢事件发生的时间周期。比如我们需要一个 500ms 周期的定时器更新中断,一般思路是先设置预分频寄存器,然后才是自动重载寄存器。考虑到我们设置的 CK_INT 为 84MHz,我们把预分频系数设置为 8400,即写入预分频寄存器的值为 8399,那么 fCK_CNT=84MHz/8400=10KHz。这样就得到计数器的计数频率为 10KHz,即计数器 1 秒钟可以计 10000 个数。我们需要 500ms的中断周期,所以我们让计数器计数 5000 个数就能满足要求,即需要设置自动重载寄存器的值为 4999,另外还要把定时器更新中断使能位 UIE 置 1, CEN 位也要置 1。
2. 基本定时器使用示例
本实验,我们主要配置定时器产生周期性溢出,从而在定时器更新中断中做周期性的操作,如周期性翻转 LED 灯。假设计数器计数模式为递增计数模式,那么实现周期性更新中断原理示意图如下所示:

CNT 计数器从 0 开始计数,当 CNT 的值和 ARR 相等时(t1),产生一个更新中断,然后 CNT 复位(清零),然后继续递增计数,依次循环。图中的 t1、 t2、 t3 就是定时器更新中断产生的时刻。
通过修改 ARR 的值,可以改变定时时间。另外,通过修改 PSC 的值,使用不同的计数频率(改变图中 CNT 的斜率),也可以改变定时时间
2.1 基本定时器配置
// 前置知识
/*
arr:自动重装载值,计数器到达该值时,计数器自动重装载,计数器重新计数。
psc:预分频值,用来设置时钟源的频率,以此来控制计数器的频率。
定时器溢出时间计算公式:
Time= ((arr+1)*(psc+1))/fclk us ,这里的fclk是定时器工作频率,单位为Hz。
我们这个工程使用的是定时器3,定时器挂在APB1上,时钟位HCLK/2
*/
TIM_HandleTypeDef TIM3_Handler; // TIM3 handler
void BSP_TIM_Init(uint16_t arr, uint16_t psc)
{
TIM3_Handler.Instance = TIM3;
TIM3_Handler.Init.Prescaler = psc;
TIM3_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
TIM3_Handler.Init.Period = arr;
TIM3_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 1分频
HAL_TIM_Base_Init(&TIM3_Handler);
HAL_TIM_Base_Start_IT(&TIM3_Handler); // 启动定时器中断
}
// 此函数会被HAL_TIM_Base_Init()调用,用来初始化定时器
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); // 使能TIM3时钟
HAL_NVIC_SetPriority(TIM3_IRQn, 1, 3); // 设置中断优先级
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
}
2.2 基本定时器中断配置
// 中断服务函数
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler); // 调用HAL库函数处理定时器3的中断
}
// 回调函数,中断服务函数会调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
LED_TOGGLE(LED1_GPIO_Pin);
printf("TIM3_IRQHandler 500ms\r\n");
}
}
2.3 BSP初始化
#include <bsp_init.h>
void bsp_init(void)
{
/* 系统初始化 */
HAL_Init(); // 初始化HAL库
sys_stm32_clock_init(336, 8, 2, 7); // 设置时钟,168Mhz
usart_init(115200); // 串口初始化
delay_init(168); // 延时初始化
/* 外设初始化 */
bsp_led_init(); // 初始化LED
/*
时器初始化,设置重装值(arr)为5000-1,分频系数(psc)为8400-1
这个工程中我们的定时器的工作频率为84MHz/8400=10KHz
周期计算Time = ((arr+1)*(psc+1))/fclk = 5000*(8400)/84000000 = 500ms
*/
BSP_TIM_Init(5000-1, 8400-1);
}
2.4 主函数测试
#include <bsp_init.h>
int main(void)
{
bsp_init();
printf("Hello World!\n");
while(1)
{
LED_TOGGLE(LED0_GPIO_Pin);
printf("LED0_GPIO_Pin 1s\r\n");
delay_ms(1000);
}
}
4. 基本定时器常见函数(HAL库)
4.1 初始化与启动
-
HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)-
功能:配置定时器时基参数(PSC预分频、ARR重装载值、计数模式)。
-
关键参数:
-
htim.Init.Prescaler = 8399; // 分频系数(84MHz÷8400=10kHz)
htim.Init.Period = 9999; // 自动重载值(10000个脉冲=1秒)
htim.Init.CounterMode = TIM_COUNTERMODE_UP; // 递增计数
-
调用时机:主函数初始化阶段。
-
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)-
功能:启动定时器并开启更新中断(中断模式)。
-
注意:CubeMX生成的代码不会自动启用中断,需在
main()中手动调用。
-
4.2 中断与回调函数
-
HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)-
功能:定时器中断统一入口,自动处理中断标志并调用回调函数。
-
位置:由CubeMX生成在
stm32fXxx_it.c的中断服务函数(如TIM6_DAC_IRQHandler)中调用34。
-
-
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)-
功能:用户必须重写的周期更新回调函数,执行定时任务(如翻转LED)。
-
示例:
-
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM6) { // 判断定时器来源
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 执行操作
}
}
- 注意:所有定时器的更新中断均调用此函数,需通过
htim->Instance区分来源
4.3 控制与状态操作
-
__HAL_TIM_GET_COUNTER(TIM_HandleTypeDef *htim)- 功能:读取当前计数器值(CNT寄存器),用于精确计时或调试。
-
__HAL_TIM_SET_COUNTER(TIM_HandleTypeDef *htim, uint32_t value)- 功能:手动设置计数器值(如清零CNT)。
-
HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim)-
功能:停止定时器并关闭中断。
-
注意:中断中调用时需手动清除标志位,否则可能重复进入中断
-
| 函数 | 功能说明 | 调用场景 |
|---|---|---|
HAL_TIM_Base_Init() |
初始化定时器时基参数 | 系统启动时 |
HAL_TIM_Base_Start_IT() |
启动定时器及更新中断 | main()中手动调用 |
HAL_TIM_PeriodElapsedCallback() |
周期中断回调(用户重写) | 定时器溢出时自动执行 |
HAL_TIM_Base_Stop_IT() |
停止定时器及中断 | 需暂停定时任务时 |
__HAL_TIM_GET_COUNTER() |
读取当前计数值 | 调试或精确计时 |

浙公网安备 33010602011771号