HardFault错误信息的挖掘

转载:http://blog.sina.com.cn/s/blog_68300e2f0102wq94.html

当前在我司代码中关于各种硬件异常的处理基本上是在屏幕上打印错误类型,并没有进一步的处理。本文讨论了一些针对Cortext-M3/M4处理器的方法,通过对寄存器数据的解读,在大多数情况下能够定位产生HardFault的位置和原因,方便在不能使用仿真器的时候或错误难以复现的情况下用来定位问题。最后用一个实例,描述了通过本文所述方法打印错误信息,并定位错误位置和原因的过程。

  1. 背景知识简介

  1. 发生中断时处理器的行为

不考虑其他细节,M3内核在发生中断时首先自动将如下8个寄存器压栈。因此在中断处理函数中,发生中断时正常执行时的寄存器数值已经被压入了堆栈中。在中断处理函数开始执行时,除了PC,LR,SP等控制寄存器,从r0-r12等这些通用寄存器的数据是没有变化的。下图描述了M3内核将寄存器压栈的顺序:

HardFault错误信息的挖掘 

  1. 编译器通过栈来实现函数调用

C编译器通过栈来实现函数的调用,即在栈中记录程序执行的轨迹并辅助寄存器进行参数传递。具体如何实现C函数的调用,历史上有很多的规范,这些规范叫做调用惯例。对于ARM处理器来说,有一个官方的规范AAPCS(Procedure Call Standard for the

ARM® Architecture)详细描述了进行函数调用时如何进行参数的传递和调用路径的记录等。如下仅对使用栈记录调用路径的行为进行简单描述:

查看编译器生成的汇编代码可以得知,大多数的函数调用通过BL语句实现,BL语句将当前程序下一条指令的地址存入LR寄存器,并跳转到指定的地方(子函数开始的地方)开始执行。子函数中如果还需要调用孙子函数,就会在函数的入口处将LR的值压栈,以便函数执行结束后能够返回父函数。因此依次找到栈中LR的数值,就能找到调用路径中各个函数的地址。最后根据map文件翻译出各函数的名称,就可以得到函数的调用路径了。

如下是一个简单函数汇编代码的例子,函数OnPowerOff调用了函数FS_Deinit,函数FS_Deinit调用了SPIFFS_unmount。可以看出OnPowerOff函数的入口如将LR压入栈中(此时LR中保存的是函数OnPowerOff的返回地址,也就是调用OnPowerOff的父函数中的某条指令的地址),然后调用了FS_Deinit。同样FS_Deinit也在入口处将LR压入栈中(此时LR中保存的是OnPowerOff函数中POP指令的地址),然后再调用SPIFFS_unmount。返回的过程,依次将栈中保存的返回地址直接出栈到PC寄存器,完成函数的返回。这样,如果某个函数将栈中的返回地址写坏,则函数在返回时就会跳转到某个随机的地方,这就是常说的“程序跑飞了”。

OnPowerOff PROC

;;;202 void OnPowerOff(void)

0001b2 b510 PUSH {r4,lr}

;;;203 {

;;;204 FS_Deinit();

0001b4 f7fffffe BL FS_Deinit

;;;205 }

0001b8 bd10 POP {r4,pc}

;;;206

ENDP

 

FS_Deinit PROC

;;;166 void FS_Deinit(void)

000158 b510 PUSH {r4,lr}

;;;167 {

;;;168 SPIFFS_unmount(&g_fs);

00015a 4810 LDR r0,|L1.412|

00015c f7fffffe BL SPIFFS_unmount

;;;169 return;

;;;170 }

000160 bd10 POP {r4,pc}

;;;171

 

ENDP

  1. 对信息的继续挖掘

    1. 通用寄存器

