11.1 初学者重要提示1 p, G5 G9 V3 S( U8 _; c& l
MDK本身也是支持硬件异常分析的,就是不够直观* B$ f$ S0 N& r3 g* P! Z* z A7 U: p
; x/ O! l: H+ h6 {
IAR8带的硬件异常分析比较好用,在本章11.6小节有说明。
) F% T. g$ [# m. T# b$ \11.2 移植方法5 `. z3 A7 A. ?/ R4 |6 q# G$ R& z1 k: m
直接移植SEGGER的硬件异常代码会有错误警告,这里针对IAR和MDK版本做了些简单修改,方便大家移植到自己的工程里面。
1 O i3 t# j; a# P8 X: @
% _" H! h& j2 I& H7 l- b) C MDK版本移植
& V: p1 X* ~3 j, p3 {3 X% m0 X y源文件位于本章配套例子的\User\segger\HardFaultHandlerMDK文件夹,添加如下两个文件到工程里面即可。
& H _0 [+ Y3 a7 o) i4 I9 K9 @/ x/ \2 X$ R5 i9 [1 k9 X
3 u# B8 f9 i5 |" f
: b1 t ~ B4 }
IAR版本移植1 X$ Y0 j/ y7 Q/ |* m2 t$ ~2 h
源文件位于本章配套例子的\User\segger\HardFaultHandlerIAR文件夹,添加如下两个文件到工程里面即可。
3 y9 m# [$ G/ ]9 h
0 h) `/ y: j( w- T* N- A! u
6 K% E5 |7 b# h! L t8 r7 z: r& W" q: y
在文件SEGGER_HardFaultHandler.c里面都添加了串口打印功能,方便不用编译器的调试功能时,通过串口打印提示是否进入硬件异常。
. S+ y+ L" y- l
# \" w8 }1 j; [3 |7 N; v! l: s. \- #define ERR_INFO "\r\nEnter HardFault_Handler, System Halt.\r\n"5 i; }$ l+ L2 D
* B7 E( }# }7 g+ T: k7 r- #if 1
. Y e5 e# W' M& w, O$ @ - {9 ~/ |/ t, F( B# t# i
- const char *pError = ERR_INFO;' Y' l9 W& `1 \5 d8 S
- uint8_t i;2 O9 R8 W2 _7 Z5 j" m0 w: L+ L
- & W8 z( t' ]- `# {
- for (i = 0; i < strlen(ERR_INFO); i++)
4 U7 a; M# i# C& |' x9 F Q+ p - {
, V$ J8 D% F3 ^2 G - USART1->TDR = pError<i>;
$ ]9 _/ j! Y3 Z, b, B7 f) }. b - /* 等待发送结束 */
, W2 }1 f" y3 H* f: Q4 m - </i> while((USART1->ISR & USART_ISR_TC) == 0); e+ V! t- ?2 B! n( ` u
- }
D* D& L: A3 I4 o) U - }
" @9 `* X. Z9 T) O8 B+ p - #endif
复制代码
' y3 H% A% G d' y/ a J" K7 s11.3 MDK锁定硬件异常位置方法( h2 |3 R8 |9 D2 w
以本章配套的例子为大家做个说明。2 k: H9 A5 w, y6 N2 E7 A9 w2 Y8 b" u0 l
( ?$ g# \6 p: s( |1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:
* Q$ e p2 V9 P4 y+ l. Q, N7 N9 l- b. H W
. ]1 x. K# X4 J
( S m7 _3 `6 N6 w/ w$ x ^4 \: h
2、在Watch1窗口添加变量_Continue$ v5 m- }, n/ B x) @5 m
6 v' u6 ~% Y( Q
1 R" F) s& b+ L) B
( _9 x& s+ M$ Q, ~3 c9 t" e' P/ ^3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:
% ^- u9 I6 N1 ]. E- R; x8 c# t' s* C( M/ } K# R4 l: [ d" `
9 \# u9 P# l7 b
' a# Q9 u/ r" R! {) w. c z11.4 IAR锁定硬件异常位置方法4 n b) D, Z6 S4 F# B1 D! s$ G3 s
以本章配套的例子为大家做个说明。$ y0 F1 p, {4 N$ \% f
0 W4 d7 B4 H0 }, C; m# {1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:' s7 Q" ]+ l. n7 K% }
. s g: t* z4 E" n6 r8 w2 e9 _4 o7 c1 I
/ F( I: \* p2 H |. F. X2 o6 P
8 Z7 f/ B5 G- J3 p/ @4 A$ b- @% A4 e2、在Watch1窗口添加变量_Continue
; o! h& v9 x' r# @: l. c! L) a/ w0 v# L- A
* Z8 v+ f" k* n: @" y$ x
$ J' ^/ s2 M! s7 T# \3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:
F E9 B: H! f- a( x) ?- g3 `
& p1 F% _/ L8 v
v8 u% M& R8 C$ K& H& a
5 h! o) t. |/ B% G, M O& @11.5 硬件异常原因分析
1 r# G `" \2 p. S# bSEGGER提供的这个机制查找出问题的位置比较方便,具体原因需要继续在调试界面里面添加HardFaultRegs结构变量,这个结构体变量添加了所有大家想看的东西。下面是MDK调试状态查看部分结构体数值:
( N/ k, @& x6 Z- n4 j7 N" ?9 K6 U! L0 n" k; {6 H2 Y' e$ ?* `7 \
) j' T; u4 k N& I$ D' X7 k4 Z
# X" _; R, m7 ?% n" y6 P4 u+ _具体上面的变量代表什么含义呢,代码里面有注释,查阅起来没有IAR自带的硬件异常提示方便(注意,下面的代码用到了位域)。& m8 [) }/ S0 {( X9 ]! f
( a* c0 Z) h0 W% m
- #if DEBUG, X! `4 w& s- }0 W% S1 b
- static volatile unsigned int _Continue; // Set this variable to 1 to run further
6 s" n M0 O$ N8 D- A2 T0 }4 m. ] - , B- r9 Z1 _' |$ B
- static struct {+ S" g/ Q5 }. j
- struct {
% j5 K2 i; ]( f - volatile unsigned int r0; // Register R0. n# o1 I0 Q0 ]- j' v3 |
- volatile unsigned int r1; // Register R1
. w! @) S" k8 a6 V% a' g) v: J5 } - volatile unsigned int r2; // Register R2
9 o9 K; h- Y( A0 s6 U% [* H - volatile unsigned int r3; // Register R3
( G ]' W$ d" ]9 e7 y' y: n5 ~5 L - volatile unsigned int r12; // Register R128 r4 }( R3 ^6 F( g+ {; ~) W
- volatile unsigned int lr; // Link register& n1 D' P6 q$ Y+ l
- volatile unsigned int pc; // Program counter
7 T) p3 |( k6 r& N% S/ \ - union {
# g; x1 d3 x$ _! n. { - volatile unsigned int byte;
! i1 ^/ J3 X& D6 ? - struct {" f d5 O; {. Y2 g3 F
- unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)% U/ n/ J! T) _0 l( {1 R$ u' Z* I
- unsigned int EPSR : 19; // Execution Program Status register (EPSR), }( [- H' Q! j0 J
- unsigned int APSR : 5; // Application Program Status register (APSR)- m8 K7 C$ S' }, [, O( ]
- } bits;
: w$ i! D1 E) C( q - } psr; // Program status register.
5 U$ Y& S4 f; l6 ]# N3 o - } SavedRegs;; r1 r: S! A4 q W% f& j
1 w# L. I$ x: ?. `% ?- union { R# W' i1 [; r
- volatile unsigned int byte;( [8 b Q$ O. e1 E% z% `
- struct {
/ \* f4 e6 S: w8 a; V K) ^2 e6 _4 z - unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active/ U% A/ ]( S, M9 D
- unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active
b( b6 g) ? F# h - unsigned int UnusedBits1 : 1;
7 N% }) i8 P9 x& M* F' I6 J4 g - unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active
7 L# d4 I1 C$ R) G/ } - unsigned int UnusedBits2 : 3;# j9 n4 c( d3 ~$ Z4 f% [; D
- unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active
0 a ^. x- w7 U6 t - unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active# {" t! r7 U6 b, ~2 Q% ^( t
- unsigned int UnusedBits3 : 1;. b& X8 @+ h' L
- unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active! }* z' y- v/ X4 l5 Z' s3 h9 H$ [
- unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
( |- k' i8 R' d& X - unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a6 ?+ t4 J. _" D7 e9 G
- higher-priority exception
@& K0 H* u& x, c; t( V3 y8 w - unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started
. d) V" {* t0 p - but was replaced by a higher-priority exception: z5 k0 O0 z9 s% C7 L/ Y6 @5 _7 W
- unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced
/ v/ k" T4 n1 ~" V6 `) h - by a higher-priority exception# m& ~4 {; ?$ e% D; U2 D
- unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority
) v9 b3 ^) S& q+ n5 l) u! [- U - exception, b: W- j" {3 Q, t4 o
- unsigned int MEMFAULTENA : 1; // Memory management fault handler enable
2 C. Z$ e. L" C& q7 U. Q( j - unsigned int BUSFAULTENA : 1; // Bus fault handler enable) Y5 D' A4 V$ R4 v' {" T& p
- unsigned int USGFAULTENA : 1; // Usage fault handler enable
5 b, }4 T. z, @3 u5 l - } bits;. D% |' f9 x; Y
- } syshndctrl; // System Handler Control and State Register (0xE000ED24)8 o5 ]0 J/ Q- c) X+ l/ W) `2 b
1 U- i" g1 x" N6 r" s- /* 省略未写 */
( L% c, S1 r1 M0 @& \
: I3 |+ e% j T- D1 s- volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
" a4 y9 ?$ [5 F2 K& q& L - } HardFaultRegs;& Z* @5 E( p# M8 t4 e/ G5 y
- #endif
复制代码 # I& `% X# ~* I8 c
11.6 IAR自带的硬件异常分析
6 o# ^2 E4 Z/ V) E还以本章配套的例子为例,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,IAR还会弹出一个硬件异常错误分析,刚进来的时候也许是个空白
: v/ T$ o2 G& G$ o1 O5 Z4 U6 {& a; N( p- o+ n- n# A
2 m0 _! `& m; B- n8 u1 s4 J% O
5 M- H2 `( G# z; A# Z# z" f
单步调试刷新下就出来了:" b0 e* m) M) t
- m$ S$ `5 q) H1 ]) L7 O
4 Q7 N0 P Y/ D' C4 q( `# D8 k3 c! ?
+ V1 p+ n9 v! o3 _' L9 }/ b指出了问题的原因是操作的数据地址有问题。2 @ \4 o- I7 h0 Z; b
0 M( b1 ]+ D1 ]. r, a) \4 n1 [
11.7 实验例程4 A) J6 P4 z1 Y, a
专门为本章节配套了一个例子:V7-009_移植SEGGER的硬件异常分析机制。大家可以按照本章教程提供的方法进行测试。
$ k7 {% k$ x: J, x* D" i5 {' R5 D) w: I* ~. x2 ^2 y( M! ?: ^# ^
11.8 总结& C0 \1 `. H6 h8 `) M% w
除了SEGGER的硬件异常分析方案,建议也测试下MDK和IAR的,以后遇到硬件异常问题,解决起来可以得心应手。* w! S5 X" l3 ~/ m, Q( e$ C
8 {1 A# r& e2 b2 t- u
: N5 p# [+ [8 W# `& ^) o
|