STM32 定时器 [ 影子寄存器 / 时钟频率 / 重载值 / 定时周期计算 / 使用细节 / PSC - 1 / ARR - 1 / 定时中断代码 / F407 ]

目录

前言:

时序图分析:

定时周期设计:

细节问题:

使用细节:ARR - 1

                 


前言:

        本篇文章将以时序图讲述自己对影子寄存器和分频的理解,以及分频系数&重装值的写入值的说明,计算定时时间的理解,最后还有定时器的一个注意点。
        关于什么是影子寄存器,可以看一下《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分频84MHz84000000/s84000/ms84/us65536/84≈780us
84分频1MHz1000000/s1000/ms1/us65536/1000≈65ms
42分频2MHz2000000/s2000/ms2/us32ms
8400分频10KHz10000/s10/ms---65536/10≈6553ms
4200分频20KHz20000/s20/ms---3276ms
42000分频2KHz2000/s2/ms65536/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 --- 计两个数的时间为1us

                ARR写入值为1,实际上有0和1这两个数                      

2.举例2        同样设计1us的中断服务函数,但分频为84

                        CK_PSC:84MHz
                                PSC  = 84 - 1;
                                ARR =  1 - 1;
                这样CK_CNT:84MHz / 84 = 1MHz,即1/us --- 计一个数的时间为1us

                ARR写入值为0,实际上有0这一个数

        举例2中,ARR写入值为0是不行的!

        由官方手册说明,ARR自动重载寄存器值为空,则计数器不工作!
因此,我们在程序设计时需要注意这一点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值