STM32學(xué)習記錄18 IAP(2)
一、在進(jìn)入主題之前我們先了解一些必要的基礎知識----stm32系列芯片的種類(lèi)和型號:
本文引用地址:http://dyxdggzs.com/article/201611/316194.htmstartup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_hd_vl.s 大容量的STM32F100xx
startup_stm32f10x_ld.s 小容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_ld_vl.s 小容量的STM32F100xx
startup_stm32f10x_md.s 中容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_md_vl.s 中容量的STM32F100xx (我項目中用的是此款芯片 stm32f100CB)
startup_stm32f10x_xl.s FLASH在512K到1024K字節的STM32F101xx,STM32F102xx,STM32F103xx
cl:互聯(lián)型產(chǎn)品,stm32f105/107系列
vl:超值型產(chǎn)品,stm32f100系列
xl:超高密度產(chǎn)品,stm32f101/103系列
ld:低密度產(chǎn)品,FLASH小于64K
md:中等密度產(chǎn)品,FLASH=64 or 128
hd:高密度產(chǎn)品,FLASH大于128
二、在拿到ST公司官方的IAP 程序后 我們要思考幾點(diǎn):
1.ST 官方IAP是什么針對什么芯片型號的,我們要用的又是什么芯片型號;
2.我們要用官方IAP適合我們芯片的程序升級使用,要在原有的基礎上做那些改變;
(我的資源里有官方IAP源碼:http://download.csdn.net/detail/yx_l128125/6445811)
初略看了一下IAP源碼后,現在我們可以回答一下上面的2個(gè)問(wèn)題了:
1.官網(wǎng)剛下載的IAP針對的是stm32f103c8芯片的,所以他的啟動(dòng)代碼文件選擇的是startup_stm32f10x_md.s,而我的芯片是stm32f100cb,所以我的啟動(dòng)代碼文件選擇的是 startup_stm32f10x_md_lv.s
2 .第二個(gè)問(wèn)題就是今天我們要做詳細分析才能回答的問(wèn)題了;
(1).知道了IAP官方源碼的芯片和我們要用芯片的差異,首先我們要在源碼的基礎上做芯片級的改動(dòng);
A.首先改變編譯器keil的芯片型號上我們要改成我們的芯片類(lèi)型---STM32F100CB;
B.在keil的options for targer 選項C/C++/PREPROMCESSOR symbols的Define欄里定義,把有關(guān)STM32F10X_MD的宏定義改成:STM32F10X_MD_VL
- /*UncommentthelinebelowaccordingtothetargetSTM32deviceusedinyour
- application
- */
- #if!defined(STM32F10X_LD)&&!defined(STM32F10X_LD_VL)&&!defined(STM32F10X_MD)&&!defined(STM32F10X_MD_VL)&&!defined(STM32F10X_HD)&&!defined(STM32F10X_HD_VL)&&!defined(STM32F10X_XL)&&!defined(STM32F10X_CL)
- /*#defineSTM32F10X_LD*//*!
- /*#defineSTM32F10X_LD_VL*//*!
- /*#defineSTM32F10X_MD*//*!
- #defineSTM32F10X_MD_VL/*!
- /*#defineSTM32F10X_HD*//*!
- /*#defineSTM32F10X_HD_VL*//*!
- /*#defineSTM32F10X_XL*//*!
- /*#defineSTM32F10X_CL*//*!
- #endif
- /*#defineSTM32F10X_LD_VL*//*!
上面代碼說(shuō)的是如果沒(méi)有定義 STM32F10X_MD_VL, 則宏定義STM32F10X_MD_VL
C.外部時(shí)鐘問(wèn)價(jià)在stm32f10x.h 依據實(shí)際修改,原文是 說(shuō)如果沒(méi)有宏定義外部時(shí)鐘HES_VALUE的值,但是宏定義了stm32f10x_cl 則外部時(shí)鐘設置為25MHZ, 否則外部時(shí)鐘都設置為8MHZ; 我用的外部晶振是8MHZ的所以不必修改這部分代碼;
- #if!definedHSE_VALUE
- #ifdefSTM32F10X_CL
- #defineHSE_VALUE((uint32_t)25000000)//ValueoftheExternaloscillatorinHz
de"class="plain">#else#defineHSE_VALUE((uint32_t)8000000)//ValueoftheExternaloscillatorinHz#endif/*STM32F10X_CL*/#endif/*HSE_VALUE*/
D.做系統主頻時(shí)鐘的更改
- #ifdefined(STM32F10X_LD_VL)||(definedSTM32F10X_MD_VL)||(definedSTM32F10X_HD_VL)
- /*#defineSYSCLK_FREQ_HSEHSE_VALUE*/
- #defineSYSCLK_FREQ_24MHz24000000
- #else
- /*#defineSYSCLK_FREQ_HSEHSE_VALUE*/
- #defineSYSCLK_FREQ_24MHz24000000
- /*#defineSYSCLK_FREQ_36MHz36000000*/
- /*#defineSYSCLK_FREQ_48MHz48000000*/
- /*#defineSYSCLK_FREQ_56MHz56000000*/
- /*#defineSYSCLK_FREQ_72MHz72000000*/
- #endif

從上圖我們看出幾個(gè)關(guān)鍵部分:
1.內部flash 是從0x0800 0000開(kāi)始 到0x0801 FFFF 結束, 0x0801FFFF-0x0800 0000= 0x20000 =128k 128也就是flash的大??;
2.SRAM的開(kāi)始地址是 0x2000 0000 ;
我們要把我們的在線(xiàn)升級程序IAP放到FLASH里以0x0800 0000 開(kāi)始的位置, 應用程序放APP放到以0x08003000開(kāi)始的位置,中斷向量表也放在0x0800 3000開(kāi)始的位置;如圖
所以我們需要先查看一下misc.h文件中的中斷向量表的初始位置宏定義為 NVIC_VectTab_Flash 0x0800 0000
那么要就要設置編譯器keil 中的 options for target 的target選項中的 IROM1地址 為0x0800 0000 大小為 0x20000即128K;
IRAM1地址為0x2000 0000 大小為0x2000;
(提示:這一項IROM1 地址 即為當前程序下載到flash的地址的起始位置)
下面我們來(lái)分析一下修改后的IAP代碼:
- /*******************************************************************************
- *@函數名稱(chēng)main
- *@函數說(shuō)明主函數
- *@輸入參數無(wú)
- *@輸出參數無(wú)
- *@返回參數無(wú)
- *******************************************************************************/
- intmain(void)
- {
- //Flash解鎖
- FLASH_Unlock();
- //配置PA15管腳
- KEY_Configuration();
- //配置串口1
- IAP_Init();
- //PA15是否為低電平
- if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)==0x00)
- {
- //執行IAP驅動(dòng)程序更新Flash程序
- SerialPutString("rn======================================================================");
- SerialPutString("rn=(C)COPYRIGHT2011Lierda=");
- SerialPutString("rn==");
- SerialPutString("rn=In-ApplicationProgrammingApplication(Version1.0.0)=");
- SerialPutString("rn==");
- SerialPutString("rn=Bywuguoyan=");
- SerialPutString("rn======================================================================");
- SerialPutString("rnrn");
- Main_Menu();
- }
- //否則執行用戶(hù)程序
- else
- {
- //判斷用處是否已經(jīng)下載了用戶(hù)程序,因為正常情況下此地址是棧地址
- //若沒(méi)有這一句話(huà),即使沒(méi)有下載程序也會(huì )進(jìn)入而導致跑飛。
- if(((*(__IOuint32_t*)ApplicationAddress)&0x2FFE0000)==0x20000000)
- {
- SerialPutString("ExecuteuserProgramrnn");
- //跳轉至用戶(hù)代碼
- JumpAddress=*(__IOuint32_t*)(ApplicationAddress+4);
- Jump_To_Application=(pFunction)JumpAddress;
- //初始化用戶(hù)程序的堆棧指針
- __set_MSP(*(__IOuint32_t*)ApplicationAddress);
- Jump_To_Application();
- }
- else
- {
- SerialPutString("nouserProgramrnn");
- }
- }
這里重點(diǎn)說(shuō)一下幾句經(jīng)典且非常重要的代碼:
第一句: if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //判斷棧定地址值是否在0x2000 0000 - 0x 2000 2000之間
怎么理解呢? (1),在程序里#define ApplicationAddress 0x8003000 ,*(__IO uint32_t*)ApplicationAddress) 即取0x8003000開(kāi)始到0x8003003 的4個(gè)字節的值, 因為我們的應用程序APP中設置把中斷向量表放置在0x08003000 開(kāi)始的位置;而中斷向量表里第一個(gè)放的就是棧頂地址的值
也就是說(shuō),這句話(huà)即通過(guò)判斷棧頂地址值是否正確(是否在0x2000 0000 - 0x 2000 2000之間) 來(lái)判斷是否應用程序已經(jīng)下載了,因為應用程序的啟動(dòng)文件剛開(kāi)始就去初始化化??臻g,如果棧頂值對了,說(shuō)應用程已經(jīng)下載了啟動(dòng)文件的初始化也執行了;
第二句: JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); [ common.c文件第18行定義了: pFunction Jump_To_Application;]
ApplicationAddress + 4 即為0x0800 3004 ,里面放的是中斷向量表的第二項“復位地址” JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 之后此時(shí)JumpAddress
第三句: Jump_To_Application = (pFunction) JumpAddress;
startup_stm32f10x_md_lv.文件中別名typedef void (*pFunction)(void); 這個(gè)看上去有點(diǎn)奇怪;正常第一個(gè)整型變量 typedef int a; 就是給整型定義一個(gè)別名 a
void (*pFunction)(void); 是聲明一個(gè)函數指針,加上一個(gè)typedef 之后 pFunction只不過(guò)是類(lèi)型void (*)(void) 的一個(gè)別名;例如:
- pFunctiona1,a2,a3;
- voidfun(void)
- {
- ......
- }
- a1=fun;
所以,Jump_To_Application = (pFunction) JumpAddress; 此時(shí)Jump_To_Application指向了復位函數所在的地址;
第四 、五句:__set_MSP(*(__IO uint32_t*) ApplicationAddress); \設置主函數棧指針
Jump_To_Application(); \執行復位函數
我們看一下啟動(dòng)文件startup_stm32f10x_md_vl。s 中的啟動(dòng)代碼,更容易理解
移植后的IAP代碼在我的資源(如果是stm32f100cb的芯片可以直接用):http://download.csdn.net/detail/yx_l128125/6475219
三、我們來(lái)簡(jiǎn)單看下啟動(dòng)文件中的啟動(dòng)代碼,分析一下這更有利于我們對IAP的理解: (下面這篇文章寫(xiě)的非常好,有木有?。?/p>
下文來(lái)自于:http://blog.sina.com.cn/s/blog_69bcf45201019djx.html
解析STM32的啟動(dòng)過(guò)程
解析STM32的啟動(dòng)過(guò)程
當前的嵌入式應用程序開(kāi)發(fā)過(guò)程里,并且C語(yǔ)言成為了絕大部分場(chǎng)合的最佳選擇。如此一來(lái)main函數似乎成為了理所當然的起點(diǎn)——因為C程序往往從main函數開(kāi)始執行。但一個(gè)經(jīng)常會(huì )被忽略的問(wèn)題是:微控制器(單片機)上電后,是如何尋找到并執行main函數的呢?很顯然微控制器無(wú)法從硬件上定位main函數的入口地址,因為使用C語(yǔ)言作為開(kāi)發(fā)語(yǔ)言后,變量/函數的地址便由編譯器在編譯時(shí)自行分配,這樣一來(lái)main函數的入口地址在微控制器的內部存儲空間中不再是絕對不變的。相信讀者都可以回答這個(gè)問(wèn)題,答案也許大同小異,但肯定都有個(gè)關(guān)鍵詞,叫“啟動(dòng)文件”,用英文單詞來(lái)描述是“Bootloader”。
無(wú)論性能高下,結構簡(jiǎn)繁,價(jià)格貴賤,每一種微控制器(處理器)都必須有啟動(dòng)文件,啟動(dòng)文件的作用便是負責執行微控制器從“復位”到“開(kāi)始執行main函數”中間這段時(shí)間(稱(chēng)為啟動(dòng)過(guò)程)所必須進(jìn)行的工作。最為常見(jiàn)的51,AVR或MSP430等微控制器當然也有對應啟動(dòng)文件,但開(kāi)發(fā)環(huán)境往往自動(dòng)完整地提供了這個(gè)啟動(dòng)文件,不需要開(kāi)發(fā)人員再行干預啟動(dòng)過(guò)程,只需要從main函數開(kāi)始進(jìn)行應用程序的設計即可。
話(huà)題轉到STM32微控制器,無(wú)論是keil
uvision4還是IAR EWARM開(kāi)發(fā)環(huán)境,ST公司都提供了現成的直接可用的啟動(dòng)文件,程序開(kāi)發(fā)人員可以直接引用啟動(dòng)文件后直接進(jìn)行C應用程序的開(kāi)發(fā)。這樣能大大減小開(kāi)發(fā)人員從其它微控制器平臺跳轉至STM32平臺,也降低了適應STM32微控制器的難度(對于上一代ARM的當家花旦ARM9,啟動(dòng)文件往往是第一道難啃卻又無(wú)法逾越的坎)。
相對于A(yíng)RM上一代的主流ARM7/ARM9內核架構,新一代Cortex內核架構的啟動(dòng)方式有了比較大的變化。ARM7/ARM9內核的控制器在復位后,CPU會(huì )從存儲空間的絕對地址0x000000取出第一條指令執行復位中斷服務(wù)程序的方式啟動(dòng),即固定了復位后的起始地址為0x000000(PC = 0x000000)同時(shí)中斷向量表的位置并不是固定的。而Cortex-M3內核則正好相反,有3種情況:
1、通過(guò)boot引腳設置可以將中斷向量表定位于SRAM區,即起始地址為0x2000000,同時(shí)復位后PC指針位于0x2000000處;
2、通過(guò)boot引腳設置可以將中斷向量表定位于FLASH區,即起始地址為0x8000000,同時(shí)復位后PC指針位于0x8000000處;
3、通過(guò)boot引腳設置可以將中斷向量表定位于內置Bootloader區,本文不對這種情況做論述;
而Cortex-M3內核規定,起始地址必須存放堆頂指針,而第二個(gè)地址則必須存放復位中斷入口向量地址,這樣在Cortex-M3內核復位后,會(huì )自動(dòng)從起始地址的下一個(gè)32位空間取出復位中斷入口向量,跳轉執行復位中斷服務(wù)程序。對比ARM7/ARM9內核,Cortex-M3內核則是固定了中斷向量表的位置而起始地址是可變化的。
有了上述準備只是后,下面以STM32的2.02固件庫提供的啟動(dòng)文件“stm32f10x_vector.s”為模板,對STM32的啟動(dòng)過(guò)程做一個(gè)簡(jiǎn)要而全面的解析。
程序清單一:
;文件“stm32f10x_vector.s”,其中注釋為行號
DA
Stack_Size EQU 0x00000400;2
AREA STACK, NOINIT, READWRITE, ALIGN = 3;3
Stack_Mem SPACE Stack_Size;4
__initial_sp;5
Heap_Size EQU 0x00000400;6
AREA HEAP, NOINIT, READWRITE, ALIGN = 3;7
__heap_base;8
Heap_Mem SPACE Heap_Size;9
__heap_limit;10
THUMB;11
PRESERVE8;12
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
IMP
AREA RESET, DA
EXPORT __Vectors;83
__Vectors;84
DCD __initial_sp;85
DCD Reset_Handler;86
DCD NMIException;87
DCD HardFaultException;88
DCD MemManageException;89
DCD BusFaultException;90
DCD UsageFaultException;91
DCD 0;92
DCD 0;93
DCD 0;94
DCD 0;95
DCD SVCHandler;96
DCD DebugMonitor;97
DCD 0;98
DCD PendSVC;99
DCD SysTickHandler;100
DCD WWDG_IRQHandler;101
DCD PVD_IRQHandler;102
DCD TAMPER_IRQHandler;103
DCD RTC_IRQHandler;104
DCD FLASH_IRQHandler;105
DCD RCC_IRQHandler;106
DCD EXTI0_IRQHandler;107
DCD EXTI1_IRQHandler;108
DCD EXTI2_IRQHandler;109
DCD EXTI3_IRQHandler;110
DCD EXTI4_IRQHandler;111
DCD DMA1_Channel1_IRQHandler;112
DCD DMA1_Channel2_IRQHandler;113
DCD DMA1_Channel3_IRQHandler;114
DCD DMA1_Channel4_IRQHandler;115
DCD DMA1_Channel5_IRQHandler;116
DCD DMA1_Channel6_IRQHandler;117
DCD DMA1_Channel7_IRQHandler;118
DCD ADC1_2_IRQHandler;119
DCD USB_HP_CAN_TX_IRQHandler;120
DCD USB_LP_CAN_RX0_IRQHandler;121
DCD CAN_RX1_IRQHandler;122
DCD CAN_SCE_IRQHandler;123
DCD EXTI9_5_IRQHandler;124
DCD TIM1_BRK_IRQHandler;125
DCD TIM1_UP_IRQHandler;126
DCD TIM1_TRG_COM_IRQHandler;127
DCD TIM1_CC_IRQHandler;128
DCD TIM2_IRQHandler;129
DCD TIM3_IRQHandler;130
DCD TIM4_IRQHandler;131
DCD I2C1_EV_IRQHandler;132
DCD I2C1_ER_IRQHandler;133
DCD I2C2_EV_IRQHandler;134
DCD I2C2_ER_IRQHandler;135
DCD SPI1_IRQHandler;136
DCD SPI2_IRQHandler;137
DCD USART1_IRQHandler;138
DCD USART2_IRQHandler;139
DCD USART3_IRQHandler;140
DCD EXTI15_10_IRQHandler;141
DCD RTCAlarm_IRQHandler;142
DCD USBWakeUp_IRQHandler;143
DCD TIM8_BRK_IRQHandler;144
DCD TIM8_UP_IRQHandler;145
DCD TIM8_TRG_COM_IRQHandler;146
DCD TIM8_CC_IRQHandler;147
DCD ADC3_IRQHandler;148
DCD FSMC_IRQHandler;149
DCD SDIO_IRQHandler;150
DCD TIM5_IRQHandler;151
DCD SPI3_IRQHandler;152
DCD UART4_IRQHandler;153
DCD UART5_IRQHandler;154
DCD TIM6_IRQHandler;155
DCD TIM7_IRQHandler;156
DCD DMA2_Channel1_IRQHandler;157
DCD DMA2_Channel2_IRQHandler;158
DCD DMA2_Channel3_IRQHandler;159
DCD DMA2_Channel4_5_IRQHandler;160
AREA |.text|, CO
Reset_Handler PROC;162
EXPORT Reset_Handler;163
IF DA
LDR R0,= 0x00000114;165
LDR R1,= 0x40021014;166
STR R0,[R1];167
LDR R0,= 0x000001E0;168
LDR R1,= 0x40021018;169
STR R0,[R1];170
LDR R0,= 0x44BB44BB;171
LDR R1,= 0x40011400;172
STR R0,[R1];173
LDR R0,= 0xBBBBBBBB;174
LDR R1,= 0x40011404;175
STR R0,[R1];176
LDR R0,= 0xB44444BB;177
LDR R1,= 0x40011800;178
STR R0,[R1];179
LDR R0,= 0xBBBBBBBB;180
LDR R1,= 0x40011804;181
STR R0,[R1];182
LDR R0,= 0x44BBBBBB;183
LDR R1,= 0x40011C00;184
STR R0,[R1];185
LDR R0,= 0xBBBB4444;186
LDR R1,= 0x40011C04;187
STR R0,[R1];188
LDR R0,= 0x44BBBBBB;189
LDR R1,= 0x40012000;190
STR R0,[R1];191
LDR R0,= 0x44444B44;192
LDR R1,= 0x40012004;193
STR R0,[R1];194
LDR R0,= 0x00001011;195
LDR R1,= 0xA0000010;196
STR R0,[R1];197
LDR R0,= 0x00000200;198
LDR R1,= 0xA0000014;199
STR R0,[R1];200
ENDIF;201
IMP
LDR R0, =__main;203
BX R0;204
ENDP;205
ALIGN;206
IF :DEF:__MICROLIB;207
EXPORT __initial_sp;208
EXPORT __heap_base;209
EXPORT __heap_limit;210
ELSE;211
IMP
EXPORT __user_initial_stackheap;213
__user_initial_stackheap;214
LDR R0, = Heap_Mem;215
LDR R1, = (Stack_Mem + Stack_Size);216
LDR R2, = (Heap_Mem + Heap_Size);217
LDR R3, = Stack_Mem;218
BX LR;219
ALIGN;220
ENDIF;221
END;222
ENDIF;223
END;224
如程序清單一,STM32的啟動(dòng)代碼一共224行,使用了匯編語(yǔ)言編寫(xiě),這其中的主要原因下文將會(huì )給出交代?,F在從第一行開(kāi)始分析:
?第1行:定義是否使用外部SRAM,為1則使用,為0則表示不使用。此語(yǔ)行若用C語(yǔ)言表達則等價(jià)于:
#define DA
?第2行:定義??臻g大小為0x00000400個(gè)字節,即1Kbyte。此語(yǔ)行亦等價(jià)于:
#define Stack_Size 0x00000400
?第3行:偽指令AREA,表示
?第4行:開(kāi)辟一段大小為Stack_Size的內存空間作為棧。
?第5行:標號__initial_sp,表示??臻g頂地址。
?第6行:定義堆空間大小為0x00000400個(gè)字節,也為1Kbyte。
?第7行:偽指令AREA,表示
?第8行:標號__heap_base,表示堆空間起始地址。
?第9行:開(kāi)辟一段大小為Heap_Size的內存空間作為堆。
?第10行:標號__heap_limit,表示堆空間結束地址。
?第11行:告訴編譯器使用THUMB指令集。
?第12行:告訴編譯器以8字節對齊。
?第13—81行:IMP
?第82行:定義只讀數據段,實(shí)際上是在CO
?第83行:將標號__Vectors聲明為全局標號,這樣外部文件就可以使用這個(gè)標號。
?第84行:標號__Vectors,表示中斷向量表入口地址。
?第85—160行:建立中斷向量表。
?第161行:
?第162行:復位中斷服務(wù)程序,PROC…ENDP結構表示程序的開(kāi)始和結束。
?第163行:聲明復位中斷向量Reset_Handler為全局屬性,這樣外部文件就可以調用此復位中斷服務(wù)。
?第164行:IF…ENDIF為預編譯結構,判斷是否使用外部SRAM,在第1行中已定義為“不使用”。
?第165—201行:此部分代碼的作用是設置FSMC總線(xiàn)以支持SRAM,因不使用外部SRAM因此此部分代碼不會(huì )被編譯。
?第202行:聲明__main標號。
?第203—204行:跳轉__main地址執行。
?第207行:IF…ELSE…ENDIF結構,判斷是否使用DEF:__MICROLIB(此處為不使用)。
?第208—210行:若使用DEF:__MICROLIB,則將__initial_sp,__heap_base,__heap_limit亦即棧頂地址,堆始末地址賦予全局屬性,使外部程序可以使用。
?第212行:定義全局標號__use_two_region_memory。
?第213行:聲明全局標號__user_initial_stackheap,這樣外程序也可調用此標號。
?第214行:標號__user_initial_stackheap,表示用戶(hù)堆棧初始化程序入口。
?第215—218行:分別保存棧頂指針和棧大小,堆始地址和堆大小至R0,R1,R2,R3寄存器。
?第224行:程序完畢。
以上便是STM32的啟動(dòng)代碼的完整解析,接下來(lái)對幾個(gè)小地方做解釋?zhuān)?br />1、AREA指令:偽指令,用于定義代碼段或數據段,后跟屬性標號。其中比較重要的一個(gè)標號為“READONLY”或者“READWRITE”,其中“READONLY”表示該段為只讀屬性,聯(lián)系到STM32的內部存儲介質(zhì),可知具有只讀屬性的段保存于FLASH區,即0x8000000地址后。而“READONLY”表示該段為“可讀寫(xiě)”屬性,可知“可讀寫(xiě)”段保存于SRAM區,即0x2000000地址后。由此可以從第3、7行代碼知道,堆棧段位于SRAM空間。從第82行可知,中斷向量表放置與FLASH區,而這也是整片啟動(dòng)代碼中最先被放進(jìn)FLASH區的數據。因此可以得到一條重要的信息:0x8000000地址存放的是棧頂地址__initial_sp,0x8000004地址存放的是復位中斷向量Reset_Handler(STM32使用32位總線(xiàn),因此存儲空間為4字節對齊)。
2、DCD指令:作用是開(kāi)辟一段空間,其意義等價(jià)于C語(yǔ)言中的地址符“&”。因此從第84行開(kāi)始建立的中斷向量表則類(lèi)似于使用C語(yǔ)言定義了一個(gè)指針數組,其每一個(gè)成員都是一個(gè)函數指針,分別指向各個(gè)中斷服務(wù)函數。
3、標號:前文多處使用了“標號”一詞。標號主要用于表示一片內存空間的某個(gè)位置,等價(jià)于C語(yǔ)言中的“地址”概念。地址僅僅表示存儲空間的一個(gè)位置,從C語(yǔ)言的角度來(lái)看,變量的地址,數組的地址或是函數的入口地址在本質(zhì)上并無(wú)區別。
4、第202行中的__main標號并不表示C程序中的main函數入口地址,因此第204行也并不是跳轉至main函數開(kāi)始執行C程序。__main標號表示C/C++標準實(shí)時(shí)庫函數里的一個(gè)初始化子程序__main的入口地址。該程序的一個(gè)主要作用是初始化堆棧(對于程序清單一來(lái)說(shuō)則是跳轉__user_initial_stackheap標號進(jìn)行初始化堆棧的),并初始化映像文件,最后跳轉C程序中的main函數。這就解釋了為何所有的C程序必須有一個(gè)main函數作為程序的起點(diǎn)——因為這是由C/C++標準實(shí)時(shí)庫所規定的——并且不能更改,因為C/C++標準實(shí)時(shí)庫并不對外界開(kāi)發(fā)源代碼。因此,實(shí)際上在用戶(hù)可見(jiàn)的前提下,程序在第204行后就跳轉至.c文件中的main函數,開(kāi)始執行C程序了。
至此可以總結一下STM32的啟動(dòng)文件和啟動(dòng)過(guò)程。首先對棧和堆的大小進(jìn)行定義,并在代碼區的起始處建立中斷向量表,其第一個(gè)表項是棧頂地址,第二個(gè)表項是復位中斷服務(wù)入口地址。然后在復位中斷服務(wù)程序中跳轉??C/C++標準實(shí)時(shí)庫的__main函數,完成用戶(hù)堆棧等的初始化后,跳轉.c文件中的main函數開(kāi)始執行C程序。假設STM32被設置為從內部FLASH啟動(dòng)(這也是最常見(jiàn)的一種情況),中斷向量表起始地位為0x8000000,則棧頂地址存放于0x8000000處,而復位中斷服務(wù)入口地址存放于0x8000004處。當STM32遇到復位信號后,則從0x80000004處取出復位中斷服務(wù)入口地址,繼而執行復位中斷服務(wù)程序,然后跳轉__main函數,最后進(jìn)入mian函數,來(lái)到C的世界。
評論