0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

從上電到啟動,帶你拿捏單片機(jī)啟動全流程

朱老師物聯(lián)網(wǎng)大講堂 ? 2024-05-07 08:10 ? 次閱讀


從事嵌入式開發(fā)的伙伴可能會思考過一個問題,我們一般都是使用芯片廠商提供的驅(qū)動庫和初始化文件,直接從main函數(shù)開始寫程序,那么系統(tǒng)上電之后,程序怎么引導(dǎo)進(jìn)main函數(shù)執(zhí)行的呢?還有,系統(tǒng)上電之后RAM的數(shù)據(jù)是隨機(jī)的,那么定義的全局變量的初始值又是怎么實(shí)現(xiàn)的呢?下面我將帶著這兩個問題,以Cortex-M架構(gòu)為例,采用IAR EWARM作為編譯工具鏈,從系統(tǒng)上電之后執(zhí)行的第一條代碼開始,梳理系統(tǒng)的啟動過程,了解編譯器在此期間所做的工作。其他的工具鏈,如Keil和GCC在系統(tǒng)初始化過程所做的工作也是相似的,但具體的實(shí)現(xiàn)有所差異。

一、啟動文件

芯片廠商提供的啟動文件,一般是采用匯編語言編寫,少數(shù)用C語言。在啟動文件中一般至少存在下面兩個部分內(nèi)容:向量表默認(rèn)的中斷和異常處理程序

向量表實(shí)際上是一個數(shù)組,放置在存儲器的零地址,每個元素存儲的是各個中斷或異常處理程序的入口地址。以STM32F107芯片基于IAR工具的啟動文件為例:

33a54eb2-0c06-11ef-9118-92fbcf53809c.png

文件的開頭定義了一個名為__vector_table的全局符號,“DATA”的作用是在代碼段中定義一個數(shù)據(jù)區(qū),用作向量表。數(shù)據(jù)區(qū)的內(nèi)容是使用DCD指令定義的32位寬度常量,除了第一個sfe(CSTACK)比較特殊以為,其他的常量都是異常和中斷服務(wù)程序的地址(在編譯時函數(shù)名會被替換成函數(shù)的入口地址)。sfe(CSTACK)是IAR匯編器段操作,用于獲取段(section)的結(jié)束地址,在這里意欲何為呢?實(shí)際上這是獲取堆?;刂返牟僮鳌AR在鏈接器腳本(*.icf)文件中定義堆棧,實(shí)際是定義了一個名為“CSTACK”的空閑塊(block),如下圖的腳本命令所示。所謂的塊就是保留一段連續(xù)的地址空間,用來作為堆?;蛘叨?。當(dāng)然,塊也可以是用內(nèi)容的,例如可以用來管理段,但不在今天的討論范圍。33bb593c-0c06-11ef-9118-92fbcf53809c.png我們知道Cortex-M架構(gòu)的堆棧模型是滿減棧,堆棧從高地址向低地址增長,因此堆棧的基地址是CSTACK的結(jié)束地址。向量表的第一個元素是?;愤@是由Cortex-M架構(gòu)定義的。系統(tǒng)上電后硬件自動從向量表中獲取,并設(shè)置主堆棧指針MSP,而不是像其他ARM架構(gòu),堆棧指針需要通過軟件來設(shè)置。向量表中第二個元素是復(fù)位異常(Reset_Handler)的入口地址。系統(tǒng)上電后,硬件自動從__vector_table + 4的位置讀取,并從讀取到的地址開始執(zhí)行。系統(tǒng)上電后CPU執(zhí)行的第一條是Reset_Handler函數(shù)的第一條語句。33c8f0d8-0c06-11ef-9118-92fbcf53809c.png

上面的THUMB命令表示接下來的代碼采用THUMB模式(Cortex-M只支持Thumb-2指令集);SECTION用于定義一個段,段名為“.ResetHandler”,段的類型是代碼(CODE);REODER指示用給定的名稱開啟一個新的段;ROOT指示鏈接器,當(dāng)段內(nèi)的符號沒有被引用,鏈接器也不可以丟棄這個段。

