人妻蜜と1~4中文字幕月野定规 ,国产精品成人va在线播放,色优久久久久综合网鬼色,WWW插插插无码视频网站

【ARM】啟動(dòng)文件(startup)-STM32F1X系列

一、文檔背景

分析以STM321x系列為例的啟動(dòng)文件解析,了解相關(guān)的代碼原理和啟動(dòng)文件的配置內(nèi)容。對(duì)MDK的售前和售后培訓(xùn)有一定的積累幫助,以及客戶對(duì)相關(guān)匯編語(yǔ)言的咨詢,同時(shí)我們可以更加深入了解到功能更復(fù)雜的CPU啟動(dòng)文件。


二、 啟動(dòng)文件簡(jiǎn)介

啟動(dòng)文件由匯編編寫,是系統(tǒng)上電復(fù)位后第一個(gè)執(zhí)行的程序。主要做了以下工作:

1. 初始化堆棧指針SP(__initial_sp)

2. 初始化PC指針(Reset_Handler)

3. 初始化中斷向量表(__Vectors)

4. 配置系統(tǒng)時(shí)鐘(SystemInit)

5. 調(diào)用C庫(kù)函數(shù)_main初始化用戶堆棧,從而最終調(diào)用main函數(shù)去到C的代碼世界。


三、查找ARM匯編指令

在MDK內(nèi)可以搜索到ARM的匯編指令,以EQU為例,檢索步驟如下:

打開MDK軟件界面,點(diǎn)擊“Help”->“Uvision Help“后進(jìn)入”ARM Development Tools“界面進(jìn)入搜索界面,輸入檢索名稱,選中”只搜索標(biāo)題“后回車搜索。

圖3-1

下面列出了啟動(dòng)文件中使用到的ARM匯編指令,該列表的指令全部從ARM Development Tools這個(gè)幫助文檔里面檢索而來(lái)。其中編譯器相關(guān)的指令WEAK和ALIGN為了方便也放在同一個(gè)表格內(nèi)。

表 2?1 啟動(dòng)文件使用的ARM匯編指令匯總

指令名稱作用
EQU給數(shù)字常量取一個(gè)符號(hào)名,相當(dāng)于C語(yǔ)言中的define
AREA匯編一個(gè)新的代碼段或者數(shù)據(jù)段
SPACE分配內(nèi)存空間
PRESERVE8當(dāng)前文件堆棧需按照8字節(jié)對(duì)齊
EXPORT聲明一個(gè)標(biāo)號(hào)具有全局屬性,可被外部的文件使用
DCD以字為單位分配內(nèi)存,要求4字節(jié)對(duì)齊,并要求初始化這些內(nèi)存
PROC定義子程序,與ENDP成對(duì)使用,表示子程序結(jié)束
WEAK弱定義,如果外部文件聲明了一個(gè)標(biāo)號(hào),則優(yōu)先使用外部文件定義的標(biāo)號(hào), 如果外部文件沒有定義也不出錯(cuò)。要注意的是:這個(gè)不是ARM的指令,是編譯器的,這里放在一起只是為了方便。
IMPORT聲明標(biāo)號(hào)來(lái)自外部文件,跟C語(yǔ)言中的EXTERN關(guān)鍵字類似
B跳轉(zhuǎn)到一個(gè)標(biāo)號(hào)
ALIGN編譯器對(duì)指令或者數(shù)據(jù)的存放地址進(jìn)行對(duì)齊,一般需要跟一個(gè)立即數(shù),缺省表示4字節(jié)對(duì)齊 要注意的是:這個(gè)不是ARM的指令,是編譯器的,這里放在一起只是為了方便。
END到達(dá)文件的末尾,文件結(jié)束
IF,ELSE,ENDIF匯編條件分支語(yǔ)句,跟C語(yǔ)言的if else類似

注意:經(jīng)測(cè)試MDK5.36及以前版本搜索出來(lái)標(biāo)題為”Assember User Guide:“


表 2?1 啟動(dòng)文件使用的ARM匯編指令匯總


圖3-2



四、啟動(dòng)文件代碼解析

1. 注釋說(shuō)明


圖4-1

該啟動(dòng)代碼適用的芯片系列、代碼版本、日期、版權(quán)所有者等相關(guān)信息。


2. Stack棧

圖4-2

