STM32啓動文件詳解——學習筆記(3)

文章內容根據野火學習教程進行整理,僅僅是學習記錄。

開發板: 野火STM32F429-挑戰者V2
官方固件庫版本: STM32F4xx_DSP_StdPeriph_Lib_V1.8.0


啓動文件介紹

位置

啓動文件(例如:startup_stm32f429_439xx.s),該文件存在於官方固件庫的目錄位置爲:STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm
在這裏插入圖片描述
具體選擇哪份文件就根據所使用的芯片版本來決定。

作用

啓動文件由彙編編寫,是系統上電覆位後第一個執行的程序。主要做了以下工作:
1、 初始化堆棧指針 SP=_initial_sp
2、 初始化 PC 指針=Reset_Handler
3、 初始化中斷向量表
4、 配置系統時鐘
5、 調用 C 庫函數_main 初始化用戶堆棧,從而最終調用 main 函數去到 C 的世界

這個說明其實在每份啓動文件的最前面都有英文註釋
在這裏插入圖片描述


所使用到的彙編指令說明

啓動文件中所使用到的彙編指令解釋
要查找更詳細的彙編指令解釋可以打開ARM Development Tools進行搜索查詢。
使用搜索功能,點開標題爲Assembler User Guide(彙編用戶指南)的主題。
ARM Development Tools
在這裏插入圖片描述
在這裏插入圖片描述


啓動文件程序講解

;分號是註釋的意思,相當於C中的/* */ 或者 //。

棧初始化

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

解釋:
1、第一行:定義Stack_Size這個變量爲0x00000400即1K。
2、第二行:定義一個名字爲STACK的代碼段或數據段,不初始化,可讀可寫,8( 2^3)字節對齊。
3、第三行:分配一個大小爲Stack_Size的內存空間,單位爲字節。
4、第四行:__initial_sp 緊挨着 SPACE 語句放置,表示棧的結束地址,即棧頂地址,棧是由高向低生長的。
5、棧的作用是用於局部變量,函數調用,函數形參等的開銷,棧的大小不能超過內部SRAM 的大小。如果編寫的程序比較大,定義的局部變量很多,那麼就需要修改棧的大小。

堆初始化

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB
解釋:
1、第一行:定義Heap_Size這個變量爲0x00000200即512 字節。
2、第二行:定義一個名字爲HEAP的代碼段或數據段,不初始化,可讀可寫,8( 2^3)字節對齊。
3、第三行:堆的起始地址,用於計算。
4、第四行:分配一個大小爲Heap_Size的內存空間,單位爲字節。
5、第五行:堆的終止地址,用於計算。
6、第六行:指定當前文件的堆棧按照 8 字節對齊。
7、表示後面指令兼容 THUMB 指令。 THUBM 是 ARM 以前的指令集, 16bit,現在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。
8、用__heap_limit減去__heap_base即可得到堆的總空間。堆是由低向高生長的,跟棧的生長方向相反。
9、堆主要用來動態內存的分配,像 malloc()函數申請的內存就在堆上面。這個在 STM32裏面用的比較少。

向量表

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size
                
__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                省略。。。
                DCD     LTDC_ER_IRQHandler                ; LTDC error
                DCD     DMA2D_IRQHandler                  ; DMA2D
                                         
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

解釋:
1、第一行註釋:說明向量表映射到了RESET字段的地址0的位置。即第二行要定義的數據段。
2、第二行:定義一個數據段,名字爲 RESET,可讀
3、第三、四、五行:聲明 __Vectors、 __Vectors_End 和__Vectors_Size 這三個標號具有全局屬性,可供外部的文件調用。
如果是 IAR 編譯器,則使用的是 GLOBAL 這個指令。
4、第六行:分配一個堆內存,以四個字節對齊,並以ESR的入口地址(即函數指針)初始化,並放在向量表的起始地址__Vectors位置。ESR對於內核來說就是異常,對外設來說就是中端。
5、第七、八、九、十、十一行:分配堆內存,以四個字節對齊,並以ESR的入口地址(即函數指針)初始化。
6、第十一行:向量表結束地址。
7、第十二行:向量表的大小,用起始地址 - 終止地址。

