|
啟動代碼的一般作用
1、堆和棧的初始化;
2、向量表定義;
3、地址重映射及中斷向量表的轉移;
4、初始化有特殊要求的斷口;
5、處理器模式;
6、進入C應用程序。
ARM復位后程序從0x00地址開始執行代碼,所以一般都會有將Flash地址映射到0x00的過程。但對于這一款Cortex M0的啟動代碼比較簡單,從存儲分布圖中我們可以看到LPC11C14擁有32K的片內Flash,地址范圍是0x0000 0000 ~ 0x0000 8000,當我們將程序(小于32K)燒寫進片內Flash時,啟動代碼中就可以不用再對Flash的地址重新映射。
NXP LPC11C14存儲分布圖主要看Flash
0.jpg (51.8 KB, 下載次數: 219)
下載附件
2015-1-10 20:28 上傳
CortexM0的啟動代碼進行分析:
一、堆棧初始化部分
在程序開始處,首先定義棧的大小及屬性,然后對堆進行初始化操作,ARM-Thumb過程調用標準和ARM、Thumb C/C++ 編譯器總是使用滿減(Full descending)類型堆棧。
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00000020 //定義堆棧大小
AREA STACK, NOINIT, READWRITE, ALIGN=3 //定義一個數據段按8個字節對齊 AREA偽指令用于定義一//個代碼段或者數據段
//NOINIT定義此數據段僅僅保留了內存單元,而沒有將各初
//始值寫入內存單元,或者將各個內存單元值初始化為0
Stack_Mem SPACE Stack_Size //保留Stack_Size 大小的堆棧空間,來分配連續Stack_Size
//節的存儲單元并初始化為0
__initial_sp //標號--堆棧頂部地址。M0中堆棧式滿遞減堆棧,堆棧指針位//于堆棧的高地址
Stack_Size EQU 0x00000020 //定義堆棧大小
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000000 // 定義堆空間大小
AREA HEAP, NOINIT, READWRITE, ALIGN=3 // 定義了一個數據段,8字節對齊
__heap_base // 標號--代表為堆末底部地址
Heap_Mem SPACE Heap_Size // 保留Heap_Size的堆空間
__heap_limit // 標號--堆的界限地址
PRESERVE8 // 指令指定當前文件保持堆棧8字節對齊。它設置PRES8編譯屬性,以
//通知鏈接器。鏈接器檢查要求堆棧8字節對齊的任何代碼是否僅由保持//堆棧8字節對齊的代碼直接或者間接地調用。
THUMB //指示編譯器以后的偽指令為Thum指令
二、中斷量表定義
在MDK生成分散加載文件中,RESET被設置在flash的0地址處,這樣就規定了向量表的地址。
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY //定義只讀數據段,位于0地址,其實放在CODE區
//EXPORT在程序中聲明一個全局的標號__Vetors號可
//可以在其他文件中使用
/*
DCD偽指令用于分配一片連續的字存儲單元并用偽指令中指定的表達式初始化。其中,表達式可以為程序標號或數字表達式。DCD也可用“&”代替。
*/
__Vectors DCD __initial_sp ; Top of Stack // 給__initial_sp分配4字節32位的地址
DCD Reset_Handler ; Reset Handler//給標號Reset_Handler分配地址
DCD NMI_Handler ; NMI Handler //給標號NMI_Handler分配地址
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler; Usage Fault Handler
DCD 0; Reserved //保留的,不給任何標號分配
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WAKEUP_IRQHandler ; 15 wakeup sources for all the
DCD WAKEUP_IRQHandler ; I/O pins starting from PIO0 (0:11)
DCD WAKEUP_IRQHandler ; all 40 are routed to the same ISR
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler ; PIO1 (0:11)
DCD CAN_IRQHandler ; CAN
DCD SSP1_IRQHandler ; SSP1
DCD I2C_IRQHandler ; I2C
DCD TIMER16_0_IRQHandler ; 16-bit Timer0
DCD TIMER16_1_IRQHandler ; 16-bit Timer1
DCD TIMER32_0_IRQHandler ; 32-bit Timer0
DCD TIMER32_1_IRQHandler ; 32-bit Timer1
DCD SSP0_IRQHandler ; SSP0
DCD UART_IRQHandler ; UART
DCD USB_IRQHandler ; USB IRQ
DCD USB_FIQHandler ; USB FIQ
DCD ADC_IRQHandler ; A/D Converter
DCD WDT_IRQHandler ; Watchdog timer
DCD BOD_IRQHandler ; Brown Out Detect
DCD FMC_IRQHandler ; IP2111 Flash Memory Controller
DCD PIOINT3_IRQHandler ; PIO INT3
DCD PIOINT2_IRQHandler ; PIO INT2
DCD PIOINT1_IRQHandler ; PIO INT1
DCD PIOINT0_IRQHandler ; PIO INT0
IF :LNOT::DEF:NO_CRP //進行宏定義判斷
AREA |.ARM.__at_0x02FC|, CODE, READONLY
/****************************************************************************************************************************************************************
這有幾個關鍵的地方“NO_CRP”、 0x02FC和0xFFFFFFFF,如果我們在前面定義有“NO_CRP”,那么我們后面的代碼也就不起作用了,所以在需要加密的時候前面就一定不能再定義了 代碼讀保護,也就是加密的關鍵字,經過加密后芯片再也無法擦除,除非之前燒寫的程序帶有IAP,IAP可以使芯片進入ISP模式。
****************************************************************************************************************************************************************/
CRP_Key DCD 0xFFFFFFFF // 加密等級
ENDIF
AREA |.text|, CODE, READONLY //代碼段定義
利用PROC,ENDP這一對偽指令把程序段分為若干個過程,是程序的結構更加清晰
; Reset Handler
Reset_Handler PROC//過程的開始
EXPORT Reset_Handler [WEAK] //[WEAK]弱定義,意思是如果在別處也定義該標
//(函數),在連接時,用別處的地址。如果沒有
//他地方定義,編譯器也不報錯,以此處地址進行鏈接
IMPORT __main // 通知編譯要使用標號在其他文件
LDR R0, =__main //使用“=”表示LDR目前是偽指令,不是標準指令。把__main
//地址賦給R0。
BX R0 //BX是ARM指令集和THUMB指令集之間程序的跳轉
ENDP //過程結束
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .//原地跳轉(即無限循環)
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WAKEUP_IRQHandler [WEAK]
EXPORT CAN_IRQHandler [WEAK]
EXPORT SSP1_IRQHandler [WEAK]
EXPORT I2C_IRQHandler [WEAK]
EXPORT TIMER16_0_IRQHandler [WEAK]
EXPORT TIMER16_1_IRQHandler [WEAK]
EXPORT TIMER32_0_IRQHandler [WEAK]
EXPORT TIMER32_1_IRQHandler [WEAK]
EXPORT SSP0_IRQHandler [WEAK]
EXPORT UART_IRQHandler [WEAK]
EXPORT USB_IRQHandler [WEAK]
EXPORT USB_FIQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT WDT_IRQHandler [WEAK]
EXPORT BOD_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT PIOINT3_IRQHandler [WEAK]
EXPORT PIOINT2_IRQHandler [WEAK]
EXPORT PIOINT1_IRQHandler [WEAK]
EXPORT PIOINT0_IRQHandler [WEAK]
WAKEUP_IRQHandler
CAN_IRQHandler
SSP1_IRQHandler
I2C_IRQHandler
TIMER16_0_IRQHandler
TIMER16_1_IRQHandler
TIMER32_0_IRQHandler
TIMER32_1_IRQHandler
SSP0_IRQHandler
UART_IRQHandler
USB_IRQHandler
USB_FIQHandler
ADC_IRQHandler
WDT_IRQHandler
BOD_IRQHandler
FMC_IRQHandler
PIOINT3_IRQHandler
PIOINT2_IRQHandler
PIOINT1_IRQHandler
PIOINT0_IRQHandler
B .
ENDP
ALIGN填充字節使地址對齊
; User Initial Stack & Heap
三、堆和棧的初始化
IF :DEF:__MICROLIB // “DEF”的用法:DEF:xx就是說xx定義了則為真,否則為假
EXPORT __initial_sp //則將棧頂定制
EXPORT __heap_base // 堆起始地址賦予全局屬性
EXPORT __heap_limit //堆末端界限地址賦予全局屬性,使外部程序可調用
ELSE //如果沒有定義__MICROLIB,則使用默認的C運行時庫
IMPORT __use_two_region_memory // 通知編譯器要使用的標號在其他文件//__use_two_region_memory
EXPORT __user_initial_stackheap // 聲明全局標號__user_initial_stackheap,這樣外程序也可調用此標號
//則進行堆棧和堆的賦值,在__main函數執行過程中調用
//如果了使用默認的C庫,程序啟動過程中不會執行標號下//的代碼
/**************************************************************************************************************************************************************
_user_initial_stackheap() 返回:
· r0 中的堆基址
· r1 中的堆棧基址,即堆棧區中的最高地址
· r2 中的堆限制
· r3 中的堆棧限制,即堆棧區中的最低地址。
有單區模型和雙區模型。
單區模型:(r0,r1)是單個堆棧和堆區。r1 大于 r0,并忽略 r2和r3。
r0--r1這一塊內存區域被堆和棧共用,堆從r0向上生長,棧從r1向下生長。
雙區模型:(r0, r2)是初始堆,(r3, r1) 是初始堆棧。r2 大于或等于r0,r3小于r1。
堆和棧分別指定了單獨的內存區域。
**************************************************************************************************************************************************************/
__user_initial_stackheap // 標號__user_initial_stackheap,表示擁護堆棧初始化程序入口
//則進行堆棧和堆得賦值,在__main函數執行過程中調用
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
|
|