1. 程式人生 > >Keil啟動檔案詳解

Keil啟動檔案詳解

概述:

  在嵌入式系統中,啟動檔案是整個系統非常關鍵的部分,它會進行一些底層的初始化,構建程式執行必要的環境,比如堆疊初始化,變數初始化等。如果啟動檔案出現錯誤,則整個系統就跑不起來,因此研究啟動檔案非常必要。

  在keil中,啟動檔案由彙編程式碼編寫,一般命名為startup_xxx.s,xxx為支援的某種晶片,比如可以是lpc15xx(NXP的LPC15xx系列)、MK60D10(飛思卡爾)、stm32f10x(意法半導體stm32f10x系列)等Cortext-M0/M3/M4核心晶片。它們的程式碼格式非常相近,根據啟動檔案程式碼由上到下的編寫順序.

可以將其分為以下5個典型部分:

  1.堆疊空間定義;

  2.存放中斷向量表;

  3.復位中斷函式(Reset_Handler);

  4.其它中斷異常服務函式,以及弱[WEAK]宣告;

  5.將堆疊地址傳遞給庫函式,利用庫函式初始化堆疊,和庫函式自身初始化。

5個部分具體說明如下:

1.堆疊空間定義

如下圖所示,定義了棧大小Stack_Size = 0X200,即512位元組;堆大小Heap_Size = 0X100,256位元組。還定義了三個標號:__initial_sp(棧頂)、__heap_base(堆起始地址)和__heap_limit(堆終止地址)它們的空間由SPACE關鍵字來申請,並記作Stack_Mem和Heap_Mem。

  通過這些我們可以很容易的知道堆疊的大小,但是它們的絕對地址或者說基地址僅僅從這裡是得不到的。編譯器編譯完工程後,根據生成.bss段(比如未初始化的全域性變數)和.data段(比如初始化的全域性變數)的大小以及RAM的起始地址,來計算堆疊的基地址。

舉個例子:

  一個晶片的RAM起始地址為0x0200_0000,RAM大小為0x500位元組,程式編譯後.bss段為0x100個位元組,.data段為0x100個位元組。堆疊大小定義如上圖。則:

  A:堆起始地址  __heap_base==Heap_Mem==0x0200_0200;

  B:堆終止地址即棧底 __heap_limit==Stack_Mem==0x0200_0300;

  C:棧頂地址 __initial_sp==0x0200_0500(棧是向下生長,棧頂處於RAM最大地址處)。

   其實,我可以在.map檔案中檢視堆疊的大小和基地址,如下圖所示:

2.存放中斷向量表

 在啟動程式碼中,會見到許多由DCD申請空間存放的一個個函式入口,即中斷向量表,如下圖所示,只列出了部分。

  關鍵字DCD代表申請一個字的空間,後面的函式名即為中斷服務函式入口地址。另外中斷向量表一般存放在Flash 0地址。

3. 復位中斷函式(Reset_Handler)

  程式上電後,首先載入SP和PC,ARM規定從0地址處載入SP,從偏移為4的地址(0x00000004)處載入PC。然後將程式控制權交給程式。我們知道0地址處存放__initial_sp,0x00000004地址處存放Reset_Handler,載入PC後,程式跳轉到Reset_Handler開始執行。Reset_Handler函式體如下圖所示:

  首先呼叫SystemInit函式來初始化系統的各種時鐘,然後呼叫__main函式(由KEIL微庫或者C庫實現)在__main函式中:.data段資料的初始化->.bss段變數清零->設定堆疊指標->庫函式初始化(比如常用的malloc函式)->如果必要會設定main函式的argc和argv兩個引數->呼叫使用者main函式->退出。

4.其它中斷異常服務函式,以及弱[WEAK]宣告

  如上圖所示,這裡的中斷服務函式是弱宣告的(由[WEAK]關鍵字標註)。所謂弱宣告,即:如果使用者定義了相同的函式則此處函式失效而使用使用者定義的中斷服務函式。這樣是為了防止使用者使能了中斷而沒有中斷服務函式,從而造成程式崩潰。假設使能了中斷,而使用者又沒有定義中斷服務函式則會進入預設中斷,如下圖所示,預設中斷為死迴圈(死迴圈與程式崩潰不是一個概念)。

5.將堆疊地址傳遞給庫函式

第三步驟中,呼叫__main函式,然後__main呼叫庫函式初始化堆疊,但庫函式並不知道堆疊的大小,因此我們需要告訴它,具體做法就是傳遞引數或宣告標號。

   下圖為具體做法,可以看到第一行為:

IF      :DEF:__MICROLIB

  是條件編譯選項,如果定義__MICROLIB,則編譯圖中紅線上面部分,否則編譯紅線下面部分。那麼就分2種情況。

  2種情況的選擇可以如下實現:

       如果勾選【Options for Target】->【Target】->【Use MicroLIB】,如下圖所示。即使用微庫,則__MICROLIB會被定義,編譯器編譯紅線以上程式碼。用EXPORT宣告 __initial_sp、__heap_base和__heap_limit。

   如果不勾選【Use MicroLIB】,則預設使用KEIL C庫,上圖紅線以下會參與編譯,KEIL C庫函式會呼叫__user_initial_stackheap,通過R0~R3將堆疊以引數形式傳遞給KEIL C庫。