.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 //保存棧頂指針
至此,啓動文件就完成了執行微控制器從“復位”到“開始執行主函數”中間這段時間(稱爲啓動過程)所必須進行的工作。定義了主要的入口,生命了很多中斷
以上僅爲個人學習整理資料,由不足的地方還請提出批評