飞机电子设备维修招聘:ARM学习日志(二)

来源:百度文库 编辑:九乡新闻网 时间:2024/04/26 07:44:32

ARM学习日志(二)-- ARM启动代码分析

(92.52 K) 该附件被下载次数 94

  --启动代码中文注解

 

(7.37 K) 该附件被下载次数 64

--这个是.s文件

 

由于片面问题,所以可能会看起来不太美观,可以看附件中的内容。

       ARM启动代码相当于我们电脑的BIOS,也就是ARM启动时对处理器的一些初始化及嵌入式系统硬件的一些初始化。由于它直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般包括:中断向量表,初始化存储器系统,初始化堆栈,初始化有特殊要求的断口,设备初始化,变量初始化等。这几天对着RealView MDK-ARM中自带的启动代码研究了一下,遇到问题又对着数据手册和指令表看了一下,总算对S3C2440A的硬件有了一个大致的了解。学习嵌入式系统重在系统,学习ARM只是为学习嵌入式系统铺路,懒猫比较笨可能在上系统之前要裸奔几天以强化以下对S3C2440A内部结构的了解。

       把MDK自带的S3C2440A.S文件的注释发一下,这些是懒猫结合数据手册与ARM指令表理解了,可能会有错误,放在这里只是引导一下像我一样还没有入门的兄弟们,希望你们不要害怕ARM害怕嵌入式,老毛他老人家说的对,世上无难事,只怕有心人,ARM指令就那么多,看一遍不会就多看几遍,还有一定要学习看软件自带的帮助文件.

;/*****************************************************************************/

;/* S3C2440.S: Startup file for Samsung S3C440                                */

;/*****************************************************************************/

;/* <<< Use Configuration Wizard in Context Menu >>>                          */

;/*****************************************************************************/

;/* This file is part of the uVision/ARM development tools.                   */

;/* Copyright (c) 2005-2008 Keil Software. All rights reserved.               */

;/* This software may only be used under the terms of a valid, current,       */

;/* end user licence from KEIL for a compatible version of KEIL software      */

;/* development tools. Nothing else gives you the right to use this software. */

;/*****************************************************************************/

;下面这些参数是与CPSR状态寄存器有关

;参数的由来:这里各个模式的参数是由寄存器CPSR的模式位设置M[4:0]得来的,

;比如这里的用户模式,CPSR的M[4:0]设置为10000就是0x10。

;

;Mode_USR -- 用户模式,正常程序执行模式,用于应用程序

;Mode_FIQ -- 快速中断模式,用于高速数据传输和通道处理。

;Mode_IRQ -- 外部中断模式,用于通用的中断处理。

;Mode_SVC -- 管理模式,使用的一种保护模式。

;Mode_ABT -- 数据访问中止模式,用于虚拟存储用存储保护

;Mode_UND -- 未定义指令中止模式,当未定义指令执行时进入此模式。

;Mode_SYS -- 系统模式,用于特权级的操作系统任务。

;I_Bit    -- 如果I位被置1,则外部中断被禁止(IRQ is disabled)

;F_Bit    -- 如果F位被置1,则快速中断被禁止(FIQ is disabled)

;

;----------------------------------------------------------------------

Mode_USR        EQU     0x10

Mode_FIQ        EQU     0x11

Mode_IRQ        EQU     0x12

Mode_SVC        EQU     0x13

Mode_ABT        EQU     0x17

Mode_UND        EQU     0x1B

Mode_SYS        EQU     0x1F

 

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled

F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

 

;-----------------------------栈初始化定义-----------------------------------

;下面这些主要是栈配置,系统的栈空间设定

;

;UND_Stack_Size -- 未定义模式的栈大小

;SVC_Stack_Size -- 超级用户模式的栈大小

;ABT_Stack_Size -- 数据访问终止模式的栈大小

;FIQ_Stack_Size -- 快速中断模式的栈大小

;IRQ_Stack_Size -- 外部中断模式的栈大小

;USR_Stack_Size -- 用户模式的栈大小

;ISR_Stack_Size -- 总堆栈的大小,也就是也有模式下堆栈相加

;

;-----------------------------------------------------------------------

 

UND_Stack_Size  EQU     0x00000000

SVC_Stack_Size  EQU     0x00000008

ABT_Stack_Size  EQU     0x00000000

FIQ_Stack_Size  EQU     0x00000000

IRQ_Stack_Size  EQU     0x00000080

USR_Stack_Size  EQU     0x00000400

 

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \

                         FIQ_Stack_Size + IRQ_Stack_Size)

                                                         

;-----------------------------------------------------------------------

