Written By Benny_Cen
44B0启动代码隔一段时间没看,趁今天有空温习一下!!
最初的头文件:
INCLUDE option.inc
INCLUDE memcfg.inc
1.前面包括的头文件,是关于存储器的控制参数和汇编程序设置
2.然后是存储器空间地址的定义, 外设和存器空间信息: 中断控制、看门狗定时器、系统时钟、存储器控制、BDMA目的寄存器、7种模式预定义常数
3.然后这一段:
GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
[ THUMBCODE
CODE32 ;for start-up code for Thumb mode
]
这段检查是否使用tasm.exe进行编译,既是thumb指令编译了;
4.
中断向量宏定义:这里先熟悉两个ARM汇编指令,LDM(加载)和STM(存储),配置类型:IA 传送后地址+1,IB传送前地址+1,DA传送后地址-1,DB传送前地址-1,FD满递减,ED空递减,FA满递增,EA空递增,!为数据传送完毕后最后地址写入基址寄存器,简单说,STM第二功能是PUSH,LDM第二功能是POP
STMFD R13!,{R0,R4-R12,LR} ;将R0,R4-R12,LR放入R13指向的堆栈,放一次R13内容减一
LDMFD R13!,{R0,R4-R12,LR} ;将栈内容恢复至R0,R4-R12,LR 中注意并不是原来内容了,R13放一次内容加一
宏源码如下:
;------------------------------------------------------------------
MACRO ;中断向量宏定义
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;减SP指针为存跳转地址 sp=sp-4
stmfd sp!,{r0} ;将ro入栈 push r0
ldr r0,=$HandleLabel ;装载处理地址到ro(中断服务入口地址)
ldr r0,[r0] ;装载处理地址内容到r0,即是将HandleLabel的内容传给r0
str r0,[sp,#4] ;存储r0到栈中,r0放到[sp+4]
ldmfd sp!,{r0,pc} ;出栈并跳到r0指向的地址
MEND
;------------------------------------------------------------------
上面程序就是将中断服务程序地址装载到pc上,r0没变化
5.声明地址
IMPORT |Image$$RO$$Base| ; ROM code start 开始地址
IMPORT |Image$$RO$$Limit| ; RAM data starts after ROM program结束地址
IMPORT |Image$$RW$$Base| ; Pre-initialised variables
IMPORT |Image$$ZI$$Base| ; uninitialised variables
IMPORT |Image$$ZI$$Limit| ; End of variable RAM space
6.
代码段入口 AREA Init,CODE,READONLY 段标识,段名称,代码段,只读
;------------------------------------------------------------------
AREA Init,CODE,READONLY ;初始人代码段
ENTRY ;程序入口
ResetEntry ;定义各种模式处理程序
b ResetHandler ;复位处理
b HandlerUndef ;未定义处理
b HandlerSWI ;SWI中断处理
b HandlerPabort ;指令中止处理
b HandlerDabort ;数据中止处理
b . ;保留
b HandlerIRQ ;中断处理
b HandlerFIQ ;快速中断处理
;------------------------------------------------------------------
其中比较重要的是复位处理函数ResetHandler,b.是保留,该处地址存放空数据,具体看ARM 芯片手册
7.ResetHandler分析
ResetHandler是44B0在复位时首先调用的,看一下作了什么:
首先进行一系列寄存器复位,看门狗、时钟、锁相环置频率等,把初始值放到相应的空间,然后这一段:
;****************************************************
;设定存储器控制寄存器 *
;****************************************************
adr r0, ResetHandler ;和下面一样
ldr r1, =ResetHandler ;r1取ResetHandler
sub r0, r1, r0 ;其实是r0清0
ldr r1, =SMRDATA ;r1取地址,SMRDATA 寄存着很重要的数据
sub r0, r1, r0 ;其实是传给r0
ldmia r0, {r1-r13} ;r0开始的连续数据空间传给r1-r13
ldr r0, =0x01c80000 ;BWSCON Address
stmia r0, {r1-r13} ;把r1-r13传到BWSCON Address指向内容
来来去去,就是把SMRDATA数据传到0x01c80000中去
然后看SMRDATA数据:是数据空间的分配,bank0-bank7数据空间进行配置,DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0
置位看另一个头文件,总之是一些初始分配数据。
再看初始化堆栈:
;****************************************************
;初始化堆栈 *
;****************************************************
ldr sp, =SVCStack ;复位后位SVC模式
bl InitStacks ;跳转至InitStacks
8.InitStacks函数
;****************************************************
;* The function for initializing stack *
;****************************************************
InitStacks
;Do not use DRAM,such as stmfd,ldmfd......
;SVCstack is initialized before
;Under toolkit ver 2.50, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
mrs r0,cpsr ;传送cpsr至r0
bic r0,r0,#MODEMASK ;清除r0中某些位
orr r1,r0,#UNDEFMODE|NOINT
msr cpsr_cxsf,r1 ;UndefMode
ldr sp,=UndefStack
就是把cpsr中某些位修改后保存,sp指向堆栈,初始化堆栈空间
后面连续语句都是对其余模式进行堆栈初始化,最后mov pc,lr,函数返回
好了,执行完堆栈初始化,然后是设置中断处理了:
;****************************************************
;设置中断处理 *
;****************************************************
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0] ;就是将IsrIRQ函数地址给HandleIRQ
HandleIRQ是中断向量地址,ldr r0,=HandleIRQ 是把中断地址给r0,
HandlerIRQ HANDLER HandleIRQ
先前进行了宏定义,向量表是进行宏替换,中断时跳入处理
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;中断向量表
;Example: HandlerADC HANDLE HandleADC 解为
;HandlerADC ;HandlerADC为中断向量表的入口
; sub sp,sp,#4 ;将sp减少一个字节,使其在堆栈高端留出存储返回地址,因为pc在寄存器组中的的位置大于r0,出栈时装入的是栈的高端的内容
; stmfd sp!,{r0} ;保存r0
; ldr r0,=HandleADC ;装载中断处理函数的指针
; ldr r0,[r0] ;装载中断处理函数的地址
; str r0,[sp,#4] ;将中断处理函数的地址存入刚才预留的位置,r0的上面
; ldmfd sp!,{r0,pc} ;出栈后,pc指向的既是中断处理函数的地址 ; INTCON^2 == 0时,vector table使能
; 发生中断->HandlerADC->HandleADC(pISR_ADC,即:_ISR_STARTADDRESS+0x20);
; 若要在程序中处理此中断,只要将中断服务函数的指针赋给pISR_ADC,如:pISR_ADC = (int)ADCIsr ;
设置中断处理,把IsrIRQ函数(服务程序)传到HandleIRQ处
9.数据拷贝
设置中段处理后,接下来进行数据拷贝,是把flash中数据拷贝到RAM中。
BaseOfROM DCD |Image$$RO$$Base|是ROM基址
TopOfROM DCD |Image$$RO$$Limit|是ROM结束地址
BaseOfBSS DCD |Image$$RW$$Base|已初始地址
BaseOfZero DCD |Image$$ZI$$Base|;未初始化基址
EndOfBSS DCD |Image$$ZI$$Limit| ; 未初始化结束地址
然后看开始一段:
;****************************************************
;拷贝并粘贴 RW data/zero initialized data *
;****************************************************
adr r0, ResetEntry ;装载入口地址
ldr r1, BaseOfROM ;r1装载flash基址
cmp r0, r1 ;比较是否地址重叠
ldreq r0, TopOfROM ;是的就装RAM data starts after
beq InitRamData ;同时调用RAM初始化
执行后,r0为入口地址,r1装载Flash起始地址
;****************************************************
;计算拷贝程序在flash中的实际位置 *
;****************************************************
ldr r2, =CopyProcBeg ;装载CopyProcBeg入口所在地址
sub r1, r2, r1 ;r1是flash基址,计算偏移相对地址
add r0, r0, r1 ;r0是入口地址
ldr r3, =CopyProcEnd ;r3装载CopyProcBeg结束所在地址
执行后,r2保存着CopyProcBeg入口所在地址,r1得到相对地址,然后加上入口地址,r0保存RAM中应在地址,r3保存CopyProcBeg结束所在地址
10.RAM初始化
11.跳到主程序Main运行
[ :LNOT:THUMBCODE
BL Main ;从汇编进入C语言代码空间,不要使用main()
B .
]
[ THUMBCODE ;for start-up code for Thumb mode
orr lr,pc,#1
bx lr
CODE16
bl Main ;从汇编进入C语言代码空间,不要使用main()
b .
CODE32
]
然后就从C程序中的Main程序执行!