GD32 startup.s

     微控制器(單片機)上電後,是如何尋找到並執行主函數的呢?很顯然微控制器無法從硬件上定位主函數的入口地址,因爲使用Ç語言作爲開發語言後,變量/函數的地址便由編譯器在編譯時自行分配,這樣一來主函數的入口地址在微控制器的內部存儲空間中不再是絕對不變的。相信讀者都可以回答這個問題,答案也許大同小異,但肯定都有個關鍵詞,叫“啓動文件”,用英文單詞來描述是“引導程序”。啓動文件的作用便是負責執行微控制器從“復位”到“開始執行主函數”中間這段時間(稱爲啓動過程)所必須進行的工作
    .Cortex-M3內核規定,起始地址必須存放堆頂指針,而第二個地址則必須存放復位中斷入口向量地址,這樣在Cortex-M3的的內核復位後,會自動從起始地址的下一個32位空間取出復位中斷入口向量,跳轉執行復位中斷服務程序。對比ARM7 / 一個RM9內核,Cortex-M3內核則是固定了中斷向量表的位置而起始地址是可變化的。

<----------------------- - ------------------------------分割線----------------- - ------------------------------------>
    我所用到的芯片是GD32,GD32是我國兆易創新公司生產的完全兼容STM32系列的Cortex-M3處理器,具有幾大亮點:1,高主頻108MHz。性能提升30%以上,可超頻到120MHz  2,Flash零等待.STM32的72MHz需要兩個等待,其實兆易創新公司本來就是做閃存起家的,具有gFlash專利


3,採用ARM Cortex-M3新內核R2p1 .STM32採用R1p1,帶有一些缺陷4,性價比高.GD32比對應的STM32芯片一般便宜20%,某些芯片便宜30%以上

    一下是相對於STM32的對比:可以參考文檔:GD32與STM32區別點擊打開鏈接

    看完這個文檔,對於我們新手來說,一定是一頭霧水,那麼接下來我們來細細研究一下他的啓動文件的的的Startup.s。

<------------------------------------------------- -----分割線------------------------------------------- ----------->

一,啓動文件的作用
 (關於啓動代碼的作用,前面已經提到過了,這裏再囉嗦一下)
(1)初始堆棧指針SP;
(2)初始化程序計數器指針PC;
(3)設置堆,棧的大小;
(4)設置異常向量表的入口地址;
(5)配置外部SRAM作爲數據存儲器(這個由用戶配置,一般的開發板可沒有外部SRAM);
(6)設置Ç庫的分支入口__main最終用來調用主函數);
(7)在3.5版的啓動文件還調用了system_stm32f10x.c文件中的SystemIni()函數配置系統時鐘。

二,啓動代碼詳解

    1,棧 - 棧

Stack_Size EQU 0x400 
                    AREA STACK,NOINIT,READWRITE,ALIGN = 3 
Stack_Mem空間Stack_Size 
__initial_sp

分配名爲STACK,不初始化,可讀可寫,8(2 ^ 3)字節對齊的1KB空間。

    棧:局部變量,函數形參等棧的大小不能超過內部SRAM大小。

        AREA:彙編一個新的代碼段或者數據段

        堆棧段名,任意命名; NOINIT表示不初始化;

        READWRITE可讀可寫;

        ALIGN = 3(2 ^ 3 = 8字節對齊)。

        __initial_sp緊挨了空間放置,表示棧的結束地址,棧是從高往低生長,結束地址就是棧頂地址。

    &Stack_Size EQU 0x400定義了棧的大小,EQU相當於彙編中的宏定義,堆棧備選0x400,即16 ^ 2 * 4 = 1024字節

    &Stack_Mem SPACE Stack_Size分配連續的Stack_Size字節的存儲單元並初始化爲0

    &__ initial_sp標號,表示堆棧頂部位置

    2,堆

Heap_Size等於0x400 

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

         分配名爲HEAP,不初始化,可讀可寫,8(2 ^ 3)字節對齊的512字節空間.__ heap_base的堆的起始地址,__ heap_limit堆的結束地址。堆由低向生長。動態分配內存用到堆

    &Heap_Size EQU 0x400定義了堆的大小,大小和棧相同爲1K

    &__ heap_base的標號,代表堆棧底部地址__heap_limit標號,代表堆棧限制地址

    &  PRESERVE8指示編譯器8字節對齊.PRESERVE8指令指定當前文件保存堆棧八字節對齊。它設置PRES8編譯屬性以通知鏈接器。鏈接器檢查要求堆棧八字節對齊的任何代碼是否僅由保持堆棧八字節對齊的代碼直接或間接地調用

    &拇指表示後面指令兼容THUMB指令.THUBM是ARM以前的指令集,16位,現在的Cortex-M系列的都使用拇指2指令集,拇指2是32位的,兼容16位和32位的指令,是拇指的超級。

3,定位中斷向量表

; 帶有例外ISR地址的向量表條目
                    區域重置,數據,READONLY
                    EXPORT __Vectors
                    EXPORT __Vectors_End
                    EXPORT __Vectors_Size