;AREA -- 是一个伪指令,用于段定义。ARM的汇编程序由段组成,段是相对独立

;        的指令或数据单位,每个段由AREA伪指令定义,并定义段的属性。

;        STACK  -- AREA指令的一个参数,定义段名称

;        NOINIT -- AREA指令的一个参数,指定本数据段仅仅保留了内在单元,而

;        将句初始值写入内存单元,也即将内存单元值初始化为0

;        READWRITE -- 指定本段为可读可写,数据段默认为READWRITE。

;                     READWRITE(读写)、READONLY(只读)

;ALIGN -- 也是一个伪指令,指定对齐方式。ALIGN n 指令的对齐值有两种方案

;  即n 或 2^n,这里采用第二种方案即指定后面的指令8字节对齐。

;

;下面这句话的意思是:

;开辟一个堆栈段,段名字为STACK,定义为可读可写,将内存单元初始化为0,

;-----------------------------------------------------------------------

 

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

                                    

;-----------------------------------------------------------------------

;SPACE -- 伪指令,用于分配一块内存单元,并用0初始化,与%同义

;其指令格式为:

;    {lable}  SPACE  expr

;lable -- 内存起始地址标号   expr -- 所要分配的内存字节数   

;-----------------------------------------------------------------------

Stack_Mem       SPACE   USR_Stack_Size    ;堆栈内存起始地址标号

__initial_sp    SPACE   ISR_Stack_Size    ;汇编代码的地址标号

 

Stack_Top    ;堆栈段内容结束, 在这里放个标号,用来获得堆栈顶部地址

 

Heap_Size       EQU     0x00000000    ;定义堆大小设置

                ;开辟一个名字为HEAP可读可写,不初始化内存单的内存单元。

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3

__heap_base     ;堆的基址

Heap_Mem        SPACE   Heap_Size    ;堆内存起始地址标号

__heap_limit    ;堆结束

 

;----------------------------内存初始化定义-----------------------------

;在一些应用系统中除了扩展Flash,RAM挂接在外部存储器接口上外,可能还有其它

;的外设挂接在外部存储器接口上,不同外设的操作时序什么的都是不一样的,所以

;在使用这些外设之前必须初始化连接这些外设存储器接口。这里因为没扩展,所以

;只定义一个片上内存基地址。

;-----------------------------------------------------------------------

 

IRAM_BASE       EQU     0x40000000    ;片上SRAM的基地址,即内存基地址

 

;-------------------------看门狗初始化定义------------------------------

;看门狗在防止程序跑飞,进入无限死循环时起着重要作用。有些应用可能用不上

;看门狗功能,也可能有些应用会用到外部看门狗。在这个时候内部看门狗必须禁

;止,所以有时候会在初始化时将内部看门狗禁止,当以后应用用到时再开启它。

;看门狗定时器包括三个寄存器:

;WTCON -- 看门狗控制寄存器,设定看门狗定时器模式

;WTDAT -- 看门狗数据寄存器,用于设定超时宽度

;WTCNT -- 看门狗计数寄存器,里面存放的是看门狗定时器当前值

;

;WT_BASE   -- 看门狗定时器基地址

;WTCON_OFS -- 看门狗控制寄存器偏移地址,相对于基址

;WTDAT_OFS -- 看门狗数据寄存器偏移地址,相对于基址

;WTCNT_OFS -- 看门狗计数寄存器偏移地址,相对于基址

;WT_SETUP  -- 看门狗设置

;WTCON_Val -- 看门狗控制寄存器设置,关闭看门狗

;WTDAT_Val -- 看门狗数据寄存器设置,初始值即为0x8000

;-----------------------------------------------------------------------

 

WT_BASE         EQU     0x53000000      ; Watchdog Timer Base Address

WTCON_OFS     EQU     0x00      ; Watchdog Timer Control Register Offset

WTDAT_OFS      EQU     0x04       ; Watchdog Timer Data Register    Offset

WTCNT_OFS      EQU     0x08       ; Watchdog Timer Count Register   Offset

 

WT_SETUP        EQU     0

WTCON_Val       EQU     0x00000000

WTDAT_Val       EQU     0x00008000

 

 

;----------------------------时钟与电源管理定义-------------------------

;S3C2440A中的时钟控制逻辑可以产生必须的时钟信号,包括CPU的FCLK,AHB总线的

;HCLK 以及APB总线外设的PCLK3C2440A内部有两个锁相环(PLL):一个提供FCLK,

;HCLK及PCLK,另一个专用于USB模块(48MHz).

;

;CLOCK_BASE   -- 时钟基地址

