11.1 初学者重要提示
% ~& [" {* c, A, g# U% a MDK本身也是支持硬件异常分析的,就是不够直观
! U9 u7 Z) a- |( q4 v" I* T1 o- J! a& `4 O+ ]
IAR8带的硬件异常分析比较好用,在本章11.6小节有说明。
! e8 L9 t5 X! f11.2 移植方法
1 s! W, N4 b# m. |直接移植SEGGER的硬件异常代码会有错误警告,这里针对IAR和MDK版本做了些简单修改,方便大家移植到自己的工程里面。) h' y# c1 y1 w* q7 b2 s( H
( r* R7 V- L- x. o/ t MDK版本移植6 Q, ~. E! [9 T2 h* y; \" n, o
源文件位于本章配套例子的\User\segger\HardFaultHandlerMDK文件夹,添加如下两个文件到工程里面即可。
M9 d5 l3 v/ C3 Y
$ J: d& M6 j# J- d
# m& C& s- z, ^4 j$ m; Y
/ |8 k5 {+ E4 @) T* |& H IAR版本移植
: b+ U, m! i5 E: y源文件位于本章配套例子的\User\segger\HardFaultHandlerIAR文件夹,添加如下两个文件到工程里面即可。
7 D1 ~% y2 n) [& j! U6 N& ~5 ?5 R! F
5 }+ @) k* [* Y# r
+ K) u3 [; P: e* o
6 s' L& T; N% j- f* h# J6 j在文件SEGGER_HardFaultHandler.c里面都添加了串口打印功能,方便不用编译器的调试功能时,通过串口打印提示是否进入硬件异常。9 B8 `: L2 y7 V' }0 r
, d. {: p; u& b2 h- #define ERR_INFO "\r\nEnter HardFault_Handler, System Halt.\r\n"6 N( C8 |5 [% B4 @: W- V+ i0 b
8 E0 ^" K4 u5 Q% ]- _- #if 1
. A# o/ K5 P$ o0 _8 l; G - {
& b! W9 {0 ~1 l% ~ - const char *pError = ERR_INFO;
8 I! L% n+ ]: M+ ^2 j - uint8_t i;- D, K+ [! U r
- % Q, y! u5 l4 L* c
- for (i = 0; i < strlen(ERR_INFO); i++)9 W7 l: w$ a' p' i
- {6 D5 N% I& e* x) ]( @3 G
- USART1->TDR = pError<i>;
( H. U9 Q2 S6 N# W% [8 o1 e - /* 等待发送结束 */$ T2 u8 e1 \% T% d
- </i> while((USART1->ISR & USART_ISR_TC) == 0);
+ c$ g& [' F1 b6 `4 U - } # ]; x. c* w6 N B- i
- }
0 B$ G9 } _& i& f) h - #endif
复制代码 9 E, r) [6 F V( [
11.3 MDK锁定硬件异常位置方法; N8 N% x+ I8 w" L* c: b$ @! |" _
以本章配套的例子为大家做个说明。( M7 b3 H w& x$ |7 k% j% @( D& K) d
% B7 `2 Y! R/ w9 V" R6 Y- a/ f1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:& u+ }9 S& C/ K# m8 |
6 y. [$ @: n) b: \9 K
& F1 X/ L/ h- @7 e9 w E O4 `6 V* \1 }6 R a6 ]9 j! r
2、在Watch1窗口添加变量_Continue
' `, T- S' ^, h. X) H+ T& d2 y- D' t0 U/ R: ^
3 t2 ~* \0 n2 J1 L! t8 ^
+ }( I2 Y5 G+ f0 x- e- ~9 C0 B6 @3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:/ q. w- \ B- G- E- C/ `, G
4 \1 |( F5 U" K/ ~6 e# m
[, n/ U) A, v; c
# s1 H/ P: L8 r$ j11.4 IAR锁定硬件异常位置方法
6 q+ ^# \/ u5 J7 @. U以本章配套的例子为大家做个说明。
* T' w. h- V0 C1 U* a3 c1 i8 e% g
1 s- p' a9 E! F5 T: Q2 B$ n. J1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:
6 ?/ ]" Q( k1 p c' l$ v) W* h: z' }. t: ^: q/ I1 m
) W* C" \" k1 P4 {
j& e8 o+ b+ `- V4 j( ~/ B2、在Watch1窗口添加变量_Continue7 z: ]# L) q0 E* m8 _2 ^
0 I$ b6 B! u, J( r6 s& h6 p; v2 k& K
* A% |/ s3 t+ E5 X# H. v# E( {9 B, T- _
3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:% d: R2 S3 {: U/ _2 A2 u
0 V$ C0 M4 D* n! Y! U9 S) @: ]& [4 x
/ ` U. U9 u2 r# E# ~, S
11.5 硬件异常原因分析
+ a. M! \ |7 [7 A; w( ]/ q1 KSEGGER提供的这个机制查找出问题的位置比较方便,具体原因需要继续在调试界面里面添加HardFaultRegs结构变量,这个结构体变量添加了所有大家想看的东西。下面是MDK调试状态查看部分结构体数值:
7 s. @9 A' p: m0 _# f! ? C
# h$ m# B4 a- C# t- a" G1 l; ^! D9 `8 v7 b4 B/ O5 }) v
" Y% h/ k5 c: L& J3 C) W' V
具体上面的变量代表什么含义呢,代码里面有注释,查阅起来没有IAR自带的硬件异常提示方便(注意,下面的代码用到了位域)。
) n+ f+ B- i& d! a8 F0 Y% b" m5 l) n3 A3 }% Z: j
- #if DEBUG- A* W3 j. E6 I: r) k; h, B* }- J
- static volatile unsigned int _Continue; // Set this variable to 1 to run further
, E! G6 X* g& i" E0 p
8 f" F4 v; \% O7 W- static struct {
* O' A. u9 Q5 y8 X4 Q - struct {
?$ O: W6 o# N4 } - volatile unsigned int r0; // Register R0
$ _" M: w% T3 H5 f" q F; o+ L) | - volatile unsigned int r1; // Register R1
& a o. p/ G8 D - volatile unsigned int r2; // Register R2+ \" z; G, w$ t, H4 t- M8 e- K
- volatile unsigned int r3; // Register R3 _& V% t- _' s9 D: I+ I9 o/ u
- volatile unsigned int r12; // Register R12# `, o L0 Q4 T i8 e. h; K
- volatile unsigned int lr; // Link register# W. B; H6 H+ a- l' H: E$ u& K
- volatile unsigned int pc; // Program counter
& ]) l } j% \1 k j6 h - union {
$ n! @: S$ T2 K5 \; O4 b - volatile unsigned int byte;
2 H! @, U, N4 G& D5 q - struct {
$ C' e0 Y6 [: V4 Y: n - unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)
6 ? S9 G" |5 a, P - unsigned int EPSR : 19; // Execution Program Status register (EPSR)( p T1 |+ S8 ` u; Z
- unsigned int APSR : 5; // Application Program Status register (APSR)1 l- ^. }2 S& V7 a$ B
- } bits;
& F R, i8 A& K- q8 g2 v; ? - } psr; // Program status register./ D: ] ?8 f$ q
- } SavedRegs;
, z, ]" X o0 B/ ?: l4 S3 X - : o4 J6 {% w2 M* O) ?3 I
- union {
! p4 d" [; Q4 `4 i! I - volatile unsigned int byte; V$ r" [/ m F3 [5 r! F
- struct {
- z. Z% h. h" P% M! S4 R% u - unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active U6 z: ~- n0 w* ?' a8 Z
- unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active
: i( ^& n# r3 T. K+ \4 t - unsigned int UnusedBits1 : 1;+ w1 e% Q+ N! e8 G. j$ S _- r
- unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active3 j! I4 c2 A8 Y
- unsigned int UnusedBits2 : 3;4 @8 G; q! w$ r/ h. Y5 a7 B* Y
- unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active
! I, `/ C% O1 b - unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active
1 u0 y# v4 M7 B8 V - unsigned int UnusedBits3 : 1;
8 @. ~. X+ M+ p& ?& [: | - unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active9 d% o% E9 ~% K% T. Y1 |
- unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
/ R4 _: z2 S* [) h - unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a! Z" u3 k+ [7 C' W9 U
- higher-priority exception
3 y5 R$ k, `/ E: V& V9 T3 k - unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started( n$ Y9 M, |$ }' l o* q. _
- but was replaced by a higher-priority exception! f1 L) E5 k; \7 |$ v
- unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced4 p+ R X) T N9 g1 L3 T
- by a higher-priority exception
8 G: S. W* K! _' M* z" Y - unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority4 {4 K b5 r+ j+ L+ b2 J
- exception
% J& z7 P w% V B6 e; I7 I - unsigned int MEMFAULTENA : 1; // Memory management fault handler enable$ u/ X, @9 R1 ~4 h) D% Y7 d9 d
- unsigned int BUSFAULTENA : 1; // Bus fault handler enable( x4 ^2 b7 U3 x9 C5 @6 G
- unsigned int USGFAULTENA : 1; // Usage fault handler enable, R f& e5 r, j- G. d2 [- I, C6 a, T
- } bits;
# J! s+ H* J }4 p r: Z9 g - } syshndctrl; // System Handler Control and State Register (0xE000ED24)5 @1 A3 s9 ]7 w# V3 H' F) _
4 k1 f) Q9 \: r$ T- /* 省略未写 */
; @& ~$ @( L# w
/ Z6 s9 M" q6 @: H* d( J$ V- volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
+ W) h( e, t8 a) j$ I - } HardFaultRegs;
9 J5 x4 I7 L7 a1 t4 t - #endif
复制代码 5 [7 o/ J1 p0 ~5 t; z" P
11.6 IAR自带的硬件异常分析
& n, w# S9 k9 E A8 J还以本章配套的例子为例,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,IAR还会弹出一个硬件异常错误分析,刚进来的时候也许是个空白- Z! n7 F! }, F* m- i" N
q( o5 ^. s, h, a7 n$ |. [
& H% k# \/ I( c( H) h4 g
: L# n6 x% V, h& o) D% r, L+ z
单步调试刷新下就出来了:
0 @: J4 C( p( p: p1 o% j* ?6 T/ V
( N5 V/ j9 I3 q) v
5 }$ \" R2 {. s& |" q! O指出了问题的原因是操作的数据地址有问题。' F8 M1 _! I1 [5 d; v
0 f Q' |% m6 U Y
11.7 实验例程; L+ w( D0 l! d
专门为本章节配套了一个例子:V7-009_移植SEGGER的硬件异常分析机制。大家可以按照本章教程提供的方法进行测试。# M- S7 q) N1 A. b7 J) \
: a+ {, r* }5 [
11.8 总结, w2 e3 N1 q* U% J( c
除了SEGGER的硬件异常分析方案,建议也测试下MDK和IAR的,以后遇到硬件异常问题,解决起来可以得心应手。* P2 X! g/ f! L; I$ ~
" w2 w1 F# ]" c9 f
: b( C2 ^% J& G4 w% y, M
|