STM32:啟動過程
前言
MDK並沒有將所有的啟動檔案的所有配置開源,我們我們只能配置一部分啟動檔案的引數;本文我們瞭解一下這一部分可以配置的引數;
上電之後,CPU首先根據boot引腳選擇儲存器重對映區域,將該區域的地址重對映為地址偏移量為0;
CPU從地址偏移量為0的地址處開始執行;一開始執行的燒錄程式碼是xx.s啟動檔案,使用匯編語言編寫;
1 啟動方式
1.1 對於STM32的F0和F4開發板而言,一共有3種啟動方式;以下的儲存空間大小和地址是以大容量F1晶片作為參考的;不同晶片有一些差別;
注意下面儲存空間大小的單位都是byte,這是因為記憶體的資料線是8bit的;記憶體的資料線和微控制器的資料線是不同的匯流排;
boot[0:1] | 啟動地址 | 儲存空間 | 啟動方式 | 事項 | |
[0:x] |
0x0800 0000- 0x0807 FFFF |
512K bytes |
Flash啟動 |
使用JTAG或SWD下載 較為常用; |
|
[1:0] |
0x1FFF F000- 0x1FFF F7FF |
2K bytes |
系統儲存器啟動 |
使用串列埠下載,程式碼是通過bootloader搬運到flash中; 該區域記憶體儲了廠家燒錄的bootloader程式(即ISP程式),需要配合st提供的下載軟體; |
|
[1:1] |
0x2000 0000- 0x2001 0000 |
64K bytes |
內嵌SRAM啟動 |
使用JTAG或SWD下載,僅支援除錯模式;較為少用; 沒有掉電儲存程式的功能,需要新增巨集定義VECT_TAB_SRAM,配合指令碼檔案使用; |
1.2 對於STM32H7x3 而言,只有一個boot引腳,配合BOOT_ADD0/BOOT_ADD1 的組合配置,可以讓系統從兩個不同的區域啟動;
boot引腳 | 啟動地址 | 預設值 | 預設值啟動方式 | 事項 |
0 | BOOT_ADD0[15:0] | 0x0800 | Flash啟動 |
啟動地址高16位由BOOT_ADD0暫存器決定,低16位為0x0000; BOOT_ADD0暫存器可以修改,修改後掉電不丟失; 0x0000 0000 到 0x3FFF 0000 的儲存器地址都可以設定; |
1 | BOOT_ADD1[15:0] | 0x1FF0 | 系統儲存器啟動 |
2 堆疊
2.1 堆疊的彙編程式
1 ;********************** 目的是分配資料段用來做"棧"**********************2 ; 表示即將宣告資料段STACK用來做"棧",不用填入初始資料,可讀寫,首地址按照2^3對齊(8位元組對齊); 3 Stack_Size EQU 0x00001000 4 AREA STACK, NOINIT, READWRITE, ALIGN=3 5 Stack_Mem SPACE Stack_Size 6 __initial_sp 7 8 ; **********************目的是分配資料段用來做"堆"********************** 9 Heap_Size EQU 0x0000800 10 AREA HEAP, NOINIT, READWRITE, ALIGN=3 11 __heap_base 12 Heap_Mem SPACE Heap_Size 13 __heap_limit
2.2那麼什麼是堆疊呢?總結了一下,大概概括成了下面的表格形式;
STACK棧 |
主要用來儲存區域性變數,變數的記憶體塊;棧空間是從頂層開始向下生長的; 對於多級呼叫的函式入口地址,需要使用棧空間配合連線暫存器來儲存主調函式的地址; |
|
Stack_Mem | 表示棧的首地址,應該是給microLIB庫使用的; | |
__initial_sp | 棧標號,表示棧頂地址;也就是棧的記憶體最大地址; | |
HEAP堆 |
動態分配時使用的記憶體塊; 如果程式中沒有使用到動態記憶體分配的話,編譯器是不會編譯出"堆"空間的; |
|
__heap_base | 堆標號,表示"堆"的起始地址 | |
Heap_Mem | 表示堆的首地址,應該是給microLIB庫使用的; | |
__heap_limit | 堆標號,表示"堆"的結束地址 |
2.3至於堆疊標號Stack_Mem和Stack_Size大概是標識堆疊地址給microLIB庫使用的把,應該沒什麼用;如下所示
;MDK針對嵌入式推出了microLIB小型庫,在功能上是用來替代C標準庫的; ;下面程式碼的功能主要是決定要不要啟用microLIB庫;以下程式碼比較不重要; IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF
3 啟動檔案
;**************中斷向量表***************************************************************** AREA RESET, DATA, READONLY ; 即將宣告一個記憶體區域RESET,是資料段,只讀; EXPORT __Vectors ; 宣告"標識__Vectors"可以被外部檔案呼叫 EXPORT __Vectors_End ; 宣告"標識__Vectors_End"可以被外部檔案呼叫 EXPORT __Vectors_Size ; 宣告"標識__Vectors_Size"可以被外部檔案呼叫 __Vectors DCD __initial_sp ; 分配4位元組記憶體,初始化為棧頂地址,該記憶體開始的地址標識為"__Vectors" DCD Reset_Handler ; 分配4位元組記憶體,放入復位中斷服務函式的"地址標識Reset Handler" DCD NMI_Handler ; ;..... DCD 0 ; DCD WAKEUP_PIN_IRQHandler ; __Vectors_End ; 記憶體結束的地址標識為"__Vectors_End" __Vectors_Size EQU __Vectors_End - __Vectors ; 宣告標識"__Vectors_Size"用來表示中斷向量表的大小 ;**************彙編程式碼段:上電覆位中斷服務程式舉例*************************************************************** AREA |.text|, CODE, READONLY ; 即將宣告一個記憶體區域.text,是程式碼段,只讀; Reset_Handler PROC ;偽指令PROC和ENDP用來宣告一個程式,當前程式段標識為"Reset_Handler" EXPORT Reset_Handler [WEAK] ;聲明當前程式可被外部函式呼叫,為弱函式 IMPORT SystemInit ;引入檔案外部宣告的函式SystemInit() IMPORT __main ;引入檔案外部宣告的函式__main() LDR R0, =SystemInit ;將32bit函式入口地址放入R0暫存器中? BLX R0 ;跳轉到R0暫存器內的地址去執行? LDR R0, =__main ; BX R0 ; ENDP ; ;**************彙編程式碼段:NMI中斷服務程式舉例*************************************************************** NMI_Handler PROC ; EXPORT NMI_Handler [WEAK] ;這裡匯入了中斷服務程式,如果外部檔案寫了,那此處的弱函式就不使用了 B . ;B. 表示在這裡跳轉當前程式? 就是跳轉到前面匯入的程式 ENDP ;3.1 偽指令 相當於前處理器指令,編譯器在編譯時會進行替換,並不佔用記憶體空間;
EQU | 宣告常數,彙編的常數單位不是bit,而是byte |
AREA | 表示即將分配一個數據段或程式碼段;需要跟分配記憶體的SPACE指令一起使用; |
EXPORT | 宣告為可被外部檔案引用,主要提供給連結器用於連線庫檔案; |
IMPORT | 引入外部檔案宣告 |
WEAK | 弱宣告,如果外部檔案有相同的宣告,則編譯外部檔案的宣告,不編譯弱宣告 |
ALIGN | 宣告編譯器需要對指令或資料的存放地址進行對齊;預設預設值為32bit對齊 |
PRESERVE8 | 當前檔案的堆疊需按照8位元組對齊,也就是64bit資料線對齊; |
THUMB | 表示接下來的指令都是thumb指令集,cm3,cm7採用的是thumb-2指令集, |
3.2彙編指令
SPACE | 分配一段記憶體空間,並初始化這些記憶體空間為0; 需要跟宣告記憶體屬性的AREA偽指令一起使用; |
DCD | Define Constant Double-words;分配4位元組的記憶體空間用來儲存一個32bit的資料;並初始化這些記憶體空間; |
LDR | Load word;載入指令,將32bit資料存入目的暫存器中; |
B | Branch,跳轉到目標地址執行,執行指令集為ARM指令集 |
BX | Branch with Exchange;跳轉到目的地址執行,並且指令集從ARM指令集切換到thumb指令集; |
BLX |
Branch with Link and Exchange;thumb-2相容許多thumb指令,但是cortex-m3不支援當前指令; |
零散1 | [地址]:地址加了[],用來表示取出地址內的資料; |
零散2 | 彙編指令中的常數都是地址,自然數的格式為#常數; |