PUBWEAK是弱定義,如果用戶在其他位置編寫了中斷處理函數(shù),在連接時實(shí)際鏈接用戶所編寫的,啟動文件中用匯編寫的服務(wù)函數(shù)會忽略。之所以要在啟動文件中以弱定義的方式編寫全部的異常和中斷服務(wù)函數(shù),是為了防止用戶在沒有編寫服務(wù)函數(shù)的情況下開啟并觸發(fā)了中斷,導(dǎo)致系統(tǒng)的不確定。

二、系統(tǒng)初始化過程

在EWARM的工程Options > Debugger > Setup中將“Run to”勾選取消,這樣在進(jìn)入調(diào)試之后就會停第一條要執(zhí)行的代碼的位置:

341a0b6c-0c06-11ef-9118-92fbcf53809c.png

進(jìn)入調(diào)試之后會停在啟動文件Reset_Handler函數(shù)第一條匯編指令位置:

34286dc4-0c06-11ef-9118-92fbcf53809c.png

此時,通過寄存器觀察窗口查看SP的值為0x20009820。通過鏈接時生成的map文件,查看CSTACK的地址范圍,0x20009820正好是CSTACK的結(jié)束地址。有了MSP,C代碼就能運(yùn)行了。

34329664-0c06-11ef-9118-92fbcf53809c.png

ystemInit函數(shù)是芯片廠商根據(jù)ARM的CMSIS標(biāo)準(zhǔn)提供的一個系統(tǒng)基礎(chǔ)配置函數(shù),配置基礎(chǔ)的時鐘系統(tǒng)和向量表重定位等。這里的LDR是偽指令,它將SystemInit函數(shù)的地址加載到寄存器R0,實(shí)際上是通過PC偏移尋址來獲取SystemInit的地址。

34426850-0c06-11ef-9118-92fbcf53809c.png3450cecc-0c06-11ef-9118-92fbcf53809c.png

從上面的圖可以發(fā)現(xiàn)一個問題,在反匯編窗口可以觀察到SystemInit的地址是0x20000150,但加載到R0寄存器后卻是0x20000151。這是因為在使用跳轉(zhuǎn)指令更新PC時,需要置PC的LSB為1,以表示THUMB模式,由于Cortex-M不支持ARM模式,因此LSB總是1。

執(zhí)行完芯片廠商提供的SystemInit函數(shù)之后,跳轉(zhuǎn)到__iar_program_start,這是IAR編譯器提供的初始化代碼的入口。

34719882-0c06-11ef-9118-92fbcf53809c.png

__iar_program_start首先會執(zhí)行兩個函數(shù):__iar_init_core和__iar_init_vfp,可以完成一些CPU和FPU相關(guān)的初始化操作,在某些ARM架構(gòu)打包好的運(yùn)行時庫會有這兩個函數(shù),用戶也可以重寫這兩個函數(shù)來自己實(shí)現(xiàn)一些相關(guān)的操作。

之后,跳轉(zhuǎn)到__cmain函數(shù)執(zhí)行。在__cmain中調(diào)用了一個__low_level_init函數(shù),該函數(shù)專門用于提供給用戶編寫一個初階的初始化操作,它在全局變量初始化之前執(zhí)行,例如可用在__low_level_init中初始化SDRAM,這樣就可以將全局變量定義到SDRAM中使用。

347c34d6-0c06-11ef-9118-92fbcf53809c.png

__low_level_init可以在任意的C文件中編寫,注意它的返回值,如果返回0,后續(xù)就會跳過變量初始化操作,正常一般都是返回1。

348992de-0c06-11ef-9118-92fbcf53809c.png

三、全局變量的初始化

此后進(jìn)入到__iar_data_init3函數(shù),在這里會完成所有具有初始值的全局/靜態(tài)變量的賦值,以及零初始化全局/靜態(tài)變量的清零操作,分別調(diào)用__iar_copy_init3和__iar_zero_init3,將保存在ROM區(qū)由鏈接器生成的變量初始值復(fù)制到變量的地址。注意,新的EWARM版本默認(rèn)變量初始化操作可能會采用壓縮算法,實(shí)際變量初始化調(diào)用的函數(shù)可能有區(qū)別。34b4b3ec-0c06-11ef-9118-92fbcf53809c.png

