1. 程式人生 > >關於keil卡在systeminit中,然後出現void HardFault_Handler(void)的幾個問題詳解

關於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;  

這一句話代表:操作時鐘控制暫存器,將內部8M高速時鐘使能,從這裡可以看出系統啟動後是首先依靠內部時鐘源而工作的。那麼問題來了,所謂的一直卡在SystemInit(),其實是卡在RCC->CR |= (uint32_t)0x00000001;  這一句(不是很絕對),

要怎麼解決呢?分為3種情況:

A:

1.選上”Use MicroLIB”這是KEIL自帶的一個簡易的庫,例如你用printf函式的時候,就會從串列埠1 輸出字串,直接預設定向到串列埠1

UseMicroLib

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的方法,有不對的地方歡迎指出,鄙人馬上修正。

也許你遇不到,也許你能遇到,反正它就在那裡。

就像我們做了一道算術題,很多人會想,怎麼老遇到著急不會的題呢?有沒有搞錯啊。。。。。。

但是就有那麼少些人會說:我真幸運,又遇到了一到自己不會的算術題。

態度決定高度,細節決定成敗!!!