__Vectors DCD __initial_sp; 堆棧頂部
                    DCD Reset_Handler; 矢量編號1,復位處理程序
                    DCD NMI_Handler; 矢量數字2,NMI處理程序
                    DCD HardFault_Handler; 矢量編號3,硬故障處理程序
                    DCD MemManage_Handler; 矢量編號4,MPU故障處理程序
                    DCD BusFault_Handler; 矢量編號5,總線故障處理程序
                    DCD UsageFault_Handler; 向量編號6,使用錯誤處理程序
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD SVC_Handler; 矢量編號11,SVCall處理程序
                    DCD DebugMon_Handler; 矢量編號12,調試監視器處理程序
                    DCD 0; 保留的
                    DCD PendSV_Handler; 矢量編號14,PendSV處理程序
                    DCD SysTick_Handler; 矢量編號15,SysTick處理程序

                    ; 外部中斷
                    DCD WWDG_IRQHandler; 矢量數16,窗口看門狗
                    DCD LVD_IRQHandler; 矢量編號17,通過EXTI線路檢測的LVD
                    DCD RTC_IRQHandler; 矢量編號18,RTC通過EXTI線
                    DCD FMC_IRQHandler; 矢量數字19,FMC
                    DCD RCC_IRQHandler; 矢量編號20,RCC
                    DCD EXTI0_1_IRQHandler; 矢量編號21,EXTI線0和EXTI線1
                    DCD EXTI2_3_IRQHandler; 矢量編號22,EXTI線2和EXTI線3
                    DCD EXTI4_15_IRQHandler; 矢量編號23,EXTI Line 4到EXTI Line 15
                    DCD TSI_IRQHandler; 矢量編號24,TSI
                    DCD DMA1_Channel1_IRQHandler; 向量編號25,DMA1通道1
                    DCD DMA1_Channel2_3_IRQHandler; 向量編號26,DMA1通道2和DMA1通道3
                    DCD DMA1_Channel4_5_IRQHandler; 向量編號27,DMA1通道4和DMA1通道5
                    DCD ADC1_CMP_IRQHandler; 矢量編號28,ADC1和比較器1-2
                    DCD TIMER1_BRK_UP_TRG_COM_IRQHandler; 向量編號29,TIMER1中斷,更新,觸發器和換向
                    DCD TIMER1_CC_IRQHandler; 向量編號30,TIMER1捕獲比較
                    DCD TIMER2_IRQHandler; 矢量編號31,TIMER2
                    DCD TIMER3_IRQHandler; 矢量數32,TIMER3
                    DCD TIMER6_DAC_IRQHandler; 矢量編號33,TIMER6和DAC
                    DCD 0; 保留的
                    DCD TIMER14_IRQHandler; 矢量編號35,TIMER14
                    DCD TIMER15_IRQHandler; 矢量編號36,TIMER15
                    DCD TIMER16_IRQHandler; 矢量編號37,TIMER16
                    DCD TIMER17_IRQHandler; 矢量編號38,TIMER17
                    DCD I2C1_EV_IRQHandler; 向量編號39,I2C1事件
                    DCD I2C2_EV_IRQHandler; 向量編號40,I2C2事件
                    DCD SPI1_IRQHandler; 矢量編號41,SPI1
                    DCD SPI2_IRQHandler; 矢量編號42,SPI2
                    DCD USART1_IRQHandler; 矢量數43,USART1
                    DCD USART2_IRQHandler; 矢量編號44,USART2
                    DCD 0; 保留的
                    DCD CEC_IRQHandler; 矢量編號46,CEC
                    DCD 0; 保留的
                    DCD I2C1_ER_IRQHandler; 向量編號48,I2C1錯誤
                    DCD 0; 保留的
                    DCD I2C2_ER_IRQHandler; 向量編號50,I2C2錯誤
                    DCD I2C3_EV_IRQHandler; 向量編號51,I2C3事件
                    DCD I2C3_ER_IRQHandler; 向量編號52,I2C3錯誤
                    DCD USB_FS_LP_IRQHandler; 矢量編號53,USB FS LP
                    DCD USB_FS_HP_IRQHandler; 矢量編號54,USB FS HP
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD USBWakeUp_IRQHandler; 矢量數字58,USB喚醒
                    DCD CAN1_TX_IRQHandler; 向量編號59,CAN1 TX
                    DCD CAN1_RX0_IRQHandler; 向量編號60,CAN1 RX0
                    DCD CAN1_RX1_IRQHandler; 向量編號61,CAN1 RX1
                    DCD CAN1_SCE_IRQHandler; 矢量編號62,CAN1 SCE     
                    DCD LCD_IRQHandler; 矢量編號63,LCD
                    DCD DMA1_Channel6_7_IRQHandler; 向量編號64,DMA1通道6和通道7
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD SPI3_IRQHandler; 矢量編號67,SPI3
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD 0; 保留的
                    DCD CAN2_TX_IRQHandler; 矢量編號86,CAN2 TX 
                    DCD CAN2_RX0_IRQHandler; 向量編號87,CAN2 RX0 
                    DCD CAN2_RX1_IRQHandler; 向量編號88,CAN2 RX1 
                    DCD CAN2_SCE_IRQHandler; 矢量編號89,CAN2 SCE              

                    空間0x5A

