|
在我们的单片机调试过程中,经常会遇到类似如下因第3只眼而导致的问题。何谓第3只眼呢?不妨先看看几个实例就知道了。 ) Q" i/ E" t2 S) s& i 第一个案例,与ADC转换标志位有关的问题。遇到该问题是一种较为频繁的情形。 经常有人在做STM32 ADC应用,进行代码单步调试时发现,明明启动了ADC转换就是等不到转换结束的那一刻,即总是检测不到EOC等于1的时候。有时让人直急得冒汗!比方类似下面的情形,开启了ADC转换指令,然后等待ADC转换结束。 8 X$ @. m2 Q. ~1 c7 c
+ S% ~' c/ M4 R- \ ?5 C ; \* X) M+ R+ v$ c- c 在那蓝色圆圈的代码处,查询等待EOC等于1,可就是等不到它为1的时候。是怎么回事呢? " u. y+ f$ ?: Z! B7 L/ W 原来,STM32芯片ADC的转换结束标志EOC位,具有读清零的特性。当你调试时打开外设寄存器显示栏时,那调试组件在不停的读取它。当你单步操作去读取该标志位时往往先被调试组件读过了。即使它之前被置位过,但因调试组件的读取后又被清零。当你单步慢悠悠去读它时,结果读到的往往是0,你就查不到为1的那一刻,此时我们可能会傻傻的等和着急。 ' A8 K' y6 L, f7 X l$ H& t 3 Z1 S2 ] G1 v* K7 g6 Z
当然,如果你将右边ADC外设寄存器显示栏关闭就不会出现上述问题了。/ I% @2 d h" w 3 ^2 E! f; T1 _; k6 s5 ]: b 第二个案例,与UART状态寄存器标志位有关的问题。遇到该问题也是较为频繁的情形。8 E2 ~7 f, u( |4 ~+ V 3 o) N5 M1 n+ ~; e% m2 g 某STM32用户使用STM32F2系列芯片的UART外设及相关功能。他发现明明接收完毕,发送标志TC位也置位了,可就是不进IDLE空闲中断。当然,相关中断使能都已正常使能无误。实际情形是这样的:1 Q: f; i3 A6 g2 u1 s# U 5 h# P* v v, S0 W" Y/ l- V2 e STM32F205的UART5发送指令(循环发送一个字节一个字节地发)给wifi芯片,WiFi芯片会返回相应数据过来,所以,正常来讲uart5会收到一帧数据之后应该进入串口接收IDLE中断.这是客户所期望的。 * l0 H) y6 I, ~ , C5 Z' Q( A J2 i: C 他将断点打在UART中断服务程序的检查到IDLE中断请求位等于1后的入口处。就像下面截图的样子。他甚至在右边的UART寄存器显示栏都看到IDLE被置位过的痕迹了,可就是进不到相关代码里去,怎么回事呢? 4 J7 E1 ~- W( b0 {! `: g5 e* E
因为他开启了UART寄存器显示窗口,意味着调试组件在不停帮他读了UART相关寄存器,其中包括DR和SR寄存器。当他在中断代码里再去读SR寄存器里的IDLE标志位时,读回来的结果总是0,所以中断程序没法进一步走下去。对于他这里,严格地说是响应了中断,只是没法进一步进入相关中断服务代码区。. i9 p" S6 c- y# q$ | # `+ z* q) {( y6 v 其实,对于stm32f2芯片UART的IDLE中断请求标志位的清零会遵循一个访问序列,即读DR寄存器,然后读SR寄存器就可将IDLE位清零。 : o3 n+ b) P; I; e" a) }* y
当然,解决上面问题的办法也很简单,调试跟踪时,将右边UART的外设寄存器显示栏关闭就好。 ; P. K9 s6 d0 b' [# n+ V( b 第三个案例,与读取RTC日历有关的问题。一个较为隐蔽而容易误导人的问题。3 D2 T; I: G# K! Z$ F 1 z) E0 g0 o8 `) ]" H 曾有人反馈说STM32F4和STM32L4的RTC脱机运行跑不起来,不运行。具体表现就是日历时间不动、不更新。奇怪的是,调试时候不论单步还是全速运行,查看日历寄存器都显示正常运行,数据也正确。 2 f5 I6 q+ h! b. M5 `# }, z3 t % G G" t9 Z8 p# }& S k 可当烧录代码到芯片后,通过调试助手查看日历的数据则原地不动了,感觉RTC没有运行。客户用STM32F4和STM32L4的板都测试过,出现同样问题。怀疑STM32F4和L4芯片的RTC是否有BUG【反正找不到原因了就想芯片bug】。) A. X2 x( \5 t8 G , L6 N* Y! i+ x _! ^& [1 j 查看其测试代码,就是读RTC的日历,很简单。如下:9 u4 k+ u# [ w! ~- c8 ? , b3 g, r% ~4 s while (1) { 4 o/ O6 ~# ]; u9 m; {: [9 D7 B ! F9 }( K) j$ P HAL_RTC_GetTime(&hrtc,&rtcTime,RTC_FORMAT_BIN);3 L: x9 `% n; _7 c7 M3 N1 a printf( ...... ); HAL_Delay(1000);2 ^/ N+ ^: H, L& G; v } 7 s. ?" y, d4 @( g( V' T! r 从上面代码不难看出就是不停地去读当前的时、分、秒时间。STM32参考手册在关于RTC日历读取操作部分有相关描述。为了读取时间的一致性,读取日历操作要求先读时分秒然后还得读日期,这样做为一个完整的操作。所以在读取TIME【时分秒】后,硬件会将当前日历值锁住,直到读取了日期寄存器。否则当你读了TIME而不读DATE的话,再去读TIME时还是原来的值维持不变。显然,客户这里的代码只有读取TIME时间的语句,没有读取DATE日期的代码。这是问题根本原因之所在。) L( |) E! X4 ? 但是,为什么同样代码在调试情况下又能正常运行呢?那是因为他在调试时打开着RTC寄存器外设显示窗口,虽然用户代码没去读DATE,但调试组件帮忙读了DATE寄存器,所以感觉上一切风调雨顺,也就没能及时发现问题,一直到程序烧进芯片后才发现异常症状。! X T! a; ?& i% [! j; j ! n5 U+ ]; @8 |+ h2 n1 c : ^( @- d+ A6 W7 G% O
到此,结合上述3个案例的分享介绍,我们应该明白了那个第3只眼了,即调试组件。个别寄存器或寄存器位具有“读则变”或“读有效”的特性。我们在调试时候要注意类似细节,调试时也不必时刻将那个外设寄存器显示栏开启挂在那里。其实,除了这个寄存器显示栏外,我们还可以利用其它输出,比方示波器、printf,或者WATCH窗口等来辅助观察运行状态或结果。 既然这里把调试组件称之为第3只眼,那另外两只眼呢?这个不难想到CPU和DMA。# V+ [- q4 p3 g q |
微信公众号
手机版