復位程序

                AREA    |.text|, CODE, READONLY

; Reset handler
;WEAK:表示弱定義,如果外部文件優先定義了該標號則首先引用該標號,如果外部文件沒有聲明也不會出錯。這裏表示復位子程序可以由用戶在其他文件重新實現,這裏並不是唯一的。
;IMPORT:表示該標號來自外部文件,跟 C 語言中的 EXTERN 關鍵字類似。這裏表示 SystemInit 和__main 這兩個函數均來自外部的文件。
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

解釋:
1、第一行:定義一個名稱爲.text 的代碼段,可讀。
2、第二行:表示復位程序Reset_Handler的開始,PROC與第十行的ENDP成對出現,表示程序的開始與結束。
3、第三行:聲明Reset_Handler函數爲全局屬性,可以被其他文件調用。後面的WEAK表示該定義是弱引用。
4、第四行:插入SystemInit這個函數,該函數在別的地方定義的,不在本彙編文件的當中。
5、第五行:插入__main這個函數,該函數在別的地方定義的,不在本彙編文件的當中。
6、第六行:把SystemInit這個函數加載到R0這個寄存器。
7、第七行:執行R0這個寄存器即SystemInit這個函數並返回。
8、第八行:把__main這個函數加載到R0這個寄存器。
9、第九行:執行R0這個寄存器即__main這個函數但是不返回。這個時候跳轉到了C的世界,即C中的main函數。
10、第十行:與PROC成對出現,表示復位程序Reset_Handler結束。
11、IMPORT:表示該標號來自外部文件,跟 C 語言中的 EXTERN 關鍵字類似。這裏表示 SystemInit 和__main 這兩個函數均來自外部的文件。
12、表示弱定義,如果外部文件優先定義了該標號則首先引用該標號,如果外部文件沒有聲明也不會出錯。這裏表示復位子程序可以由用戶在其他文件重新實現,這裏並不是唯一的。

中斷程序

拿其中一個舉例,與復位程序類似

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
                 省略。。。
                 ALIGN
解釋:
1、第一行:表示中斷程序NMI_Handler的開始,PROC與第四行的ENDP成對出現,表示程序的開始與結束。
2、第二行:聲明NMI_Handler函數爲全局屬性,可以被其他文件調用。後面的WEAK表示該定義是弱引用。
3、第三行:跳轉到標號(.),也就是進入了無限循環。
4、第四行:與PROC成對出現,表示中斷程序NMI_Handler結束。
5、第五行:對指令或者數據存放的地址進行對齊,缺省表示 4 字節對齊(也就是ALIGN=2的意思)。
6、這裏面的WEAK的意思是弱引用,詳細的說就是如果別的地方有定義一個名字爲NMI_Handler的函數則不會進入本彙編函數中寫的NMI_Handler函數,也就不會執行到( B       .)這個命令。如果外部沒有定義名字爲NMI_Handler的函數則進入本彙編函數後會進入無限循環。

用戶堆棧初始化

                 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

                 END

解釋:
1、IF,ELSE,ENDIF:彙編的條件分支語句,跟 C 語言的 if ,else 類似。
2、END:文件結束。
3、如果定義了__MICROLIB則賦予標號__initial_sp(棧頂地址)、__heap_base(堆起始地址)、 __heap_limit(堆結束地址)全局屬性,可供外部文件調用。
4、如果沒有定義__MICROLIB則使用默認的 C 庫,然後初始化用戶堆棧大小,這部分有 C 庫函數__main 來完成,當初始化完堆棧之後,就調用 main函數去到 C 的世界。

參考文檔:【野火】零死角玩轉STM32—F429挑戰者V2
下載地址:http://products.embedfire.com/zh_CN/latest/stm32/ebf_stm32f429_tiaozhanzhe_v2.html
野火論壇:http://www.firebbs.cn/forum.php?fromuid=61258

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章