倒数第四篇:附录E
倒数第三篇:E.2 设计 Fault 服务例程
主页
倒数第一篇:E.4 理解发生 fault 的原因
第一篇:第1章
文章列表

E.3 在 C 中上报入栈的寄存器和各 fault 状态寄存器

《Cortex-M3 权威指南》,嵌入式处理器开发教程。

大多数的 CM3项目还是以 C语言为主的。然而,在 C中不方便定位和直接访问堆栈帧(入栈的 寄存器)。因为在标准 C语言中是不能获取 SP指针的。因此,如果使用 C来写 fault服务例程,最 好配合一小段汇编码来获取 SP的值,再把该值以一个参数传送给 fault上报函数。

译注:在使用 MDK 自带的 ARM 编译器时,可以使用__builtin_frame_address()函数来获取堆栈帧的地址。 在GNU工具中也可以这样做。此法方便,并且可取代上文的通用作法,但降低了编译器间的可移植性。

这个机制与第 12章讲的 SVC范例相同(“在 C中使用 SVC”)。下例就以嵌入式汇编的方式来演 示。这个例子可以在 RealView MDK中编译。
示例程序的第一部分是个汇编封皮。在使用前,要在向量表中的硬 fault入口地址项中,填写 好该封皮的入口地址。这个封皮代码把正确的堆栈指针值拷贝到 R0 中,以作为参数来传送给 C 函 数。

// 使用汇编写就的硬fault服务例程

// 该例程提取堆栈帧的位置并且把它传递

// 给C程序

asm void hard_fault_handler_asm(void)

{

IMPORT hard_fault_handler_c

TST LR, #4

ITE EQ

MRSEQ R0, MSP MRSNE R0, PSP

B hard_fault_handler_c

}

示例程序的第二部分,也是主体部分,使用 C语言来写。在这里,我们主要是演示如何访问入
栈的寄存器和 fault状态寄存器。

// 使用C写就的硬fault服务例程

// 第一个参数即堆栈帧的位置

void hard_fault_handler_c(unsigned int * hardfault_args)

{

unsigned int stacked_r0; unsigned int stacked_r1; unsigned int stacked_r2;

unsigned int stacked_r3; unsigned int stacked_r12; unsigned int stacked_lr; unsigned int stacked_pc; unsigned int stacked_psr;

stacked_r0 = ((unsigned long) hardfault_args[0]); stacked_r1 = ((unsigned long) hardfault_args[1]); stacked_r2 = ((unsigned long) hardfault_args[2]); stacked_r3 = ((unsigned long) hardfault_args[3]);

stacked_r12 = ((unsigned long) hardfault_args[4]); stacked_lr = ((unsigned long) hardfault_args[5]); stacked_pc = ((unsigned long) hardfault_args[6]); stacked_psr = ((unsigned long) hardfault_args[7]);

printf ("[Hard fault handler]\n"); printf ("R0 = %x\n", stacked_r0); printf ("R1 = %x\n", stacked_r1); printf ("R2 = %x\n", stacked_r2); printf ("R3 = %x\n", stacked_r3); printf ("R12 = %x\n", stacked_r12); printf ("LR = %x\n", stacked_lr); printf ("PC = %x\n", stacked_pc); printf ("PSR = %x\n", stacked_psr);

printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38)))); printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28)))); printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C)))); printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30))));

printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));

exit(0); // terminate return;

}

请注意:如果发生了堆栈溢出或其它错误,使 SP 指向了无效的存储空对空区域,则上段代码会失 能。在大多数情况下,这种错误都会影响 C代码,因为所有 C代码都需要堆栈。