下面以startup_LPC17xx.s为例,简单看一下CMSIS中的启动代码完成了哪些工作。

主要完成了三项工作:

1.1 堆栈以及堆的初始化

1.1.1 堆栈的初始化

程序清单 1.1 startup_LPC17xx.s 中的堆栈初始化代码

Stack_Size EQU 0x00000200 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp

Stack_Size EQU 0x00000200,这个语句相当于 Stack_Size 这个标号(标号:链接器的术语,下文中提到的所有“标号”,指的都是链接器中的标号)等于 0x00000200,相当于 C 语言中的#define Stack_Size 0x00000200,也就是说此语句只是一个声明,并未分配地址。

AREA STACK, NOINIT, READWRITE, ALIGN=3,此语句定义了一个名叫 STACK 的代码段,并指明 8 字节对齐(ALIGN=3)。

Stack_Mem SPACE Stack_Size,为 Stack_Mem 分配 Stack_Size 大小的一块内存区域,注意这里分配的是 RAM。

__initial_sp此标号有一层隐含的意思那就是在 M3 中堆栈是满递减堆栈,因为它指定了堆栈指针位于堆栈的高地址(在 Stack_Mem 之后),具体如下图所示。

cortex-m3使用什么架构(Cortex-M3内核进阶之注解)(1)

图 1.1 堆栈指针 SP 位置

上图来自一个 LPC1700 工程的.map 文件。可以看出栈的起始地址为 0x10001f70,大小为512 字节(即 0x00000200 = Stack_Size)。而堆栈指针的位置在 0x10002170,其等于栈的起始地址 0x10001f70 0x00000200,说明 LPC1700 系列的 Cortex-M3 微控制器的堆栈为满递减堆栈。

1.1.2 堆的初始化

程序清单 1.2 startup_LPC17xx.s 中的堆初始化代码

Heap_Size EQU 0x00000200 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit

具体过程与栈的初始化相同。

1.2 中断向量表的初始化

程序清单 1.3 中断向量表的初始化代码

PRESERVE8 THUMB ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD 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

PRESERVE8 指定了以下的代码位 8 字节对齐,这是 keil 编译器的一个编程要求,对齐情况如下图所示:

cortex-m3使用什么架构(Cortex-M3内核进阶之注解)(2)

图 1.2 .list 文件中的 8 字节对齐示意图

THUMB 指定了接下来的代码为 THUMB 指令集。

AREA RESET, DATA, READONLY,此语句声明 RESET 数据段。

EXPORT __Vectors,导出向量表标号,EXPORT 作用类似于 C 语言中的 extern。之后的代码就是为向量表分配存储区域。

1.3 调用 Reset Handler

程序清单 1.4 调用 Reset Handler 的代码

; Reset Handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit ; IMPORT __main LDR R0, =SystemInit; BLX R0 ; LDR R0, =__main BX R0 ENDP

此段代码只完成了一个功能,引导程序进入__main。 __main 的具体行为在第三章中有具体的描述。

1.4 其他的代码

程序清单 1.5 CRP 加密级别

IF :LNOT::DEF:NO_CRP AREA |.ARM.__at_0x02FC|, CODE, READONLY CRP_Key DCD 0xFFFFFFFF ENDIF

此段代码指定了接下来的代码存储与 0x02FC 的地址,具体情况如下图所示。这段代码是 NXP 公司的 LPC1700 系列的 MCU 特有的一段代码,其他公司的 Cortex-M3 MCU 的启动程序是没有这段代码的。

这段代码是指定 LPC1700 的 CRP 加密级别的代码段,芯片上电后会自动读取这一地址的值以确定加密方式,其中 CRP_Key = 0xffffffff 为不加密(0 级加密), CRP_Key = 0x12345678为 1 级加密, CRP_Key = 0x87654321 为 2 级加密, CRP_Key = 0x43218765 为 3 级加密(最高级加密), 3 级加密将会禁止所有的 ISP 指令,也就是说,芯片将不能读写、不能擦除。

程序清单1.6 具体的堆栈以及堆的初始化行为

; User Initial Stack & Heap IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem Stack_Size) LDR R2, = (Heap_Mem Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF

,