通用寄存器中可供挖掘的信息并不多,通常情况下r0-r3寄存器保存着函数的前四个参数(其余的参数在栈中保存),需要注意的是:这四个寄存器的数值仅在函数开始执行的时候是可靠的,在函数执行的过程中可能被改变。在函数返回时,寄存器r0和r1用于保存返回值(根据返回数据的大小,决定仅使用r0还是同时使用r0和r1)。同样这两个寄存器仅在子函数刚返回时数值才是可靠的。

  1. 特殊功能寄存器

特殊功能寄存器就是PC、LR和SP了。

SP指向当前的栈顶,在知晓栈的结构时,可以根据SP访问栈中的数据。

在中断处理函数中LR有特殊用法,其中保存了返回被中断地点的方法,而不是通常情况下的返回地址。因此在Hardfault处理函数中寄存器LR和PC的值没有太多参考意义,被处理器自动压栈的LR和PC最有用,PC记录了被中断打断前正在执行的指令地址(也是正在执行的函数地址),LR记录了被中断打断前,正在执行的函数的父函数的地址。根据这两个地址,可以找到引发Hardfault异常的函数和语句,以及其父函数(如果辅以汇编代码继续对栈的内容进行分析,则可以回溯整个调用路径)。

而具体引发Hardfault异常的原因,可以根据下面章节介绍的SCB寄存器来查看。

  1. SCB寄存器

在M3/M4处理器标准外设中,有一个叫做SCB(System Control Block)的部分,其中有6个寄存器记录了发生Hardfault异常的原因。

CMSIS规范中对SCB寄存器的定义:

typedef struct

{

__I uint32_t CPUID;

__IO uint32_t ICSR;

__IO uint32_t VTOR;

__IO uint32_t AIRCR;

__IO uint32_t SCR;

__IO uint32_t CCR;

__IO uint8_t SHP[12];

__IO uint32_t SHCSR;

__IO uint32_t CFSR;

__IO uint32_t HFSR;

__IO uint32_t DFSR;

__IO uint32_t MMFAR;

__IO uint32_t BFAR;

__IO uint32_t AFSR;

__I uint32_t PFR[2];

__I uint32_t DFR;

__I uint32_t ADR;

__I uint32_t MMFR[4];

__I uint32_t ISAR[5];

uint32_t RESERVED0[5];

__IO uint32_t CPACR;

} SCB_Type;

高亮的几个寄存器CFSR、HFSR、MMFAR、BFAR是我们需要关注的,AFSR是平台相关的暂时忽略。上述寄存器中CFSR又可以分为三个寄存器分别是:UFSR,BFSR,MFSR。上述寄存器的内存分布如下图所示:


HardFault错误信息的挖掘
各寄存器数据的描述如下:

HardFault错误信息的挖掘

HardFault错误信息的挖掘

HardFault错误信息的挖掘

HardFault错误信息的挖掘

解读SCB寄存器时应首先根据HFSR寄存器判断产生Hardfault的原因,如果确认是fault上访的情况,则依次检查BFSR、UFSR和HFSR确定具体的错误原因和地址。

附录一:Hardfault Handler示例代码

附加一段Hardfault Handler中断响应函数的示例,该函数中打印所有通用寄存器,特殊功能寄存器和SCB寄存器,并对SCB寄存器的内容进行了基本的解读,配合上述文字,可以更精确的定位发生Hardfault的原因和位置。

#define SCB_CFSR_MMFSR_IACCVIOL (0x01 << 0)

#define SCB_CFSR_MMFSR_DACCVIOL (0x01 << 1)

#define SCB_CFSR_MMFSR_MUNSTKERR (0x01 << 3)

#define SCB_CFSR_MMFSR_MSTKERR (0x01 << 4)

#define SCB_CFSR_MMFSR_MMARVALID (0x01 << 7)

 

#define SCB_CFSR_BFSR_IBUSERR (0x01 << (0 + 8))

#define SCB_CFSR_BFSR_PRECISERR (0x01 << (1 + 8))

#define SCB_CFSR_BFSR_IMPRECISERR (0x01 << (2 + 8))