__Vectors_End
__Vectors_Size EQU __Vectors_End  -  __Vectors

    &   定義一個名爲RESET,可讀的數據段。並聲明__Vectors,__ Vectors_End和__Vectors_Size這三個標號可被外部的文件使用。

    &_Vectors爲向量表起始地址,__ Vectors_End爲向量表結束地址,兩個相減即可算出向量表大小。

    &向量表從FLASH的0地址開始放置,以4個字節爲一個單位,地址0存放的是棧頂地址,0X04存放的是復位程序的地址,以此類推。從代碼上看,向量表中存放的都是中斷服務函數的函數名,可我們知道ç語言中的函數名就是一個地址。

4,復位程序

AREA | .text |,CODE,READONLY

    &定義了一個名爲的.text,可讀的代碼段

; 重置處理程序
Reset_Handler PROC 
                    EXPORT Reset_Handler [弱] 
                    IMPORT __main 
                    IMPORT System_Init   
                    LDR R0,= System_Init 
                    BLX R0 
                    LDR R0,= __ main 
                    BX R0 
                    ENDP

    SystemInit()函數初始系統時鐘,然後調用C庫函數_main。復位中斷(復位入口矢量被硬件固定在地址0x0000_0004)的處理函數:復位子程序是系統上電後第一個執行的程序,Reset_Handler,它的作用就是將保存於閃存中的初始化數據複製到SRAM中,調用上面說到的SystemInit來初始化時鐘,接着跳轉到主執行。

     __main()是編譯系統提供的一個函數,負責完成庫函數的初始化和初始化應用程序執行環境,最後自動跳轉到主()

5,終端服務子程序

NMI_Handler PROC 
                    EXPORT NMI_Handler [弱] 
                    B. 
                    ENDP 

HardFault_Handler PROC 
                    EXPORT HardFault_Handler [弱] 
                    B. 
                    ENDP 

MemManage_Handler PROC 
                    EXPORT MemManage_Handler [WEAK] 
                    B. 
                    ENDP

    &啓動文件裏面已經幫我們寫好所有中斷的中斷服務函數,跟我們平時寫的中斷服務函數不一樣的就是這些函數都是空的,真正的中斷復服務程序需要我們在外部的Ç文件裏面重新實現,這裏只是提前佔了一個位置而已。如果我們在使用某個外設的時候,開啓了某個中斷,但是又忘記編寫配套的中斷服務程序或者函數名寫錯,那當中斷來臨的時,程序就會跳轉到啓動文件預先寫好的空的中斷服務程序中,並且在這個空函數中無線循環,即程序就死在這裏。

    &B:“”跳到一個,表示無限循環.B表示將程序流程轉移到另一個程序中

    &Default_Handler,這個是作爲其他所有中斷的默認處理函數,作用就是死循環,所以你假如開啓了某個中斷,請按照這裏面的中斷函數名給它寫中斷處理函數,例如串口中斷處理函數名是USART1_IRQHandler ,你開了串口中斷,如果不重寫USART1_IRQHandler,就默認執行Default_Handler,死循環了。而如果你有重寫,那麼中斷向量表中的處理函數的地址就會更新爲你自己寫的那個函數的地址了。

如圖6所示,用戶堆棧初始化

                ALIGN 

; ******************* ******************************* 
; 用戶堆棧和堆初始化
; ******************* ****************************** 
                 如果:DEF:__ MICROLIB 
                
                 EXPORT __initial_sp 
                 EXPORT __heap_base 
                 EXPORT __heap_limit 
                
                 其他
                
                 導入__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 

                 萬一

                 結束

    &ALIGN:對指令或者數據存放的地址進行對齊,後面會跟一個立即數缺省表示4字節對齊。

    &判斷是否定義了__MICROLIB,如果定義了則賦予標號__initial_sp(棧頂地址),__ heap_base(堆起始地址),__ heap_limit(堆結束地址)全局屬性,可供外部文件調用。如果沒有定義的__MICROLIB)則使用默認的ç庫,然後初始化用戶堆棧大小,這部分有ç庫函數__main來完成。

    &__ user_initial_stackheap,此處是初始化兩區的堆棧空間,堆是從低到高的增長,堆棧是由高向低生長的,兩個是互相獨立的數據段,並且不能交叉使用。

    &LDR R0,= HeapMem //保存堆始地址

            LDR R1,=(StackMem + Stack)//保存棧的大小

            LDR R2,=(HeapMem + Heap)//保存堆的大小 

            LDR R3,= StackMem //保存棧頂指針

至此,啓動文件就完成了執行微控制器從“復位”到“開始執行主函數”中間這段時間(稱爲啓動過程)所必須進行的工作。定義了主要的入口,生命了很多中斷


        以上僅爲個人學習整理資料,由不足的地方還請提出批評

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