原文在此: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中启动文件详解(汇编语言)的更多相关文章

  1. Liunx中fstab文件详解

    Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...

  2. 【转】linux中inittab文件详解

    原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...

  3. 第14章 启动文件详解—零死角玩转STM32-F429系列

    第14章     启动文件详解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ...

  4. STM32启动文件详解

    启动文件使用的 ARM 汇编指令汇总 启动程序源码注释(点此下载) 1. Stack—栈 Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE ...

  5. 单片机STM32的启动文件详解--学习笔记

    启动文件简介 启动文件由汇编编写,是系统上电复位后第一个执行的程序.主要做了以下工作: 1.初始化堆栈指针SP=_initial_sp 2.初始化PC 指针=Reset_Handler 3.初始化中断 ...

  6. (转)stm32启动文件详解

    在<<STM32不完全手册里面>>,用的是STM32F103RBT6,所有的例程都采用了一个叫STM32F10x.s的启动文件,里面定义了STM32的堆栈大小以及各种中断的名字 ...

  7. linux中inittab文件详解

    init的进程号是1(ps -aux | less),从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序. init程序需要读取配置文件/etc/ ...

  8. (转) eclipse项目中.classpath文件详解

    背景:对于java项目中.classpath文件中的相关定义一直不是很了解,有必要进行深入的学习. 1 前言 在使用eclipse或者myeclipse进行Java项目开发的时候,每个project( ...

  9. windows中.msc文件详解

    msc是Microsoft Management Console的缩写.其实是一种可执行程序类型,可.exe类似.一般可以通过直接双击.msc文件或者在windows的运行中输入相应的文件名来启动. ...

随机推荐

  1. EntityFramework之数据库以及表基本创建(一)

    前言 之前有学过EF一段时间那时EF才4.0似乎还不太稳定,而现在EF都已7.0版本,同时AspNet Identity都与此大有关联,看来是大势所趋于是开始学习EF,在学EF过程中也遇到一些小问题, ...

  2. 高级javascript---严格模式

    严格模式 (JavaScript)   严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,你无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 本主题后面的代码 ...

  3. JavaScript框架设计(四) 字符串选择器(选择器模块结束)

    JavaScript框架设计(四) 字符串选择器(选择器模块结束) 经过前面JavaScript框架设计(三) push兼容性和选择器上下文的铺垫,实现了在某一元素下寻找,现在终于进入了字符串选择器 ...

  4. ORA-01439: 要更改数据类型, 则要修改的列必须为空

    此方法有3处update操作,建议根据实际情况的数据量测试评估效率后选用. 思路:定义要更新数据类型的列为[col_old],数据类型为[datatype_old],临时列为[col_temp],数据 ...

  5. 韩顺平_linux_随堂笔记

    这还是自己3年前(2011年)整理的笔记,记得当时那会儿自己对Linux还特别的憧憬,也很喜欢韩老师的讲课风格,边看边做笔记乐此不彼,现在开通了技术博客,所以把当年的笔记也放上来和大家分享.同时推荐没 ...

  6. MySQL的多存储引擎架构

    支持多种存储引擎是众所周知的MySQL特性,也是MySQL架构的关键优势之一.如果能够理解MySQL Server与存储引擎之间是怎样通过API交互的,将大大有利于理解MySQL的核心基础架构.本文将 ...

  7. 简述ASP.NET MVC原理

    1.为什么ASP.NET需要MVC? 因为随着网站的的数量级越来越大,原始的网站方式,这里指的是WebForm,在运行速度和维护性方面,以及代码量上面,越来越难以满足日益庞大的网站维护成本.代码的重构 ...

  8. Handler类、异步线程和Message类的参数传递

    声明HandlerThread对象,实现了使用Looper来处理消息队列的功能 //这个类由Android操作系统提供 HandlerThread handlerThread = new Handle ...

  9. Microsoft Visual Studio 插件

    AnkhSVN BatchFormat CodeMaind Nuget Package Manager

  10. 数字限时增长效果实现:numberGrow.js

    这是上周工作中写到的一个功能,大概的效果就是页面中有几处数字,统计公司的一些业务信息,需要在第一次出现的时候,做一个从0开始增长,大概2秒自动增长到真实数值,并停止增长的效果.这个问题的重点在于解决如 ...