1. 程式人生 > >手把手教你查詢stm32 HardFault_Handler除錯及問題方法

手把手教你查詢stm32 HardFault_Handler除錯及問題方法

版權宣告:本文為博主原創文章,未經博主允許不得轉載。https://blog.csdn.net/u013184273/article/details/84440177

在編寫STM32程式程式碼時由於自己的粗心會發現有時候程式跑著跑著就進入了HardFault_Handler中斷,導致異常的原因很多,例如:直接使用未分配空間的指標、棧溢位等一場非法操作便會使程式進入HardFault異常狀態。按照經驗來說進入HardFault_Handler故障的原因主要有兩個方面

 

1:記憶體溢位或則訪問越界。

2:堆疊溢位。

發生異常後我們可以首先檢視LR暫存器的值,確認當前使用的堆疊是MSP還是PSP,然後找到相對應的堆疊指標,並在記憶體中檢視相對應堆疊的內容,核心將R0~R3,R12,LR,PC,XPRS暫存器依次入棧,其中LR即為發生異常前PC將要執行的下一條指令地址。

那麼Cortex-M3 核心HardFault錯誤除錯定位方法有:

方法1   如何精確定位出問題程式碼的所在位置:

以訪問越界為例:(對STM32F103C8T6內部flash模擬EEPROM)

#define STM32_FLASH_SIZE 64                                         

#define STM32_FLASH_WREN 1           

#define FLASH_SAVE_ADDR  0X08078000            

#define FLASH_HIS_ADDR  0X08078002      

...

FLASH_SAVE_ADDR是開始儲存的基地址,STM32F103C8T6內部flash大小是64K,在STM32的內部快閃記憶體(FLASH)地址起始於0x08000000

,一般情況下,程式就從此地址開始寫入。因此STM32F103C8T6的結束地址應該是64*1024轉換成16進位制後加上微控制器flash的基地址得到的結果就是0x08010000,那麼以上程式碼設定了FLASH_SAVE_ADDR為0X08078000已經超出了該微控制器的範圍,因此如果在用此微控制器操作flash是如果對這個地址進行寫和讀的會發生錯誤。現在假設你在不知情的狀況下對這個地址進行了操作,然後程式執行時進入

HardFault_Handler中斷中。那麼要找出錯誤程式碼在哪個地方,可以使用以下方法(除錯軟體MDK):

1:進入除錯除錯介面在HardFault_Handlerwhile(1)處打上斷點。

3:等待程式碼執行到此,這時檢視LR暫存器的

如果是正常執行那麼顯示的暫存器類似下圖所示:

 

 

如果進入HardFault_Handler中斷,那麼顯示的暫存器如下圖所示:

發生異常之後可首先檢視LR暫存器中的值,確定當前使用堆疊為MSP或PSP,然後找到相應堆疊的指標,並在記憶體中檢視相應堆疊裡的內容。

在Cortex_M3權威指南中可以看到如下圖所示:

由這張圖可見,這個按位或的作用是把R14暫存器的低4位設為D,在這個異常返回後進入執行緒模式,使用執行緒堆疊PSP,因為任務執行時要確保使用的是執行緒模式,只有發生中斷或異常時,才讓系統進入Handle模式並使用MSP。在xPortPendSVHandler裡之所以沒有這一行,是因為在進入這個異常前,系統正在跑任務,使用的就是執行緒模式和PSP,進入異常後變成Handle模式和MSP,但在異常返回時會自動回到這個異常發生前的模式也就是執行緒模式與PSP。在vPortSVCHandler這個函式被呼叫之前,系統一直是處於Handle模式並使用MSP的。(因為復位後就是Handle模式,因此大部分沒用上系統的STM32的工程,都是讓STM32處於Handle這個最高模式下執行的。)

看到LR暫存器中的值是0xFFFFFFF9,因此我應該去看MSP的地址,找到該地址的地址然後如下圖所示開啟記憶體,輸入上面找到的暫存器地址,右鍵選擇以long型檢視地址如下所示:

然後檢視這個地址向下數六個long地址,為什麼是6個long地址呢,因為由於異常發生時,核心將R0~R3、R12、Returnaddress、PSR、LR暫存器依次入棧,其中Return address即為發生異常前PC將要執行的下一條指令地址;大概是0x08xxxxxx這樣開始的即為出錯的程式碼位置,然後可以反彙編檢視,如下圖所示:

可以看到是對應的C語言程式是在讀FLASH函式中發生了錯誤,因此可以判斷為訪問越界的問題。

方法2:最簡單,最明顯

在除錯狀態下,當進入HardFault斷點後,選單欄Peripherals >Core Peripherals >FaultReports開啟異常發生的報告,檢視發生異常的原因。

跳出如下表格:

上面的報告發生了BUS FAULT,並將Fault的中斷服務轉向Hard Fault

相對於檢測發生了什麼異常,定位異常發生位置顯得更重要。
(1)開啟Call Stack視窗(如下圖,斷點停在Hard Fault服務程式中)

(2)在Call Stack的HardFault_Handler上右鍵Show CallerCode

這時將跳轉到發生異常的原始碼位置(如上圖),即發生錯誤的地方在讀FLASH處,可以想到應該是剛剛設定的FLASH地址越界問題

方法3:此方法和方法一大致相同

下面介紹怎麼找出程式中的異常。

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010101458539-1528104762.png

接下來在keil_MDK工程中,編譯程式碼,並debug,之後全速執行,可以看到如下圖所示程式進入HardFault異常。

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010101745336-1054407022.png

如下所示我們找到SP暫存器,0x200045B8即為棧地址,棧裡面的值依次為R0~R3R12PC(Return address)xPSR(CPSRSPSR)LR。如圖我們看到劃紅線的地方,注意從右往左看。分別為0x0800427D0x08004BFA

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010101933430-173904825.png

show code at address中輸入0x08004BFA,點選go to即找到出現異常的程式碼段附近下面要執行的程式。

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010102531227-1035806375.png

我們用同樣的方法在show code at address中輸入0x0800427D,找到如下程式碼段

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010102949180-211320752.png

可以發現異常程式碼就在uart_send_noackdata這個函式裡,這個函式裡我們定義了一個指標,沒有給他分配空間便開始使用了。由此我們掌握了第一種查詢異常的方法。只要記錄棧裡面第21~24以及2528位元組的內容即可方便的找到異常程式碼。下面介紹使用.map檔案查詢異常。.map檔案在keil工程裡面隨著程式的編譯會自動生成。

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010103413743-1934954010.png

.map檔案裡我們查詢0x08004BFA,找到了0x08004bd8指示是uart_send_noackdata函式,到此我們找到了異常程式碼所在的位置。

https://images2015.cnblogs.com/blog/917705/201610/917705-20161010103633758-700474568.png

 由此我們知道我們只要找到棧裡面PC(Return address)xPSR(CPSRSPSR)暫存器裡的記憶體地址便可以找到異常程式碼。

CPSR

當前程式狀態暫存器 (Current Program State Register)

SPSR

儲存的程式狀態暫存器 (Saved Program State Register), 6,主要是在處理異常的時候使用.

每一種處理器模式下都有一個專用的物理暫存器作為備份的程式狀態暫存器SPSR , 當特定的異常發生時,這個物理暫存器負責儲存CPSR當前程式狀態暫存器的內容, 當異常處理程式返回時,再將內容恢復到當前程式狀態器中,繼續向下執行原來程式.

PC

程式計數器,是用來計數的,指示指令在儲存器的存放位置,也就是個地址資訊.