你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一次事件会触发两次中断?

[复制链接]
fightingboon 发布时间:2020-7-6 13:00
有人说在MCU的开发应用过程中遇到过一次中断事件触发两次中断的奇怪事情。有这样的事吗?应该说有真有假,这里以STM32为例来聊聊该话题。

( w6 {* C4 t7 m- O
所谓假的,就是指基于误会以为一次事件触发了两次甚至多次中断。比方按键事件没有做好消抖处理,或者中断请求标志位没有被及时清零等。顺便说下,对于STM32芯片而言,如果中断请求标志没有被清零会没完没了的循环进相应中断服务程序。
. G: g) G2 {2 o& I  _6 C5 K$ t- g
这里重点聊聊真的,即一次中断事件进入两次中断服务程序,的确有机会碰到。偶尔也有人反映类似问题,比方做UART通信时,一个空闲事件进入两次空闲中断,感觉相关标志没法清除;有人通过定时器触发SPI传输,一个定时器事件竟然进入两次中断连续给SPI数据寄存器赋值两次。

% G" R0 y8 r5 O- r& o( m# P; h
发生这种一次触发事件进入两次中断的情况时,一般有个非常明显的特征,那就是在中断服务程序里对中断请求标志的清零代码往往放在中断服务程序的最末尾。我们不妨弄个具体的实例感受下。

+ i6 O- q3 F: E) @, k
下面以一个定时器更新中断为例。我让定时器工作在基于向上计数的单脉冲PWM模式,即启动计数器后,当发生溢出产生更新事件时即告停止。那么每次启动定时器后按理有且只有一次进入更新中断服务程序。我在中断服务程序里放个计数变量,统计进入中断的次数。我这里使用STM32F4的开发板测试的。

* t6 b+ F4 `1 Q7 u5 n$ `7 J
先看看中断服务程序里清除中断请求标志的代码不是放在最后一行的情况。其中变量counterX用来统计进入中断服务程序次数。

' n* i9 \" J7 Z) l. n) O+ _8 B
11.jpg

& A2 a$ u7 m/ ]9 |6 y
这次测试结果没问题,一次更新事件对应进入一次中断服务程序。我将上面的中断服务程序稍微调整下代码前后顺序,让清除中断请求位的代码放在最后,再看看下面结果。
: X' ~2 \$ w( l3 t
22.jpg