;LOCKTIME_OFS -- 锁相环锁定时间计数寄存器偏移地址,相对于基址

;MPLLCON_OFS  -- MPLL配置寄存器偏移地址,相对于基址,主时钟源PLL

;UPLLCON_OFS  -- UPLL配置寄存器偏移地址,相对于基址,USB时钟源PLL

;CLKCON_OFS   -- 时钟控制寄存器偏移地址,相对于基址

;CLKSLOW_OFS  -- 时钟减慢控制寄存器偏移地址,相对于基址

;CLKDIVN_OFS  -- 时钟分频器控制寄存器偏移地址,相对于基址

;CAMDIVN_OFS  -- 摄像头时钟分频器控制寄存器偏移地址,相对于基址,UPLL提供

;

;CLOCK_SETUP  -- 时钟设置

;LOCKTIME_Val -- PLL锁定时间计数器值

;MPLLCON_Val  -- MPLL配置寄存器值

;UPLLCON_Val  -- UPLL配置寄存器值

;CLKCON_Val   -- 时钟配置寄存器值

;CLKSLOW_Val  -- 时钟减慢控制寄存器值

;CLKDIVN_Val  -- 时钟分频控制寄存器值

;CAMDIVN_Val  -- 摄像头分频控制寄存器值

;-----------------------------------------------------------------------

CLOCK_BASE      EQU     0x4C000000      ; Clock Base Address

LOCKTIME_OFS    EQU     0x00    ; PLL Lock Time Count Register   Offset

MPLLCON_OFS     EQU     0x04    ; MPLL Configuration Register    Offset

UPLLCON_OFS     EQU     0x08     ; UPLL Configuration Register    Offset

CLKCON_OFS      EQU     0x0C      ; Clock Generator Control Reg    Offset

CLKSLOW_OFS     EQU     0x10     ; Clock Slow Control Register    Offset

CLKDIVN_OFS     EQU     0x14        ; Clock Divider Control Register Offset

CAMDIVN_OFS     EQU     0x18       ; Camera Clock Divider Register  Offset

 

CLOCK_SETUP     EQU     0

LOCKTIME_Val    EQU     0x0FFF0FFF

MPLLCON_Val     EQU     0x00043011

UPLLCON_Val     EQU     0x00038021

CLKCON_Val      EQU     0x001FFFF0

CLKSLOW_Val     EQU     0x00000004

CLKDIVN_Val     EQU     0x0000000F

CAMDIVN_Val     EQU     0x00000000

 

;--------------------存储控制器设置定义---------------------------------

;下面这些都是一些关于存储控制器的地址宏定义

;

;MC_BASE      -- 存储控制器基地址

;BWSCON_OFS   -- 总线宽度和等待控制寄存器偏移地址

;BANKCON0_OFS -- BANK1控制寄存器偏移地址

;   .

;   .

;BANKCON7_OFS -- BANK7控制寄存器偏移地址

;REFRESH_OFS  -- DRAM/SDRAM刷新控制寄存器偏移地址

;BANKSIZE_OFS -- 可调的bank大小寄存器偏移地址

;MRSRB6_OFS   -- bank6模式控制寄存器偏移地址

;MRSRB7_OFS   -- bank7模式控制寄存器偏移地址

;

;MC_SETUP     -- 存储器控制寄存器设置

;BWSCON_Val   -- 写入总线宽度和等待控制寄存值

;BANKCON0_Val -- 写入Blank0的值

;   .

;   .

;BANKCON7_Val -- 写入BANK7 的值

;REFRESH_Val  -- 写入DRAM/SDRAM刷新控制寄存的值

;BANKSIZE_Val -- 写入可调的bank大小寄存的值

;MRSRB6_Val   -- 写入bank6模式控制寄存器的值

;MRSRB7_Val   -- 写入bank7模式控制寄存器的值

;-----------------------------------------------------------------------

MC_BASE         EQU     0x48000000      ; Memory Controller Base Address

BWSCON_OFS      EQU     0x00     ; Bus Width and Wait Status Ctrl Offset

BANKCON0_OFS    EQU     0x04    ; Bank 0 Control Register        Offset

BANKCON1_OFS    EQU     0x08     ; Bank 1 Control Register        Offset

BANKCON2_OFS    EQU     0x0C     ; Bank 2 Control Register        Offset

BANKCON3_OFS    EQU     0x10      ; Bank 3 Control Register        Offset

BANKCON4_OFS    EQU     0x14      ; Bank 4 Control Register        Offset

BANKCON5_OFS    EQU     0x18       ; Bank 5 Control Register        Offset