#define SCB_CFSR_BFSR_UNSTKERR (0x01 << (3 + 8))

#define SCB_CFSR_BFSR_STKERR (0x01 << (4 + 8))

#define SCB_CFSR_BFSR_BFARVALID (0x01 << (7 + 8))

 

#define SCB_CFSR_UFSR_UNDEFINSTR (0x01 << (0 + 16))

#define SCB_CFSR_UFSR_INVSTATE (0x01 << (1 + 16))

#define SCB_CFSR_UFSR_INVPC (0x01 << (2 + 16))

#define SCB_CFSR_UFSR_NOCP (0x01 << (3 + 16))

#define SCB_CFSR_UFSR_UNALIGNED (0x01 << (8 + 16))

#define SCB_CFSR_UFSR_DIVBYZERO (0x01 << (9 + 16))

 

void FAULT_PrintFaultRegs(void)

{

DBG_SendPolling("MMFSR : 0xx[%s%s%s%s%s]\r\n",

SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk,

(0 == (SCB_CFSR_MMFSR_IACCVIOL & SCB->CFSR)) ? "":"IACCVIOL|",

(0 == (SCB_CFSR_MMFSR_DACCVIOL & SCB->CFSR)) ? "":"DACCVIOL|",

(0 == (SCB_CFSR_MMFSR_MUNSTKERR & SCB->CFSR)) ? "":"MUNSTKERR|",

(0 == (SCB_CFSR_MMFSR_MSTKERR & SCB->CFSR)) ? "":"MSTKERR|",

(0 == (SCB_CFSR_MMFSR_MMARVALID & SCB->CFSR)) ? "":"MMARVALID|");

 

DBG_SendPolling("BFSR : 0xx[%s%s%s%s%s%s]\r\n",

SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk,

(0 == (SCB_CFSR_BFSR_IBUSERR & SCB->CFSR)) ? "":"IBUSERR|",

(0 == (SCB_CFSR_BFSR_PRECISERR & SCB->CFSR)) ? "":"PRECISERR|",

(0 == (SCB_CFSR_BFSR_IMPRECISERR & SCB->CFSR))? "":"IMPRECISERR|",

(0 == (SCB_CFSR_BFSR_UNSTKERR & SCB->CFSR)) ? "":"UNSTKERR|",

(0 == (SCB_CFSR_BFSR_STKERR & SCB->CFSR)) ? "":"STKERR|",

(0 == (SCB_CFSR_BFSR_BFARVALID & SCB->CFSR)) ? "":"BFARVALID|");

 

DBG_SendPolling("UFSR : 0xx[%s%s%s%s%s%s]\r\n",

SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk,

(0 == (SCB_CFSR_UFSR_UNDEFINSTR & SCB->CFSR)) ? "":"UNDEFINSTR|",

(0 == (SCB_CFSR_UFSR_INVSTATE & SCB->CFSR)) ? "":"INVSTATE|",

(0 == (SCB_CFSR_UFSR_INVPC & SCB->CFSR)) ? "":"INVPC|",

(0 == (SCB_CFSR_UFSR_NOCP & SCB->CFSR)) ? "":"NOCP|",

(0 == (SCB_CFSR_UFSR_UNALIGNED & SCB->CFSR)) ? "":"UNALIGNED|",

(0 == (SCB_CFSR_UFSR_DIVBYZERO & SCB->CFSR)) ? "":"DIVBYZERO|");

 

DBG_SendPolling("HFSR : 0xx[%s%s%s]\r\n",

SCB->HFSR,

(0 == (SCB_HFSR_DEBUGEVT_Msk & SCB->HFSR)) ? "":"DEBUGEVT|",

(0 == (SCB_HFSR_FORCED_Msk & SCB->HFSR)) ? "":"FORCED|",

(0 == (SCB_HFSR_VECTTBL_Msk & SCB->HFSR)) ? "":"VECTTBL|");

DBG_SendPolling("DFSR : 0xx\r\n", SCB->DFSR);

DBG_SendPolling("MMFAR : 0xx\r\n", SCB->MMFAR);

DBG_SendPolling("BFAR : 0xx\r\n", SCB->BFAR);

 

DBG_SendPolling("\r\n");

 

return;

}

 