- e6 e( U! r, {; M7 ^* l- q! H
嗯?counterX结果变为2了,一次触发事件怎么进了两次中断服务程序呢?!

% Y( `, s' J8 w
这时不同的人往往会有不同的判断或结论。比方中断请求标志一次清不掉啊;同样的写法别的系列或型号却可以,认为太莫名其妙啦!【其实,到底是不是完全相同的写法只是感觉,就像我上面的写法不细究的话也可以说是一样的写法】,或者说芯片很奇葩啊云云。

1 q& @( O4 x: M0 U3 x2 j6 O# K
怎么会这样呢?原因就在于那行清除中断请求位的代码放在最后,在第一次退出中断服务程序时该请求位尚未完成被清零的状态。程序指令执行速度越快,这种可能性就越高。既然该中断请求位依然保持置1的有效状态,经硬件触发再次进入中断服务程序就顺理成章了。
3 o* ?- P1 E% }( ?+ ]+ l
有人会问,我在退出中断服务程序之前不是已经做了中断请求位的清零操作吗?怎么没有立即生效呢?再怎么“立即”也是需要时间的,程序指令的执行完毕和指令执行后的状态改变并不一定同步。比方你到包子铺去跟老板说买3个馒头,老板满口应诺后,你不能立即扭头就走啊。他还需要点时间来处理,不然一辈子都买不到3个馒头。具体结合到stm32芯片,程序执行是基于哈佛结构的流水线形式,前面代码执行时依然可以执行后序的指令代码。
1 s4 ?! y) Z7 A9 j3 ?! s
谈到这里,有人或许想到在清除中断请求位的代码后面加上一句内存屏蔽指令,即DSB。应该说加这个DSB指令是有效的,即该指令前的所有内存访问指令执行完毕后才执行后序指令代码。不过,一般来讲,在这个地方用不着它,我们只须注意别将清除中断请求位的代码放在服务程序的末尾,稍微给清零操作留点实现时间。就像上面打比方买馒头一样,给老板一点为你取馒头的时间就行。

/ W  r5 M9 A, s/ W* y
也许有人会说,我中断服务程序里就只需做中断请求位清零这一件事怎么办呢?那你就随便在清零操作代码后面随便一两行无关紧要的代码也行,确保不发生1次事件进两次中断即可。

% `1 O# [. \: j- [3 Q$ ~
刚才前面说了,当清除中断请求位的代码放在服务程序最后时,程序指令执行速度越快,一次触发事件进入两次中断服务程序的可能性就越高。我们不妨看看下面基于STM32H7系列的一段中断服务程序代码。是TIM3的更新中断服务程序,截图里的两行代码为中断服务程序的最末两行。注意,清除中断标志的代码没有在最末一行。

& M! [0 u. J4 s& N  K
33.jpg
# `* ?1 m5 b( s  x- E
其基本功能就是每进一次更新中断,先清中断标志,然后给SPI数据寄存器赋值令其发送一个16位数据。显然,结合我们前面的分析,如果代码这样写一般来讲是不太可能发生一次事件触发2次中断的,事实上当程序代码在FLASH里运行时也的确没有任何问题。
但当将中断服务程序放到RAM里,比方放到DTCM里去运行时发生了功能异常。结果变成了每次更新事件发送的数据不是16位而是32位了。这个32位数据正是因为一次更新事件连续两次进入中断服务程序,两次发送SPI数据。那为什么完全相同的代码在FLASH里运行没这个问题呢,因为代码在DTCM的运行速率要比在FLASH里快,尽管在清中断请求标志的代码后面已经有了兼具延时功能的那句针对SPI数据寄存器的赋值语句,在退出中断前该请求标志位还是未完成清零而再进了一次中断。

) \/ b$ B7 D0 O6 G5 {, n1 Z
看来,这里还得稍微加多点延时以保证中断请求标志在退出中断前被清零。为了避免加延时代码的盲目性,即要么短了要么长了,我们可以使用对标志位的轮询方式,将代码稍加改动变成下面的样子。
4 D# f; C0 S( y, E/ Z# W
44.jpg

" _$ q$ g4 q/ b
之后,再行验证测试都是正常的。若有兴趣的话,可以在清标志位的代码后面加DSB指令验证测试下。
4 w- l+ A1 ]( ]& ~+ W( J. P
OK,该话题今天就聊到这里,平时做应用开放时稍加留意即可。祝君好运~~!

7 l7 b* P3 J8 m8 }
  i+ m/ I* s5 b9 l' \% {: p+ L
收藏 3 评论7 发布时间:2020-7-6 13:00

举报

7个回答
qiangtech 回答时间:2020-7-6 16:46:27
很好的经验分享,这样的问题发生时真的很容易让人抓狂而找不到原因。
sutichunxiao 回答时间:2020-7-6 17:14:24
学习了 mark
cruelfox 回答时间:2020-7-6 18:18:10
清片上设备的状态标志的时候,寄存器的写操作(STR指令)执行之后,到对应的状态标志撤消可能有一到几个时钟周期延迟,这个延迟多少取决于设备里面数字逻辑设计。
1 ^8 P+ y( N& F3 f加上DSB指令会让STR指令的写操作访问完成后再执行下一条指令,多一点延迟。也许问题就避免了,只是“也许”,因为硬件设备里面的延迟是DSB指令管不了的。' B' e/ [; k' y' E* {
什么情况下算程序运行太快导致问题呢? 注意一下 HCLK 和设备 PCLK 的频率差别,CPU执行的时钟是 HCLK, 而 PCLK 是这个时钟的分频(有的条件下也可以相等),如果分频数高的话,就相当于设备里面的状态变化要比 CPU 动作慢很多,就容易出这类问题。
+ R; b+ \9 p, j8 X: o( u
guoyuli 回答时间:2020-7-6 22:10:26
学习了!
STM1024 回答时间:2020-7-7 17:36:11
感谢分享~~~
switcc 回答时间:2020-7-7 21:35:34
学习了
sherlock_mt 回答时间:2020-7-9 10:10:26
尤其当CPU的主频提上来,而外设的主频不高的情况,可能容易出现这种奇怪的现象,细节决定成败。

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版