BANKCON6_OFS    EQU     0x1C       ; Bank 6 Control Register        Offset

BANKCON7_OFS    EQU     0x20        ; Bank 7 Control Register        Offset

REFRESH_OFS     EQU     0x24          ; SDRAM Refresh Control Register Offset

BANKSIZE_OFS    EQU     0x28           ; Flexible Bank Size Register    Offset

MRSRB6_OFS      EQU     0x2C           ; Bank 6 Mode Register           Offset

MRSRB7_OFS      EQU     0x30           ; Bank 7 Mode Register           Offset

 

MC_SETUP        EQU     1

BWSCON_Val      EQU     0x22000000

BANKCON0_Val    EQU     0x00000700

BANKCON1_Val    EQU     0x00000700

BANKCON2_Val    EQU     0x00000700

BANKCON3_Val    EQU     0x00000700

BANKCON4_Val    EQU     0x00000700

BANKCON5_Val    EQU     0x00000700

BANKCON6_Val    EQU     0x00018005

BANKCON7_Val    EQU     0x00018005

REFRESH_Val     EQU     0x008404F3

BANKSIZE_Val    EQU     0x00000032

MRSRB6_Val      EQU     0x00000020

MRSRB7_Val      EQU     0x00000020

 

;---------------------I/O端口宏定义--------------------------------------

;GPA_BASE   -- 端口A基地址

;   .

;GPJ_BASE   -- 端口J基地址

;GPCON_OFS  -- 端口配置寄存器偏移地址

;GPDAT_OFS  -- 端口数据寄存器偏移地址

;GPUP_OFS   -- 端口上拉寄存器偏移地址

;GP_SETUP   -- 端口设置

;GPA_SETUP  -- 端口A配置

;GPACON_Val -- 写入端口A配置寄存器的值

;      .

;      .

;GPJ_SETUP  -- 端口J配置

;GPJCON_Val -- 写入端口J配置寄存器的值

;GPJUP_Val  -- 写入端口J上拉寄存器的值

;-----------------------------------------------------------------------

GPA_BASE        EQU     0x56000000      ; GPA Base Address

GPB_BASE        EQU     0x56000010      ; GPB Base Address

GPC_BASE        EQU     0x56000020      ; GPC Base Address

GPD_BASE        EQU     0x56000030      ; GPD Base Address

GPE_BASE        EQU     0x56000040      ; GPE Base Address

GPF_BASE        EQU     0x56000050      ; GPF Base Address

GPG_BASE        EQU     0x56000060      ; GPG Base Address

GPH_BASE        EQU     0x56000070      ; GPH Base Address

GPJ_BASE        EQU     0x560000D0      ; GPJ Base Address

GPCON_OFS       EQU     0x00            ; Control Register Offset

GPDAT_OFS       EQU     0x04            ; Data Register Offset

GPUP_OFS        EQU     0x08            ; Pull-up Disable Register Offset

 

GP_SETUP        EQU     1

 

;-----------------------------------------------------------------------

;端口A配置

;-----------------------------------------------------------------------

GPA_SETUP       EQU     0

GPACON_Val      EQU     0x000003FF

 

;-----------------------------------------------------------------------

;端口B配置

;-----------------------------------------------------------------------

GPB_SETUP       EQU     0

GPBCON_Val      EQU     0x00000000

GPBUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口C配置

;-----------------------------------------------------------------------

GPC_SETUP       EQU     0

GPCCON_Val      EQU     0x00000000

GPCUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口D配置

;-----------------------------------------------------------------------

GPD_SETUP       EQU     0

GPDCON_Val      EQU     0x00000000

GPDUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口E配置

;-----------------------------------------------------------------------

GPE_SETUP       EQU     0

GPECON_Val      EQU     0x00000000

GPEUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口F配置

;-----------------------------------------------------------------------

GPF_SETUP       EQU     0

GPFCON_Val      EQU     0x00000000

GPFUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口G配置

;-----------------------------------------------------------------------

GPG_SETUP       EQU     0

GPGCON_Val      EQU     0x00000000

GPGUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口H配置

;-----------------------------------------------------------------------

GPH_SETUP       EQU     0

GPHCON_Val      EQU     0x00000000

GPHUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;端口J配置

;-----------------------------------------------------------------------

GPJ_SETUP       EQU     0

GPJCON_Val      EQU     0x00000000

GPJUP_Val       EQU     0x00000000

 

;-----------------------------------------------------------------------

;PRESERVE8 -- 伪指令,指示当前文件请求堆栈为8字节对齐。

;  汇编程序数据8字节对齐,c和汇编有8位对齐的要求.