void FAULT_PrintGeneralRegs(uint32_t *stackaddr)

{

DBG_SendPolling("\r\nExceptions:\r\n");

 

DBG_SendPolling("r0 : 0xx\r\n", stackaddr[14]);

DBG_SendPolling("r1 : 0xx\r\n", stackaddr[15]);

DBG_SendPolling("r2 : 0xx\r\n", stackaddr[16]);

DBG_SendPolling("r3 : 0xx\r\n", stackaddr[17]);

 

DBG_SendPolling("r4 : 0xx\r\n", stackaddr[6]);

DBG_SendPolling("r5 : 0xx\r\n", stackaddr[7]);

DBG_SendPolling("r6 : 0xx\r\n", stackaddr[8]);

DBG_SendPolling("r7 : 0xx\r\n", stackaddr[9]);

DBG_SendPolling("r8 : 0xx\r\n", stackaddr[10]);

DBG_SendPolling("r9 : 0xx\r\n", stackaddr[11]);

DBG_SendPolling("r10 : 0xx\r\n", stackaddr[12]);

DBG_SendPolling("r11 : 0xx\r\n", stackaddr[13]);

 

DBG_SendPolling("r12 : 0xx\r\n", stackaddr[18]);

DBG_SendPolling("lr : 0xx\r\n", stackaddr[19]);

DBG_SendPolling("pc : 0xx\r\n", stackaddr[20]);

DBG_SendPolling("xpsr: 0xx\r\n", stackaddr[21]);

 

DBG_SendPolling("current xpsr: 0xx\r\n", stackaddr[5]);

DBG_SendPolling("current lr : 0xx\r\n", stackaddr[4]);

DBG_SendPolling("primask : 0xx\r\n", stackaddr[3]);

DBG_SendPolling("basepri : 0xx\r\n", stackaddr[2]);

DBG_SendPolling("faultmask: 0xx\r\n", stackaddr[1]);

DBG_SendPolling("control : 0xx\r\n", stackaddr[0]);

 

DBG_SendPolling("\r\n");

 

return;

}

__ASM void HardFault_Handler(void)

{

IMPORT FAULT_PrintGeneralRegs

IMPORT FAULT_PrintFaultRegs

 

PRESERVE8

 

push {r4,r5,r6,r7,r8,r9,r10,r11}

 

mrs r0, xpsr

push {r0}

mov r0, lr

push {r0}

mrs r0, primask

push {r0}

mrs r0, basepri

push {r0}

mrs r0, faultmask

push {r0}

mrs r0, control

push {r0}

 

mov r0, sp

ldr r1, =FAULT_PrintGeneralRegs

blx r1

ldr r1, =FAULT_PrintFaultRegs

blx r1

 

b .

nop

}

上述代码的输出见附录二所示。

附录二:MAP文件和List文件的解读

Keil在生成程序的时候,可以生成两个辅助文件非常有帮助,他们分别是map文件和list文件。打开他们的方法如下图所示:

 HardFault错误信息的挖掘

 

 这两个文件我仅能大概读懂,下面介绍一下如何根据PC和LR寄存器中的地址数据,通过map文件找到该指令所在的函数,并根据list文件找到出错的代码行号。抛砖引玉,欢迎大牛拍砖:

map文件是连接器生成的二进制文件的信息,其中描述了各符号的交叉引用、函数和数据排布的顺序和大小等各种有用信息。要根据指令地址查找函数主要关注map文件中的Image Symbol Table这一部分内容。

Symbol Name Value Ov Type Size Object(Section)

