;======================================================================================
; 文件名: 2440INIT.s
; 描述: 1).ARM 指令集,
; 2).小端格式
; 3).NOR Flash总线16位,大小2MB
; 4).NAND Flash型号:K9F2G08,大小256MB
; 5).GPIO总线32位,
; 更改日期: 2012年1月31日
;======================================================================================
GET 2440addr.inc ; 引入寄存器地址
_STACK_BASEADDRESS EQU 0x33FF8000 ; 堆栈基地址
_MMUTT_STARTADDRESS EQU 0x33ff8000 ; 这个没用着啊~
_ISR_STARTADDRESS EQU 0x33FFFF00 ; 定义第一级中断向量表的寻址地址
;============================== 系统时钟参数 ================================
; UCLK:UPLL固定为1:1,其中UCLK必须为48MHz(p7-24),UPLL由UPLLCON设置,后面的代码将其设置为48MHz
; 可选择的是 Fclk:Hclk:Pclk 分频比,该分频比可通过CLKDIV_VAL来选择:
; 0=1:1:1, 1=1:1:2, 2=1:2:2, 3=1:2:4, 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
CLKDIV_VAL EQU 5 ;选择Fclk:Hclk:Pclk=1:4:8
; FCLK由MPLLCON寄存器设置,可通过M_MDIV,M_PDIV,M_SDIV来调整,此处设置为400MHz,见P7-21
M_MDIV EQU 92 ; Fin=12.0MHz FCLK=400MHz
M_PDIV EQU 1
M_SDIV EQU 1
;============================= 预定义模式常量 ==============================
;用于设置CPSR的M[4:0],实现异常模式跳转
USERMODE EQU 0x10
FIQMODE EQU 0x11
IRQMODE EQU 0x12
SVCMODE EQU 0x13
ABORTMODE EQU 0x17
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f
NOINT EQU 0xc0
;============================== 栈地址定义 ==================================
; 各异常模式的栈在SDRAM内存中的地址
; 地址 栈空间
UserStack EQU (_STACK_BASEADDRESS-0x3800) ; 0x33ff4800, 未知
SVCStack EQU (_STACK_BASEADDRESS-0x2800) ; 0x33ff5800, 4KB
UndefStack EQU (_STACK_BASEADDRESS-0x2400) ; 0x33ff5c00, 1KB
AbortStack EQU (_STACK_BASEADDRESS-0x2000) ; 0x33ff6000, 1KB
IRQStack EQU (_STACK_BASEADDRESS-0x1000) ; 0x33ff7000, 4KB
FIQStack EQU (_STACK_BASEADDRESS-0x0) ; 0x33ff8000, 4KB
;================================= 中断例程相关宏定义 ==================================
;
; 在本启动程序的最下面在SDRAM中定义一段空间,用于存放异常处理程序的入口地址,即异常向量表,
; 除了ResetHandler外,其他异常在本启动程序入口跳转后执行的第一段程序就是下面宏定义这段程
; 序,这段程序从SDRAM中取出异常处理程序的入口地址,并跳转到此地址处开始执行.要本启动程序中,
; 复位异常几乎就是本启动程序,而IRQ异常处理程序是IsrIRQ,其他异常处理程序未定义.
MACRO
$HandlerLabel HANDLER $HandleLabel ; $HandlerLabel比$HandleLabel多了个‘r’,两者不一样!
$HandlerLabel
SUB SP, SP, #4 ; SP=SP-4,此地址用于存放转跳地址,也即中断程序的入口地址
STMFD SP!, {R0} ; 把工作寄存器R0压入栈,sp=SP-4(sp先减)
LDR R0, =$HandleLabel ; 将HandleXXX的值放入r0
LDR R0, [R0] ; 把HandleXXX的值所指向的内容(也就是中断程序的入口地址)放入R0
STR R0, [SP,#4] ; 通过R0把中断服务程序(ISR)入口地址压入栈的sp=SP+4处
LDMFD SP!, {R0,pc} ; 出栈(sp后增),恢复r0的原值,pc值更新为中断服务程序的入口
; 地址(也就完成了到ISR的转跳)
MEND
;===================================== RO RW ZI =======================================
;
; 要了解RO,RW和ZI区是什么意思,需要首先了解以下知识:
; 1)ARM程序的组成
; 注意:此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件。
; 一个ARM程序包含3部分:RO区,RW区和ZI区
; RO是程序中的指令和常量,就是readonly
; RW是程序中的已初始化"全局"变量,就是read/write
; ZI是程序中的未初始化的"全局"变量,就是zero initialise(0初始化)
; 2)ARM映像文件的组成
; 所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。
; Image文件包含了RO和RW区数据,之所以Image文件不包含ZI区数据,是因为ZI区数据都是0,没必要包含,只
; 要程序运行之前将ZI区数据所在的区域一律清零即可。包含进去反而浪费存储空间。
; 3)ARM程序的执行过程
; 从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此
; 就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。实际上,RO区中的指令至少应该有这样
; 的功能:
; 1. 将RW区从ROM中搬到SDRAM内存中,因为RW区都是"全局"变量,不能存在ROM中,因为ROM只读不写
; 2. 将ZI区所在的SDRAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI区地址
; 及大小来将相应得RAM区域清零。ZI区也都是"全局"变量,同理,"全局"变量不能存在ROM/SROM中
; 在main()执行前,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含"全局"变量的代码。
;
; RO区存放的起始地址 = RO base = |Image$ER_ROM1$RO$Base|,
; RO区存放的终止地址 = RO limit - 1 = |Image$ER_ROM1$RO$Limit| - 1
; RW区在SDRAM中存放的起始地址 = RW base = |Image$RW_RAM1$RW$Base|
; ZI区在SDRAM中存放的起始地址 = ZI base = |Image$RW_RAM1$ZI$Base|
; ZI区在SDRAM中存放的终止地址 = ZI limit - 1 = |Image$RW_RAM1$ZI$Limit| - 1
; RW区在SDRAM中存放的终止地址 = RW limit - 1 = ZI base - 1,所以没必要再给出
;
; RO base 为 Target Options 中设置的ROM1 start , RO limit = ROM1 start + 代码长度,根据ROM1 start
; 的不同设置,RO区可能存放在NOR flash中,也可能在SDRAM中.
; RW base 为 Target Options 中设置的RAM1 start , RW limit = ROM1 start + 已初始化全局变量长度
; ZI base = RW limit, ZI limit = ZI base + 未初始化全局变量长度. RW,ZI区一定或者说必须在SDRAM中
IMPORT |Image$ER_ROM1$RO$Base| ; Base of ROM code
IMPORT |Image$ER_ROM1$RO$Limit| ; End of ROM code (=start of ROM data)
IMPORT |Image$RW_RAM1$RW$Base| ; Base of RAM to initialise
IMPORT |Image$RW_RAM1$ZI$Base| ; Base of area to zero initialise
IMPORT |Image$RW_RAM1$ZI$Limit| ; limit of area to zero initialise
;************************************** 代 码 段 ***************************************
PRESERVE8 ; 不知何用
AREA RESET,CODE,READONLY ; 代码段开始处
ENTRY ; 标识程序入口处,要求编译器不将下面的异常跳转列表进行优化
EXPORT __ENTRY ; 声明__ENTRY可用被其他源文件全局引用,应该是用于.c文件
__ENTRY
;==================================== 异常跳转 ======================================
ResetEntry ; 程序开始的地方 相对地址
B ResetHandler ; 0x00000000
B HandlerUndef ; handler for Undefined mode, 0x00000004
B HandlerSWI ; handler for SWI interrupt, 0x00000008
B HandlerPabort ; handler for PAbort, 0x0000000C
B HandlerDabort ; handler for DAbort, 0x00000010
B . ; reserved, 0x00000014
B HandlerIRQ ; handler for IRQ interrupt, 0x00000018
B HandlerFIQ ; handler for FIQ interrupt, 0x0000001C
B EnterPWDN ; 由正常模式进入低功耗模式(power down),地址必须是
; 0x00000020,貌似是约定好的
;===================================== 使用宏 ===================================
;
; 采用上面定义的HANDLER宏去建立Hander***和Handle***之间的联系
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
;================================== IRQ中断例程 ===============================
;
; IRQ中断可细分为多个中断源的中断,如果异常向量表是一级向量表的话,细分后的中
; 断向量表就是二级向量表,下面的程序就是二级向量表的查询,下面会用到
IsrIRQ
SUB SP, SP, #4 ; reserved for PC,SP=SP-4
STMFD SP!, {R8-R9} ; SP=SP-8
LDR R9, =INTOFFSET ; 把INTOFFSET寄存器的值装入R9,其值指向的存储地址存着中断的
; 偏移量(以字为单位,即4字节,见P14-16)
LDR R9, [R9] ; 中断的偏移量装入R9
LDR R8, =HandleEINT0 ; 将HandleEINT0装入R8,其值为二级向量表的入口地址
ADD R8, R8, R9, lsl #2 ; R8=R8+R9*4
LDR R8, [R8] ; 将所要的中断处理程序的入口地址装入R8
STR R8, [SP,#8] ; SP=SP+8,将中断处理程序的入口地址推入堆栈
LDMFD SP!, {R8-R9,pc} ; 出线,R8,R9还原其值,将中断处理程序的入口地址装入pc
LTORG ; 文字池
;-----------------------------------------------------------------------------------------
ResetHandler
;================================= 关闭看门狗, 屏蔽所有中断 ==============================
LDR R0, =WTCON ; 关看门狗定时器,p18-3
LDR R1, =0x0
STR R1, [R0]
LDR R0, =INTMSK
LDR R1, =0xFFFFFFFF ; 关所有中断,p14-12
STR R1, [R0]
LDR R0, =INTSUBMSK
LDR R1, =0x7FFF ; 关所有子中断,p14-18
STR R1, [R0]
;================================== 配置系统时钟 =====================================
; 通过设置LOCKTIME寄存器,减少PLL锁存时间
LDR R0, =LOCKTIME ; P7-20,
LDR R1, =0x01360136 ; p7-20,将S_LTIME和U_LTIME由初始值的OxFFFF改为0x136,只有310>300
STR R1, [R0]
LDR R0, =CLKDIVN ; CLKDIVN寄存器,p7-8,p7-24
LDR R1, =CLKDIV_VAL ; CLKDIV_VAL在上面定义,为UCLK:UPLL和FCLK:HCLK:PCLK的分频比选项,UPLL下面设置为48MHz
STR R1, [R0] ; CLKDIVN取0x00000101,即5
; 如果FCLK:HCLK不是1:1的关系的话,就要转成异步总线模式。反之,如果是这个比例关系的话,就转
; 成快速总线模式。
; MMU_SetAsyncBusMode 和 MMU_SetFastBusMode 都在4K代码以上,不可能拷到4K大小的steppingstone中,
; 而nandflash启动后,会将前4KB的引导代码拷到steppingstone(4KB大小)中执行,因此不能使用这两个
; 函数如果你不想nandflash启动的话,就可以直接用上面的代码调用MMU_SetAsyncBusMode和MMU_SetFastBusMode
; 下面的代码就是实现和上面两函数一样的功能. 利用的协处理器指令实现了对总线模式的设置
; 至于为什么要用协处理器指令,代码是什么意思,我也不清楚。
IF CLKDIV_VAL>1 ; 意味着Fclk:Hclk不是1:1.
MRC p15, 0, R0, c1, c0, 0 ; MMU_SetAsyncBusMode,对协处理器15的c1和c0进行
; 操作0(第0类),并将结果送入r0
ORR R0, R0, #0xC0000000 ; R1_nF:OR:R1_iA
MCR p15, 0, R0, c1, c0, 0
ELSE
MRC p15, 0, R0, c1, c0, 0 ; MMU_SetFastBusMode
BIC R0, R0, #0xC0000000 ; R1_iA:OR:R1_nF
MCR p15, 0, R0, c1, c0, 0
ENDIF
; 配置UPLL
LDR R0, =UPLLCON
LDR R1, =((56<<12)+(2<<4)+2) ; UPLL=48MHz,Fin=12MHz
STR R1, [R0]
NOP ; 配置完UPLL后延迟7-clocks,才能配置MPLL,P7-21
NOP
NOP
NOP
NOP
NOP
NOP
; 配置 MPLL
LDR R0, =MPLLCON
LDR R1, =((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ; Fin=12MHz
STR R1, [R0]
;检查这次启动是否是睡眠模式的唤醒导致的
LDR R1, =GSTATUS2 ; p9-35
LDR R0, [R1]
TST R0, #0x2
;如果是则跳到SLEEP_WAKEUP handler
BNE WAKEUP_SLEEP
;============================= 配置SDRAM内存控制寄存器 =====================================
;
;此段代码把13个存储控制器的内容批量的读取到了对应的特殊功能寄存器中首先是有一个数据区SMRDATA,
;在程序的后面有定义,这个数据区给13个寄存器分配52字节的地址空间。BWSCON寄存器的地址0x48000000
;貌似是所有寄存器中最低的,故从它开始设置。
EXPORT StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp
;配置存储器控制寄存器
ADRL R0, SMRDATA ; 用adrl要比ldr要好,因为可以省去设置文字池的麻烦
LDR R1,=BWSCON ; BWSCON的地址,p5-14
ADD R2, R0, #52 ; End address of SMRDATA
0
LDR R3, [R0], #4
STR R3, [R1], #4
CMP R2, R0
BNE %B0 ; 向后(BACK)搜索标号为0的行,以此实现循环,具体可看局部标号的知识
; 延时等待SDRAM稳定运行,好像设置SDRAM寄存器后都要延时,只是没说要延时多久
MOV R0, #256
1
SUBS R0, R0, #1 ; 向后(BACK)搜索标号为1的行,以此实现循环,具体可看局部标号的知识
BNE %B1
;==================================== 初始化栈 =====================================
;
; 下面这段用初始于初始化堆栈,所以不能用stmfd,ldmfd之类用到的堆栈指令,UndefStack,
; AbortStack,IRQStack,FIQStack,SVCstack上面定义过。有一点要搞清楚,每一种模式都有专门
; 的SP寄存器(r13,r13_fiq,r13_svc……),下面代码貌似在设置同一个SP寄存器,其实是设置不同的
上一篇:mini2440---keil for ARM下的调试与下载环境的搭建
下一篇:S3C2440在MDK开发环境下的相关配置
推荐阅读最新更新时间:2024-11-06 00:44
设计资源 培训 开发板 精华推荐
- LT6654AHLS8-2.5 升压输出电流电压基准的典型应用
- DEV-15866,电源管理 IC 开发套件直流电机驱动器 HAT
- LF25CPT-TR 2.5V 超低压降稳压器的典型应用
- LF50ABDT-TR 5V低压灯泡典型应用
- ESP32 Keyboard104:ESP32 键盘,USB供电
- 1W,一个用于 LED 照明的 LED 白光 LED 驱动器
- LTC4089 的典型应用 - 具有高压开关充电器的 USB 电源管理器
- NCV33072DR2G 二阶高通有源滤波器的典型应用
- NCV891930MW00-50GEVB:汽车低 Iq 2 MHz 同步降压控制器,5.0 V - 6A 评估板
- MIC384 的典型应用:三区热监控器