;-----------------------------------------------------------------------

                PRESERVE8

 

;-----------------------------------------------------------------------

;存储区设定和程序入口点

;启动代码必须连接到第一个地址才能运行

;下面这句话的意思是:

;       声明一个名为RESET的代码段,属性为只读

;-----------------------------------------------------------------------

                AREA    RESET, CODE, READONLY

                ARM    ;ARM模式运行程序

;-----------------------------------------------------------------------              

;IMPORT -- 相当于C语言中的关键字extern

;        指当前的符号在其他源文件中定义的,在本源文件中可能引用该符号.

;EXPORT -- 相当于C语言中的关键字global

;          声明一个符号可以被其它文件引用.相当于声明了一个全局变量

;下面这几句话是的意思是:

;    如果定义了_EVAL这个变量,引用RO输出区的字节长度与RW输出区的字节长度

;注意:

;ARM连接器定义了一些包含$$的符号。这些符号及其他所有包含$$的名称都是ARM的

;保留字。这些符号被用于指定域的基地址,输出段的基地址和输入段的基地址及其

;大小。我们可以自己的汇编语言程序中引用这些符号地址,把它们用作可重定位的

;地址,也可能在C或C++代码中使用extern关键字来引用它们。这个可以查看uVision

;Help 的Region-related symbols这一节。

;-----------------------------------------------------------------------

                IF      :LNOT::DEF:__EVAL     ;逻辑判断是否定义了_EVAL这个变量

                IMPORT  ||Image$$ER_ROM1$$RO$$Length||

                IMPORT  ||Image$$RW_RAM1$$RW$$Length||

                ENDIF

 

;-----------------------------------------------------------------------

;    异常向量,映射到地址0,必须使用绝对寻址方式,子程序用无限循环方式

;实现可以被修改。

;-----------------------------------------------------------------------

Vectors         LDR     PC, Reset_Addr ;将复位地址装载到程序指针,即复位       

                LDR     PC, Undef_Addr ;未定义指令

                LDR     PC, SWI_Addr   ;软件中断

                LDR     PC, PAbt_Addr  ;中止(预取)

                LDR     PC, DAbt_Addr  ;中止(数据)

                IF      :DEF:__EVAL    ;如果定义了__EVAL 变量

                  DCD   0x4000         ;分配2k空间

                ELSE                   ;否则分配空间大小为RO输出区的字节

                                                            ;长度与RW输出区的字节长度之和

                  DCD   ||Image$$ER_ROM1$$RO$$Length||+\

                        ||Image$$RW_RAM1$$RW$$Length||

                ENDIF

                LDR     PC, IRQ_Addr   ;外部中断

                LDR     PC, FIQ_Addr   ;快速中断

 

                IF      :DEF:__RTX     ;如果定义了__RTX

                IMPORT  SWI_Handler    ;则定义中断子程序

                IMPORT  IRQ_Handler_RTX ;定义快速中断子程序

                ENDIF

;-----------------------------------------------------------------------

;下面这几句的任务是把各个子程序的入口地址分配给相应的地址变量

;-----------------------------------------------------------------------

Reset_Addr      DCD     Reset_Handler ;复位子程序入口地址赋值给Reset_Addr

Undef_Addr      DCD     Undef_Handler ;未定义子程序入口地址赋值给Undef_Addr

SWI_Addr        DCD     SWI_Handler   ;中断子程序入口地址赋值给SWI_Addr

PAbt_Addr       DCD     PAbt_Handler  ;中止(预存)子程序入口地址赋给PAbt_Addr

DAbt_Addr       DCD     DAbt_Handler  ;中止(数据)子程序入口地址赋给DAbt_Addr

                DCD     0             ;保留地址     

                IF      :DEF:__RTX    ;如果定义了__RTX

IRQ_Addr        DCD     IRQ_Handler_RTX  ;快速中断子程序入口地址给IRQ_Addr

                ELSE

IRQ_Addr        DCD     IRQ_Handler      ;否则把IRQ_Handler入口地址给IRQ_Addr

                ENDIF

FIQ_Addr        DCD     FIQ_Handler      ;快速中断入口地址给FIQ_Addr

 

;-----------------------------------------------------------------------

;这些子程序都是用无限循环方式实现的可以被修改。

;-----------------------------------------------------------------------

 

Undef_Handler  

                                     B       Undef_Handler    ;跳转到Undef_Handler,还是在这个地方

                IF      :DEF:__RTX       ;如果定义了DEF:__RTX,在此等待中断

                ELSE

SWI_Handler   

                                     B       SWI_Handler      ;否则跳转到软件中断

                ENDIF

