《解析-STM32-的启动过程5725.pdf》由会员分享,可在线阅读,更多相关《解析-STM32-的启动过程5725.pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、解析的启动过程解析 STM32的启动过程当前的嵌入式应用程序开发过程里,并且 C 语言成为了绝大部分场合的最佳选择。如此一来 main 函数似乎成为了理所当然的起点因为 C 程序往往从 main 函数开始执行。但一个经常会被忽略的问题是:微控制器(单片机)上电后,是如何寻找到并执行 main 函数的呢?很显然微控制器无法从硬件上定位 main 函数的入口地址,因为使用 C 语言作为开发语言后,变量/函数的地址便由编译器在编译时自行分配,这样一来 main 函数的入口地址在微控制器的内部存储空间中不再是绝对不变的。相信读者都可以回答这个问题,答案也许大同小异,但肯定都有个关键词,叫“启动文件”,
2、用英文单词来描述是“Bootloader”。无论性能高下,结构简繁,价格贵贱,每一种微控制器(处理器)都必须有启动文件,启动文件的作用便是负责执行微控制器从“复位”到“开始执行 main 函数”中间这段时间(称为启动过程)所必须进行的工作。最为常见的51,AVR 或 MSP430等微控制器当然也有对应启动文件,但开发环境往往自动完整地提供了这个启动文件,不需要开发人员再行干预启动过程,只需要从 main 函数开始进行应用程序的设计即可。话题转到 STM32微控制器,无论是 keil uvision4还是 IAR EWARM 开发环境,ST 公司都提供了现成的直接可用的启动文件,程序开发人员可以
3、直接引用启动文件后直接进行 C 应用程序的开发。这样能大大减小开发人员从其它微控制器平台跳转至 STM32平台,也降低了适应 STM32微控制器的难度(对于上一代 ARM的当家花旦 ARM9,启动文件往往是第一道难啃却又无法逾越的坎)。相对于上一代的主流内核架构,新一代内核架构的启动方式有了比较大的变化。内核的控制器在复位后,会从存储空间的绝对地址取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为()同时中断向量表的位置并不是固定的。而内核则正好相反,有 种情况、通过引脚设置可以将中断向量表定位于区,即起始地址为,同时复位后指针位于处;、通过引脚设置可以将中断向量表定位于
4、区,即起始地址为,同时复位后指针位于处;、通过引脚设置可以将中断向量表定位于内置区,本文不对这种情况做论述;而内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在内核复位后,会自动从起始地址的下一个位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比内核,内核则是固定了中断向量表的位置而起始地址是可变化的。有了上述准备只是后,下面以的固件库提供的启动文件“”为模板,对的启动过程做一个简要而全面的解析。程序清单一:;文件“”,其中注释为行号;如程序清单一,的启动代码一共行,使用了汇编语言编写,这其中的主要原因下文将会给出交代。现在从第一行开始分析:第 行:
5、定义是否使用外部,为 则使用,为 则表示不使用。此语行若用语言表达则等价于:第 行:定义栈空间大小为个字节,即。此语行亦等价于:第 行:伪指令,表示第 行:开辟一段大小为的内存空间作为栈。第 行:标号,表示栈空间顶地址。第 行:定义堆空间大小为个字节,也为。第 行:伪指令,表示第 行:标号,表示堆空间起始地址。第 行:开辟一段大小为的内存空间作为堆。第行:标号,表示堆空间结束地址。第行:告诉编译器使用指令集。第行:告诉编译器以 字节对齐。第行:指令,指示后续符号是在外部文件定义的(类似语言中的全局变量声明),而下文可能会使用到这些符号。第行:定义只读数据段,实际上是在区(假设从启动,则此中断向
6、量表起始地址即为)第行:将标号声明为全局标号,这样外部文件就可以使用这个标号。第行:标号,表示中断向量表入口地址。第行:建立中断向量表。第行:第行:复位中断服务程序,结构表示程序的开始和结束。第行:声明复位中断向量为全局属性,这样外部文件就可以调用此复位中断服务。第行:为预编译结构,判断是否使用外部,在第 行中已定义为“不使用”。第行:此部分代码的作用是设置总线以支持,因不使用外部因此此部分代码不会被编译。第行:声明标号。第行:跳转地址执行。第行:结构,判断是否使用(此处为不使用)。第行:若使用,则将,亦即栈顶地址,堆始末地址赋予全局属性,使外部程序可以使用。第行:定义全局标号。第行:声明全局
7、标号,这样外程序也可调用此标号。第行:标号,表示用户堆栈初始化程序入口。第行:分别保存栈顶指针和栈大小,堆始地址和堆大小至,寄存器。第行:程序完毕。以上便是的启动代码的完整解析,接下来对几个小地方做解释:、指令:伪指令,用于定义代码段或数据段,后跟属性标号。其中比较重要的一个标号为“”或者“”,其中“”表示该段为只读属性,联系到的内部存储介质,可知具有只读属性的段保存于区,即地址后。而“”表示该段为“可读写”属性,可知“可读写”段保存于区,即地址后。由此可以从第、行代码知道,堆栈段位于空间。从第行可知,中断向量表放置与区,而这也是整片启动代码中最先被放进区的数据。因此可以得到一条重要的信息:地
8、址存放的是栈顶地址,地址存放的是复位中断向量(使用位总线,因此存储空间为 字节对齐)。、指令:作用是开辟一段空间,其意义等价于语言中的地址符“”。因此从第行开始建立的中断向量表则类似于使用语言定义了一个指针数组,其每一个成员都是一个函数指针,分别指向各个中断服务函数。、标号:前文多处使用了“标号”一词。标号主要用于表示一片内存空间的某个位置,等价于语言中的“地址”概念。地址仅仅表示存储空间的一个位置,从语言的角度来看,变量的地址,数组的地址或是函数的入口地址在本质上并无区别。、第行中的标号并不表示程序中的函数入口地址,因此第行也并不是跳转至函数开始执行程序。标号表示标准实时库函数里的一个初始化
9、子程序的入口地址。该程序的一个主要作用是初始化堆栈(对于程序清单一来说则是跳转标号进行初始化堆栈的),并初始化映像文件,最后跳转程序中的函数。这就解释了为何所有的程序必须有一个函数作为程序的起点因为这是由标准实时库所规定的并且不能更改,因为标准实时库并不对外界开发源代码。因此,实际上在用户可见的前提下,程序在第行后就跳转至文件中的函数,开始执行程序了。至此可以总结一下的启动文件和启动过程。首先对栈和堆的大小进行定义,并在代码区的起始处建立中断向量表,其第一个表项是栈顶地址,第二个表项是复位中断服务入口地址。然后在复位中断服务程序中跳转标准实时库的函数,完成用户堆栈等的初始化后,跳转文件中的函数开始执行程序。假设被设置为从内部启动(这也是最常见的一种情况),中断向量表起始地位为,则栈顶地址存放于处,而复位中断服务入口地址存放于处。当遇到复位信号后,则从处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转函数,最后进入函数,来到的世界。