main 0x00006245 Thumb Code 106 main.o(.text)

OnPowerOff 0x000062af Thumb Code 8 main.o(.text)

FAULT_PrintFaultRegs 0x000062dd Thumb Code 464 fault.o(.text)

FAULT_PrintGeneralRegs 0x000064ad Thumb Code 194 fault.o(.text)

随意取出其中的一部分,对于每行共有5列数据,每行代表一个符号(函数和全局变量)的数据。分别是:“符号名,符号地址,类型,长度,所在的.o文件”。我们主要关注“符号名,符号地址和长度”这部分内容。

根据地址查找函数名,就是检索MAP文件的“Image Symbol Table”部分,查找到函数地址和所查询地址最接近,且函数地址比所查询地址小的函数。并根据函数的长度确认是否在函数的范围内。

举一个真实的例子:

比如说在发生Hardfault错误时,使用上述Hardfault Handler示例代码打印出的错误信息如下:

Exceptions:

r0 : 0x4006a000

r1 : 0x0000000a

r2 : 0x00000002

r3 : 0x00000003

r4 : 0x0001f420

r5 : 0x0001f420

r6 : 0x00000000

r7 : 0x00000000

r8 : 0x00000000

r9 : 0x00000000

r10 : 0x00000000

r11 : 0x00000000

r12 : 0x40048000

lr : 0x00010681

pc : 0x00010642

xpsr: 0x01000000

current xpsr: 0x00000003

current lr : 0xffffffe9

primask : 0x00000000

basepri : 0x00000000

faultmask: 0x00000000

control : 0x00000000

 

MMFSR : 0x00000000[]

BFSR : 0x00000400[IMPRECISERR|]

UFSR : 0x00000000[]

HFSR : 0x40000000[FORCED|]

DFSR : 0x00000008

MMFAR : 0xe000ed34

BFAR : 0xe000ed38

错误类型为“非精确的数据访问违例”。处理器自动压入堆栈的PC寄存器的数据为0x00010642,在map文件中查找字符串“0x000106”(故意抹去末尾数据),找到的两个匹配数据是如下黄色表示的内容,但是这里两个地址都比出现错误的地址0x00010642大,错误并非发生在这两个函数中。

FS_Lock 0x000104ef Thumb Code 24 fs.o(.text)

FS_Unlock 0x00010507 Thumb Code 10 fs.o(.text)

UART7816_Init 0x00010673 Thumb Code 16 uart7816.o(.text)

UART7816_SetBaudRate 0x00010683 Thumb Code 228 uart7816.o(.text)

UART7816_StartClock 0x00010767 Thumb Code 18 uart7816.o(.text)

UART7816_StopClock 0x00010779 Thumb Code 28 uart7816.o(.text)

接着扩大范围继续循环,搜索“0x000105”,结果如下(仅关注Image Symbol Table部分):

.text 0x00010530 Section 0 uart7816.o(.text)

_timerInit 0x00010531 Thumb Code 58 uart7816.o(.text)

_gpioInit 0x0001056b Thumb Code 118 uart7816.o(.text)

_uartInit 0x000105e1 Thumb Code 146 uart7816.o(.text)

FS_Unlock 0x00010507 Thumb Code 10 fs.o(.text)

其中和0x00010642最为接近且小于0x00010642的为函数_uartInit,且函数地址0x000105e1+146大于0x00010642,,所以确认发生错误的指令在函数_uartInit中。

下面轮到List文件出场了,List文件实际上是编译器生成的C代码和汇编的对照表,其中_uartInit的部分内容如下:

_uartInit PROC

;;;110 static void _uartInit(void)

0000b0 48d2 LDR r0,|L1.1020|

