目录
前言:
本篇文章将以时序图讲述自己对影子寄存器和分频的理解,以及分频系数&重装值的写入值的说明,计算定时时间的理解,最后还有定时器的一个注意点。
关于什么是影子寄存器,可以看一下《STM32预装载功能》
时序图分析:
该图为在执行程序时,预分频器分频由1变为4时的计数器时序图,接下来将对图中标记的序号一一说明。下面是我对它们的见解,如有错误请大佬们指正~
以STM32F407 --- TIM2为参数 ---- TIM2挂着在APB1总线,其定时器时钟源频率为84MHz
①:CK_PSC
1.直接来源于定时器时钟(TIM_CLK)(84MHz)
2.是定时器预分频器的时钟源(给定时器预分频器提供时钟)
3.CK_PSC经过预分频器分频后得到是定时器计数时钟(CK_CNT)
4.其CK_CNT的计算方法下面会讲
②:CNT_EN
1.定时器开始工作的信号(开启计数器)
2.通过TIMx->CR1寄存器的CEN位(0位)置1
③:CK_CNT
1. 定时器计数的时钟
2.每来一个脉冲周期,计数器寄存器的值加1
3.CK_CNT时钟是CK_PSC经过预分频器分频的结果
4.计算公式:如图
5.由公式可见
当PSC预分频器的值为0即1分频,那么CK_CNT等同CK_PSC(84MHz)
当PSC预分频器的值为3即4分频,那么CK_CNT为(84MHz/4 == 21MHz)
④:计数器寄存器(CNT [15:0] )
1.当前的计数值
2.计数器寄存器一般是16位有效,即最多可计65536个数
3.受自动重装寄存器(ARR)的值作用,当CNT计数到ARR设定值
会产生更新事件和更新中断(若更新中断使能),这也叫计数溢出
若定时器是循环计数模式,CNT会重新从0开始计数(递增计数)
ARR也是16位有效的寄存器,即最多设置0~65535的数值
4.如图,ARR设置值为0xFC,当CNT计数到0xFC就会计数溢出
产生更新事件,并重新从0开始计数(递增计数)
⑤:更新事件(UEV)
1.计数溢出时产生
2.更新中断标志位(UIF)会置1,标志位:
可用于延时等待函数
可用于定时中断函数
3.若寄存器的缓冲功能开启,等待更新事件,将预装载寄存器的值更新到影子寄存器
若没有开启缓冲功能,没有此更新事件,预装载寄存器的值立马更新到影子寄存器
说白了影子寄存器的作用就是:等待更新事件,从而 保护脉冲的一个完整周期
⑥:预分频器控制寄存器(PSC [15:0] )
1.对CK_PSC的分频写入值
PSC为16位有效的寄存器,即最多设置0~65535的数值
2.CK_CNT计算公式中的分频参数
3.程序代码写入值:需要CK_CNT为1MHz时:
TIMx->PSC = 84 - 1;即对CK_PSC进行84分频
为什么是写入83数值呢?可回过去看CK_CNT计数公式
4.该寄存器为PSC预装载寄存器,即我们可以直接操作的本体寄存器
⑧:预分频器缓冲区
1.是PSC的影子寄存器
2.是真正起作用(分频)的PSC [15:0]值
⑦:在TIMx_PSC中写入新值
1.PSC的缓存功能是默认开启的
2.程序代码中对TIMx->PSC写入新值
会在产生更新事件时,才会将该新值加载到预分频器缓冲区(PSC影子寄存器)
从而实现需要的CK_CNT时钟
⑨;预分频器计数器
1.对CK_PSC脉冲进行计数,从而给CK_CNT提供时钟脉冲信号
2.根据(PSC影子寄存器的值+1)进行计数
对CK_PSC每隔(PSC影子寄存器的值+1)个脉冲
就驱动CK_CNT产生一个脉冲
3.举例
当PSC值为0,即对CK_PSC进行1分频
CK_PSC来一个脉冲,CK_CNT也相应输出一个脉冲,CNT寄存器值+1
当PSC值为3,即对CK_PSC进行4分频
CK_PSC来四个脉冲,CK_CNT才输出一个脉冲,CNT寄存器值+1
以上便是我对PSC分频时序图的理解~
定时周期设计:
1.还是以STM32F407 TIM2为例 --- TIM2挂着在APB1总线,其定时器时钟源频率为84MHz
2.计数器重装最多放65536个数
分频 | 计数频率 | 每s计次 | 每ms计次 | 每us计次 | 溢出最多计时 |
1分频 | 84MHz | 84000000/s | 84000/ms | 84/us | 65536/84≈780us |
84分频 | 1MHz | 1000000/s | 1000/ms | 1/us | 65536/1000≈65ms |
42分频 | 2MHz | 2000000/s | 2000/ms | 2/us | 32ms |
8400分频 | 10KHz | 10000/s | 10/ms | --- | 65536/10≈6553ms |
4200分频 | 20KHz | 20000/s | 20/ms | --- | 3276ms |
42000分频 | 2KHz | 2000/s | 2/ms | 65536/2=32768ms |
3.把图表理解完,应该能配置我们想要的更新事件时长了
4.以下给出TIM7定时中断函数代码,以传参的方式设置想要的时间
/***************************************
*函数名 :timer7_interrupt_ms
*函数功能 :定时器7定时中断初始化配置
*函数参数 :u16 ms
*函数返回值 :无
*函数描述 :84M ---- 42000分频
2/ms 最多计时32768ms
****************************************/
void timer7_interrupt_ms_init(u16 ms)
{
//打开定时器7时钟
RCC->APB1ENR |= 1<<5;
//CR1
TIM7->CR1 |= 1<<7;//开启ARR影子寄存器
TIM7->CR1 &= ~(1<<3);//循环计数模式
TIM7->CR1 &= ~(1<<2);//更新事件选择三种
TIM7->CR1 &= ~(1<<1);//使能更新事件
//分频寄存器
TIM7->PSC = 42000 - 1;
//重载值寄存器
TIM7->ARR = ms * 2 - 1;
//使能更新中断
TIM7->DIER |= 1<<0;
//人为产生更新事件UG
TIM7->EGR |= 1<<0;
//清除计数完成标志位
TIM7->SR &= ~(1<<0);
/*NVIC*控制器配置*/
//优先级分组 ----在主函数
//计算优先级编码值
u32 pri= NVIC_EncodePriority (5,2,1);
//设置具体中断源
NVIC_SetPriority(TIM7_IRQn, pri);
//使能NVIC响应通道
NVIC_EnableIRQ(TIM7_IRQn);
//使能计数器
TIM7->CR1 |= 1<<0;
}
/**********************************************
*函数名 :TIM7_IRQHandler
*函数功能 :TIM7定时中断服务函数
*函数参数 :无
*函数返回值 :无
*函数描述 :
***********************************************/
void TIM7_IRQHandler(void)
{
//判断定时中断信号触发
if(TIM7->SR & (1<<0))
{
//清除中断标志位
TIM7->SR &= ~(1<<0);
//紧急事件
printf("TIM7定时中断测试\r\n");
}
}
细节问题:
真正的计数器使能信号 CNT_EN 在 CEN 置 1 的一个时钟周期后被置 1
1.通过时序图可以发现,开启定时器的一个时钟周期后
CK_CNT才真正产生时钟,即计数器才真正开始工作
2.这意味着从软件层面设置CEN位到计数器真正开始计数之间,存在一个时钟周期的延迟
3.在某些对时间要求非常精确的应用中,这个延迟可能会导致存在一定的误差
使用细节:ARR - 1
通过以上可知,在程序设计时PSC的值需要减1,那为什么ARR的值也要减1呢?
1.ARR(自动重载寄存器)用于存放与计数器值进行比较的数值。
当计数器的值达到 ARR 的值时,计数器会溢出并可能触发中断和更新事件
2.在递增计数模式下,计数器从0计数到ARR(自动重载寄存器),
然后重新从0开始计数并生成计数器上溢事件
3.计数器从0开始计数,因此实际上计数的总次数是 ARR + 1
在程序设计时,我们想让计数器计1000次,那么就应该写入1000 - 1
这样0到999才是共1000个数
以STM32F407 TIM2为例 --- TIM2挂着在APB1总线,其定时器时钟源频率为84MHz
1.举例1 设计1us的中断服务函数,分频为42
CK_PSC:84MHz
PSC = 42 - 1;
ARR = 2 - 1;
这样CK_CNT:84MHz / 42 = 2MHz,即2/us --- 计两个数的时间为1usARR写入值为1,实际上有0和1这两个数
2.举例2 同样设计1us的中断服务函数,但分频为84
CK_PSC:84MHz
PSC = 84 - 1;
ARR = 1 - 1;
这样CK_CNT:84MHz / 84 = 1MHz,即1/us --- 计一个数的时间为1usARR写入值为0,实际上有0这一个数
举例2中,ARR写入值为0是不行的!
由官方手册说明,ARR自动重载寄存器值为空,则计数器不工作!
因此,我们在程序设计时需要注意这一点