EQU:給數(shù)字常量取一個(gè)符號(hào)名,相當(dāng)于C語(yǔ)言中的define。

本例中使用EQU命名 Stack_Size為0x00000400。

AREA:匯編一個(gè)新的代碼段或者數(shù)據(jù)段。

STACK:命名為(HEAP)棧,

NOINIT:不進(jìn)行初始化

READWRITE:可讀可寫

ALIGN=3 :8(2^3)字節(jié)對(duì)齊,為8字節(jié)對(duì)齊。

SPACE:用于分配一定大小的內(nèi)存空間,單位為字節(jié)。

本例中使用SPACE分配 Stack_Mem大小為 Stack_Size的內(nèi)存空間,即為0x00000400(1024個(gè)字節(jié))(1K)

標(biāo)號(hào)__initial_sp緊挨著SPACE語(yǔ)句放置,表示棧的結(jié)束地址,即棧頂?shù)刂罚5脑鲩L(zhǎng)方向是從高地址到低地址)


注:

棧存儲(chǔ)函數(shù)的形參、以及函數(shù)里定義的局部變量,所以在本例中函數(shù)的局部變量、數(shù)組這些不能超過(guò)1K(含嵌套的函數(shù)),否則程序就會(huì)崩潰進(jìn)入hardfaul.

除去這些局部變量以外,還有一些實(shí)時(shí)操作系統(tǒng)的現(xiàn)場(chǎng)保護(hù)、返回地址都是存儲(chǔ)在棧里面。



3. Heap堆

圖4-3

EQU:給數(shù)字常量取一個(gè)符號(hào)名,相當(dāng)于C語(yǔ)言中的define。

本例中使用EQU命名 Heap_Size為0x00000200。

AREA:匯編一個(gè)新的代碼段或者數(shù)據(jù)段。

HEAP:命名為(HEAP)棧,

NOINIT:不進(jìn)行初始化

READWRITE:可讀可寫

ALIGN=3 :8(2^3)字節(jié)對(duì)齊,為8字節(jié)對(duì)齊。

SPACE:用于分配一定大小的內(nèi)存空間,單位為字節(jié)。

本例中使用SPACE分配 Heap_Mem大小為 Heap_Size的內(nèi)存空間,即為0x00000200(512個(gè)字節(jié))

標(biāo)號(hào)_heap_limit緊挨著SPACE語(yǔ)句放置,表示堆的結(jié)束地址(堆的增長(zhǎng)方向是從低地址到高地址)


注:

堆主要用來(lái)動(dòng)態(tài)內(nèi)存的分配,意味著如果你用malloc()函數(shù),那么最大分配的內(nèi)存不能大于512字節(jié),否則程序會(huì)崩潰。


PRESERVE8:指定當(dāng)前文件的堆棧按照8字節(jié)對(duì)齊。

THUMB:THUMB指令指示匯編程序使用UAL語(yǔ)法將后續(xù)指令解釋為T32指令。


4. 向量表



圖4-4-1

AREA:匯編一個(gè)新的代碼段或者數(shù)據(jù)段。

RESET:命名為RESET(復(fù)位),

DATA:包含數(shù)據(jù),而不是指令。默認(rèn)為READWRITE

READONLY:只可讀不可寫

EXPORT:聲明一個(gè)標(biāo)號(hào)可被外部的文件使用,使標(biāo)號(hào)具有全局屬性。

本例中:聲明 __Vectors、__Vectors_End和__Vectors_Size這三個(gè)標(biāo)號(hào)具有全局屬性,可供外部的文件調(diào)用。

圖4-4-2

DCD:分配一個(gè)或者多個(gè)以字為單位的內(nèi)存,以四字節(jié)對(duì)齊,并要求初始化這些內(nèi)存。在向量表中,DCD分配了一堆內(nèi)存,并且以ESR的入口地址初始化它們。


__initial_sp:棧頂?shù)刂?0x0000 0000

Reset_Handler:復(fù)位程序 0x0000 0004

NMI_Handler:不可屏蔽中斷,RCC時(shí)鐘安全程序連接到NMI向量0x0000 0008

HardFault_Handler: 所有類型的錯(cuò)誤 0x0000 000C

MemManage_Handler:存儲(chǔ)器管理 0x0000 0010

BusFault_Handler:預(yù)取指失敗,存儲(chǔ)器訪問(wèn)失敗 0x0000 0014

