目录
一、什么是基本定时器
基本定时器属于片上外设,基本定时器 TIM6 和 TIM7 包含一个 16 位递增自动重载计数器(0~65535),该计数器由16 位可编程预分频器(分频系数介于1~65536之间)驱动。
二、如何配置基本定时器
1、基本定时器框图
1.1 时钟源部分
时钟来源:HSE经过PLL得到SYSCLK后,再HCLK->PCLKx
如何才能知道APBx总线是否进行了分频?
因为APBx频率从AHB总线过来,使用APBx频率与AHB频率作比较
当APBx总线分频为1时,定时器频率为APBx频率*1
当APBx总线分频不为1时,定时器频率为APBx频率*2
1.2 分频器
对输入过来的时钟脉冲进行分频,有相应的寄存器可以配置
407定时器6和定时器7挂载在APB1,时钟频率是42*2==84M
84000000/s 84000/ms 84/us 计数器重载最多放65536
84分频后: 1000000/s 1000/ms 1/us 最多计时:65536/1000=65ms 65536us
42分频后: 2000000/s 2000/ms 2/us 最多计时:65536/2000=32ms 32768us
8400分频后: 10000/s 10/ms 最多计时:6553ms
42000分频后: 2000/s 2/ms
1.3 计数器
此计数器是向上计数
1.4 自动重装载寄存器
根据需要定时的时间 计算出的要计数多少个
2、计时原理
计数器向上计数,当计数器出现上溢的时候,会触发更新事件(更新重装载值和分频值)或者中断,表示定时(计数完成)
说明:
预分频寄存器决定计数速度
重装载决定计数的个数(重装载值)
所以两个共同决定定时时间
3、影子寄存器
由图可发现,自动重装载寄存器和预分频寄存器都有影子寄存器
影子寄存器的机制:
我们用程序操作的是寄存器本体,真正作用的是寄存器的影子寄存器
当产生更新事件的时候,寄存器本体会把自己里面的数值加载到影子寄存器中,影子寄存器起作用。
影子寄存器的作用:
当计数正在进行(没有产生更新事件的时候),人为去改寄存器的值是没有作用的,因为没有更新到影子寄存器中,从而保护定时器周期的完整性.
注意:
- 预分频器的影子寄存器缓冲功能是一直开启
- 重装载寄存器的影子寄存器缓冲功能不一定需要开启(改变重装载值时,需要开启缓冲功能)/(不改变重装载值时,不需要开启缓冲功能)
- 影子寄存器只有在产生一次更新事件才会将预分频寄存器的值更新到影子寄存器,那第一次计数时怎么办?
解决方法:
软件更新,通过对更新事件寄存器的UG位置1,人为产生一次更新事件,就会更新所有寄存器和更新事件效果一样,从而将寄存器中的值更新到对应的影子寄存器,影子寄存器第一次就起作用了.
4、程序设计
①基本定时器延时过程函数(要延时时间)
{
//定时器时钟线使能
//CR1
//①影子寄存器使能
//②单次脉冲模式 ---->如果没有这个模式则在最后需要人为关闭定时器
//③允许产生更新事件的条件---计数器上溢/将UG位置1 用以选择 UEV 事件源
//PSC预分频器---分频倍数-1
//ARR自动重载寄存器
//人为产生更新事件(UG位置1)---重新初始化定时器计数器并生成寄存器更新事件
//清除计数完成标志位(状态寄存器)---该位在发生更新事件时通过硬件置 1。但需要通过软件清零
//④使能计数器
//等待计数完成
}
毫秒级延时函数
微妙级延时函数
当自动重载值为空时,计数器不工作
为什么需要人为产生更新事件(UG位置1)?
为了将自动重装载寄存器和预分频寄存器的值更新到对应的影子寄存器中,让影子寄存器第一次就起作用了。本质上就是在没有更新事件的情况下,让第一次计数能进行
②基本定时器中断过程函数(定时中断触发的时间)
{
//定时器时钟线使能
//CR1
//①影子寄存器使能
//②连续脉冲模式
//③允许可以产生中断的条件
//④允许可以产生更新事件的条件---计数器上溢/将UG位置1 用以选择 UEV 事件源
//定时器中断使能
//预分频器---分频倍数-1
//自动重载寄存器
//人为产生更新事件(UG位置1)---重新初始化定时器计数器并生成寄存器更新事件
//清除计数完成标志位(状态寄存器)---该位在发生更新事件时通过硬件置 1。但需要通过软件清零
/*NVIC控制器配置*/
//优先级分组
//计算编码值
//设置具体中断源
//使能NVIC响应通道
//⑤使能计数器
}
如果不加清除计数完成标志位的话,设备一上电就会触发中断;显然,这不是我们想要的效果,所以必须加上清除计数完成标志位。
是否需要清除计数完成标志位,是要看接下来是否需要使用到状态标志位(比如用来等待延时、触发中断),如果没有用到,就不需要清除;
注意:
不同的分频会导致最多计时时间不一样
不同分频会导致计时精度不一样
故分频尽量大些,因为分频越大计的数就越少,精度就越高
三、具体使用基本定时器
需求1:基本定时器timer6的延时
/***************************************
*函数名 :timer6_delay_ms
*函数功能 :定时器6毫秒级延时函数
*函数参数 :u16 ms
*函数返回值 :无
*函数描述 :84M ---- 42000分频 --- 2/ms
****************************************/
void timer6_delay_ms(u16 ms)
{
//使能时钟
RCC->APB1ENR |= 1 << 4;
//CR1
TIM6->CR1 |= 1 << 7; //使能影子寄存器
TIM6->CR1 |= 1 << 3;//单次脉冲计数 发生下一更新事件时停止计数(将 CEN 位清零)
TIM6->CR1 &= ~(1 << 1); //更新 (UEV) 事件可通过以下事件之一生成:计数器上溢/下溢或将 UG 位置 1
//预分频器
TIM6->PSC = 42000 - 1;
//自动重载器
TIM6->ARR = ms * 2 - 1;
//人为产生更新事件UG(事件生成寄存器 )
TIM6->EGR |= 1 << 0;//重新初始化定时器计数器并生成寄存器更新事件
//清除计数完成标志位
TIM6->SR &= ~(1 << 0);
//使能计数器
TIM6->CR1 |= 1 << 0;
//等待计数完成
while(!(TIM6->SR & (1 << 0)));
}
/***************************************
*函数名 :timer6_delay_us
*函数功能 :定时器6微秒级延时函数
*函数参数 :u16 us
*函数返回值 :无
*函数描述 :84M ---- 42分频
2/us
****************************************/
void timer6_delay_us(u16 us)
{
//定时器时钟使能
RCC->APB1ENR |= (1<<4);
//CR1
TIM6->CR1 |= (1<<7); //使能重载影子寄存器
TIM6->CR1 |= (1<<3); //单次计数模式
TIM6->CR1 &= ~(1<<1); //允许产生更新事件
//PSC分频寄存器
TIM6->PSC = 42-1;
//ARR重载寄存器
TIM6->ARR = 2 * us - 1;
//人为产生更新事件UG
TIM6->EGR |= (1<<0);
//清除计数完成标志位
TIM6->SR &= ~(1<<0);
//使能计数器
TIM6->CR1 |= (1<<0);
//等待计数完成
while(!(TIM6->SR & (1<<0)));
}
需求2:基本定时器timer7定时中断
/***************************************
*函数名 :timer7_Interrupt_ms_init
*函数功能 :定时7毫秒级定时中断函数
*函数参数 :u16 ms
*函数返回值 :无
*函数描述 :84M ---- 42000分频 --- 2/ms
****************************************/
void timer7_Interrupt_ms_init(u16 ms)
{
/*定时器控制器配置*/
//定时器时钟使能
RCC->APB1ENR |= (1<<5);
//CR1
//①影子寄存器使能
TIM7->CR1 |= 1 << 7;
//②连续脉冲模式
TIM7->CR1 &= ~(1 << 3);
//③使能可以产生中断的条件
TIM7->CR1 &= ~(1 << 2);
//④使能可以产生更新事件的条件---计数器上溢/将UG位置1 用以选择 UEV 事件源
TIM7->CR1 &= ~(1<<1);
//中断使能
TIM7->DIER |= 1 << 0;
//预分频器---分频倍数-1
TIM7->PSC = 42000 - 1;
//自动重载寄存器
TIM7->ARR = 2 * ms - 1;
//人为产生更新事件(UG位置1)---重新初始化定时器计数器并生成寄存器更新事件
TIM7->EGR |= 1 << 0;
//清除计数完成标志位(状态寄存器)---该位在发生更新事件时通过硬件置1,但需要通过软件清零
TIM7->SR &= ~(1<<0);
/*NVIC控制器配置*/
u32 pri = NVIC_EncodePriority (5, 1,2);//计算优先级编码值,设置抢占和响应的级别值
NVIC_SetPriority(TIM7_IRQn, pri);//设置具体某个中断源的优先级
NVIC_EnableIRQ(TIM7_IRQn);//使能NVIC响应通道
//⑤使能计数器
TIM7->CR1 |= (1<<0);
}
基本定时器主要是用来做延时和定时中断