關於keil卡在systeminit中,然後出現void HardFault_Handler(void)的幾個問題詳解
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/u013184273/article/details/83860802
相信很多程式設計師在用keil的時候都會遇到在模擬除錯時,一直停在SystemInit()中的等待晶振中,怎麼也出不來。出現HardFault時,可能不是因為記憶體溢位,訪問越界或是堆疊溢位,有可能僅僅只是自己一些最基本的規則沒弄清楚而導致,所以分享下自己一些小問題,讓新手朋友們出現類似問題可以借鑑下;
STM32啟動檔案簡單解析(V3.5.0) 以:SystemInit函式詳解
具體的函式呼叫順序如下:
①startup_stm32f10x_hd.s(啟動檔案)→②SystemInit()→③SetSysClock ()→④SetSysClockTo72()
說明:由於在上面只定義了SYSCLK_FREQ_72MHz,因而在執行SetSysClock ()函式的時候,會選擇進入SetSysClockTo72()函式,以設定系統時鐘為72MHz
如果還是想深究SystemInit()函式,那你就開啟瀏覽器上百度或者谷歌進行搜尋,網上自來一大堆的。。。。。。。俗話說:小孩勤,喜歡人。
話不多說,進入正題:
當我們模擬一段程式,進入模擬介面的時候,首先是一下這個圖:
這是DEBUG的程式的入口,對,就是這幾個彙編的啟動程式碼。如果想知道啟動檔案的詳情,那就開啟瀏覽器上百度或者谷歌進行搜尋,網上自來一大堆的。。。。。。。俗話說:小孩勤,喜歡人。
接下來看到 SystemInit函式,按F12進入這個函式你會看到:
如果想知道這個函式是幹什麼的,那就開啟瀏覽器上百度或者谷歌進行搜尋,網上自來一大堆的。。。。。。。俗話說:小孩勤,喜歡人。
你會看到 RCC->CR |= (uint32_t)0x00000001;
要怎麼解決呢?分為3種情況:
A:
1.選上”Use MicroLIB”這是KEIL自帶的一個簡易的庫,例如你用printf函式的時候,就會從串列埠1 輸出字串,直接預設定向到串列埠1
2.microlib 是預設 C 庫的備選庫。 它用於必須在極少量記憶體環境下執行的深層嵌入式應用程式。 這些應用程式不在作業系統中執行。microlib 不會嘗試成為符合標準的 ISO C 庫。
microlib 進行了高度優化以使程式碼變得很小。 它的功能比預設 C 庫少,並且根本不具備某些 ISO C 特性。某些庫函式的執行速度也比較慢,例如,memcpy()。
3.
microlib 與預設 C 庫之間的主要差異是:
microlib 不符合 ISO C 庫標準。 不支援某些 ISO 特性,並且其他特性具有的功能也較少。
microlib 不符合 IEEE 754 二進位制浮點演算法標準。
microlib 進行了高度優化以使程式碼變得很小。
無法對區域設定進行配置。 預設 C 區域設定是唯一可用的區域設定。
不能將 main() 宣告為使用引數,並且不能返回內容。
不支援 stdio,但未緩衝的 stdin、stdout 和 stderr 除外。
microlib 對 C99 函式提供有限的支援。
microlib 不支援作業系統函式。
microlib 不支援與位置無關的程式碼。
microlib 不提供互斥鎖來防止非執行緒安全的程式碼。
microlib 不支援寬字元或多位元組字串。
與 stdlib 不同,microlib 不支援可選擇的單或雙區記憶體模型。 microlib 只提供雙區記憶體模型,即單獨的堆疊和堆區。
可以合理地將 microlib 與 --fpmode=std 或 --fpmode=fast 配合使用。
microlib 中的函式負責:
建立一個可在其中執行 C 程式的環境。 這包括:
建立一個堆疊
建立一個堆(如果需要)
初始化程式所用的庫的部分組成內容。
呼叫 main() 以開始執行程式。
要使用 microlib 構建程式,必須使用命令列選項 ??library_type=microlib。 根據需要,編譯器、彙編程式或連結器可使用此選項處理不同的檔案。 將此選項與連結器配合使用時,將覆蓋所有其他選項。
4.
//加入以下程式碼,支援printf函式,而不需要選擇use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//標準庫需要的支援函式
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
_sys_exit(int x)
{
x = x;
}
//重定義fputc函式
int fputc(int ch, FILE *f)
{
#ifdef COM_EX
com_snd(COM2, 1, (unsigned char*)&ch);
#else
com_snd(COM1, 1, (unsigned char*)&ch);
#endif
return ch;
}
#endif
/*使用microLib的方法,在keil裡面要勾選“Use MicroLIB”*/
// 以便使用 printf 函式
// #define UTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
// UTCHAR_PROTOTYPE
// {
// #ifdef COM_EX
// com_snd(COM2, 1, (unsigned char*)&ch);
// #else
// com_snd(COM1, 1, (unsigned char*)&ch);
// #endif
//
// return ch;
// }
我試了試確實是可以,不知道,如果不用Use MicroLIB,prinft 和sprintf 其他影響不,我去驗證一下
B:
1、採用target對話方塊中的ROM和RAM地址
採用此方式,需在Linker選項卡中勾選Use Memory Layout from Target Dialog選項(選中這一項實際上是預設在Target中對Flash和RAM的地址配置,編譯連結時會產生一個預設的指令碼檔案),並且在Target中設定好RAM、ROM地址,圖2所示。MDK會根據Target選項中設定的RAM和ROM地址自動載入生成一個載入檔案。最後連結器會根據此檔案中的資訊對目標檔案進行連結,生成axf映象檔案。
C:
選項One ELF Section per Function
選項One ELF Section per Function的主要功能是對冗餘函式的優化。通過這個選項,可以在最後生成的二進位制檔案中將冗餘函式排除掉(雖然其所在的檔案已經參與了編譯連結),以便最大程度地優化最後生成的二進位制程式碼。
而該選項實現的機制是將每一個函式作為一個優化的單元,而並非整個檔案作為參與優化的單元。
選項One ELF Section per Function所具有的這種優化功能特別重要,尤其是在對於生成的二進位制檔案大小有嚴格要求的場合。人們習慣將一系列介面函式放在一個檔案裡,然後將其整個包含在工程中,即使這個檔案將只有一個函式被用到。這樣,最後生成的二進位制檔案中就有可能包含眾多的冗餘函式,造成了寶貴儲存空間的浪費。
選項One ELF Section per Function對於一個大工程的優化效果尤其突出,有時候甚至可以達到減半的效果。當然,對於小工程或是少有冗餘函式的工程來說,其優化效果就沒有那麼明顯了。
以上三種方法是解決系統啟動卡在SystemInit的方法,有不對的地方歡迎指出,鄙人馬上修正。
也許你遇不到,也許你能遇到,反正它就在那裡。
就像我們做了一道算術題,很多人會想,怎麼老遇到著急不會的題呢?有沒有搞錯啊。。。。。。
但是就有那麼少些人會說:我真幸運,又遇到了一到自己不會的算術題。
態度決定高度,細節決定成敗!!!