UsageFault_Handler:未定義的指令或非法狀態(tài) 0x0000 0018

0:保留函數(shù)當(dāng)前狀態(tài),0x0000 001C - 0x0000 002B

SVC_Handler:通過(guò)SWI指令的系統(tǒng)服務(wù)調(diào)用 0x0000 002C

DebugMon_Handler :調(diào)試監(jiān)控器 0x0000 0030

0:0:保留函數(shù)當(dāng)前狀態(tài) 0x0000 0030 - 0x0000 0034

PendSV_Handler :可掛起的系統(tǒng)服務(wù) :0x0000 0034

SysTick_Handler:系統(tǒng)嘀嗒定時(shí)器:0x0000 0038


外部中斷:

WWDG_IRQHandler :窗口定時(shí)器中斷 0x0000 0040

PVD_IRQHandler:連到EXTI的電源電壓檢測(cè)(PVD)中斷 0x0000 0044

TAMPER_IRQHandler:侵入檢測(cè)中斷 0x0000 0048

RTC_IRQHandler:實(shí)時(shí)時(shí)鐘(RTC)全局中斷 0x0000 004C

FLASH_IRQHandler:閃存全局中斷 0x0000 0050

RCC_IRQHandler:復(fù)位和時(shí)鐘控制(RCC)中斷 0x0000 0054


根據(jù)芯片手冊(cè),在起始文件內(nèi)進(jìn)行地址的設(shè)置。


__Vectors為向量表起始地址,__Vectors_End 為向量表結(jié)束地址,兩者標(biāo)號(hào)的差值即可算出向量表大小。

向量表從FLASH的0地址開始放置,以4個(gè)字節(jié)為一個(gè)單位,地址0存放的是棧頂?shù)刂罚?X04存放的是復(fù)位程序的地址,以此類推。從代碼上看,向量表中存放的都是中斷服務(wù)函數(shù)的函數(shù)名,可我們知道C語(yǔ)言中的函數(shù)名就是一個(gè)地址。



5. 復(fù)位程序



圖4-5

AREA:定義一個(gè)名稱為.text的代碼段,可讀。

復(fù)位子程序是系統(tǒng)上電后第一個(gè)執(zhí)行的程序,調(diào)用SystemInit函數(shù)初始化系統(tǒng)時(shí)鐘,然后調(diào)用C庫(kù)函數(shù)_mian,最終調(diào)用main函數(shù)去到C的世界。

WEAK:表示弱定義,如果外部文件優(yōu)先定義了該標(biāo)號(hào)則首先引用該標(biāo)號(hào),如果外部文件沒有聲明也不會(huì)出錯(cuò)。這里表示復(fù)位子程序可以由用戶在其他文件重新實(shí)現(xiàn),這里并不是唯一的。

IMPORT:表示該標(biāo)號(hào)來(lái)自外部文件,跟C語(yǔ)言中的EXTERN關(guān)鍵字類似。這里表示SystemInit和__main這兩個(gè)函數(shù)均來(lái)自外部的文件。

SystemInit()是一個(gè)標(biāo)準(zhǔn)的庫(kù)函數(shù),在system_stm32f103xe.c這個(gè)庫(kù)文件中定義。主要作用是配置系統(tǒng)時(shí)鐘,這里調(diào)用這個(gè)函數(shù)之后,單片機(jī)的系統(tǒng)時(shí)鐘被配置為72M。

指令名稱作用
LDR從存儲(chǔ)器中加載字到一個(gè)寄存器中
BL跳轉(zhuǎn)到由寄存器/標(biāo)號(hào)給出的地址,并把跳轉(zhuǎn)前的下條指令地址保存到LR
BLX跳轉(zhuǎn)到由寄存器給出的地址,并根據(jù)寄存器的LSE確定處理器的狀態(tài),還要把跳轉(zhuǎn)前的下一條指令地址保存到LR
BX跳轉(zhuǎn)到由寄存器/標(biāo)號(hào)給出的地址,不用返回__main是一個(gè)標(biāo)準(zhǔn)的C庫(kù)函數(shù),主要作用是初始化用戶堆棧,并在函數(shù)的最后調(diào)用main函數(shù)去到C的世界。這就是為什么我們寫的程序都有一個(gè)main函數(shù)的原因。


6. 中斷服務(wù)程序

