在《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。
編譯,運行!
此次的結果就是咱們想要的了。