KEIL中启动文件详解(汇编语言)
原文在此:http://www.cnblogs.com/mddblog/p/4920063.html
概述
在嵌入式系统中,启动文件是整个系统非常关键的部分,它会进行一些底层的初始化,构建程序运行必要的环境,比如堆栈初始化,变量初始化等。如果启动文件出现错误,则整个系统就跑不起来,因此研究启动文件非常必要。
在keil中,启动文件由汇编代码编写,一般命名为startup_xxx.s,xxx为支持的某种芯片,比如可以是lpc15xx(NXP的LPC15xx系列)、MK60D10(飞思卡尔)、stm32f10x(意法半导体stm32f10x系列)等Cortext-M0/M3/M4内核芯片。它们的代码格式非常相近,根据启动文件代码由上到下的编写顺序.
可以将其分为以下5个典型部分:
1.堆栈空间定义;
2.存放中断向量表;
3.复位中断函数(Reset_Handler);
4.其它中断异常服务函数,以及弱[WEAK]声明;
5.将堆栈地址传递给库函数,利用库函数初始化堆栈,和库函数自身初始化。
5个部分具体说明如下:
1.堆栈空间定义
如下图所示,定义了栈大小Stack_Size = 0X200,即512字节;堆大小Heap_Size = 0X100,256字节。还定义了三个标号:__initial_sp(栈顶)、__heap_base(堆起始地址)和__heap_limit(堆终止地址),它们的空间由SPACE关键字来申请,并记作Stack_Mem和Heap_Mem。

通过这些我们可以很容易的知道堆栈的大小,但是它们的绝对地址或者说基地址仅仅从这里是得不到的。编译器编译完工程后,根据生成.bss段(比如未初始化的全局变量)和.data段(比如初始化的全局变量)的大小以及RAM的起始地址,来计算堆栈的基地址。
举个例子:
一个芯片的RAM起始地址为0x0200_0000,RAM大小为0x500字节,程序编译后.bss段为0x100个字节,.data段为0x100个字节。堆栈大小定义如上图。则:
A:堆起始地址 __heap_base==Heap_Mem==0x0200_0200;
B:堆终止地址即栈底 __heap_limit==Stack_Mem==0x0200_0300;
C:栈顶地址 __initial_sp==0x0200_0500(栈是向下生长,栈顶处于RAM最大地址处)。
其实,我可以在.map文件中查看堆栈的大小和基地址,如下图所示:

2.存放中断向量表
在启动代码中,会见到许多由DCD申请空间存放的一个个函数入口,即中断向量表,如下图所示,只列出了部分。

关键字DCD代表申请一个字的空间,后面的函数名即为中断服务函数入口地址。另外中断向量表一般存放在Flash 0地址。
另外,对于NXP微控制器,均实现了芯片的加密,加密的设置在向量表的结尾处,具体地址为0x02FC处。通过在此地址存放不同的值实现是否加密或者加密的等级。加密分为三个等级,CRP1:0x12345678;CRP2:0x87654321;CRP3:0x43218765。至于每个等级的具体说明请参考芯片用户手册。下面说一下加密步骤,以CRP1为例:

首先将下图中0xFFFFFFFF,修改为0x12345678。
其次,图中IF :LNOT::DEF:NOCRP表示如果没有定义宏NOCRP则执行下面的代码,那么必须保证汇编中没有定义NOCRP宏。即保证下图中Define:一栏中没有定义NOCRP即可。

3. 复位中断函数(Reset_Handler)
程序上电后,首先加载SP和PC,ARM规定从0地址处加载SP,从偏移为4的地址(0x00000004)处加载PC。然后将程序控制权交给程序。我们知道0地址处存放__initial_sp,0x00000004地址处存放Reset_Handler,加载PC后,程序跳转到Reset_Handler开始运行。Reset_Handler函数体如下图所示:

首先调用SystemInit函数来初始化系统的各种时钟,然后调用__main函数(由KEIL微库或者C库实现),在__main函数中:.data段数据的初始化->.bss段变量清零->设置堆栈指针->库函数初始化(比如常用的malloc函数)->如果必要会设置main函数的argc和argv两个参数->调用用户main函数->退出。
4.其它中断异常服务函数,以及弱[WEAK]声明

如上图所示,这里的中断服务函数是弱声明的(由[WEAK]关键字标注)。所谓弱声明,即:如果用户定义了相同的函数则此处函数失效而使用用户定义的中断服务函数。这样是为了防止用户使能了中断而没有中断服务函数,从而造成程序崩溃。假设使能了中断,而用户又没有定义中断服务函数则会进入默认中断,如下图所示,默认中断为死循环(死循环与程序崩溃不是一个概念)。

5.将堆栈地址传递给库函数
第三步骤中,调用__main函数,然后__main调用库函数初始化堆栈,但库函数并不知道堆栈的大小,因此我们需要告诉它,具体做法就是传递参数或声明标号。
下图为具体做法,可以看到第一行为:
IF :DEF:__MICROLIB
是条件编译选项,如果定义__MICROLIB,则编译图中红线上面部分,否则编译红线下面部分。那么就分2种情况。

