一. STM32 调试特性简介
' m$ s# k6 D. |5 f: T bSTM32 的 CPU 采用的是 Cortex-M 系列的内核(M3 或 M4),CM3 和 CM4 的调试技能较之普通的单片机有了质的飞跃。一般情况下,CM3 的调试功能可被分为两类,
/ c/ z- ^/ `1 o7 D6 D. ]+ p
; n1 v0 `: W0 y! `1. 侵入式调试
+ q& O$ Z) T1 B" o 停机以及单步执行程序+ p1 d# A0 A8 k l2 e; f
硬件断点
+ I" I4 j) B* x7 j 断点指令(BKPT), z/ v) q* m' a; Y2 _, A
数据观察点,作用于单一地址、一个范围的地址,以及数据大的值+ T4 D! A6 t; C: I1 N% B
访问寄存器的值
4 k( X: H/ b% O+ j: \$ k; S+ T0 T 调试监视器异常
/ a8 z' Y0 w4 _5 Q ?" A7 v2. 非侵入式调试
# O9 s2 r" S3 E { 在内核运行的时候访问存储器
7 x5 I7 @! p+ I1 O' k2 P 指令跟踪,需要通过可选的嵌入式跟踪宏单元(ETM)6 h0 D! W& [& L& F$ x0 @% N
通常情况下,我们采用的是侵入式调试,这种调试会打破程序的全速运行。非侵入式调试则可以再保证在程序全速运行的情况下,了解程序运行的情况,当调试大型软件和多任务系统时,非侵入式调试有着不可比拟的作用。
. m& l) ~8 P; T. S8 R
: ]7 f3 K/ c3 n: n二. IAR 下使用 J-Trace 进行指令跟踪. p2 L$ I% F5 f3 S
1. 使能 ETM 接口
6 y6 ~5 n' A* ?/ e$ }( C8 S# ]/ ` 两种方法,文件见附件:
$ e4 I) v, q8 O; V. W 通用方法在初始化代码中添加
3 Y5 y! z: }7 u7 {7 a2 D/ l- static void JTrace_Init(void)0 U. D+ `2 y3 |' s" x3 |
- {3 j1 G/ f3 \- P9 G0 [
- #define ETM_LockAccess (*(uint32_t *)0xE0041FB0)" N( S* e% [: |& `
- #define ETM_Control (*(uint32_t *)0xE0041000)& O! I) O4 w0 Q8 x& [4 z0 ?7 o1 a
- #define ETM_Status (*(uint32_t *)0xE0041010)1 |8 F$ b7 B' J/ I3 h2 ~. q" ~
- #define ETM_TriggerEvent (*(uint32_t *)0xE0041008)( _! f1 C" C6 Z
- #define ETM_TraceEnControl (*(uint32_t *)0xE004101C)
( B9 y2 n0 w' _7 O, d& G - #define ETM_TraceEnEvent (*(uint32_t *)0xE0041020)# g* z% k6 y1 s/ y) S: R; w
- #define ETM_TraceStartStop (*(uint32_t *)0xE0041024)
$ w$ h! Q7 y9 c! D9 l3 ?7 W7 K - #define ETM_TraceID (*(uint32_t *)0xE0041200)
* A4 p6 }0 L' v0 _( A - #define DBGMCU_CR (*(uint32_t *)0xE0042004)( {0 }3 c$ i$ ~" q5 K/ |
- #define DEMCR (*(uint32_t *)0xE000EDFC)
+ Y2 g8 u6 y$ b9 W# X' [- T - DBGMCU_CR |= 0xE0;1 s9 O6 t% [1 y
- GPIO_InitTypeDef GPIO_InitStructure;
6 ]# ~7 P) ~$ I* o - // Enable GPIOE clocks) T6 b9 o/ v l4 m/ r) J7 D1 {
- __GPIOE_CLK_ENABLE();
6 ?) M: o; v8 o9 M+ ^7 V - // Set PE.2~6 as Output push-pull, used as Trace9 P/ m: D* G! i& c2 A r5 R2 g
- GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 |% I: }2 i+ w# ~- h
- GPIO_PIN_5 | GPIO_PIN_6;
& f- R4 k+ z5 ^) D - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;& K& q5 V; \+ B7 O2 C/ D: Q% n
- GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
* O' i+ L3 {9 u - //GPIO_InitStructure.Pull = GPIO_NOPULL;
7 V( b! Z9 h' w" z/ t" } - GPIO_InitStructure.Alternate = GPIO_AF0_TRACE;7 W. W! b0 M7 x/ Y/ I
- HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
' P8 j9 r0 F0 j" b1 x - // unlock the write access to the ETM registers( R) h' }- z' u' Q4 `9 q, U
- ETM_LockAccess = 0xC5ACCE55;
: d) Y+ j6 n1 h5 n - ETM_Control = 0x00001D1E;. M# m) }; @. n8 y/ w
- ETM_TriggerEvent = 0x0000406F;3 k# a' t3 j5 u1 V, D; ]
- ETM_TraceEnEvent = 0x0000006F;
; k) E7 j2 U& z L - ETM_TraceStartStop = 0x01;
1 @# i5 `2 v: V7 |! W) `' M& [ - ETM_Control = 0x0000191E;, d3 f1 Q+ s ` Z7 F' c9 |) e1 V
- // config DEMCR's TRCENA bit(bit 24)& {4 i* @; {( U/ n' S& P t t8 Z
- DEMCR |= 1L << 24;
+ S* o+ M! I/ V# k9 G! ^0 o - }
复制代码 利用 IAR 的 Macro (Project -> Debugger -> User Marcos)
/ T! u( @; W! y% s- execUserReset()
% q0 N; y* D2 K5 |4 B - {
i& j! ?% V* d2 N* }" R# {' O - __message("-I- execUserReset");9 i& p4 w, N$ e% t
- __writeMemory32(0x00000010, 0x40023830, "Memory"); //Enable GPIOE clock0 i0 J: J/ p. `
- __writeMemory32(0x00002AA0, 0x40021000, "Memory"); //Configure PE2, PE3, PE4, PE5, and
/ L6 N2 g* T! y' m5 s0 J - PE6 as AF
# Z/ a; n+ L3 \6 Z2 A% b' f2 \ - __writeMemory32(0x00001550, 0x4002100C, "Memory"); // Configure GPIOE as pull-up
1 c* O0 g; T) Y3 p - __writeMemory32(0x00001550, 0x40021008, "Memory"); // Configure GPIOE speed as 25Mhz4 F/ e2 U$ L+ I9 m5 ` ^+ P
- __writeMemory32(0x00000000, 0x40021020, "Memory"); //Enable AF0 in GPIOE_AFRL register9 e* `2 a; `1 ?; }
- }
复制代码 4 y/ ~# ]. W( k5 Q0 X
3 x6 Y) W# k7 X# Y2. ETM Trace Settings
1 z0 a8 K) N) T5 @" g. e8 Z; ?+ K) u: a/ V4 R
( r y$ z4 {" K6 f5 e8 C3 g
5 I- B5 Q( V7 @% y& ~* J; B0 E5 x$ m/ D: p7 K# G o
Trace port width: CM3 和 CM4 内核支持 1 位,2 位,4 位数据输出,具体的数据宽度需考虑实际硬件设计情况。
. \3 k# Y4 d9 G Trace buffer size:J-Trace 内部的缓冲区大小为 2Mbytes,FIFO 结构。
5 l" W" L9 ~( c, C- D8 Q3 h2 S Stall processor on FIFO full: FIFO 在 CM3 内核中,建议开启。
' `. n3 ~( l' `5 _: s3. ETM Trace 窗口0 w! |4 d c6 z3 o' a# T( I& z
3 @$ i! h' ~. y& u# f! q
! w6 g: I1 x0 O7 R
# S! G7 l5 Z* w4 K' P
/ M" C" C5 F8 U! _ n
工具栏从左到右依次是:9 A& R, m( r- N2 S. H
使能 ETM Trace6 q4 o5 ]( ] O% J# Z
清空 PC 端 Trace 记录4 ~( K+ P, f- e d5 w3 I
在 Trace 中嵌入源码,上图中的灰色代码 int main(void)4 r) c3 \, c! E( A/ B* M) v
浏览 Trace 所对应的源码
9 |6 c( G: m* @3 P# C 搜索
d2 x6 u- c& b7 S- Q, E 存储
8 G+ I$ P D/ p0 o ETM Trace Setting
0 ~8 K' ]. _6 R1 y 使能 ETM Trace 后,缺省情况下 Trace 已被触发,此时就可以对程序进行指令跟踪了。
+ a; h5 X- S' S5 q- h, X9 F4. 利用 Trace 断点跟踪关心的代码
8 Z' T' b- O' q/ _6 \! [1 e 非侵入式调试时,往往会对某一段代码的运行状况更敢兴趣。这里所采用的例子是 Cube 中 FreeRTOS_SemaphoreFromISR,所关心的就是程序中运行程序后,通过外部中断释放信号量,从而唤醒被阻塞的任务(涉及任务系统调度)。利用 Trace 功能可以捕获RTOS 是如何进行任务调度的。 d- E% Z- W* Y! X. r3 Q8 J
通过在相关的设置 Trace Star/Stop 的断点,就可以实现此项功能,需要注意的是断点条件可以设置,数据断点:Read/Write1 F0 O' R4 m' j% s- r1 {0 [
指令访问断点:Fetch
+ P7 u- \0 R" Z4 ^. i0 P 只有在正确的设置触发条件后才可以实现调试目的。9 Y6 z; O [9 H
+ ~: V2 y( {- v* Y( _
" W) j$ \ u5 \4 W7 }. d
# V! f" u# Z( ~& F/ T& q$ I
IAR 下的 Trace 还具有其他功能,比如函数运行统计等,这里就不一一列举了。
7 e5 Y1 k; R$ p3 c! J8 m! B, d$ Y j2 M- g+ e7 j
|