在全局變量未初始化之前,通過watch窗口可以看到,變量的值都是隨機(jī)數(shù)。

34c207c2-0c06-11ef-9118-92fbcf53809c.png

在__iar_data_init3執(zhí)行完成后,全部變量的初值賦值已經(jīng)完成。

34cd1270-0c06-11ef-9118-92fbcf53809c.png

在__cmain函數(shù)的最后,跳轉(zhuǎn)到用戶的main函數(shù),最終開始用戶的代碼執(zhí)行。

34dc4286-0c06-11ef-9118-92fbcf53809c.png

四、總結(jié)

339b54ca-0c06-11ef-9118-92fbcf53809c.png

了解了編譯器所提供的初始化過程和處理器架構(gòu),我們可以根據(jù)自己的需求定制系統(tǒng)的初始化。例如,在進(jìn)入__iar_program_start之前,就可以執(zhí)行必要的硬件初始化操作,可以用匯編寫,也可以用C寫。還可以手動控制變量的初始化操作,自己實(shí)現(xiàn)變量的初始化。甚至,完全不采用IAR編譯器提供的初始化操作,自己從復(fù)位序列引導(dǎo)至main函數(shù)那也是可以的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 芯片
    +關(guān)注

    關(guān)注

    456

    文章

    50947

    瀏覽量

    424705
  • 單片機(jī)
    +關(guān)注

    關(guān)注

    6039

    文章

    44579

    瀏覽量

    636417
  • 嵌入式
    +關(guān)注

    關(guān)注

    5086

    文章

    19145

    瀏覽量

    306107
