轉自:https://www.cnblogs.com/Ilmen/p/3356147.html
最近在STM32做一個關於USB音頻的應用,調試過程中一直被一個隨機產生的HARD FAULT折磨。問題很奇怪,進入HARD FAULT的時間不定,可能連上USB後幾秒就觸發HARD FAULT,也可能程序跑幾分鐘甚至幾十分鐘纔會觸發。儘管感覺問題極有可能來自USB部分代碼,但起初一直沒有辦法找到導致問題的代碼,百度上搜素了一下,但是感覺對自己沒什麼啓發。經過努力,最終找到了問題所在,同時也學到了新的東西,現在先介紹下調試過程。
首先在KEIL下進入DEBUG模式運行程序,待MCU"死掉"後停止,顯然是卡在HARD FAULT的while(1)裏面的。接下來打開FAULT REPORTS窗口:
可以看到HARD FAULT是由USAGE FAULT導致的,原因是INVSTATE,從“STM32常見Hard+Fault的診斷”的PPT中可以瞭解到,INVSTATE表示MCU嘗試進入ARM狀態,這是非法的,所以產生了USAGE FAULT。此外,PPT裏還有這樣的描述:
另外,在Cortex-M3權威指南中也有這樣描述:使用BXL的時候要小心,因爲它還帶有改變狀態的功能。因此reg的LSB必須是1,以確保不會嘗試進入ARM狀態,如果忘記置位LSB,則FAULT伺候;同理,你也必須保證送給PC的值必須是奇數(LSB=1)。
雖然瞭解了這些,但是還是不能直接產生幫助。接下來,在GOOGLE上找到了一個名爲“keil_hardfault”的PDF文件,是KEIL公司寫的,該文檔的後半部分通過一個例子介紹了定位產生HARD FAULT之前代碼的方法。通過一些摸索,利用這個方法我成功找到了問題的位置。
首先,看到此時MCU停在了HARD FAULT裏,寄存器LR裏放存放的是HARD FAULT返回的地址,可以看到此時LR = 0xFFFF_FFF1,顯然是一個錯誤的指令地址,所以可以判斷是程序跳轉到錯誤的地址,並且該地址的LSB是0,故觸發了USAGE FAULT。出錯的流程是這樣的:
[正確代碼] --(出錯)--> [0xFFFF_FFF0]。接下來需要做的是,找到導致錯誤跳轉的代碼,
注意到寄存器SP(R13),它指向當前使用的棧頂,在MEMORY窗口中輸入SP的值[0x2000A190]:
根據CM3內核的棧是從上往下生長,並且按寄存器標號從大到小的順序壓入棧,這樣就可以找到“出事”時的寄存器情況。
在 keil_hardfault.pdf 裏的這幅圖可以看得更直觀:
可以看到跳轉到中斷函數HARD FAULT之前的寄存器情況,例如R0 = 0000_0066,R2 = FFFF_FFFF。
這時候我們需要關注的是LR寄存器,其值指向 錯誤跳轉指令 的下一條指令,可以看到棧裏的LR = 0x0800_04FF,那麼產生錯誤的指令就是 LR = 0x0800_04FC,
在彙編窗口裏定位這個地址,如下圖:
可以看到指令是BLX R0,而“當時”寄存器R0的值是0x0000_0066,顯然是錯誤的跳轉地址。找到了指令的地址也就可以看到對應的C語言代碼,大概也能猜到了,問題出在數組越界了,導致越界的原因就是EPindex,之後在這裏加了一些調試代碼,很容易的就確認了問題是由EPindex的值爲0導致的。
之後的就不多說了,既然找到了產生HARD FAULT的代碼,也可以鬆一口氣了。總之這個方法在處理這個問題上的確很有效。