问题:" F/ Z8 ]! _; u, a& A
该问题由某客户提出,发生在 STM32F072 器件上。据其软件工程师讲述:使用 STM32F0 系列的标准外设库中 RTC_Tamper 的例程来进行修改,例程中配置的是当在 RTC_TAMP1 引脚检测到上升沿的时候,复位备份寄存器并产生中断;客户根据其实际应用将配置中的上升沿改成下降沿,以期待在检测到下降沿的时候,复位备份寄存器并产生中断。结果,客户发现经常是只要一开始跑程序,会产生中断,复位备份寄存器。使用示波器观察 RTC_TAMP1 引脚,并没有发现下降沿波形。客户百思不得其解。
9 f+ c9 t" Z! g) L$ j# W1 Y5 s; Z( z1 s3 i, `$ @/ @
调研:9 o6 {0 f$ f* U- K
问题(一)! M8 R( k( T7 y6 e8 N) [
首先,我们检测一下 STM32F0xx 标准外设库中 RTC_Tamper 例程的 Readme.txt,我们发现其中描述此程序的功能为将 Tamper 引脚配置成下降沿并使能中断,当给 RTC_AF1(RTC_TAMPER1/PC13)施加低电平时,RTC 备份数据寄存器将被复位,并产生 Tamper 中断。不过我们再看一下例程,发现在例程中配置其实是上升沿的中断,也就是说,此 Readme.txt 与程序不符。所以,我们先不管
% }# [- A9 x; }8 r& c! j: c9 CReadme.txt 所描述的内容。9 {4 U. B; @" [6 m+ n a+ {: F
) G3 S [+ {: z5 q- _我们来看一下客户的程序,发现客户只是在例程中修改了两个配置的语句。修改如下:
( d1 _3 t' t3 ?. }* k2 Q9 n' |(1) 例程:' l3 }- s Z2 x/ W& m, P3 Z" o5 c- O
- /* Enable The external line19 interrupt */
. c6 Z' S: O, v2 o4 a: y5 Q) x" I - EXTI_ClearITPendingBit(EXTI_Line19);
& k! y& O5 `' S/ x - EXTI_InitStructure.EXTI_Line = EXTI_Line19;
9 Y# Z- e: w( l1 e - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;" e" \/ D& V: }7 G, N/ ]
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
5 E# v: L/ Z" [* @" _+ R - EXTI_InitStructure.EXTI_LineCmd = ENABLE;) C N& z1 }" j; y5 P! n t
- EXTI_Init(&EXTI_InitStructure);1 H9 D$ P0 h# F* T O; v. c
- 客户程序修改为:8 g/ e/ L+ G( w) B& E* L
- /* Enable The external line19 interrupt */
' D2 H9 _ D* O/ y( v" a" S" P - EXTI_ClearITPendingBit(EXTI_Line19);, r# K/ B9 O% F( j
- EXTI_InitStructure.EXTI_Line = EXTI_Line19;
9 G. e# `, ]; g; M+ B; ?0 l$ Z* a4 k - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;% e' B: M: r( m1 z( _! [! G
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;- b0 Q% ~9 U8 N! C& D* ~/ v
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
- N3 t" x7 H& w a$ o - EXTI_Init(&EXTI_InitStructure);
复制代码
; T' m; T9 X. Q3 V(2) 例程:5 D2 s- W6 @; L: n
- /* Configure the Tamper 1 Trigger */
$ l/ e" H$ B9 C/ U- x; q - RTC_TamperTriggerConfig(RTC_Tamper_1, RTC_TamperTrigger_RisingEdge);' c3 j. m6 Q, ~/ r, M
- 客户程序修改为:: f- q5 M/ D, D( I. _
- /* Configure the Tamper 1 Trigger */
6 ~. S3 A1 w. w# ^4 z3 h& K$ b7 j - RTC_TamperTriggerConfig(RTC_Tamper_1, RTC_TamperTrigger_FallingEdge);
复制代码
* ?1 ?7 o4 K' R: @& Q: ]我们使用 STM320518_EVAL(使用芯片为 STM32F051R8T6)来跑客户的程序,发现确实如客户所说的现象,只要一运行程序,经常会进入中断并复位备份数据寄存器。; K+ T* P; H. h1 I" p1 Y
) W8 N( X( f9 w7 U1 P# p
我们来看一下客户所修改的两个部分:第 2 个部分修改是将 RTC_Tamper_1 的触发设置为下降沿触发,这个肯定没有问题,那么我们主要来看一下第 1 个部分是否需要修改。# h, J& u8 K# R, x+ q
/ D; @& G, u4 A l( I9 H
我们查看参考手册 RM0091 的“12 Interrupts and events”一章,在“12.2 Extended interrupts and events controller (EXTI)”这里看到这么一句话:“The active edge of each external interrupt line can be chosen independently, whilst for internal interrupt the active edge is always the rising one.”和“12.2.4 Functional description”中也有一句“For the internal interrupt lines, the active edge is always the rising edge”。它们的意思就是说内部中断的有效边沿永远是上升沿!我们再来看 EXTI 的8 I% y) O5 q: C9 H6 a
6 w0 _* M5 Z& p
5 T1 O5 x4 f5 @. P0 {0 M% ~% V框图:
% F9 j0 U* {( w O% c
" b4 H- v7 c% Y9 Y
6 D0 M! c! x G2 Y9 c5 o" ?% ~; t
从框图也可以看到,内部事件确实是要将 EXTI 设置为上升沿中断的。EXTI 线 19 是连接到 RTC Tamper 和 TimeStamp 事件的,为内部事件。
) t* q: p& {* [0 z4 B& c1 T0 S5 o* c, F! c- O6 N
由此,我们知道客户修改的两部分代码中,第一部分关于 EXTI 的边沿是无需修改的,应该保持为上升沿的配置,所以需要改回来:EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
$ w5 c' }1 {, f; W2 T) Y+ z# X. w
$ g- e- @4 U- C再测试,发现问题已经解决,不会再一运行程序就经常进入中断。2 Y6 w" T1 K6 l! K
8 ?8 x( ?& p# Z$ J" K$ A) X0 i问题(二)7 [( R( i& k! N0 v. u$ l
这个时候,我们又发现一个问题:一旦进入中断之后,就出不来了,中断程序运行完之后又中断了,又开始跑中断程序了。这看来,中断处理程序有些问题,我们来看一下例程中 RTC 中断的处理函数:% b% B" `9 q. p5 c7 p) [
- void RTC_IRQHandler(void)! g7 E- g( B% p
- {8 y. q" g! G3 X& P4 {2 @
- if(RTC_GetFlagStatus(RTC_FLAG_TAMP1F) != RESET)
$ B$ b9 D+ p2 F1 } - {
% t" @/ F* }$ I( L: ~- E* A4 H2 L1 x - /* Tamper 1 detection event occurred */
. I2 Z# j0 \" l6 p1 a! g6 j4 [ - /* Check if RTC Backup Data registers are cleared */
# m, \0 ^( w9 O" [ - if(IsBackupRegReset() == 0)
5 e7 R5 C* z) B, \' F- ? - {; ?7 i6 v$ d, U0 ?# }' Z
- /* OK, RTC Backup Data registers are reset as expected */
4 v+ a( X& Y) \3 S0 {- A( _ - /* Turn on LED2 */
9 a% y8 P5 C3 R; F3 @! s - STM_EVAL_LEDToggle(LED2);
% u; K& F8 O5 r, {) U - }, V1 L7 w5 }, v2 k3 v$ i& ]9 u) ]
- else4 u" ^$ E9 E$ w/ T, B; N* O
- {$ B( K; L( J( p0 [5 w1 h
- /* RTC Backup Data registers are not reset */$ u3 m' R, Y8 a! ^- U% B1 q
- /* Turn on LED4 *// M" a6 u- K6 W! Y
- STM_EVAL_LEDToggle(LED4);7 Y5 k" p! k. A# z. V/ B
- }' Y9 A5 q4 k( u0 H# v. N4 R
- " ?$ J; M) a W; ~% T
- /* Clear Tamper 1 pin Event(TAMP1F) pending flag */
1 L6 F- t' k! w& {; A$ C$ a% N - RTC_ClearFlag(RTC_FLAG_TAMP1F);
$ j3 S- W# s) L( F, X9 O - /* Disable Tamper pin 1 */0 S& m: k! {/ O2 w# D' u4 t
- RTC_TamperCmd(RTC_Tamper_1, DISABLE);
$ b& N4 s1 ]# {! `" j! { R w, Y
* t1 m8 v( A$ }! r- /* Enable Tamper pin */! f: f* H3 I1 x, u+ B6 F2 r
- RTC_TamperCmd(RTC_Tamper_1, ENABLE);) K/ G8 x3 X7 a" b9 [4 f5 b
- }
1 C% f1 v( B! Z - }
复制代码 1 |' G! p' X8 i. t0 _5 J
我们设断点下来看一下程序的运行状况:在 RTC_ClearFlag(RTC_FLAG_TAMP1F)清 TAMP1F 之后,运行到 RTC_TamperCmd(RTC_Tamper_1, DISABLE),我们发现 TAMP1F 会被重新置 1,我们暂且不管,先将 RTC_ClearFlag(RTC_FLAG_TAMP1F)挪到 RTC_TamperCmd(RTC_Tamper_1,DISABLE)之后,程序变成:$ V* N! m" w9 ^8 c
- void RTC_IRQHandler(void)' Q2 d# [% `* X$ n& ~) Q! y. h2 p
- {
4 _$ `4 G' q" z- d - if(RTC_GetFlagStatus(RTC_FLAG_TAMP1F) != RESET)
- `; H, O5 L' I& K. W- t& W - {
% n) @0 I6 p; K" V+ D9 H2 x2 k2 R- Y - /* Tamper 1 detection event occurred */9 R& ~/ o& S1 s. W0 q
- /* Check if RTC Backup Data registers are cleared */: q$ Q. Q+ _- }( Y3 q
- if(IsBackupRegReset() == 0)
- _7 Z) F% h+ D5 V/ C$ \/ N - {
& l+ z! b! ^5 e) i - /* OK, RTC Backup Data registers are reset as expected */: h) E/ J3 p \) X9 @1 s$ m7 Z$ _
- /* Turn on LED2 */
6 e, Q6 b0 t: s; R# T0 y - STM_EVAL_LEDToggle(LED2);
6 `& I; \0 ~: s# q9 j - }
$ ^' B6 Y5 J1 ^ - else; O6 U0 h5 F+ [
- {6 o! @# ^! V9 G" G
- /* RTC Backup Data registers are not reset */
3 B- N( ^* P5 T# W; a - /* Turn on LED4 */' v/ K- b% x! n
- STM_EVAL_LEDToggle(LED4);, e3 _7 F' Q7 W! N1 D
- }
; e# o: p, @9 n9 k7 D
9 z) s" {+ @6 s0 a% _2 T- /* Disable Tamper pin 1 */8 W" Q* I W- B! D) Q; o) Q
- RTC_TamperCmd(RTC_Tamper_1, DISABLE);
3 d) g4 Q9 F6 U
' |1 t2 L0 `: n- /* Clear Tamper 1 pin Event(TAMP1F) pending flag */ ^1 T# s* x; g- i7 x4 j) ^2 H& [( t8 v/ W
- RTC_ClearFlag(RTC_FLAG_TAMP1F);
% [: |& o- T; d( D- P - /* Enable Tamper pin */
+ u3 [- h% E* ^$ Q& J# I4 H l - RTC_TamperCmd(RTC_Tamper_1, ENABLE);4 }) w( |: d2 R: a6 d
- }0 C8 h; C" V( j6 P- J' }, G; c
- }
复制代码
& X" f* B- v% r" J! E$ j; ^6 j6 J我们发现,还是仍然在不停地进中断,而且这个时候 TAMP1F 已经是 0;这是为什么呢?我们再想一下第一个问题,关于 EXTI 的,原来在这段例程的代码中,并没有清除 RTC Tamper 对应的 EXTI 的5 ?% W7 H1 x4 n" s5 U4 A2 v
PR19 挂起位,从而导致在不停地重复进入中断。加入清除 PR19 的代码,新的代码如下:) o& z C+ Q) ?6 Q+ q& [
- void RTC_IRQHandler(void)
8 Q! i( v6 v" l6 P' ] - {
O# a) X3 v, m* Q) y7 S: k. D - if(RTC_GetFlagStatus(RTC_FLAG_TAMP1F) != RESET)
: G% H- L% P9 o4 e& W - {
; S( x( c: J% u8 m1 s) s - /* Tamper 1 detection event occurred */6 W6 v5 `( N% `7 S7 z" T
- /* Check if RTC Backup Data registers are cleared */' d- g2 N3 R6 |4 o/ m5 H
- if(IsBackupRegReset() == 0)
" F* H7 ~# o) Q3 H0 X - {- G5 b5 }" _# }2 e \- d% O
- /* OK, RTC Backup Data registers are reset as expected */
6 @ H- p7 E$ Q) ^ - /* Turn on LED2 */# ?' g( Q5 r" O0 x2 @
- STM_EVAL_LEDToggle(LED2);
' b( r/ S* b* g3 H% D - }
, r2 f3 B& N. \- [4 A - else
7 }1 O: Z d$ C - {
0 g* E( ~& S* y2 S' S4 E7 ? - /* RTC Backup Data registers are not reset */
7 x- m# v T. J* B$ e - /* Turn on LED4 */
4 | M+ @" s% b4 T. x: O7 V% F: d - STM_EVAL_LEDToggle(LED4);
5 T: \5 x# }2 D - }
* b2 ^. N0 l4 z' \1 T$ D* v
" @2 T$ x1 h: C% v* Z( r- /* Disable Tamper pin 1 */" u: B. n+ P! W7 c1 x: B
- RTC_TamperCmd(RTC_Tamper_1, DISABLE);; e) o3 S* }/ ?+ `0 e p
- 8 Q& k3 W# W+ \' N
- /* Clear Tamper 1 pin Event(TAMP1F) pending flag */& D3 X! [1 f- T7 X
- RTC_ClearFlag(RTC_FLAG_TAMP1F);2 e4 \* X* j( B, J! D9 H
- EXTI_ClearITPendingBit(EXTI_Line19);- _& Q0 C. N6 j$ W9 }, C3 ~' |8 y
- /* Enable Tamper pin */
, \. W% ?5 ]+ L) T9 a; Q+ B/ f - RTC_TamperCmd(RTC_Tamper_1, ENABLE);% D- G% P# w/ t6 r6 X5 E
- }1 ]4 h& o5 R' b+ R; o/ G
- }
复制代码
! I; c+ ^3 l$ Q9 O再测试,问题解决。7 U3 k& L0 l8 E
- w/ P+ |6 t I+ w
问题(三)7 j' ?8 {, B$ W
我们还发现一个问题:每次上电开始调试,还总是会进入一次中断。若这次已经进入中断,通过“Run to ‘main’”按钮将 PC 复位,再运行,则不会发生进入中断;但是再通过“Run to ‘main’”按钮将 PC复位,再运行,则再会发生进入中断。”这么奇怪的问题,我们来具体查找其发生问题的所在:- v5 {+ H5 r' O$ G% j
我们发现,在初始化代码中,当程序运行到 RTC_TamperCmd(RTC_Tamper_1, DISABLE)之前,TAMP1F 为 0,PR19 也为 0,可是,当运行完 RTC_TamperCmd(RTC_Tamper_1, DISABLE)之后,TAMP1F 为 1,PR19 也为 1,再往下运行必然会产生一次 RTC Tamper 中断。
0 z( \# m( g- w5 F: JRTC_TamperCmd(RTC_Tamper_1, DISABLE)函数只是对 TAMP1E 的标志位进行修改,由此证明,当 TAMP1F 为 0,PR19 也为 0 的时候,执行将 TAMP1E 关闭会导致 TAMP1F 为 1,PR19 也为 1。
7 `5 n, O$ {3 z" }
0 w) B$ H/ N' S2 o' x/ h我们再回想一下问题(二)中,我们在中断处理函数中,也遭遇过“在RTC_ClearFlag(RTC_FLAG_TAMP1F)清 TAMP1F 之后,运行到 RTC_TamperCmd(RTC_Tamper_1,DISABLE),我们发现 TAMP1F 会被重新置 1”。所以看来,这个问题是真实存在的了。
5 F" b5 G& K+ P0 f
% s# A1 w+ {) q& l) [. g& mST 给出的解决办法是:要避免此假的边沿检测现象,需要确保在通过 TAMPxE 对 Tamper 通道进行关闭之前,TAMPFLT 的值不等于 0(边沿检测失效)。如果使用 Cube 进行开发的话,可在 Disable TAMPxE 之前加入“RtcHandle.Instance->TAFCR |= RTC_TAFCR_TAMPFLT_1;”如果使用标准外设库的话,可在 Disable TAMPxE 之前加入
, c J) |8 F& Z4 v% K* W7 q2 ]“RTC_TamperFilterConfig(RTC_TamperFilter_2Sample);”0 @6 U+ n8 ?; V6 Q1 w! S: ?
* B, R5 U7 e3 [* N那我们现在修改程序:在“RTC_TamperCmd(RTC_Tamper_1, DISABLE);”之前加入
3 | N" Y+ M3 w# }( {% F“RTC_TamperFilterConfig(RTC_TamperFilter_2Sample);”之后加入4 f5 K( u$ V' f0 W
“RTC_TamperFilterConfig(RTC_TamperFilter_Disable);”。
1 m v, g) m; a! \8 O# |也就是说,原程序:7 N3 e) h2 I6 Y* \' \# o( g) y
RTC_TamperCmd(RTC_Tamper_1, DISABLE);5 k5 y9 f5 [# Z$ H
需要修改为:
9 a( ~$ Y; @$ ^7 TRTC_TamperFilterConfig(RTC_TamperFilter_2Sample);
+ _, Q5 J$ H; k }$ _+ h: s6 gRTC_TamperCmd(RTC_Tamper_1, DISABLE);
7 C T& Y/ w0 RRTC_TamperFilterConfig(RTC_TamperFilter_Disable);1 Z! N7 R$ Y4 s& [% [, D8 a+ X
修改之后,再进行测试,问题解决。, F: T- J q* x( ?
0 W6 b# g' ]) d结论:
" s4 T$ b: e* ?3 T3 I/ M& `客户需要将 RTC Tamper 设置为下降沿检测,在设置 RTC Tamper 下降沿的时候将 EXTI 的边沿也一同设置为下降沿,造成错误;STM32F0xx 的标准外设库中 RTC_Tamper 的例程也有问题,需要进行修改。; g2 i. S- c C& X1 L% `
& k5 x/ ?* C7 U
处理:6 h! ?# A$ h' ~( v# H
将 EXTI 的边沿恢复成设置为上升沿,修改 RTC_Tamper 中例程存在的一些问题。
' V6 n& |4 }, s% {4 o- ~) c3 Y+ R, t' R1 V# h/ y8 T( m, v
4 h# S% R4 M1 E: _6 X建议:
2 e/ W* U! X4 k4 H: K, p6 Q2 Z上面三个问题,总结一下:' |# q0 b& n' |% U
(1)不管 RTC Tamper 边沿检测设置为上升沿还是下降沿,作为内部事件连接到 EXTI19,对 EXTI19的边沿设置始终是上升沿;
4 s5 a4 G7 ~; X- ?$ [ V) D/ W(2)在 RTC Tamper 中断处理函数中,不仅要清除 TAMPxF,也需要清除 PR19;" ? Y7 S& e2 V6 m3 K! l
(3)对 TAMPxE 进行 Disable 的时候,可能会产生伪边沿检测,所以需要在 Disable TAMPxE 之前先将 TAMPFLT 的值设置成非 0 值(即关闭边沿检测),等到 Disable TAMPxE 之后再将 TAMPFLT 的值恢复为 00。* K4 J, ~) h$ P% s
" g2 H5 K% Q1 e( p/ e5 K
|