收藏 人收藏

    評論

    相關(guān)推薦

    單片機(jī)復(fù)位和冷啟動詳細(xì)介紹

    單片機(jī)啟動很簡單,關(guān),再上,正常復(fù)位后,就冷啟動成功。單片機(jī)
    的頭像 發(fā)表于 02-12 13:38 ?2w次閱讀
    <b class='flag-5'>單片機(jī)</b>復(fù)位和冷<b class='flag-5'>啟動</b>詳細(xì)介紹

    淺析STM32單片機(jī)啟動文件

    今天來總結(jié)的stm32基礎(chǔ)知識是啟動文件相關(guān)。要想對是stm32單片機(jī)有個深入的理解,那么啟動文件就是一個繞不過去的坎。
    發(fā)表于 06-12 16:49 ?2083次閱讀
    淺析STM32<b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>啟動</b>文件

    單片機(jī)啟動代碼

    帶你來看看單片機(jī)啟動代碼!
    發(fā)表于 05-26 12:45

    單片機(jī)有時候上啟動是為什么

    遇到有些國產(chǎn) 單片機(jī)有時候上啟動的問題,一般出現(xiàn)的情況是LVR設(shè)置有問題,最常見是兼容3.3V 5V供電的MCU,有時候默認(rèn)是5V配置,在3.3V可能出現(xiàn)上無法
    發(fā)表于 11-03 06:11

    單片機(jī)是如何啟動

    接觸單片機(jī)有幾年的時間了,一直專注于如何在單片機(jī)上寫一些應(yīng)用,對單片機(jī)如何啟動的知之甚少,慚愧慚愧。。。今天得空整理了一下,加深了對單片機(jī)
    發(fā)表于 11-03 08:58

    單片機(jī)是如何啟動

    單片機(jī)啟動過程是加后,先運(yùn)行芯片內(nèi)部固有程序(這個程序是用戶訪問不到也改寫不了的),即啟動代碼。啟動代碼程序建立完運(yùn)行環(huán)境后,會去讀串口
    發(fā)表于 11-25 09:26

    RK3188 從上開始啟動內(nèi)核為止的過程是怎樣去完成的

    如何獲取RK3066的BootRom呢?有哪些步驟?RK3188從上開始啟動內(nèi)核為止的過程是怎樣去完成的?
    發(fā)表于 02-18 07:11

    單片機(jī)啟動代碼詳細(xì)資料說明

    對于熟悉電腦的伙伴們來說,BIOS(那個藍(lán)色的界面)可能不會太陌生吧,這貨就是電腦的啟動代碼。沒有BIOS的電腦,那注定是一塊板磚!BIOS主要是做一些開機(jī)前的準(zhǔn)備工作,例如系統(tǒng)時間設(shè)定、啟動順序。。。扯遠(yuǎn)了!其實(shí)電腦本身就是從單片機(jī)
    發(fā)表于 07-29 17:36 ?0次下載
    <b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>啟動</b>代碼詳細(xì)資料說明

    單片機(jī)啟動代碼

    。。。扯遠(yuǎn)了!其實(shí)電腦本身就是從單片機(jī)而來,那么單片機(jī)也是有啟動代碼的,只是我們絕大部分情況不去關(guān)心它。啟動代碼究竟都干了些什么工作,為何需要它?想想你在c語言中用到了什么東西,而這些
    發(fā)表于 11-13 16:06 ?30次下載
    <b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>啟動</b>代碼

    單片機(jī)啟動過程

    啟動過程簡介單片機(jī)啟動過程是加后,先運(yùn)行芯片內(nèi)部固有程序(這個程序是用戶訪問不到也改寫不了的),即啟動代碼。
    發(fā)表于 11-17 10:21 ?8次下載
    <b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>啟動</b>過程

    單片機(jī)啟動流程分析

    單片機(jī)啟動流程概述單片機(jī)后一直到準(zhǔn)備好C語言運(yùn)行環(huán)境并跳轉(zhuǎn)到main函數(shù)執(zhí)行總共經(jīng)歷了5個步驟:1.內(nèi)核初始化;2.強(qiáng)制PC指針指向
    發(fā)表于 11-17 11:36 ?10次下載
    <b class='flag-5'>單片機(jī)</b><b class='flag-5'>啟動</b><b class='flag-5'>流程</b>分析

    STM32 單片機(jī)啟動流程

    STM32 單片機(jī)啟動流程剛接觸ARM的cortex-m系列單片機(jī)時,被告知一切都從main() 函數(shù)開始,要將程序?qū)懺趍ain()函數(shù)中。而仿真時也貌似是從main() 函數(shù)開始的,
    發(fā)表于 11-19 10:21 ?35次下載
    STM32 <b class='flag-5'>單片機(jī)</b><b class='flag-5'>啟動</b><b class='flag-5'>流程</b>

    單片機(jī)啟動過程

    啟動過程簡介單片機(jī)啟動過程是加后,先運(yùn)行芯片內(nèi)部固有程序(這個程序是用戶訪問不到也改寫不了的),即啟動代碼。
    發(fā)表于 11-19 12:06 ?55次下載
    <b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>啟動</b>過程

    單片機(jī)中程序和數(shù)據(jù)存放位置、與電腦內(nèi)存和硬盤的類比,單片機(jī)和計算機(jī)程序啟動流程對比

    單片機(jī)中程序和數(shù)據(jù)存放位置、與電腦內(nèi)存和硬盤的類比,單片機(jī)和計算機(jī)程序啟動流程對比
    發(fā)表于 11-26 17:51 ?14次下載
    <b class='flag-5'>單片機(jī)</b>中程序和數(shù)據(jù)存放位置、與電腦內(nèi)存和硬盤的類比,<b class='flag-5'>單片機(jī)</b>和計算機(jī)程序<b class='flag-5'>啟動</b><b class='flag-5'>流程</b>對比

    STM32的啟動過程如何分析

    本文分析STM32單片機(jī)從上運(yùn)行的過程,目的在于了解STM32單片機(jī)啟動到運(yùn)行的整個過程。
    的頭像 發(fā)表于 02-10 10:48 ?1421次閱讀
    STM32的<b class='flag-5'>啟動</b>過程如何分析