國產(chǎn)超低功耗華大單片啟動文件詳解及SystemInit函數(shù)分析
啟動代碼文件名一般可命名為如startup_hc32xxxx.s。啟動代碼作用一般是:
1) 堆和棧的初始化
包括堆棧的大小,MSP(main stack pointer)值等。MSP的初始值在復(fù)位階段取自存儲區(qū)的第一個字(即0地址處的值)。
棧Stack: 由編譯器自動分配和釋放,存放函數(shù)的參數(shù)值、局部變量的值等,其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。向低地址擴展。
堆Heap: 一般由程序員分配和釋放,若程序員不釋放,程序結(jié)束時可能由操作系統(tǒng)回收。分配方式類似于數(shù)據(jù)結(jié)構(gòu)中的鏈表。向高地址擴展。
2)向量表定義
定義了各MSP初值,以及各個中斷服務(wù)程序(ISR)的入口地址。
3)中斷服務(wù)程序
CPU根據(jù)中斷號從向量表中獲取入口地址后,跳轉(zhuǎn)至對應(yīng)的ISR
4)設(shè)置系統(tǒng)時鐘頻率
可在復(fù)位中斷服務(wù)程序Reset_handler中調(diào)用系統(tǒng)時鐘頻率初始化程序
5) 中斷寄存器的初始化
6)進入C應(yīng)用程序
在復(fù)位中斷服務(wù)程序即Reset_handler中實現(xiàn)進入C程序。如:
LDR R0 , =_main ;使用“=”表示LDR目前是偽指令。這里是把_main的地址給R0。
BX R0 ; BX是ARM指令集和THUMB指令集之間程序的跳轉(zhuǎn)。
以下以華大單片機HC32L136K8TA的啟動文件為例,對1) — 6)進行詳細分析。
;先在RAM中分配系統(tǒng)使用的棧,RAM的起始地址為0x2000_0000
;然后在RAM中分配變量使用的堆
;然后在CODE區(qū)(flash)分配中斷向量表,flash的起始地址為0x0000_0000,該中斷向量表就從這個起始地址開始分配
;分配完成后,再定義和實現(xiàn)相應(yīng)的中斷函數(shù),
;所有的中斷函數(shù)全部帶有[weak]特性,即弱定義,如果編譯器發(fā)現(xiàn)在別處文件中定義了同名函數(shù),在鏈接時用別處的地址進行鏈接。
;中斷函數(shù)僅僅實現(xiàn)了Reset_Handler,其他要么是死循環(huán),要么僅僅定義了函數(shù)名稱
;HC32被設(shè)置為從內(nèi)部FLASH啟動時(這也是最常見的一種情況),當(dāng)HC32遇到復(fù)位信號后,
;從0x0000_0000處取出棧頂?shù)刂反娣庞贛SP寄存器,從0x0000_0004處取出復(fù)位中斷服務(wù)入口地址放入PC寄存器,
;繼而執(zhí)行復(fù)位中斷服務(wù)程序Reset_Handler,
;Reset_Handler僅僅執(zhí)行了兩個函數(shù)調(diào)用,一個是SystemInit,另一個__main,
;SystemInit定義在system_hc32xxxx.c中,主要初始化了HC的時鐘系統(tǒng):RCH,RCL,XTH,XTL,PLL,SystemClk,HCLK,PCLK等等.
;__main函數(shù)由編譯器生成,負責(zé)初始化棧、堆等,并在最后跳轉(zhuǎn)到用戶自定義的main()函數(shù),來到C的世界。
HC32L136K8TA芯片的中斷向量表如下圖所示:
; Stack Configuration; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>Stack_Size EQU 0x00000200 ;定義堆棧大小AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定義一個數(shù)據(jù)段 按8字節(jié)對齊 ;AREA 偽指令用于定義一個代碼段或數(shù)據(jù)段 ;NOINIT:指定此數(shù)據(jù)段僅僅保留了內(nèi)存單元, ;而沒有將各初始值寫入內(nèi)存單元, ;或者將各個內(nèi)存單元值初始化為0Stack_Mem SPACE Stack_Size ;保留Stack_Size大小的堆?臻g分配連續(xù) ;Stack_Size字節(jié)的存儲單元并初始化為 0 __initial_sp ;標(biāo)號,代表堆棧頂部地址,后面有用; Heap Configuration; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>Heap_Size EQU 0x00000000 ;定義堆空間大小AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;定義一個數(shù)據(jù)段,8字節(jié)對齊__heap_base ;標(biāo)號,代表堆末底部地址,后面有用Heap_Mem SPACE Heap_Size ;保留Heap_Size的堆空間 __heap_limit ;標(biāo)號,代表堆界限地址,后面有用 ;PRESERVE8 指令指定當(dāng)前文件保持堆棧八字節(jié)對齊。 ;它設(shè)置 PRES8 編譯屬性以通知鏈接器。 ;鏈接器檢查要求堆棧八字節(jié)對齊的任何代碼是否僅由 ;保持堆棧八字節(jié)對齊的代碼直接或間接地調(diào)用。PRESERVE8 ;指示編譯器8字節(jié)對齊THUMB ;指示編譯器以后的指令為THUMB指令; Vector Table Mapped to Address 0 at Reset ;中斷向量表定義AREA RESET, DATA, READONLY;定義只讀數(shù)據(jù)段,其實放在CODE區(qū),位于0地址EXPORT __Vectors ;EXPORT:在程序中聲明一個全局的標(biāo)號__Vectors, ;該標(biāo)號可在其他的文件中引用EXPORT __Vectors_EndEXPORT __Vectors_Size__Vectors DCD __initial_sp ;Top of Stack;給__initial_sp分配4字節(jié)32位的地址0x0DCD Reset_Handler ;Reset ; 給標(biāo)號Reset Handler分配地址為0x00000004 DCD NMI_Handler ; NMI; 給標(biāo)號NMI Handler分配地址0x00000008DCD HardFault_Handler ; Hard FaultDCD 0 ; Reserved ; 這種形式就是保留地址,不給任何標(biāo)號分配DCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCallDCD 0 ; ReservedDCD 0 ; ReservedDCD PendSV_Handler ; PendSVDCD SysTick_Handler ; SysTickDCD PORTA_IRQHandlerDCD PORTB_IRQHandlerDCD PORTC_IRQHandlerDCD PORTD_IRQHandlerDCD DMAC_IRQHandlerDCD TIM3_IRQHandlerDCD UART0_IRQHandlerDCD UART1_IRQHandlerDCD LPUART0_IRQHandlerDCD LPUART1_IRQHandlerDCD SPI0_IRQHandlerDCD SPI1_IRQHandlerDCD I2C0_IRQHandlerDCD I2C1_IRQHandlerDCD TIM0_IRQHandlerDCD TIM1_IRQHandlerDCD TIM2_IRQHandlerDCD LPTIM_IRQHandlerDCD TIM4_IRQHandlerDCD TIM5_IRQHandlerDCD TIM6_IRQHandlerDCD PCA_IRQHandlerDCD WDT_IRQHandlerDCD RTC_IRQHandlerDCD ADC_IRQHandlerDCD PCNT_IRQHandlerDCD VC0_IRQHandlerDCD VC1_IRQHandlerDCD LVD_IRQHandlerDCD LCD_IRQHandlerDCD EF_RAM_IRQHandlerDCD CLKTRIM_IRQHandler __Vectors_End__Vectors_Size EQU __Vectors_End - __VectorsAREA |.text|, CODE, READONLY ;代碼段定義; Reset Handler ;利用PROC、ENDP這一對偽指令把程序段分為若干 ;個過程,使程序的結(jié)構(gòu)加清晰Reset_Handler PROC ;過程的開始EXPORT Reset_Handler [WEAK] ;[WEAK] 弱定義,意思是如果在別處也定義該標(biāo)號 ;(函數(shù)),在鏈接時用別處的地址。 如果沒有其它地方 ;定義,編譯器也不報錯,以此處地址進行鏈接。 ;EXPORT提示編譯器該標(biāo)號可以為外部文件引用。IMPORT SystemInit ;代碼中的系統(tǒng)初始化程序IMPORT __main ;通知編譯器要使用的標(biāo)號在其他文件;reset NVIC if in rom debug LDR R0, =0x20000000LDR R2, =0x0MOVS R1, #0 ; for warning,ADD R1, PC,#0 ; for A1609W,CMP R1, R0BLS RAMCODE; ram code base address.ADD R2, R0,R2RAMCODE; reset Vector table address.LDR R0, =0xE000ED08STR R2, [R0]LDR R0, =SystemInit BLX R0 ;BX是ARM指令集和THUMB指令集之間程序的跳轉(zhuǎn) LDR R0, =__main ;使用“=”表示LDR目前是偽指令不是標(biāo)準(zhǔn)指令。 ;這里是把__main的地址給RO。BX R0 ;BX是ARM指令集和THUMB指令集之間程序的跳轉(zhuǎn)ENDP ;過程的結(jié)束; Dummy Exception Handlers (infinite loops which can be modified)NMI_Handler PROCEXPORT NMI_Handler [WEAK]B .ENDPHardFault_Handler\PROCEXPORT HardFault_Handler [WEAK]B . ;原地跳轉(zhuǎn)(即無限循環(huán)),等同于while(1);ENDPSVC_Handler PROCEXPORT SVC_Handler [WEAK]B .ENDPPendSV_Handler PROCEXPORT PendSV_Handler [WEAK]B .ENDPSysTick_Handler PROCEXPORT SysTick_Handler [WEAK]B .ENDPDefault_Handler PROCEXPORT PORTA_IRQHandler [WEAK]EXPORT PORTB_IRQHandler [WEAK]EXPORT PORTC_IRQHandler [WEAK]EXPORT PORTD_IRQHandler [WEAK]EXPORT DMAC_IRQHandler [WEAK]EXPORT TIM3_IRQHandler [WEAK]EXPORT UART0_IRQHandler [WEAK]EXPORT UART1_IRQHandler [WEAK]EXPORT LPUART0_IRQHandler [WEAK]EXPORT LPUART1_IRQHandler [WEAK]EXPORT SPI0_IRQHandler [WEAK]EXPORT SPI1_IRQHandler [WEAK]EXPORT I2C0_IRQHandler [WEAK]EXPORT I2C1_IRQHandler [WEAK]EXPORT TIM0_IRQHandler [WEAK]EXPORT TIM1_IRQHandler [WEAK]EXPORT TIM2_IRQHandler [WEAK]EXPORT LPTIM_IRQHandler [WEAK]EXPORT TIM4_IRQHandler [WEAK]EXPORT TIM5_IRQHandler [WEAK]EXPORT TIM6_IRQHandler [WEAK]EXPORT PCA_IRQHandler [WEAK]EXPORT WDT_IRQHandler [WEAK]EXPORT RTC_IRQHandler [WEAK]EXPORT ADC_IRQHandler [WEAK]EXPORT PCNT_IRQHandler [WEAK]EXPORT VC0_IRQHandler [WEAK]EXPORT VC1_IRQHandler [WEAK]EXPORT LVD_IRQHandler [WEAK]EXPORT LCD_IRQHandler [WEAK]EXPORT EF_RAM_IRQHandler [WEAK]EXPORT CLKTRIM_IRQHandler [WEAK]PORTA_IRQHandlerPORTB_IRQHandlerPORTC_IRQHandlerPORTD_IRQHandlerDMAC_IRQHandlerTIM3_IRQHandlerUART0_IRQHandlerUART1_IRQHandlerLPUART0_IRQHandlerLPUART1_IRQHandlerSPI0_IRQHandlerSPI1_IRQHandlerI2C0_IRQHandlerI2C1_IRQHandlerTIM0_IRQHandlerTIM1_IRQHandlerTIM2_IRQHandlerLPTIM_IRQHandlerTIM4_IRQHandlerTIM5_IRQHandlerTIM6_IRQHandlerPCA_IRQHandlerWDT_IRQHandlerRTC_IRQHandlerADC_IRQHandlerPCNT_IRQHandlerVC0_IRQHandlerVC1_IRQHandlerLVD_IRQHandlerLCD_IRQHandlerEF_RAM_IRQHandlerCLKTRIM_IRQHandler B .ENDPALIGN ;填充字節(jié)使地址對齊; User Initial Stack & Heap ;堆和棧的初始化IF :DEF:__MICROLIB ;“DEF”的用法:DEF:X就是說X定義了則為真,否則為 ;如果定義了MICORLIB,EXPORT __initial_sp ;則將棧頂?shù)刂,EXPORT __heap_base ;堆起始地址賦予全局屬性,EXPORT __heap_limit ;堆末端界限地址賦予全局屬性,使外部程序可調(diào)用ELSE ;如果沒定義__MICROLIB,則使用默認的C運行時庫IMPORT __use_two_region_memory ;通知編譯器要使用的標(biāo)號在其他文件 ;__use_two_region_memory EXPORT __user_initial_stackheap ;聲明全局標(biāo)號__user_initial_stackheap, ;這樣外程序也可調(diào)用此標(biāo)號,則進行堆棧和堆的賦 ;值,在__main函數(shù)執(zhí)行過程中調(diào)用, 如果使用默認的 ;C庫,程序啟動過程中就不會執(zhí)行該標(biāo)號下的代碼__user_initial_stackheap ;標(biāo)號,表示用戶堆棧初始化程序入口 ;進行堆棧和堆的賦值,在_main函數(shù)執(zhí)行過程中調(diào)用。LDR R0, = Heap_Mem ;保存堆始地址LDR R1, =(Stack_Mem + Stack_Size) ;保存棧的大小LDR R2, = (Heap_Mem + Heap_Size) ;保存堆的大小LDR R3, = Stack_Mem ;保存棧頂指針BX LRALIGN ;填充字節(jié)使地址對齊ENDIFEND
編輯:admin 最后修改時間:2020-09-07