深入理解定时器中的影子寄存器、更新操作和更新事件

本人是stm32初学者,近期在看定时器参考文档时遇到些阻碍,再参考了众多CSDN博主的文章后,得到了一些个人理解,将其发布出来,本文也对一些参考的文章进行了不少复制粘贴同时也在这些文章必要位置加入了个人看法,文章链接附在最后

由于本人是初学者,本文一定存在不少错误,欢迎大家批评指正,不胜感激

//=============影子寄存器============================================
百度百科对影子寄存器解释
影子寄存器的引入是ARM的一个特点(X86,PowerPC都没有)。我们知道,ARM有16个通用寄存器,这16个通用寄存器在指令中使用4个bit来标识,但是在不同的模式下,同样的4个bit 指向不同的物理寄存器,这些不同的物理寄存器就被称之为影子寄存器。不同的通用寄存器的影子寄存器个数也不相同,有的没有,有的只有1个,有的多达5个。要记住一点:所有的影子寄存器都是一个实际存在的物理寄存器。

个人认为影子寄存器就是存储的数据和原寄存器完全一样但是分别在不同模式下行使其功能的寄存器(物理上不是一个寄存器,这两个只是4bit一样,但是工作在不同模式,之所以叫影子,大概是指令中标识的4个bit一样吧)但是你完全可以把他们当成两个不一样的寄存器,这里定时器中影子寄存器就被当作缓冲器使用了

在图71高级控制定时器框图中可以看到,那些底下有阴影的模块就是有影子寄存器的,比如预分频器,自动重载寄存器,通道/比较1-4寄存器,REP寄存器(高级定时器特有的重复寄存器的影子寄存器)

你实际功能上的值都是参考的影子寄存器而不是原寄存器,比如预分频寄存器,这个分频数看的是预分频寄存器影子寄存器上的,而不是预分频寄存器上的。又比如重载值寄存器,那个重载值是看的影子寄存器上的值,而不是原寄存器上的值

具体如果要使影子寄存器发挥缓冲作用(就是开启预装载,什么是预装载,下面有解释),你要打开相应的使能位,
注意stm32中预分频(TIMx_PSC)和重复计数寄存器(TIMx_RCR)这些都是不带预装使能(影子寄存器开启)控制位的,也就是系统已经给他开启预装载功能了

首先说明影子寄存器在这里定时器中的作用,影子寄存器具有缓冲作用(前提是打开那个预装载功能,不开的话影子寄存器感觉没什么用),就是在更新事件到来前,如果你的程序对具有影子寄存器的这些寄存器进行更改,如果没有开启预装载功能,那么这些你写入寄存器的值会立刻存入影子寄存器中上,又由于程序参考的是影子寄存器的值,因此可能会导致错乱(就像你正在演讲一个主题,在演讲的过程中,突然打断并要求你更改一个主题并立刻讲出来)所以我们需要一个缓冲(就是需要你把那个预装载使能位打开),在更新事件产生后,再将你新的设定值送入影子寄存器,也就是在中间加个缓冲(就是在这次演讲虽然在讲的时候有更改主题的需求,但是并没有打断你,而是在你这次演讲结束后再告诉你在下一次换一个主题)。这有利于提高程序的稳健性

影子寄存器缓冲功能(相应寄存器预装载使能位)是否开启是可以进行软件控制的,但是建议都打开。

自动重载寄存器影子寄存器开启控制位,这个复位值为0,所以使用时要打开
ARPE自动重载预装载使能 (Auto-reload preload enable),预装指的是先把东西预装在一个寄存器时,在更新事件来临时,东西被送到另一个寄存器中(就是影子寄存器),如果这个位禁止,则没有预装功能,就是你装到的寄存器直接把值送到影子寄存器上

可以看到图78和图79,两者一个是关闭影子寄存器预装载功能,另一个是打开这个功能,可以根据时序图看出其造成的影响

 

//==============更新操作和更新事件=====================================
下面讲更新操作和更新事件,就是什么时候你的值从原寄存器更新入影子寄存器
当产生更新事件的时候,那些寄存器就会把自己的值送入影子寄存器
使能更新事件实际上等同于使能影子寄存器预装载功能(就是缓冲功能)
预装寄存器是用户访问的为实际影子寄存器准备数据或指令的寄存器。(就是你将数据写入的地方,但是电路看的不是你这个,看的是那个影子寄存器)
它们分别是:
TIMx_PSC 分频寄存器 TIMx_ARR 自动重装载寄存器
TIMx_CCR捕捉寄存器 TIMx_RCR 重复计数寄存器[高级定时器有]
PSC和RCR寄存器是不带预装载使能控制位的,电路默认给其带上预装载功能,ARR和CCR是可以自己通过程序配置,

