关于SYSTICK的COUNTFLAG标志的小疑惑

描述

前不久在研究SYSTICK有关问题阅读相关技术资料时,无意间产生了个小疑惑。

问题是这样的,我们知道SYSTICK定时器是个24位向下计数器,每当发生从1记到0时会让一个名为COUNTFLAG的标志位置1,如果此时SYSTICK的滴答中断请求使能了的话,可以对CPU发起中断请求。

STM32

根据我们平常STM32的开发经验,通常各种外设事件发起中断请求时,往往有相应的事件标志跟中断响应关联,在中断服务程序里并将相关事件标志做清零操作,否则它会没完没了地发起中断请求。基于这点,我想这个COUNTFLAG标志应该也是跟SYSTICK中断密切相关,溢出时被置位,在SYSTICK中断服务程序里将其清零。

可是,我们平常的SYSTICK的中断服务程序里根本没看到哪里有对COUNTFLAG标志做清零。ARM Cortex内核手册针对COUNTFLAG标志的描述中涉及它可以清零的地方有两处:

STM32

第一个地方是在SYSTICK控制寄存器【SYST_CSR】里有介绍,读它可以清零。说实在的,这点我是通过咨询ARM公司才理解到位的。第二个地方是在介绍SYSTCIK的当前计数器寄存器时提到,即对当前计数器寄存器进行写操作时也会将COUNTFLAG标志清零。

问题是平常的SYSTICK的中断服务程序里根本就没有涉及到上面提到的可能对COUNTFLAG标志清零的操作啊?!既没有读SYSTICK控制寄存器,也没有对计数器做写操作。那这个标志啥时候被清零的呢?如果不清零的话,难道不会没完没了地申请中断,可现在的实践结果又不是这样的!

后来,找同事咨询、讨论,有同事说他印象中计数器重装时会将该标志清零。如果说重装可以清COUNTFLAG的话,这样可以很好地解释目前的结果。因为既然每次重装可以清零,自然用不着到中断服务程序里再做清零,那么在中断服务程序里见不到对COUNTFLAG的清零操作也就再正常不过了。也因此一时以为找到了答案。可后来一想,还是有些不对劲的地方。至少有2点说不通。

第一、如果是重装时清零,该标志是溢出时被置1的,而溢出和重装两个动作可以看成同一时刻完成,即置位后马上被清零。这样的话,用户永远没有机会见到该标志为1的时候。何时能被软件用得上呢?定义这个标志意义何在呢?

第二、关于这个标志,在ARM 内核手册里还说了下面一句话:

STM32

意思就是说用户软件可以通过查看COUNTFLAG标志来确认SYSTICK之前有发生过溢出。如果重装可以清零的话,用户软件是不可能有机会读到该标志为1的时候。也就是说重装清零结论跟这句话是矛盾的。

经过与同事的来回讨论,以及查找其它相关信息,后来认为这个标志可能跟中断没有必然关系。这个过程中我也意识到我提出这个标志哪里清零的问题,可能是先入为主的惯性思维在作怪。具体点说,我们认为这个COUNTFLAG标志在发生溢出时置位没问题,前面提到的两种情形下会被清零也没问题。但是,SYSTICK的滴答中断不跟这个标志位关联,它只与计数器发生从1计到0的事件有关,即手册中下面绿色方框框住的这句话。

STM32

说实在的,这句话我老早就看到了,只是觉得溢出做为中断触发条件没错,但一门心思老纠结着哪个地方对COUNTFLAG清零了。

如果说COUNTFLAG只是个溢出事件标志,滴答中断不跟它关联也是可以理解和接受的。首先,根据ARM手册描述来理解这个结论没有问题,没有说不通的地方,然后,实现逻辑上也没啥问题,反正溢出一次就申请一次中断。

聊到这里,很多STM32用户【包括本人在内】可能会觉得有点别扭或不习惯,这点我们下面继续聊。我就我们针对COUNTFLAG标志跟SYSTICK中断的关系的理解,说得直白点就是SYSTICK中断跟COUNTFLAG有无关系、服务程序里要不要清零再次找ARM公司做了确认,他们完全认同我们的理解。即COUNTFLAG只是个溢出事件标志,SYSTICK中断不跟它关联,只与计数器溢出事件本身关联,并不关心COUNTFLAG的值是0还是1。到此,关于COUNTFLAG要不要在服务程序里清零的疑惑算是尘埃落定。

但是------

 

用过STM32外设事件申请中断的人应该很清晰地知道,要想各个外设事件中断申请能得到响应的话,除了NVIC端接受响应、外设端允许申请中断外,还得有相应的事件发生【包括软件方式】以及对应的事件标志被置位【或者说应该有效】,中断服务程序跟相应事件标志直接相关,即发生中断响应时中断事件标志必须有效,并需在中断服务程序里对标志清零,否则会没完没了地申请中断、响应中断。显然,这个过程跟前面SYSTICK中断有点不一样。SYSTICK中断虽然设置了溢出事件标志,但其中断并没有跟该标志关联起来。事实上这样运行起来也没有任何问题,那么ST设计的外设申请中断怎么非要跟标志位关联在一起呢?我们平常做STM32开发时,有时因为疏忽或原理不够清晰,没及时清零中断请求标志让CPU没完没了地进中断而陷入异常。

why?

 

整体上,STM32微控制器是由ARM处理器和ST外设集成而来,ARM 处理器又包括内核、核外设【以示区别于ST公司设计的外设】。其中SYSTICK、FPU、MPU、NVIC等均属于核外设。也就是说,SYSTICK是ARM的外设,不是ST设计的。既然这样,难道只是设计思路上的差异?但是,SYSTICK中断可以不跟事件标志关联起来,可以行得通,为什么ST不也这样设计呢?

一起来看看,尝试找找原因。

原因在于SYSTICK外设就一个溢出事件可以申请中断,在NVIC那边独立对应一个中断请求号【IRQ#],所以CPU在响应SYSTICK中断时根本无需关注那个溢出标志,有那个溢出事件就够了,因为除了这个溢出事件没别的事件来申请TICK中断。

   【下图是来自STM32G4参考手册里有关中断矢量表的部分截图】

STM32

而ST的外设申请中断时就没有SYSTICK那么好的福分了。往往是一个外设的多个事件共用1个中断请求。比方以上面STM32G4系列ADC3的中断来看,因为它只有1个中断申请号,在CPU看来就一个中断入口,即所有ADC3相关事件触发的中断共用一个中断服务程序入口,但可以申请中断的事件可多了,见下图【我后面把能触发同一中断请求的事件称之为兄弟事件】:

STM32

再以SPI3和LPTIM1的中断为例,它俩也各只有一个中断请求号,同样可以申请中断的事件也不少,分别见下面两幅图。

STM32

STM32

显然,ST设计的外设不能照搬SYSTICK的玩法。如果中断服务程序不跟触发事件标志关联起来,进了中断就不知该基于哪个事件来运行程序;基于某个事件运行了中断服务程序若不对它清零【包括读清零、写清零等】,等兄弟事件触发再进来时如何分得清哪是过时事件、哪是新触发的即时事件?

或许有人会说,为什么不给每个ST外设事件都安排一个中断请求号呢?这要考虑到必要性和中断请求号的有限性。不难理解必要性并不强,目前ST的设计其实没有啥不合理的地方。另外,内核开放的中断请求号数目是也有限的,视不同内核而定。

今天的话题就聊到这里,该问题属于好奇型的,即使不知道问题原因,一般也不会影响到我们平常的STM32开发。探究下也就是满足下好奇心,让内心偶尔掀起一阵涟漪,给生活增添一抹色彩。

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分