11.1 初学者重要提示
' I$ h" Z4 p+ q" ], L& q0 P MDK本身也是支持硬件异常分析的,就是不够直观
- m- g5 v7 a }
" v4 A6 i& W4 J IAR8带的硬件异常分析比较好用,在本章11.6小节有说明。( @" L/ j, D; e& `' s, [
11.2 移植方法, X8 \. l/ q, E5 F9 u
直接移植SEGGER的硬件异常代码会有错误警告,这里针对IAR和MDK版本做了些简单修改,方便大家移植到自己的工程里面。
+ q4 s! |* [: e+ h; J( F
/ n/ _# K% t: \- @7 Z o, w MDK版本移植
6 z4 q0 X* ~1 r5 I# g源文件位于本章配套例子的\User\segger\HardFaultHandlerMDK文件夹,添加如下两个文件到工程里面即可。
& y* _; K. Z" D! F V# W$ t& [8 ]- k
' G4 O9 h" k3 e' s
& h9 p; p0 c1 {2 }& A0 O* w4 p IAR版本移植
1 _ M! w. V( b! @! s% E/ Y& t' D- G$ W源文件位于本章配套例子的\User\segger\HardFaultHandlerIAR文件夹,添加如下两个文件到工程里面即可。8 O; a7 d1 [2 \7 {$ M: M& a* U W
7 i, H2 U8 m) v' \& s( |- ^6 Y
9 o& L' o/ @9 {, v M: a: B5 A/ Q
- N; p3 |& q, r+ u4 f在文件SEGGER_HardFaultHandler.c里面都添加了串口打印功能,方便不用编译器的调试功能时,通过串口打印提示是否进入硬件异常。2 C/ _( P( }3 ~+ [. J, u( H: v
# n- h: J0 F5 g8 C$ E8 G' A- #define ERR_INFO "\r\nEnter HardFault_Handler, System Halt.\r\n"; l) `5 |$ P- O6 |! I
- ' \) h- t* I5 @6 `/ l+ x. \
- #if 1. _/ @/ e8 V: }' ~! y2 ? ?
- {
1 ~, |8 |' W: z% @0 Z8 B. W - const char *pError = ERR_INFO;
2 U9 s# u5 G2 H/ Q! z - uint8_t i;) D" d# V0 H% u% I( o
- 5 {. K9 @ i2 o5 n4 l9 ^
- for (i = 0; i < strlen(ERR_INFO); i++)
5 A, l! O* K" D6 k1 h, ^ - {
; v/ a& o$ w+ ^: A - USART1->TDR = pError<i>;
/ o [* h e. T - /* 等待发送结束 */
/ h1 m$ v% D; F& \9 R0 Q! v6 Q - </i> while((USART1->ISR & USART_ISR_TC) == 0);+ A- A7 u3 ^, l" t; `8 e
- }
4 Z. p7 x- Q' O( J1 r - }& I$ _' U. c' O0 T4 o1 P/ p- [" `
- #endif
复制代码 $ ^8 B+ K& {: L% k6 J0 a+ S! B
11.3 MDK锁定硬件异常位置方法
( Y/ P' R* x0 ]: |( }1 K( o; `以本章配套的例子为大家做个说明。$ _5 C" |9 x w B
! v9 q5 C# f* t/ N0 E1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:
, _7 b$ u7 n: H1 u, C, D5 N, P. Z. m1 W& r2 t
; x3 x: A% P: f- W5 j9 `/ g% U& X# \8 C! _7 Z$ ~
2、在Watch1窗口添加变量_Continue
2 Y' ]. f7 j1 R; z/ M' P8 j9 I5 U7 H7 q3 w- D% ?4 J/ _4 C
. Y! x0 p. v6 \' c& U( S
9 b+ Y1 p _& D3 M# ~
3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:9 P: l' Q3 C/ X3 z
* n* L* x3 I1 T5 J! d* i% I6 O
6 Y2 q' ~% v# M7 Y, @% v7 O* N2 D3 K9 c: k+ @# C0 X/ [( D
11.4 IAR锁定硬件异常位置方法
4 h; {7 {5 |3 K% U& q& s$ O0 y以本章配套的例子为大家做个说明。
' x, s+ D4 a+ J O" W' y# P9 {( w3 k8 f
1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:: a/ b$ k* @2 Q" \; z
7 x. O9 p, g1 j- h, a$ y8 S
9 V4 s1 H) i; [9 K& o
1 Q" d9 W+ F( U% p0 W& k
2、在Watch1窗口添加变量_Continue1 r( v' g, Z9 x& _
Y+ D- g, F3 `3 V
- @: t% R: R, T$ J! C" y& j" q- e" V
3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:
0 Z, b1 P% q9 U# t& ?) f- `- \* F1 o. Z4 O( \* B4 b/ R
* H/ Q3 x/ m! @# f
% f' Z* e% H. J$ e/ w
11.5 硬件异常原因分析1 K. G/ u( {+ e
SEGGER提供的这个机制查找出问题的位置比较方便,具体原因需要继续在调试界面里面添加HardFaultRegs结构变量,这个结构体变量添加了所有大家想看的东西。下面是MDK调试状态查看部分结构体数值:6 A- Z0 n4 s; t! u) n e
1 p: J# A" u& U& J! m: `& ^ Q+ p: f3 v7 Q( t
0 [1 Y4 ]% `, y4 N) \* D4 t4 l1 s具体上面的变量代表什么含义呢,代码里面有注释,查阅起来没有IAR自带的硬件异常提示方便(注意,下面的代码用到了位域)。8 z! W% t$ [6 r4 a/ T4 ]" n% Q" [
7 _2 h7 p% n( N6 `- #if DEBUG
( s& C" ^5 S% Q4 f0 | - static volatile unsigned int _Continue; // Set this variable to 1 to run further( E( Q( G7 B9 k# i: t
: ^3 n8 A4 O' \5 Y# \- static struct {2 z* |! S$ L) C: I+ I m+ N2 R
- struct {% Q1 U; W# T* H, i
- volatile unsigned int r0; // Register R0
) X0 C( ]) f- J+ A. T - volatile unsigned int r1; // Register R13 [/ H" |4 U' }1 S! \2 f0 v: z/ k
- volatile unsigned int r2; // Register R2
' L; \5 @2 P: Y) ?; W - volatile unsigned int r3; // Register R3
$ ~; C# c9 L2 R - volatile unsigned int r12; // Register R12
( R( Z' Q" B, |2 K* K0 Z# g - volatile unsigned int lr; // Link register
) K) T. W% f! T/ ]8 p - volatile unsigned int pc; // Program counter+ w+ h7 S- u1 ^7 v
- union {, ~/ E3 `: X0 u
- volatile unsigned int byte;
" `; J* [# b& Z0 x - struct {
+ B) U8 i3 v. Z D/ r - unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)+ M- d- Y! I2 h4 V; T
- unsigned int EPSR : 19; // Execution Program Status register (EPSR)
9 V; N6 H2 z$ T! t# N, I1 D - unsigned int APSR : 5; // Application Program Status register (APSR), X0 X$ r- ^' W. N f5 l
- } bits;- a" s- Y3 w% H. ` _
- } psr; // Program status register.8 x! ~. P! ?' K: @
- } SavedRegs;% L; @, l# j* K+ b
- G8 h3 v# s/ d2 a3 g2 I
- union {) X/ ^ c: o* Y# ~% X4 m" N" ?
- volatile unsigned int byte;( n2 Y! u( O. J* J- z
- struct {8 z1 ~& P( }2 }2 J7 V
- unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active9 q! Y5 T( b0 i+ t
- unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active: x7 B8 R4 X; f6 y7 G) Z* W/ t
- unsigned int UnusedBits1 : 1;* C. F& ^2 {# E! |1 S y
- unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active
, n M# b9 i; u* B& t- Y/ R! J - unsigned int UnusedBits2 : 3;
- Y7 r: _ C ^- u; j7 B, V7 R - unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active
% @4 A/ a" p8 i) ]2 e { - unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active2 s1 l7 _/ W6 {! E
- unsigned int UnusedBits3 : 1;
$ Z' F& _! u* L& k+ I - unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active+ G; J5 F: h+ F1 z& b8 a5 y% Z
- unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
7 w4 b' o& `3 | ?+ [- v3 _$ N - unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a% U+ G E/ H0 S
- higher-priority exception& O( V% W4 L9 b4 [
- unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started; F+ h p% _. |1 ~: D% ~" Y4 Z
- but was replaced by a higher-priority exception
2 P0 |/ U2 X7 l! m4 F - unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced
/ P5 p, m. C/ I1 [7 f - by a higher-priority exception
& p3 p0 i- h/ o7 ]3 U - unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority
( a* y8 Q+ Z" H - exception7 d/ \4 u' O8 V7 s5 ~2 ~
- unsigned int MEMFAULTENA : 1; // Memory management fault handler enable
7 Z6 k% F7 k8 ? - unsigned int BUSFAULTENA : 1; // Bus fault handler enable( }7 J8 E( f2 X) D
- unsigned int USGFAULTENA : 1; // Usage fault handler enable5 n2 u* r1 @& Q* F# C3 M* A
- } bits;
& g7 h+ G+ K# p1 N$ Z* R \$ z - } syshndctrl; // System Handler Control and State Register (0xE000ED24)5 ~! Z- o n# d0 G% b) ?
- 7 m; r& N# t, o% H
- /* 省略未写 */5 M5 `) P5 x' ]- B3 G9 E1 ^/ K
% I: Q7 N! F* {% M' _) S- volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)9 p# M9 {% c- C/ G% w7 p: {9 A
- } HardFaultRegs;* m; [$ \4 A" {" L. W v
- #endif
复制代码 $ Q. C" z# P1 y5 V/ x; l
11.6 IAR自带的硬件异常分析
. Q7 `0 M! G- v0 f2 z3 i3 Q; f还以本章配套的例子为例,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,IAR还会弹出一个硬件异常错误分析,刚进来的时候也许是个空白& R% C k, ~- s/ R* M) t% [# ?
8 h; M" S! I4 [9 l
3 [% b6 ]/ W% W# [( ^/ O# b
8 @) y# n8 p% d0 S单步调试刷新下就出来了:5 O) g2 f1 z8 a) d9 L
1 L; X7 @6 n* b( I
3 R% |" [( z: R" Z* R
3 X% j) R& f* {8 T
指出了问题的原因是操作的数据地址有问题。+ C E8 w6 c5 P
2 ]4 d& i* g, m11.7 实验例程2 @4 h: }+ I( J
专门为本章节配套了一个例子:V7-009_移植SEGGER的硬件异常分析机制。大家可以按照本章教程提供的方法进行测试。0 |+ ~# `2 j: C. f
$ t( I, X- j( e1 r
11.8 总结2 ]0 y; z" M- T
除了SEGGER的硬件异常分析方案,建议也测试下MDK和IAR的,以后遇到硬件异常问题,解决起来可以得心应手。4 a# f7 [# n- U
: W3 a- V, z: V0 ?- I4 v, A, S+ q! E' C4 D/ `! w E
|