更新操作是一种动作,是更新事件的源头,即事件源;
更新事件是基于更新操作所导致的后续影响或结果。(前提是你的更新操作可以进阶为更新事件)

可能的更新操作【事件源】有3类:
1、核心计数器的溢出【上溢或下溢】
2、软件复位操作【对UG@TIMX_EGR置位】
3、工作在复位模式下的定时器收到触发信号【即复位触发信号】
【特别提醒,对于高级定时器必须发生RCR+1次溢出动作后才可以产生更新事件。对于通用或基本定时器,每溢出一次就可以产生更新事件。】
因为只有高级定时器才有重复计数寄存器RCR,基本和通用定时器没有这个重复计数寄存器RCR

那么更新操作何时可以升级为更新事件呢?这里涉及到一个控制寄存器的控制位UDIS@TIMx_CR1(UDIS指的是update disable,即禁止更新寄存器)
当该控制位UDIS@TIMx_CR1为0时,更新操作升级为更新事件,更新事件会产生如下影响或效果:(不管产不产生更新中断都会有下列影响)
1、‍实现从预装寄存器的数据到影子寄存器的内容拷贝,即完成影子寄存器的内容更新;
2、实现计数器【预分频计数器、核心计数器、重复计数器】的重新初始化;
3、置位状态寄存器的更新中断请求【UIF@TIMx_SR】位,并可以触发定时器更新中断或触发DMA请求;(是否还会产生中断或者DMA请求,要看URS位,这里将其附在了本文最后)

当‍该控制位UDIS@TIMx_CR1为1时,更新操作不能升级为更新事件,其相应的结果或影响:
仅限于计数器的重新初始化,影子寄存器不做内容更新;
无更新标志的置位,不触发中断或DMA请求(相当于只有第二个影响还会保留,其余的那些影响都会没有)(注意此时你的影子寄存器内容还是没有更新前的内容,因为你的数据没有写入影子寄存器)

那么发生更新操作时计数器的重新初始化具体是指什么呢?
1、分频计数器重装为0,然后重新开始计数;
2、重复计数器重装为RCR寄存器里的值,然后重新递减计数;
这里的RCR寄存器指的是RCR影子寄存器
3、核心计数器的初始化由计数模式来定,如果是向上计数或中心对齐计数模式,CNT归0;如果是向下计数器模式,CNT重装为ARR,然后重新向下计数;

发生更新事件时,影子寄存器的更新与计数器的重装有先后顺序吗?
有!影子寄存器【ARR/CCR….】的更新操作在前,计数器的重装操作在后!
因为这样可以保障计数器的重装值使用更新过的数据。该个细节要特别注意!

//=========================
最后,不妨做个基于更新事件的案例分享:

问题描述:TIMER初始化阶段,经常有人反馈,不管定时器周期的长短,只要一使能更新中断,就立即进中断服务程序?令人不解,往往给开发带来些困扰,原因可能是什么?如何解决?

我们知道,定时器应用的初始化时,往往需要对有关时基寄存器进行些基本的数据赋值。比方对ARR/PSC/RCR这些寄存器赋予初始值。结合前面的介绍,这些寄存器都是些带预装功能的寄存器,我们用户操作的寄存器都是预装寄存器,还不是实际起作用的影子寄存器。
对于ARR寄存器倒还好,因为芯片复位后默认状态下,ARR寄存器的预装功能是关闭的【CCR寄存器的预装载功能默认条件下也是关闭的】,那么我们用户给ARR赋值就相当于给其实际影子寄存器赋值了。
但PSC/RCR寄存器是不带预装控制位的,前面也说了,它们两个的影子寄存器的更新必须借助于更新事件。所以,在定时器的时基参数的初始化代码里,为了让用户写进预寄存器的数据生效,就用到了上面提到过的软件复位操作,即对UG@TIMx_EGR进行置位而产生更新事件,从而完成影子寄存器的数据更新。

