杭州嵌入式培訓
達內杭州嵌入式培訓中心

13732203138

熱門課程

淺談嵌入式MCU軟件開發之startup過程詳解

  • 時間:2019-02-14 17:39
  • 發布:轉載
  • 來源:網絡

1. 嵌入式MCU的復位源

嵌入式MCU在硬件復位或者系統復位后,都是從復位向量所指向的復位中斷ISR開始運行的,因此復位中斷ISR一般也是整個嵌入式應用工程的入口(_EntryPoint)函數/啟動(startup/boot)函數。

通常導致嵌入式MCU復位的復位源如下

雖然我們用戶編程一般不涉及MCU的軟件啟動過程,用戶代碼一般都是從main()函數開始,完成系統的時鐘,工作模式和外設初始化,全局變量/數據結構的初始化,最后打開內核CPU全局中斷,進入主程序(主循環狀態機),用戶的程序設計跟標準C語言程序設計無異。但了解清楚嵌入式MCU的startup過程對于深入理解嵌入式系統軟件還是大有裨益的。

2. Startup相關的幾個問題:

我們先來思考幾個問題:

a. 我們都知道,C語言中的全局變量分為有初始化值(.data段變量)和無初始化值(.bss段變量—初始化值為0的全局變量和.common段變量—無初始化值的全局變量)兩大類。我們在main()函數中,用戶代碼并沒有對全局變量進行初始化,但使用全局變量時其初始化值已經確定--.data段變量已為其定義時的初始化值,而 .bss/.common段變量全為零,這是如何做到的呢 (要知道,嵌入式MCU中這些全局變量都是被分配到RAM中的,其每次上電之后的值時隨機的。) ?如果我們想要某些全局變量在只在POR上電復位時被初始化而其他系統復位時不初始化,有該如何實現呢?

b. 在汽車MCU中,為了保證RAM數據的可靠,其RAM也常帶有ECC功能(比如Qorivva MPC56xx/57xx系列MCU和S32K系列MCU),而每次上電之后RAM中的數據是隨機的,如果不對其進行初始化就去讀取其數據,就極容易產生ECC錯誤,從而進入系統異常,那么對RAM的初始化又是如何完成的呢?

c. C語言正常運行所需要的堆棧(stack)是如何指向RAM中用戶指定的地址空間的呢?

d. 我們的用戶程序都是從main()開始運行的,那么可不可以是其他的函數呢?main()函數一定是必須運行的嗎?

3. 嵌入式MCU Startup過程詳解

你知道到嗎?以上準備工作幾乎全是在startup的過程中完成的,且看以下嵌入式MCU的一般startup流程:

具體每一步完成的初始化工作如下:

初始化CPU寄存器/關閉全局中斷:每次POR上電或者復位之后,CPU寄存器除了PC寄存器有意見復位邏輯賦以存儲在默認復位向量中的復位函數ISR(即_EntryPoint()/startup()函數) 地址外,其余的CPU寄存器值都是隨機的,所以有必要對其進行初始化,然后再使用;除此之外,為了保證整個startup過程不受外設中斷的影響,需要將內核CPU全局中斷關閉(disable);

初始化看門狗:為了保證整個startup初始化過程正常進行,有必要對MCU內部看門狗進行初始化,關閉或者初始化一個溢出周期并在初始化過程中進行喂狗;(這個過程一般由編譯器預編譯變量控制,在工程屬性編譯器設置,對于有些MCU,比如S12系列MCU,其片上COP看門狗在正常模式(normal single chip mode)下只能配置一次,這樣如果在startup的過程將其關閉了,在用戶程序中就無法重新使能和配置了);

初始化RAM ECC:如果使用的MCU其RAM帶有ECC功能(比如Qorivva MPC56x/57xx系列MCU),必須在使用前對其進行初始化(其過程就是往RAM中寫出初始化數據已產生確定的ECC結果,一般是將之前的CPU寄存器值循環寫入整個RAM空間,當然,對其賦值零也是可以的,之所以使用CPU寄存器是因為其訪問速度快,而且有專門的單指令多數據(SIMD)支持將多個CPU寄存器寫入RAM),否則會造成ECC錯誤,進入系統異常(比如PowerPC e200Z0內核的IVOR1—machine check/IVOR2—data storge exception);

下表列出了Freescale/NXP的汽車MCU存儲器ECC功能

配置存儲器控制器:對于很多32位MCU來說,由于其內核CPU運行速度比較快(100~300MHz)而存儲器的工作時鐘頻率往往較低,所以一般器存儲器控制器都設計有buffer來控制訪問效率;

比如Qorivva MPC564xA系列MCU通過BIUCR寄存器的WWSC和RWSC位來抽空讀寫片上P-Flash時的總線等待周期,當內核系統時鐘較高時就需要減小等待周期以保證MCU正常工作

另外,如果所使用的MCU內核CPU有片上指令/數據緩存(I-cache和D-cache,比如S32K14x和MPC57xx系列MCU),還需要對其進行初始化—Flush操作;

初始化C語言堆棧(stack):從鏈接結果中,讀取棧頂地址將其寫入CPU寄存器的SP寄存器,從而完成堆棧(stack)的初始化工作,這之后內核CPU就可以正常運行C語言代碼了。

初始化RAM(copydown):在對RAM初始化時,內核CPU會讀取編譯鏈接結果中的啟動結構體(鏈接器自定義,不同的編譯器其結構和形式可能不同),從中獲得RAM的初始化信息,其包括如下信息:

1..data段全局變量初始化值在Flash中的存儲地址和在其RAM中的運行時地址,以及長度(單位為字節);

2..bss/.common段全局變量在RAM中的運行時地址,以及長度(單位為字節);

根據這些信息,內核CPU就可以對RAM進行初始化了:

(注意:編譯器默認都是把所有相同段的數據放在連續的地址空間以提高初始化效率)

.data段全局變量區:從Flash中讀取初始化值并將其寫到對應的RAM地址空間;

.bss/.common段全局變量區:對其RAM地址寫入零一完成初始化;

(從以上分析可知,.bss/.common段全局變量是不占Flash空間的,即在編譯結果S19文件中也沒有其初始化值)

下面是一個S12G128的具體工程編譯鏈接結果:

啟動結構體—startupData存儲在地址0xC044(P-Flash地址),占用6個字節,包含RAM初始化信息:

在工程map信息中可以看到工程鏈接后系統棧頂(__SEG_END_SSTACK, stack結束地址+1)結果,期用于startup工程中對SP寄存器進行初始化:

該工程中的全局變量定義以及其默認編譯鏈接結果

初始化系統時鐘:可以將MCU的時鐘初始化放在main()函數之前,以縮短startup的時間。根據不同的編譯器,其為可選配置。

跳轉至main()函數: 在完成以上startup過程之后,在startup函數的最后就是調用main()函數,跳轉至用戶應用程序執行;(PS: 這里,其實我們可以修改讓內核CPU跳轉到自定義的任何函數,而非默認的main()函數)。

預約申請免費試聽課

怕錢不夠?就業掙錢后再付學費!    怕學不會?從入學起,達內定制課程!     擔心就業?達內多家實踐企業供你挑選!

上一篇:嵌入式Linux內核調試技術
下一篇:嵌入式培訓靠譜嗎
選擇城市和中心
江西省

貴州省

廣西省

海南省

中文字幕人成乱码在线观看