mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1175 字
3 分钟
STM32-06:TIM定时器
2026-07-22

TIM(Timer,定时器),在捕捉脉冲宽度,计数PWN占空比,输出PWM波形,编码器计数中是不可或缺的一部分。


定时器原理#

计数器与时钟信号相连,当时钟信号的上升沿/下降沿到达时,计数器会+1,但计数器仅有16位,仅能存储65535数据,因此在计数器之前还有一个预分频器(Prescaler),默认地,预分频器设置为0,即不对进入预分频器的信号进行处理。当预分频器被设置为1时,会将每两个脉冲分频为一个脉冲,同样地,当预分频器被设置为n-1时,会将每n个脉冲分频为一个脉冲,即n分频预分频器同样是16位,因此最大能进行65536分频。

但是,仅有计数器还不足以实现定时功能,我们还需要一个自动重装载寄存器,它的作用就是检测计数器的值是否与自己相同,当计时器的值和自己相同时,则将计时器的值重设为0,并且触发定时器更新中断,提醒定时时间已到,然后通过中断回调函数执行指令。和预分频器类似,由于是从0开始计数,设定自动重装载寄存器数值为m-1时,才能实现在m个脉冲后触发中断。


芯片配置#

  1. 先设置HSE为外部晶振:在左侧的System Core中选择RCC,在High Speed Clock(HSE)下拉菜单中选择Crystal/Ceramic Resonator,即可启用外部晶振。
  2. 在顶部选择Clock Configuration栏目,将HCLK更改为72MHz
  3. 在左侧Timers中选择通用定时器TIM4,勾选Internal Clock,对于TIM1TIM2,则是在Clock Source中选择Internal Clock
  4. 底侧各个选项,则是对此定时器的配置:
  • Prescaler预分频器,例如要进行7200分频,则填入7200-1,即将72MHz的输入信号预分配为10000Hz
  • Counter Period自动重装载寄存器,例如希望计数器从0计数到9999,则可设置为10000-1,这样即可每1秒触发一次计数器中断回调函数。

代码配置#

定时器寄存器#

启动定时器TIM4的函数:

HAL_TIM_BASE_START(&htim4);

宏定义函数__HAL_TIM_GET_COUNTER可用于获取定时器计数器的值,然后拼接字符串并通过串口将其发送出来:

int counter=0;
char sendBuffer[20];
counter=__HAL_TIM_GET_COUNTER(&htim4);
sprintf(sendBuffer, "counter:%d",counter);
HAL_UART_Transmit_IT(&huart2, (uint8_t*)sendBuffer, strlen(sendBuffer));
HAL_Delay(100-1);

可以看到,每间隔100ms,便发来计数器中存储的值:

还有以下宏函数:

  • __HAL_TIM_SET_COUNTER:设置计数器的值;
  • __HAL_TIM_GET_AUTORELOAD:获得当前重装载寄存器的值;
  • __HAL_TIM_SET_AUTORELOAD:设定重装载寄存器的值;
  • __HAL_TIM_SET_PRESCALER:设定预分频器的值。

除此之外,还有一个叫做影子寄存器的机制,它存在于预分频器自动重装载寄存器中,它的作用是更改预分频器自动重装载寄存器后,修改的数值会在下一个计数周期再生效,从而不影响当前的计数周期。
默认地,预分频器影子寄存器是开启的,而自动重装载寄存器影子寄存器需要我们手动开启,在CubeMX的设置栏中启用auto-reload preload即可。
若不启用影子寄存器机制,则修改自动重装载寄存器时,有可能修改后的数值小于当前的计数器数值,导致计时器一直计数到65535才归零。

中断回调函数#

回到CubeMX,先将TIM4NVIC SettingsTIM4 global interrupt开启;
再将HAL_TIM_Base_Start替换为HAL_TIM_Base_Start_IT,即可开启中断计时模式;
计时器自动重装载寄存器数值相同触发计时器归零时,会触发以下中断回调函数:

HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

重新定义此函数,再对其进行重写,即可实现定时功能:

static const char *message="1234567890";
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim==&htim4){
HAL_UART_Transmit_IT(&huart2, (uint8_t*)message, strlen(message));
}
return;
}

可以看到,通过定时器中断回调函数,实现了每秒通过串口发送消息:


总结#

相关函数#

操作寄存器宏函数:

__HAL_TIM_GET_COUNTER(__HANDLE__);//获得计数器寄存器的数值
__HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__);//设置计数器寄存器的数值
__HAL_TIM_GET_AUTORELOAD(__HANDLE__);//获得自动重装载寄存器的数值
__HAL_TIM_SET_AUTORELOAD(__HANDLE__, __AUTORELOAD__);//设置自动重装载寄存器的数值
__HAL_TIM_SET_PRESCALER(__HANDLE__, __PRESC__);//设置预分频器的数值

抽象层函数:

HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);//启用定时器
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);//启动定时器(中断模式)
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//自动重装载寄存器中断回调函数

定时器内部寄存器#

  • 预分频器:将时钟信号预分频;
  • 计数器:遇到时钟信号上升沿/下降沿时,使自身计数+1;
  • 自动重装载寄存器:将计数器归零,并触发中断。
STM32-06:TIM定时器
https://nf3eb.github.io/posts/stm32/stm32-06/
作者
NF3eb
发布于
2026-07-22
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录