在STM32标准库里的TIM_TimeBaseInit( )函数里都有如下代码:
TIMx->EGR =TIM_PSCReloadMode_Immediate;
在Cube库里的HAL_TIM_Base_Init( )函数里的函数有如下代码:
TIMx->EGR = TIM_EGR_UG;

‍结合前面的介绍,这两行代码使用的软件更新操作产发了更新事件,但它不仅仅实现了影子寄存器的数据更新,同时呢,还置位了状态寄存器的更新中断请求标志位UIF@TIMx_SR(这里是你的更新事件使其硬件置位的,可以看下面截取的寄存器UIF位说明)。那么,如果在这之后,我们使能定时器更新中断的话,进入更新中断服务程序就再自然不过了。为了规避这个问题,我们在时基参数初始化完成之后、使能定时器更新中断之前,可以先做更新中断标志的清除操作。
(这里是这样的,,软件产生更新事件是通过将EGR中的更新生成UG位由0置成1(然后有硬件置零,相当于产生一个矩形脉冲),如果你的URS设置为0,UIF会检测到这个脉冲并通过硬件使UIF置1,注意UIF不是由硬件立刻清零,而是会一直保持,直到你用软件清零,如果不清零,将一直处于挂起状态发出更新中断请求,产生更新中断。所以之前进行了更新事件产生后,应该先用软件将这一位置0,再打开定时器更新中断功能。同时如果想要使用定时器更新中断,则应该在中断程序中将这一位置0,否则这次中断退出还会再次发生这个中断)

UIF:更新中断标志 (Update interrupt flag)
该位在发生更新事件时通过硬件置 1。但需要通过软件清零。
0:未发生更新。
1:更新中断挂起。该位在以下情况下更新寄存器时由硬件置 1:
— 上溢或下溢并且当 TIMx_CR1 寄存器中 UDIS = 0 时。
— 当由于 TIMx_CR1 寄存器中 URS = 0 且 UDIS = 0 而通过软件使用 TIMx_EGR 寄存器中
的 UG 位重新初始化 CNT 时。

UG:更新生成 (Update generation)(可用于软件生成更新中断)(这个当发生更新事件时会先变1后变0,相当于产生一个脉冲,相关的寄存器会接收这个脉冲)(这个位操作仅用于软件和从模式生成更新中断,其他模式不通过这个)
该位可通过软件置 1,并由硬件自动清零。
0:不执行任何操作。
1:重新初始化定时器计数器并生成寄存器更新事件。请注意,预分频器计数器也将清零(但
预分频比不受影响)。

URS:更新请求源 (Update request source)
此位由软件置 1 和清零,用以选择 UEV 事件源。
0:使能时,所有以下事件都会生成更新中断或 DMA 请求 (意思是这两个你都可以产生) 。此类事件包括:
— 计数器上溢/下溢
— 将 UG 位置 1
— 通过从模式控制器生成的更新事件
1:使能时,只有计数器上溢/下溢会生成更新中断或 DMA 请求。
这个位 置成1时的作用(不管发不发出更新中断,只要产生UEV,那么前两个功能就一定会实现)
如果TIMx_CR1 寄存器中的 URS 位(更新请求选择)已置 1,则将 UG 位置 1 会生成更新事件UEV,但不会将 UIF 标志置 1(因此,不会发送任何中断或 DMA 请求)。这样一来,将既会产生更新事件的前两个作用(1、影子寄存器更新2、计数器初始化)

开启定时器更新中断通过这个寄存器
TIM6 和 TIM7 DMA/中断使能寄存器 (TIMx_DIER)

注意所有的中断标志位都是要通过软件清除的,不是硬件清除,因为这样可以使你发生过的产生中断的事件不会白白发生,不会因为你没有打开中断使能而被忽略,这样就可以在打开我的中断使能后响应此中断。如果你只想让这个事件产生一次中断,那么你一定要在中断处理中将这些位置0,否则将一直产生中断

本文章不少内容是直接复制粘贴下面两篇文章上的,同时也对官方参考文档进行了引用,本文仅个人理解,欢迎批评指正,不喜误喷(求生欲极强)

参考文章链接
http://t.csdn.cn/5pkA3
http://t.csdn.cn/Vy7gu

物联沃分享整理
物联沃-IOTWORD物联网 » 深入理解定时器中的影子寄存器、更新操作和更新事件

发表评论