stm32基本定时器学习
写于2024/8/15 晚
文章目录
在F1系列中,一个有三种定时器,分为基本定时器,通用定时器,高级定时器
三种定时器的区别为
- 基本定时器没有输入输出通道,常用作时基,即定时功能
- 通用定时器具有多路独立通道,可用于输入捕获/输出比较,也可用作时基
- 高级定时器除具备通用定时器所有功能外,还具备带死区控制的互补信号输出、刹车输入等功能(可用于电机控制、数字电源设计等)
1.1基本定时器简介
1.1.1 特性
TIM6/TIM7为F103系列的基本定时器
- 16位递增计数器(计数值:0-65535)
- 16位预分频器(分频系数:1-65536)
- 可用于触发DAC在更新事件(计数器溢出)时,会产生中断/DMA请求
1.2 基本框图
1.2.1 基本定时器时钟源
基本定时器可以分为三部分,第一部分为定时器时钟源,我手上的板子是正点原子的战舰开发板,可以看到图中内核框图,TIM6与TIM7是挂载在图中APB1总线上,而APB1总线最高频率为36Mhz,那么TIM6与TIM7是不是最高频率也为36Mhz呢?
答案并不是,通过时钟树我们可以看到,虽然TIM6TIM7都挂载在最高时钟频率为36Mhz的APB1总线上,但如果APB1分频器不为1分频,那么TIM6TIM7的时钟频率乘2,也就是72Mhz。
1.2.2 基本定时器触发控制器
控制器除了控制定时器复位、使能、计数等功能之外,还可以用于触发 DAC 转换。
1.2.3 基本定时器时基单元
从图上可以看到,时基单元由三部分组成
- 计数器寄存器(TIMx_CNT)
- 预分频寄存器(TIMx_PSC)
- 自动重装载寄存器(TIMx_ARR)
其中预分频寄存器(TIMx_PSC)与自动重装载寄存器(TIMx_ARR)具有影子寄存器,影子寄存器是实际起作用的寄存器,当计数器寄存器(TIMx_CNT)需要进行重装载时,是将自动重装载寄存器(TIMx_ARR)的影子寄存器的值重装载进自身内。该寄存器不能直接访问,需要通过修改自动重装载寄存器,等到一次更新事件后,才会将新的重装载值载入到影子寄存器中,这才真正的修改了重装载数值。
从图中可以看出,预分频寄存器(TIMx_PSC)和自动重装载寄存器(TIMx_ARR)都具有影子寄存器。不同点在于自动重载寄存器是否具有缓冲作用还受到 ARPE 位的控制,当该位置 0 时,ARR 寄存器不进行缓冲,我们写入新的 ARR值时,该值会马上被写入 ARR 影子寄存器中,从而直接生效;当该位置 1 时,ARR 寄存器进行缓冲,我们写入新的 ARR 值时,该值不会马上被写入 ARR 影子寄存器中,而是要等到更新事件发生才会被写入 ARR 影子寄存器,这时才生效。预分频器寄存器则没有这样相关的控制位,这就是它们不同点。
值得注意的是,更新事件的产生有两种情况,一是由软件产生,将 TIMx_EGR 寄存器的位UG 置 1,产生更新事件后,硬件会自动将 UG 位清零。二是由硬件产生,满足以下条件即可:计数器的值等于自动重装载寄存器影子寄存器的值。下面来讨论一下硬件更新事件。基本定时器的计数器(CNT)是一个递增的计数器,当寄存器(TIMx_CR1)的 CEN 位置1,即使能定时器,每来一个 CK_CNT 脉冲,TIMx_CNT 的值就会递增加 1。当 TIMx_CNT 值与 TIMx_ARR 的设定值相等时,TIMx_CNT 的值就会被自动清零并且会生成更新事件(如果开启相应的功能,就会产生 DMA 请求、产生中断信号或者触发 DAC 同步电路),然后下一个CK_CNT 脉冲到来,TIMx_CNT 的值就会递增加 1,如此循环。在此过程中,TIMx_CNT 等于TIMx_ARR 时,我们称之为定时器溢出,因为是递增计数,故而又称为定时器上溢。定时器溢出就伴随着更新事件的发生。
1.3 STM32定时器计数模式与溢出条件
STM32定时器一共有三种计数模式
计数模式 | 溢出条件 |
---|---|
递增计数模式 | CNT==ARR |
递减计数模式 | CNT==0 |
中心对齐模式 | CNT==ARR-1、CNT==1 |
1.4 基本定时器寄存器
1.4.1 控制寄存器 1(TIMx_CR1)
- 位 0(CEN)用于使能或者禁止计数器,该位置 1 计数器开始工作,置 0 则停止。
- 位 7(APRE)用于控制自动重载寄存器 ARR 是否具有缓冲作用
1.4.2 DMA/中断使能寄存器(TIMx_DIER)
- 该寄存器位 0(UIE)用于使能或者禁止更新中断,因为本实验我们用到中断,所以该位需要置 1。
- 位 8(UDE)用于使能或者禁止更新 DMA 请求。
1.4.3 状态寄存器(TIMx_SR)
该寄存器位 0(UIF)是中断更新的标志位,当发生中断时由硬件置 1,然后就会执行中断服务函数,需要软件去清零,所以我们必须在中断服务函数里把该位清零。如果中断到来后,不把该位清零,那么系统就会一直进入中断服务函数,这显然不是我们想要的。
1.4.4 计数器寄存器(TIMx_CNT)
该寄存器位[15:0]就是计数器的实时的计数值。
1.4.5预分频寄存器(TIMx_PSC)
该寄存器是 TIM6/TIM7 的预分频寄存器,比如我们要 7200 分频,就往该寄存器写入 7199。注意这是 16 位的寄存器,写入的数值范围是 0 到 65535,分频系数范围:1 到 65536。
1.4.6 自动重载寄存器(TIMx_ARR)
该寄存器可以由 APRE 位设置是否进行缓冲。计数器的值会和 ARR 寄存器影子寄存器进行比较,当两者相等,定时器就会溢出,从而发生更新事件,如果打开更新中断,还会发生更新中断。
1.5 定时器溢出时间计算公式
1.6 通过定时器中断点亮LED灯
LED.c
#include "./BSP/TIMER/timer.h"
#include "./BSP/LED/led.h"
TIM_HandleTypeDef g_timer_handle;
void timer_init(uint16_t psc,uint16_t arr)
{
g_timer_handle.Instance =TIM6;
g_timer_handle.Init.Prescaler = psc;
g_timer_handle.Init.Period = arr;
HAL_TIM_Base_Init(&g_timer_handle); //初始化定时器参数
HAL_TIM_Base_Start_IT(&g_timer_handle); //使能更新中断和计时器计数
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) //在MSP函数中设置优先级和时钟
{
if(htim -> Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM6_IRQn,2,2);
HAL_NVIC_EnableIRQ(TIM6_IRQn);
}
}
void TIM6_IRQHandler(void) //初始化中断函数
{
HAL_TIM_IRQHandler(&g_timer_handle);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim -> Instance == TIM6)
{
LED0_TOGGLE(); //在中断回调函数中设置LED0翻转
}
}
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */
delay_init(72); /* 初始化延时函数 */
led_init(); /* 初始化LED */
timer_init(1199,4999); /* 设置定时器计时500ms */
while(1)
{
delay_ms(500); //主函数只需要初始化定时器,具体功能在中断函数中实现
}
}