;;;111 {

;;;112

;;;113 SIM_SCGC4 |= SIM_SCGC4_UART0_MASK;

0000b2 6b40 LDR r0,[r0,#0x34]

0000b4 f4406080 ORR r0,r0,#0x400

0000b8 49d0 LDR r1,|L1.1020|

0000ba 6348 STR r0,[r1,#0x34]

;;;114

;;;115 UART0_C2 &= (uint8_t)~(uint8_t)((UART_C2_TE_MASK | UART_C2_RE_MASK));

0000bc 48d1 LDR r0,|L1.1028|

0000be 78c0 LDRB r0,[r0,#3]

0000c0 f00000f3 AND r0,r0,#0xf3

0000c4 49cf LDR r1,|L1.1028|

0000c6 70c8 STRB r0,[r1,#3]

;;;116

;;;117 UART0_BDH = UART_BDH_SBR(0x02);

0000c8 2002 MOVS r0,#2

0000ca 7008 STRB r0,[r1,#0]

;;;118

;;;119 UART0_BDL = UART_BDL_SBR(0xE8);

0000cc 20e8 MOVS r0,#0xe8

0000ce 7048 STRB r0,[r1,#1]

;;;120

;;;121 UART0_MA1 = UART_MA1_MA(0x00);

0000d0 2000 MOVS r0,#0

0000d2 7208 STRB r0,[r1,#8]

;;;122

;;;123 UART0_MA2 = UART_MA2_MA(0x00);

0000d4 7248 STRB r0,[r1,#9]

;;;124

;;;125 UART0_C4 = UART_C4_BRFA(0x00);

0000d6 7288 STRB r0,[r1,#0xa]

;;;126

;;;127 UART0_C1 = (UART_C1_M_MASK | UART_C1_PE_MASK);

0000d8 2012 MOVS r0,#0x12

0000da 7088 STRB r0,[r1,#2]

;;;128

;;;129 UART0_S2 = (UART_S2_LBKDIF_MASK | UART_S2_RXEDGIF_MASK);

0000dc 20c0 MOVS r0,#0xc0

0000de 7148 STRB r0,[r1,#5]

;;;130

;;;131 UART0_MODEM = 0x00U;

0000e0 2000 MOVS r0,#0

0000e2 7348 STRB r0,[r1,#0xd]

;;;132

;;;133 UART0_IR = UART_IR_TNP(0x00);

0000e4 7388 STRB r0,[r1,#0xe]

;;;134

;;;135 UART0_TWFIFO = UART_TWFIFO_TXWATER(0x00);

0000e6 74c8 STRB r0,[r1,#0x13]

;;;136

;;;137 UART0_RWFIFO = UART_RWFIFO_RXWATER(0x01);

0000e8 2001 MOVS r0,#1

0000ea 7548 STRB r0,[r1,#0x15]

;;;138

;;;139 UART0_SFIFO = UART_SFIFO_TXEMPT_MASK |

0000ec 20c7 MOVS r0,#0xc7

0000ee 7488 STRB r0,[r1,#0x12]

;;;140 UART_SFIFO_RXEMPT_MASK |

;;;141 UART_SFIFO_RXOF_MASK |

;;;142 UART_SFIFO_TXOF_MASK |

;;;143 UART_SFIFO_RXUF_MASK;

;;;144

;;;145 UART0_CFIFO = (UART_CFIFO_TXFLUSH_MASK | UART_CFIFO_RXFLUSH_MASK);

0000f0 20c0 MOVS r0,#0xc0

0000f2 7448 STRB r0,[r1,#0x11]

;;;146

;;;147 UART0_PFIFO &= (uint8_t)~(uint8_t)(

0000f4 4608 MOV r0,r1

0000f6 7c00 LDRB r0,[r0,#0x10]

0000f8 f0000077 AND r0,r0,#0x77

0000fc 7408 STRB r0,[r1,#0x10]

;;;148 UART_PFIFO_TXFE_MASK |

;;;149 UART_PFIFO_RXFE_MASK

;;;150 );

;;;151

;;;152 UART0_WN7816 = UART_WN7816_GTN(0x00);

0000fe 2000 MOVS r0,#0

000100 7708 STRB r0,[r1,#0x1c]

;;;153

;;;154 UART0_WF7816 = UART_WF7816_GTFD(0x01);

000102 2001 MOVS r0,#1

000104 7748 STRB r0,[r1,#0x1d]

;;;155

;;;156 UART0_WP7816A_T0 = UART_WP7816A_T0_WI_H(0x00);

000106 2000 MOVS r0,#0

000108 49be LDR r1,|L1.1028|

00010a 313c ADDS r1,r1,#0x3c

00010c 7008 STRB r0,[r1,#0]

;;;157 UART0_WP7816B_T0 = UART_WP7816B_T0_WI_L(0x0A);

00010e 210a MOVS r1,#0xa

000110 48bc LDR r0,|L1.1028|

000112 f880103d STRB r1,[r0,#0x3d]

;;;158 //UART0_WP7816 = UART_WP7816_WTX(0x0A);

;;;159

;;;160 UART0_ET7816 = (UART_ET7816_TXTHRESHOLD(0x00) | UART_ET7816_RXTHRESHOLD(0x00));

000116 2000 MOVS r0,#0

000118 49ba LDR r1,|L1.1028|

00011a 7788 STRB r0,[r1,#0x1e]

;;;161

;;;162 UART0_IS7816 = UART_IS7816_WT_MASK |

00011c 20f7 MOVS r0,#0xf7

00011e 7688 STRB r0,[r1,#0x1a]

;;;163 UART_IS7816_CWT_MASK |

;;;164 UART_IS7816_BWT_MASK |

;;;165 UART_IS7816_INITD_MASK |

;;;166 UART_IS7816_GTV_MASK |

;;;167 UART_IS7816_TXT_MASK |

;;;168 UART_IS7816_RXT_MASK;

;;;169

;;;170 UART0_IE7816 = 0x00U;

000120 2000 MOVS r0,#0

000122 7648 STRB r0,[r1,#0x19]

;;;171

;;;172 UART0_C7816 = (UART_C7816_INIT_MASK | UART_C7816_ISO_7816E_MASK);

000124 2005 MOVS r0,#5

000126 7608 STRB r0,[r1,#0x18]

;;;173 (void) UART0_S1;

000128 4608 MOV r0,r1

00012a 7900 LDRB r0,[r0,#4]

;;;174 (void) UART0_D;

00012c 4608 MOV r0,r1

00012e 79c0 LDRB r0,[r0,#7]

;;;175

;;;176 UART0_C5 &= (uint8_t)~(uint8_t)(

000130 4608 MOV r0,r1

000132 7ac0 LDRB r0,[r0,#0xb]

000134 f0000010 AND r0,r0,#0x10

000138 72c8 STRB r0,[r1,#0xb]

;;;177 UART_C5_TDMAS_MASK |

;;;178 UART_C5_RDMAS_MASK |

;;;179 0x4FU

;;;180 );

;;;181

;;;182 UART0_C3 = 0x00U;

00013a 2000 MOVS r0,#0

00013c 7188 STRB r0,[r1,#6]

;;;183

;;;184 UART0_C2 = 0x00U;

00013e 70c8 STRB r0,[r1,#3]

;;;185

;;;186 return;

;;;187 }

000140 4770 BX lr

;;;188

ENDP

出错的地址是0x00010642,相对于函数起始地址0x000105e1的偏移为0x61,根据List文件中的内容,_UartInit中第一条指令在文件中的偏移是0xb0,因此出错代码的偏移应该是0x61+0xb0 = 0x111。因此可以定位出发生错误的语句在uart7816.c中的157行。上图中黄色加亮部分所在的语句。

找到产生错误的语句后,在有针对性的查找CPU手册中串口寄存器WP7816B的部分,发现用错了头文件,的确是访问了非法的内存区域。至此,问题解决。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值