在啟動(dòng)文件里面已經(jīng)幫我們寫好所有中斷的中斷服務(wù)函數(shù),跟我們平時(shí)寫的中斷服務(wù)函數(shù)不一樣的就是這些函數(shù)都是空的,真正的中斷復(fù)服務(wù)程序需要我們?cè)谕獠康腃文件里面重新實(shí)現(xiàn),這里只是提前占了一個(gè)位置而已。

如果我們?cè)谑褂媚硞€(gè)外設(shè)的時(shí)候,開啟了某個(gè)中斷,但是又忘記編寫配套的中斷服務(wù)程序或者函數(shù)名寫錯(cuò),那當(dāng)中斷來(lái)臨時(shí),程序就會(huì)跳轉(zhuǎn)到啟動(dòng)文件預(yù)先寫好的空的中斷服務(wù)程序中,并且在這個(gè)空函數(shù)中無(wú)限循環(huán),即程序就死在這

里。


圖4-6

例如:NMI_Handler

PROC/ENDP:定義子程序,PROC與ENDP成對(duì)使用,表示子程序結(jié)束

EXPORT:聲明一個(gè)標(biāo)號(hào)具有全局屬性,可被外部的文件使用

B:跳轉(zhuǎn)到一個(gè)標(biāo)號(hào)。這里跳轉(zhuǎn)到一個(gè)‘.’,即表示無(wú)限循環(huán)。



7. 用戶堆棧初始化

ALIGN:對(duì)指令或者數(shù)據(jù)存放的地址進(jìn)行對(duì)齊,后面會(huì)跟一個(gè)立即數(shù)。缺省表示4字節(jié)對(duì)齊。

圖4-7-1

首先判斷是否定義了__MICROLIB,如果定義了這個(gè)宏則賦予標(biāo)號(hào)__initial_sp(棧頂?shù)刂罚?__heap_base(堆起始地址)、__heap_limit(堆結(jié)束地址)全局屬性, 可供外部文件調(diào)用。有關(guān)這個(gè)宏我們?cè)贙EIL里面配置,具體見下圖。然后堆棧的初始化就由C庫(kù)函數(shù)_main來(lái)完成。

圖4-7-2

如果沒有定義__MICROLIB,則插入標(biāo)號(hào)__use_two_region_memory,這個(gè)函數(shù)需要用戶自己實(shí)現(xiàn),具體要實(shí)現(xiàn)成什么樣, 可在KEIL的幫助文檔里面查詢到。

圖4-7-3

然后聲明標(biāo)號(hào)__user_initial_stackheap具有全局屬性,可供外部文件調(diào)用,并實(shí)現(xiàn)這個(gè)標(biāo)號(hào)的內(nèi)容。

IF,ELSE,ENDIF?:匯編的條件分支語(yǔ)句,跟C語(yǔ)言的if ,else類似

END?:程序結(jié)束。


五、討論分析

1. 如何快速修改啟動(dòng)文件的值?

在編輯界面將”Text Editor“選擇為”Configuration Wizard“,在編輯器中打開文件。大多數(shù)啟動(dòng)文件都包含對(duì)配置向?qū)峁╊愃?GUI 的控件來(lái)設(shè)置值。在下方”Value“內(nèi)修改相關(guān)數(shù)據(jù)即可。

圖5-1


六、總結(jié)

無(wú)論是何種MCU 都必須有啟動(dòng)文件,因?yàn)閷?duì)于嵌入式開發(fā),絕大部分情況都是使用C語(yǔ)言,而C語(yǔ)言一般都是從main 函數(shù)開始,但是對(duì)于MCU來(lái)說(shuō),它是如何找到并執(zhí)行main函數(shù)的,就需要用到“啟動(dòng)文件”,就是各種 startup_xxxx.s 文件。

啟動(dòng)文件是使用機(jī)器可以理解的匯編語(yǔ)言,經(jīng)過(guò)一些必要的配置,最終能夠調(diào)用 main 函數(shù),使得用戶程序能夠在 MCU上正常運(yùn)行起來(lái)的必備文件。

本文對(duì)較為簡(jiǎn)易的STM32F1x系列的啟動(dòng)文件進(jìn)行分析,對(duì)內(nèi)部的匯編語(yǔ)言進(jìn)行剖析,進(jìn)行逐行分析和說(shuō)明注釋。