Cortex-M3開發經驗(三):在HardFault中打印棧信息

Cortex-M3開發經驗(三):在HardFault中打印棧信息

在《Cortex-M3開發經驗(二):確認發生HardFault的地方》中,咱們提到如何查找出錯地方。可是這有一個問題,就是必須連接調試器。那麼在某些狀況下,咱們沒法鏈接調試器,那麼就沒法讀取到棧信息了嗎?咱們能夠在進入HardFault時,獲取棧指針,而後經過串口的方式打印出來嗎?函數

說幹就幹,有好的想法,也必須有實際的行動驗證本身的想法。ui

如何獲取棧指針?

卡住咱們的第一個問題就是如何獲取棧指針了。就是如何獲取SP,MSP(主堆棧指針),PSP(進程堆棧指針)的值了。指針

在《Cortex-M3權威指南》中,有這麼一段話:CM3微控制器內核中共有兩個堆棧指針,因而也就是支持兩個堆棧,當引用R13(寫做SP)時,引用到的是當前正在使用的那一個(MSP或PSP),另外一個必須用特殊的指令來訪問(MRS和MSR指令)。調試

也就是說,咱們須要用匯編的指令來獲取棧指針。code

uint32_t get_msp_addr()
{
    __asm("mrs r0, msp");
    __asm("bx lr");
}

uint32_t get_psp_addr()
{
    __asm("mrs r0, psp");
    __asm("bx lr");
}

uint32_t get_sp_addr()
{
    __asm("mov r0, sp");
    __asm("bx lr");
}

注:每一個編譯器所支持C嵌入彙編的方式不一樣,也可能一些編譯器不支持__asm指令。進程

經過棧指針獲取內核寄存器的值

uint32_t reg_buff[10];
uint32_t *sp = NULL;

void HardFault_Handler()
{
    sp = (uint32_t*)get_msp_addr();
    reg_buff[0] = *(sp++);
    reg_buff[1] = *(sp++);
    reg_buff[2] = *(sp++);
    reg_buff[3] = *(sp++);
    reg_buff[4] = *(sp++);
    reg_buff[5] = *(sp++);
    reg_buff[6] = *(sp++);
    reg_buff[7] = *(sp++);
    reg_buff[8] = *(sp++);
    reg_buff[9] = *(sp++);
    while(1){}
}

編譯,運行!開發

結果有點意料以外!get

LR的值和PC的值跟咱們以前單步調試的不同!偏移了12個字節。爲何?後面單步看了一下後發現,咱們在HardFault中調用了get_msp_addr這個函數,而調用函數就意味着使用棧空間。若是我把reg_buff放到HardFault中,這樣就不止偏移12個字節了!編譯器

有沒有更好的方法啊!?io

咱們在進入HardFault_Handler函數以前就獲取SP指針的值,並做爲參數傳入到HardFault_Handler中不就能夠了嗎?

誰在調用中斷處理函數?

要解決上面的問題,咱們就須要知道內核在哪裏調用中斷函數的,這樣咱們才能修改對應的中斷處理函數,使其能夠接收參數。

《Cortex-M3開發經驗(二):確認發生HardFault的地方》中,咱們提到過,在發生中斷/異常時,內核會去中斷向量表中找到對應的中斷,找到中斷的入口地址。那麼咱們就看看中斷向量表在哪?

最終,在startup_xxx.S文件中找到了向量表的定義1。咱們也找到了HardFault_Handler的定義

.weak   HardFault_Handler
.type   HardFault_Handler, %function

HardFault_Handler:
    B .

雖然可能不瞭解彙編,不知道什麼意思,但也能猜想出大概的意思,也可能查資料。發現B是跳轉指令,這應該就是跳轉到同名的C函數中。那麼咱們能夠修改成:

.weak   HardFault_Handler
.type   HardFault_Handler, %function

HardFault_Handler:
    MOV     r0, lr
    MOV     r1, sp
    BL      hardfault_handler

這樣就吧LR的值和SP的值傳入到hardfault_handler函數中去了2

編譯,運行!

此次的結果就是咱們想要的了。


  1. 這是一個啓動文件,裏面包含了該芯片啓動須要的一些過程。

  2. R0~R3寄存器保存的是函數調用時所傳入的參數,同時也可做爲函數返回值。hardfault_handler定義以下:void hardfault_handler(uint32_t lr, uint32_t sp)

相關文章
相關標籤/搜索