11.1 初学者重要提示# X5 p8 @) C* d
MDK本身也是支持硬件异常分析的,就是不够直观
3 v2 v: k" ^4 i3 }, N5 }0 W
8 M$ v! L6 A- A$ W8 ?# a. ~* o5 B IAR8带的硬件异常分析比较好用,在本章11.6小节有说明。
9 S: K: C6 _! X) ?* G8 y" r' `11.2 移植方法
6 i- J* z+ B0 u) O0 X' G9 \直接移植SEGGER的硬件异常代码会有错误警告,这里针对IAR和MDK版本做了些简单修改,方便大家移植到自己的工程里面。
' D o$ o4 V% Z% P* R- F) h: `1 i3 x, K
6 z5 J4 W; J' J( S( J4 k: E: Z% Y7 B MDK版本移植
+ F$ Y0 r; v9 m* J( `源文件位于本章配套例子的\User\segger\HardFaultHandlerMDK文件夹,添加如下两个文件到工程里面即可。( w" S7 G. O% s3 d
( ?! Y$ E: Q1 x" b7 v: h
# b! p D% d0 ]. P+ B! A! X1 q/ B" J
IAR版本移植
, i& ]& f1 u; A3 T. R- x源文件位于本章配套例子的\User\segger\HardFaultHandlerIAR文件夹,添加如下两个文件到工程里面即可。
0 O4 c, L7 B4 s# ?' v
* N) X" s* C: M+ d: {6 \2 S6 z W* k" ]
. [" D( P" H5 P; g: r
在文件SEGGER_HardFaultHandler.c里面都添加了串口打印功能,方便不用编译器的调试功能时,通过串口打印提示是否进入硬件异常。' E& Q% k* ?7 U( d9 n, o
1 D: M5 G8 ~2 V' A; v8 @. X& y9 M
- #define ERR_INFO "\r\nEnter HardFault_Handler, System Halt.\r\n"5 J- a b! J: _( }0 L5 K
! L2 B1 f0 y ?5 c5 ?% u, s) s- #if 1, M. B5 k, K+ u# w
- {
/ \* |; `: A' R& O( N - const char *pError = ERR_INFO;
+ U+ x$ S! g+ k/ b - uint8_t i;
3 e% ]+ J. p8 w% ]8 p8 l4 N
# `- ~+ Z# C) p$ g- for (i = 0; i < strlen(ERR_INFO); i++)0 ?8 m. F. X5 ?+ i3 E
- {
/ p+ |! O. Y6 i; H) N$ [ - USART1->TDR = pError<i>;
+ {, G7 I8 x. h; ^; l6 o& L$ _ - /* 等待发送结束 */' t- A0 m$ r4 }) L( |0 i a* u
- </i> while((USART1->ISR & USART_ISR_TC) == 0);
# X+ O, o- ^/ V7 J& d# |3 l( J - } * K( H+ f3 M/ x3 s
- }
6 Y2 P+ h( ?* y, N t/ _+ p - #endif
复制代码 4 l* @$ R q; C+ j# C
11.3 MDK锁定硬件异常位置方法5 }2 Z# a7 j2 @; z l
以本章配套的例子为大家做个说明。. }1 Y& t, b, D6 b4 Z3 _
, Q! o9 B3 y9 k- q# e1 S1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:
+ {) t- F8 M. z5 b9 m& x3 W' S; e
6 X/ q3 |1 {2 W7 d$ [
; X" j8 R# i3 d" G/ ]: O4 x% J& G2、在Watch1窗口添加变量_Continue( t! o' j" |$ y/ Z# u
* P; f( I# n7 ^* N
1 n. L/ a/ f9 S! v
" H* f, U7 A' P9 l3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:" U7 L- T$ h4 d
3 G% r7 \6 h& g6 g
7 `9 s- T5 i2 f# t% m6 _0 w9 v4 o, y$ [2 T9 P' A& [+ \: M
11.4 IAR锁定硬件异常位置方法
. x5 p" ~4 v S, `5 V以本章配套的例子为大家做个说明。2 r0 @1 s+ q$ C5 g$ v, V7 ?' g
$ F: {/ W9 g& ]
1、测试方法比较简单,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,程序就会自动定位到如下位置:
' F& w- d) l0 C- {: A8 j, A7 n1 F5 [# b$ T; x
& ^) @. ~( }8 ?8 c9 P1 o9 f# B6 K: j/ j, I' G8 |
2、在Watch1窗口添加变量_Continue
/ N3 n! I3 C5 p- a( l. }9 e7 r9 c2 }
, z* G3 C- |9 v( x# }& `1 h
+ s7 q2 c! ?5 L3、修改为任何非0数值,就可以继续单步调试。这个代码后面还有一个第1步中的while循环,也可以继续采用第2步的方法修改。退出硬件异常后就是大家进入硬件异常前下一条要执行的指令(可能还是这个函数本身,因为一个函数由多个指令完成)。定位到出问题的位置:: B, |' C5 h" `% O
1 ]9 V& i, x7 @8 Y# U' f! H, S; q# J$ I+ e" a& U
1 I& y4 k7 K4 T11.5 硬件异常原因分析
; U, q G8 @- l d: }% ~SEGGER提供的这个机制查找出问题的位置比较方便,具体原因需要继续在调试界面里面添加HardFaultRegs结构变量,这个结构体变量添加了所有大家想看的东西。下面是MDK调试状态查看部分结构体数值:
2 p. N% @# h3 N B
. C) W, Z! |: H _+ t/ c
7 Y0 v1 Y% B& F& S6 c0 W7 _6 z5 O- W7 p) T9 B0 O( c
具体上面的变量代表什么含义呢,代码里面有注释,查阅起来没有IAR自带的硬件异常提示方便(注意,下面的代码用到了位域)。
/ O# z) [1 f0 ?+ ]4 p1 E: T
, G% C: `3 O6 S# D/ b4 C- #if DEBUG
, |( O! i0 Q' o* g3 Y& ? - static volatile unsigned int _Continue; // Set this variable to 1 to run further9 Z* {' m, J# I0 j: _6 X
- # ~4 y# @/ U; Q0 A( o4 S9 I7 z
- static struct {) b& V6 |- z" n7 H' |3 @' m+ h
- struct {
9 Q: M7 x( Z% D' | - volatile unsigned int r0; // Register R0 q. x( a: E, W
- volatile unsigned int r1; // Register R1
6 P$ a* D" z# ^ - volatile unsigned int r2; // Register R28 O" z, N. t' d7 o' Y& f) z, A
- volatile unsigned int r3; // Register R3
. K2 U6 Q0 f5 P3 q3 C - volatile unsigned int r12; // Register R12
* @$ C! o/ {8 v - volatile unsigned int lr; // Link register1 f( ~6 o+ | z) s2 ]& {
- volatile unsigned int pc; // Program counter- p6 b, C- I; d# k# j* u0 l
- union {
\/ m8 K( E! z - volatile unsigned int byte;
; S% `) ^, t! p5 G0 T# D# ` - struct {% j. B6 d) ]- c+ L
- unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)
" i0 Q4 C: |5 r' A - unsigned int EPSR : 19; // Execution Program Status register (EPSR), L3 j' A& b. f& S, l
- unsigned int APSR : 5; // Application Program Status register (APSR)$ p* Y8 @4 A Z! o
- } bits;
5 C b' q3 e8 j L+ h3 K$ ?3 E - } psr; // Program status register.
' E& y* x2 |6 B4 ]5 O - } SavedRegs;# u7 }# ~1 _$ u/ r4 x, n+ E, e- n
, A- F8 w: @: W3 {- union {
! l T9 o: D: b+ {( n( L - volatile unsigned int byte;
; @% U1 f' i0 K3 ^) I2 ]( B3 n. N" o - struct {2 T$ V% ?' j0 E4 z2 m. @
- unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active* U( l+ A, e, _! A" b* q z+ C4 ?
- unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active. W$ g6 s/ F# A0 J1 X; ?4 v
- unsigned int UnusedBits1 : 1;
9 P0 M2 t( r! G4 d! D0 F - unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active
* W! t/ H/ B4 p! m0 n3 S - unsigned int UnusedBits2 : 3;. j7 i8 r, F% ]8 b) r) t9 ]
- unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active/ f5 p3 @' D4 e# E9 K4 {
- unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active
; @" K: K% _+ K9 D8 W/ ~ - unsigned int UnusedBits3 : 1;$ T# ?: B0 B( I$ E
- unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active% {$ `4 O. z3 t8 ^ _+ l
- unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
2 J2 ~2 [. P$ ]2 u - unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a( S" B8 b4 B! v" C% Y
- higher-priority exception& l' @+ I1 Z5 D1 W3 ]( S
- unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started
0 z" _9 @ B' z2 p+ _ - but was replaced by a higher-priority exception# _# o9 O8 G; [- A' i* `2 X6 S; _% O
- unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced. `% U; V2 R* u; N+ j: g
- by a higher-priority exception. C8 t! U, s( {+ I# P/ ]
- unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority( R. p- m8 ~4 j5 Z& l% D
- exception
! ]+ V) y* u9 i _! @& v8 I4 U! z6 i - unsigned int MEMFAULTENA : 1; // Memory management fault handler enable o+ A0 q! P t( G
- unsigned int BUSFAULTENA : 1; // Bus fault handler enable
/ i. q7 n/ V+ D4 } - unsigned int USGFAULTENA : 1; // Usage fault handler enable
8 e5 q3 I, J( b9 k0 X2 ^ J( p - } bits;: I1 O2 w3 j' B
- } syshndctrl; // System Handler Control and State Register (0xE000ED24)
A6 O2 [3 v# V - * l/ g$ Y4 r! L! c1 e
- /* 省略未写 */+ H4 N% Z1 t7 v5 g: ~3 y
- 9 x3 f$ u3 U2 [
- volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional): y6 h$ [( b+ M7 J+ q9 l$ L* Z: F
- } HardFaultRegs;- e* n' z# O4 x
- #endif
复制代码 ! I. B) \5 l. [6 Q
11.6 IAR自带的硬件异常分析
# H! V* B7 l: \5 T0 |- A# i9 y* b还以本章配套的例子为例,进入调试状态,全速运行,然后按下K1按键,就会进入硬件异常中断,此时停止调试,IAR还会弹出一个硬件异常错误分析,刚进来的时候也许是个空白: a5 A" z( ~ S. J1 @6 {. y0 Y
5 @! Y+ @$ o% P3 m# W% h
8 u: B( ]8 O# m- z6 A. l: Z3 w# I$ Y6 [
单步调试刷新下就出来了:- m3 O$ V. k# B2 W
# k# S4 C( ^1 @8 G
5 L) i+ A& F. s* T( Z- S# L2 M a6 K7 q" I5 @3 S0 Y
指出了问题的原因是操作的数据地址有问题。
. C5 T X# H( x
, P; U+ F, A& i- @) F11.7 实验例程 h4 p6 Z: _9 z+ {: Q' \0 H# m7 j
专门为本章节配套了一个例子:V7-009_移植SEGGER的硬件异常分析机制。大家可以按照本章教程提供的方法进行测试。. K# o# P' N# G6 _
2 ^: z6 y8 m h4 q+ m11.8 总结$ A: P p; U9 e! R& @7 C
除了SEGGER的硬件异常分析方案,建议也测试下MDK和IAR的,以后遇到硬件异常问题,解决起来可以得心应手。
2 a/ b0 L- j$ q
+ E4 ^5 ^0 F; l& v7 E$ O) {* J5 ^1 e' E/ j
|