2种情况的选择可以如下实现:
如果勾选【Options for Target】->【Target】->【Use MicroLIB】,如下图所示。即使用微库,则__MICROLIB会被定义,编译器编译红线以上代码。用EXPORT声明 __initial_sp、__heap_base和__heap_limit。

如果不勾选【Use MicroLIB】,则缺省使用KEIL C库,上图红线以下会参与编译,KEIL C库函数会调用__user_initial_stackheap,通过R0~R3将堆栈以参数形式传递给KEIL C库。
KEIL中启动文件详解(汇编语言)的更多相关文章
- Liunx中fstab文件详解
Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...
- 【转】linux中inittab文件详解
原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...
- 第14章 启动文件详解—零死角玩转STM32-F429系列
第14章 启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ...
- STM32启动文件详解
启动文件使用的 ARM 汇编指令汇总 启动程序源码注释(点此下载) 1. Stack—栈 Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE ...
- 单片机STM32的启动文件详解--学习笔记
启动文件简介 启动文件由汇编编写,是系统上电复位后第一个执行的程序.主要做了以下工作: 1.初始化堆栈指针SP=_initial_sp 2.初始化PC 指针=Reset_Handler 3.初始化中断 ...
- (转)stm32启动文件详解
在<<STM32不完全手册里面>>,用的是STM32F103RBT6,所有的例程都采用了一个叫STM32F10x.s的启动文件,里面定义了STM32的堆栈大小以及各种中断的名字 ...
- linux中inittab文件详解
init的进程号是1(ps -aux | less),从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序. init程序需要读取配置文件/etc/ ...
- (转) eclipse项目中.classpath文件详解
背景:对于java项目中.classpath文件中的相关定义一直不是很了解,有必要进行深入的学习. 1 前言 在使用eclipse或者myeclipse进行Java项目开发的时候,每个project( ...
- windows中.msc文件详解
msc是Microsoft Management Console的缩写.其实是一种可执行程序类型,可.exe类似.一般可以通过直接双击.msc文件或者在windows的运行中输入相应的文件名来启动. ...
随机推荐
- 从零开始编写自己的C#框架(7)——需求分析
本章内容虽然叫“需求分析”,实际上关于具体的需求分析操作步骤并没有深入去写,因为细化的话那将是一本厚厚的书,而需求分析在本系列中,是帮助大家了解项目的基本要求(主要针对本项目而已).而写本章的主要目的 ...
- 我的iOS开发系列博文
之前目录性的总结了发表过的关于OC方面的文章,今天在目录性的总结一下有关iOS开发的文章.走过路过不要错过哦,今天的博文也全都是干货.写技术博客与大家交流一下思想也是不错的. 下面是我的技术博客中有关 ...
- ELF静态链接
一直对ELF目标文件是怎样链接成可执行文件感到比较的疑惑,ELF文件里面的重定位段是怎样解决符号引用问题的?前几天偶然看了<深入理解计算机系统>里面讲了这个问题,看了之后对里面的实现机制终 ...
- EntityFramework 如何进行异步化(关键词:async·await·SaveChangesAsync·ToListAsync)
应用程序为什么要异步化?关于这个原因就不多说了,至于现有项目中代码异步化改进,可以参考:实际案例:在现有代码中通过async/await实现并行 这篇博文内容针对的是,EntityFramework ...
- C算法编程题(二)正螺旋
前言 上一篇<C算法编程题(一)扑克牌发牌> 写东西前总是喜欢吐槽一些东西,还是多啰嗦几句吧,早上看了一篇博文<谈谈外企涨工资那些事>,里面楼主讲到外企公司包含的五类人,其实不 ...
- ZOJ Problem Set - 1001 A + B Problem
ZOJ ACM题集,编译环境VC6.0 #include <stdio.h> int main() { int a,b; while(scanf("%d%d",& ...
- aud$定位错误用户密码登陆数据库的具体信息
环境:Oracle 11.2.0.3 客户端使用错误的用户密码登陆数据库 查询最近1天由于密码错误登陆失败的信息 查询当前审计中有哪些returncode值 1. 客户端使用错误的用户密码登陆数据库 ...
- Cesium原理篇:6 Render模块(4: FBO)
Cesium不仅仅提供了FBO,也就是Framebuffer类,而且整个渲染过程都是在FBO中进行的.FBO,中文就是帧缓冲区,通常都属于高级用法,但其实,如果你了解了它的基本原理后,用起来还是很简单 ...
- Electron Angular 开发小记
一介绍 electron分为主进程和渲染进程,主进程负责和原生交互,控制窗口等. 渲染进程就是普通网页.主进程和渲染进程可以通过ipcMain(主进程使用)及ipcRenderer(渲染进程用)通信 ...
- CSS魔法堂:"那不是bug,是你不懂我!" by inline-block
前言 每当来个需要既要水平排版又要设置固定高宽时,我就会想起display:inline-block,还有为了支持IE5.5/6/7的hack*display:inline;*zoom:1;.然后发 ...