前言:
如今咱们对“armc语言编程”大约比较注意,大家都想要了解一些“armc语言编程”的相关资讯。那么小编也在网络上收集了一些有关“armc语言编程””的相关知识,希望小伙伴们能喜欢,兄弟们快快来了解一下吧!1.1 最简单的启动代码(Startup.s Version-1.0)
经过上面的理论知识的铺垫,我们已经对 ARM(LPC17xx)的启动过程有了一个清晰的认识,下面就来实战一下,编写一个自己的 ARM 启动代码。
1.1.1 启动代码必须完成的三项工作
对于一个复杂系统的话,则启动代码需要完成的工作比较多过程也比较复杂,比如一个存储体系比较复杂的系统(在一个系统中由内部、外部的 RAM 又有内部和外部的ROM)或带有操作系统的。对于不同的 ARM 架构的芯片其启动代码差别也比较大,比如在 ARM9 中各种运行模式的堆栈的分配、管理以及切换全部都要由用户自己实现,而在 M0 或 M3 中就不需要用户去实现这些过程。对于 Cortex-M3 内核的处理器,其启动代码必须完成三项任务:
指定 MSP 的初始值;指定程序运行的入口;初始化一个堆栈(如果执行的是 C 代码则需要执行此过程,否则可以省略)。
1.1.2 程序实现(Startup.s Version-1.0)
ARM 处理器的运行是从 ROM 空间的 0x00000000 地址开始的,而在 Cortex-M3 中从0x00000000 地址开始的区域里维护了一张中断向量表。向量表中的第一个字中存储的是主堆栈指针 MSP 的值, 第二个字中存储的是程序的入口地址(一些资料上说这个字里存储的是复位向量或复位程序的入口地址,实际上这种说法不够准确,这个字可以不用来指定复位程序的入口。),之后就是真正的中断向量表了。
结合 Cortex-M3 内核的处理器启动代码必须完成三项任务,首先需要指定 ROM 空间的第一个以及第二个字的内容,其中第二个字我们将其指定为__main,为 C 语言的运行搭建环境,以此为入口开始运行程序,其中__main 的运行需要用户自己实现__user_initial_stackheap,故启动代码实现如下:
程序清单 1.1 Startup.s(V1.0)
;/*; * File : Startup.s; * Version : 1.0; * Date : 11-18-2011; * Author : ZhengYuanChao; *; * LPC1700 ARM Cortex-M3 启动代码; */; /*; * 说明: Cortex-M3 启动需要完成以下三项工作(在使用标准 C Library 的情况下); * 1. 指定栈顶; * 2. 引入__main 并跳转过去; * 3. 重写__user_initial_stackheap; */ PRESERVE8 ; 指定 8 字节对齐(keil 的要求) AREA LPC1700Startup, DATA, READONLY ; 指定数据段 IMPORT __main ; 导入__main 标号__Vectors DCD 0x10008000 ; 指定 MSP DCD __main; 跳转到__mainAREA |.text|, CODE, READONLY; 指定代码段 EXPORT __user_initial_stackheap ; 导出此标号供__main 调用__user_initial_stackheap BX LR; 不执行任何操作直接跳回__mian ALIGN; 对齐填充使代码段双字对齐 END
1.1.3 指定加载方式让程序跑起来
仅仅完成了 Startup.s 的编写还是不够的,还需要为程序生成的镜像文件指定加载方式(第四章有具体描述),否则程序无法运行。对于 Version-1.0 的启动代码加载方式的指定比较简单,只需将__Vectors 放在 ROM 的顶端即可,至于其他代码的摆放则无关紧要,可由链接器自行安排。描述如下:
程序清单 1.2 分散加载代码(V1.0)
ROM_LOAD 0x00000000{ VECTOR 0x00000000 { *.o (LPC1700Startup, +FIRST) ; 将__Vectors 放在 ROM 的顶端 .ANY (+RO) } SRAM 0x10000000 { * (+RW,+ZI) }}
现在程序可以运行了,使用软模拟调试可以发现程序顺利的进入了 main()函数。
1.1.4 Version-1.0 的缺点
Version-1.0 只对处理器进行了最简单配置并令其运行起来,它甚至连中断向量表都没有初始化,也就是说处理器将不能响应中断,这当然是不可以的。故此代码是没有什么使用价值的。
1.2 一个实用的启动代码(Startup.s Version-2.0)
1.2.1 设计描述
要让启动代码真正具有实用性必须还要定位一张向量表,某些程序还可能用到堆,所以我们还要再管理一个堆,对于 NXP 的 LPC17xx 还要在启动代码中指定芯片的加密方式。
因为 Version-1.0 的启动代码已经成功的配置了 C 语言的运行环境,故接下来的代码我们将用 C 语言来编写。
1.2.2 程序实现(Startup.s)
程序清单 1.3 Startup.s(V2.0)
; /*; * 说明: Cortex-M3 启动需要完成以下三项工作(在使用标准 C Library 的情况下); * 1. 指定栈顶; * 2. 引入__main 并跳转过去; * 3. 重写__user_initial_stackheap; * 4. 对于 NXP 的 LPC1700 系列还要指定其加密级别; */SP_TOP EQU 0x10008000HEAP_TOP EQU 0x10007000 PRESERVE8 ; 指定 8 字节对齐(keil 的要求) AREA StartupEntry, DATA, READONLY ; 指定数据段 IMPORT __main ; 导入__main 标号__Start DCD SP_TOP ; 指定 MSP DCD __main ; 跳转到__main IF :LNOT::DEF:NO_CRPAREA |.ARM.__at_0x02FC|, CODE, READONLY ; 加密标志存储于 0x02fc 地址 CRP_Key DCD 0xFFFFFFFF; 0 级加密(即不加密) ENDIF AREA |.text|, CODE, READONLY; 指定代码段 EXPORT __user_initial_stackheap; 导出此标号供__main 调用__user_initial_stackheap LDR R0, =HEAP_TOP ; 指定堆顶 BX LR; 跳回__mian ALIGN; 对齐填充使代码段双字对齐 END
1.2.3 向量表 Vectors.h 和 Vectors.c
程序清单 1.4 Vectors.h
Vectors.h 的代码如下:#ifndef __VECTORS_H__#define __VECTORS_H__#define MAX_VICTORS 51#define NMI_Handler defaultVectorHandle#define HardFault_Handler defaultVectorHandle#define MemManage_Handler defaultVectorHandle#define BusFault_Handler defaultVectorHandle#define UsageFault_Handler defaultVectorHandle#define SVC_Handler defaultVectorHandle#define DebugMon_Handler defaultVectorHandle#define PendSV_Handler defaultVectorHandle#define SysTick_Handler defaultVectorHandle……#define QEI_IRQHandler defaultVectorHandle#define PLL1_IRQHandler defaultVectorHandle#define USBActivity_IRQHandler defaultVectorHandle#define CANActivity_IRQHandler defaultVectorHandle#endif
因为中断向量表存储的是中断函数的入口地址而且又是一张表,故我们用函数指针数组来表示中断向量表, Vectors.c 的代码如下:
程序清单 1.5 Vectors.c
#include "vectors.h"#include "stddef.h"#include "syscfg.h"void * const __VectorsTable[MAX_VICTORS] ={ (void *)(NMI_Handler), (void *)(HardFault_Handler), (void *)(MemManage_Handler), (void *)(BusFault_Handler), (void *)(UsageFault_Handler), (void *)defaultVectorHandle, (void *)defaultVectorHandle, (void *)defaultVectorHandle, (void *)defaultVectorHandle, (void *)(SVC_Handler), (void *)(DebugMon_Handler), NULL, (void *)(PendSV_Handler), (void *)(SysTick_Handler), …… (void *)(QEI_IRQHandler), (void *)(PLL1_IRQHandler), (void *)(USBActivity_IRQHandler), (void *)(CANActivity_IRQHandler)};
这里需要注意的是,数组必须用 const 限定,因为中断向量表必须是 Read-Only 的,最后还需要在创建一个文件重写 defaultVectorHandle 函数即可(此段代码省略)。
1.2.4 指定加载方式
向量表必须定位在__Start 标号之后,否则是无意义的,故描述如下:
程序清单 1.6 分散加载代码(V2.0)
ROM_LOAD 0x00000000{ STARTUP 0x00000000 { *.o (StartupEntry, +FIRST) } VECTORS 0x00000008 { vectors.o(+RO-DATA, +FIRST) .ANY (+RO) } SRAM 0x10000000 { * (+RW,+ZI) }}
之后再将芯片的时钟以及 MPU 等具体的硬件初始化代码添加进来我们的芯片就可以正常运行了。
标签: #armc语言编程