PAbt_Handler   

                                     B       PAbt_Handler     ;中止(预存)子程

DAbt_Handler   

                                     B       DAbt_Handler     ;中止(数据)子程

;-----------------------------------------------------------------------

;外部中断子程序

;     如果函数标有PROC与ENDP,但没有FRAME PUSH 或 FRAME POP,则堆栈作用量

;假定为0.这意味着无需手动添加FRAME PUSH 0或FRAME POP 0

;-----------------------------------------------------------------------                                  

IRQ_Handler                            

                                     PROC

                EXPORT  IRQ_Handler  [WEAK] ;声明一个全局变量,并且其它

                                                                 ;同名符优先于本符号被引用

                B       .   ;跳转到当前地址即在此等待“.”代表当前指令地址

                ENDP

FIQ_Handler                                ;快速中断子程序 

                B       FIQ_Handler

 

;-----------------------------------------------------------------------

;复位子程序

;-----------------------------------------------------------------------

                EXPORT  Reset_Handler     ; 声明一个全局变量

Reset_Handler  

 

;-----------------------------------------------------------------------

;配置看门狗

;前面已经初始化 WT_SETUP == 0,要想执行下面的程序需将WT_SETUP置1

;-----------------------------------------------------------------------

                IF      WT_SETUP != 0

                LDR     R0, =WT_BASE    ;加载看门狗基址

                LDR     R1, =WTCON_Val  ;加载看门狗控制寄存器数据

                LDR     R2, =WTDAT_Val  ;加载看门狗数据寄存器数据

                STR     R2, [R0, #WTCNT_OFS]  ;将WTDAT_Val配置给看门狗

                                                                   ;计数寄存器

                STR     R2, [R0, #WTDAT_OFS]  ;将WTDAT_Val 配置给看门狗

                                                                   ;数据寄存器

                STR     R1, [R0, #WTCON_OFS]  ;将WTCON_Val配置给看门狗

                                                                   ;控制寄存器

                ENDIF

               

;-----------------------------------------------------------------------

;配置时钟

;如果逻辑上没有定义 NO_CLOCK_SETUP并且 CLOCK_SETUP != 0执行下面程序

;-----------------------------------------------------------------------                                             

                IF      (:LNOT:(:DEF:NO_CLOCK_SETUP)):LAND:(CLOCK_SETUP != 0)

                LDR     R0,      =CLOCK_BASE    ;加载时钟基址

                LDR     R1,      =LOCKTIME_Val  ;加载PLL锁定时间计数值

                STR     R1, [R0, #LOCKTIME_OFS] ;并将该值配置到PLL锁定时间计数器

                MOV     R1,      #CLKDIVN_Val 

                STR     R1, [R0, #CLKDIVN_OFS]  ;配置时钟分频器

                LDR     R1,      =CAMDIVN_Val

                STR     R1, [R0, #CAMDIVN_OFS]  ;配置摄像头分频控制寄存器

                LDR     R1,      =MPLLCON_Val

                STR     R1, [R0, #MPLLCON_OFS]  ;配置MPLL配置寄存器

                LDR     R1,      =UPLLCON_Val

                STR     R1, [R0, #UPLLCON_OFS]  ;配置UPLL配置寄存器

                MOV     R1,      #CLKSLOW_Val

                STR     R1, [R0, #CLKSLOW_OFS]  ;配置时钟减慢控制寄存器

                LDR     R1,      =CLKCON_Val

                STR     R1, [R0, #CLKCON_OFS]   ;配置时钟配控制寄存器

                ENDIF

 

;-----------------------------------------------------------------------

;存储器设定

;如果没有定义NO_MC_SETUP且CLOCK_SETUP != 0则执行下面的程序

;-----------------------------------------------------------------------

                IF      (:LNOT:(:DEF:NO_MC_SETUP)):LAND:(CLOCK_SETUP != 0)

                LDR     R0,      =MC_BASE  ;加载存储控制器基址

                LDR     R1,      =BWSCON_Val

                STR     R1, [R0, #BWSCON_OFS]   ;配置总线宽度和等待控制寄存器

                LDR     R1,      =BANKCON0_Val

                STR     R1, [R0, #BANKCON0_OFS] ;配置BLANK0控制寄存器

                LDR     R1,      =BANKCON1_Val

                STR     R1, [R0, #BANKCON1_OFS] ;配置BLANK1控制寄存器

                LDR     R1,      =BANKCON2_Val

                STR     R1, [R0, #BANKCON2_OFS] ;配置BLANK2控制寄存器

                LDR     R1,      =BANKCON3_Val

                STR     R1, [R0, #BANKCON3_OFS] ;配置BLANK3控制寄存器

                LDR     R1,      =BANKCON4_Val

                STR     R1, [R0, #BANKCON4_OFS] ;配置BLANK4控制寄存器

                LDR     R1,      =BANKCON5_Val

                STR     R1, [R0, #BANKCON5_OFS] ;配置BLANK5控制寄存器

                LDR     R1,      =BANKCON6_Val

                STR     R1, [R0, #BANKCON6_OFS] ;配置BLANK6控制寄存器

                LDR     R1,      =BANKCON7_Val

                STR     R1, [R0, #BANKCON7_OFS] ;配置BLANK7控制寄存器

                LDR     R1,      =REFRESH_Val

                STR     R1, [R0, #REFRESH_OFS]  ;配置DRAM/SDRAM刷新控制寄存器

                MOV     R1,      #BANKSIZE_Val

                STR     R1, [R0, #BANKSIZE_OFS] ;配置可调的bank大小寄存器

                MOV     R1,      #MRSRB6_Val

                STR     R1, [R0, #MRSRB6_OFS]   ;配置bank6模式控制寄存器

                MOV     R1,      #MRSRB7_Val

                STR     R1, [R0, #MRSRB7_OFS]   ;配置bank7模式控制寄存器

                ENDIF                           

 

;-----------------------------------------------------------------------

;IO端口配置

;如果没有定义NO_GP_SETUP并且GP_SETUP != 0则执行下面的程序

;-----------------------------------------------------------------------           

                IF      (:LNOT:(:DEF:NO_GP_SETUP)):LAND:(GP_SETUP != 0)

 

                IF      GPA_SETUP != 0

                LDR     R0, =GPA_BASE      ;配置端口A功能

                LDR     R1, =GPACON_Val    ;A口有25个口,做IO时只能做输出口

                STR     R1, [R0, #GPCON_OFS] 

                ENDIF

 

                IF      GPB_SETUP != 0

                LDR     R0, =GPB_BASE      ;配置端口B功能

                LDR     R1, =GPBCON_Val

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPBUP_Val     ;配置端口B上拉寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPC_SETUP != 0

                LDR     R0, =GPC_BASE      ;配置端口C功能

                LDR     R1, =GPCCON_Val

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPCUP_Val     ;配置端口C上拉寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPD_SETUP != 0

                LDR     R0, =GPD_BASE      ;配置端口D功能

                LDR     R1, =GPDCON_Val

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPDUP_Val     ;配置端口D上位寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPE_SETUP != 0

                LDR     R0, =GPE_BASE

                LDR     R1, =GPECON_Val   ;配置端口E功能

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPEUP_Val    ;配置端口E上位寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPF_SETUP != 0

                LDR     R0, =GPF_BASE

                LDR     R1, =GPFCON_Val   ;配置端口F功能

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPFUP_Val    ;配置端口F上位寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPG_SETUP != 0

                LDR     R0, =GPG_BASE

                LDR     R1, =GPGCON_Val   ;配置端口G功能

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPGUP_Val    ;配置端口G上位寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPH_SETUP != 0

                LDR     R0, =GPH_BASE

                LDR     R1, =GPHCON_Val   ;配置端口H功

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPHUP_Val    ;配置端口H上位寄存器

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

 

                IF      GPJ_SETUP != 0

                LDR     R0, =GPJ_BASE

                LDR     R1, =GPJCON_Val   ;配置端口J功

                STR     R1, [R0, #GPCON_OFS]

                LDR     R1, =GPJUP_Val    ;配置端口J上位寄存

                STR     R1, [R0, #GPUP_OFS]

                ENDIF

              

                ENDIF

               

;-----------------------------------------------------------------------

;拷贝异常向量到内部RAM

;如果定义了RAM_INTVEC就执行下面一段程序

;-----------------------------------------------------------------------

                IF      :DEF:RAM_INTVEC

                ADR     R8,  Vectors    ; 读取向量源地址

                LDR     R9, =IRAM_BASE  ; 读取片上SRAM的基地址

                LDMIA   R8!, {R0-R7}    ; 批量加载异常向量

                STMIA   R9!, {R0-R7}    ; 批量存储向量

                LDMIA   R8!, {R0-R7}    ; 加载程序入口地址(Load Handler Addresses )

                STMIA   R9!, {R0-R7}    ; 存储程序入口地址(Store Handler Addresses)

                ENDIF

 

;-----------------------------------------------------------------------

;配置相应模式栈的大小(Setup Stack for each mode )

;下面这一段主要是设置各个异常模式的堆栈,注意在设置的时候需要禁止IRQ和FIQ.

;这段代码也是系统复位后执行的第一段代码。执行完这段代码后系统处于系统模

;式,并且IRQ和FIQ都是禁止的。

;-----------------------------------------------------------------------   

                LDR     R0, =Stack_Top  ;加载栈顶指针地址

 

;-----------------------------------------------------------------------

;进入未定义模式,并设定其栈指针

;-----------------------------------------------------------------------

                ;将(Mode_UND | I_Bit | F_Bit)赋值给 CPSR_c即CPSR[7:0]

                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit

                MOV     SP, R0    ;栈顶指针地址赋值给SP指针

                SUB     R0, R0, #UND_Stack_Size  ;分其栈指针

                                    

;-----------------------------------------------------------------------

;进入异常中断模式,并设定其栈指针

;下面这三句话与上面原理一样

;-----------------------------------------------------------------------

                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit

                MOV     SP, R0

                SUB     R0, R0, #ABT_Stack_Size

 

;-----------------------------------------------------------------------

;进入FIQ模式,并设定其栈指针

;下面这三句话与上面原理一样

;-----------------------------------------------------------------------

                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit

                MOV     SP, R0

                SUB     R0, R0, #FIQ_Stack_Size

 

;-----------------------------------------------------------------------

;进入IRQ模式,并设定其栈指针

;下面这三句话与上面原理一样

;-----------------------------------------------------------------------

                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit

                MOV     SP, R0

                SUB     R0, R0, #IRQ_Stack_Size

 

;-----------------------------------------------------------------------

;进入Supervisor模式,并设定其栈指针

;下面这三句话与上面原理一样

;-----------------------------------------------------------------------

                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit

                MOV     SP, R0

                SUB     R0, R0, #SVC_Stack_Size

 

;-----------------------------------------------------------------------

;进入用户模式,并设定其栈指针

;下面这三句话与上面原理一样

;-----------------------------------------------------------------------

;  Enter User Mode and set its Stack Pointer

                MSR     CPSR_c, #Mode_USR

                MOV     SP, R0

                SUB     SL, SP, #USR_Stack_Size

 

;-----------------------------------------------------------------------

;进入用户模式

;-----------------------------------------------------------------------

                MSR     CPSR_c, #Mode_USR

                IF      :DEF:__MICROLIB  ;如果定义了__MICROLIB

 

                EXPORT __initial_sp      ;那么就声明__initial_sp

 

                ELSE

 

                MOV     SP, R0           ;否则就设定用户模式栈指针

                SUB     SL, SP, #USR_Stack_Size

 

                ENDIF

 

;-----------------------------------------------------------------------

;些处开始正式进入C代码区

;反汇编以后C程序中的main函数名就变成了__main

;-----------------------------------------------------------------------

                IMPORT  __main      ;声明__main 函数

                LDR     R0, =__main ;加载__main 函数入口地址

                BX      R0          ;跳转到__main处

 

 

                IF      :DEF:__MICROLIB  ;如果定义了__MICROLIB

 

                EXPORT  __heap_base      ;则声明__heap_base

                EXPORT  __heap_limit     ;声明__heap_limit

 

                ELSE

;-----------------------------------------------------------------------

;用户初始化堆与栈,用于动态申请内存使用

;__use_two_region_memory这是MDK的库函

;__user_initial_stackheap也是一个库函数,它的返回值有

;           * 堆基址(heap base)  --> R0

;           * 栈基址(stack base) --> R1 一般为栈的最高地址

;           * 堆顶(heap limit)   --> R2

;           * 栈顶(stack limit)  --> R3

;

;-----------------------------------------------------------------------

                AREA    |.text|, CODE, READONLY

 

                IMPORT  __use_two_region_memory

                EXPORT  __user_initial_stackheap

__user_initial_stackheap

 

                LDR     R0, =  Heap_Mem  ;堆内存起始地址 -->R0

                LDR     R1, =(Stack_Mem + USR_Stack_Size) ;栈起始地址 -->R1

                LDR     R2, = (Heap_Mem +      Heap_Size) ;堆顶 -->R2

                LDR     R3, = Stack_Mem  ;栈顶地址 --> R3

                BX      LR               ;子程序返回

                ENDIF

 

                END

        因为这只是系统自带的启动代码,所以不是太难,但是研究一下他只是为了起到抛砖引玉的作用,如果自己根据需要所写的启动代码,无非也是包括这几大版块,只是添加了一些其它内容。

最后再吼一下自己的口号:

每天进步一点点,开心多一点^_^