<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path> </svg> <p></p>
讯享网
一. 硬件环境工作
2.初始化(清零)ZI域。
3.初始化堆栈指针
三. Cortex M3启动
CortexM3有3种启动方式
1、 BOOT1=1 BOOT0=1
中断向量表定位于SRAM区,即起始地址为0x,同时复位后PC指针位于0x处。
2、 BOOT1=x BOOT0=0
中断向量表定位于FLASH区,即起始地址为0x,同时复位后PC指针位于0x处。
3、 BOOT1=0 BOOT0=1
中断向量表定位于内置Bootloader区,此时可通过串口下载程序的二进制文件到flash区。
而Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比ARM7/ARM9内核,Cortex-M3内核则是固定了中断向量表的位置而起始地址是可变化的。即是对于flash启动来说(正常工作也是flash启动),0x地址存放的是栈顶地址__initial_sp,0x地址存放的是复位中断向量Reset_Handler入口地址(STM32使用32位总线,存储空间为4字节对齐);在编写多段程序时,偏移地址空间需注意,如编写一个BootLoader,从BootLoader到应用程序段的相互跳转。
https://mp.weixin..com/s/EuXIECgKUBM45c2-7dpscw
大学里第一开始接触到的单片机也许就是51单片机,但是有多少人还记得51单片机的启动流程。
MSC-51单片机的总结结构是冯诺依曼结构,指令和数据总线分开编址。传统的51单片机片内RAM:128字节,ROM:4K;两个16位的定时器;1个全双工的串口;8位CPU;32个I/O口(P0-P3),其中P0口是开漏输出,做输出脚时需要接上拉电阻10k。
上电的时候,cpu指向0000H的位置,一般在000H放置一条跳转指令,跳转指令调至main函数的入口,外部中断0的服务入口地址为0003H,定时器0中断服务入口地址为:000BH;外部中断1的服务入口地址为0013H,定时器1中断服务入口地址为:001BH;串口中断服务入口地址位0023H.定时器2中断入口地址位:002BH
下面是一个52的汇编程序,程序的开头是一个伪指令ORG,接着就一个跳转指令跳转到主程序的指令AJMP.然后就是中断服务函数的入口出口的定义.
讯享网
启动代码通常都烧写在flash中,它是系统一上电就执行的一段程序,它运行在任何用户c代码之前。上电后,arm处理器处于arm态,运行于管理模式,同时系统所有中断被禁止,pc到地址0处取指令执行。一个可执行映像文件必须有个入口点,而能放在rom起始处的映像文件的入口地址也必须设置为0。
在汇编语言中,我们已经说过怎样定义一个程序的入口点,当工程中有多个入口点时,需要在连接器中使用-entry指出程序的入口点。如果用户创建的程序中,包含了main函数,则与c库初始化代码对应的也会有个入口点。
总的来说,启动代码主要完成两方面的工作,一是初始化执行环境,例如中断向量表、堆栈、i/o等;二是初始化c库和用户应用程序。
在第一阶段,启动代码的人物可以描述为:
- (1)建立中断向量表;
- (2)初始化存储器;
- (3)初始化堆栈寄存器;
- (4)初始化i/o以及其他必要的设备;
- (5)根据需要改变处理器的状态。
建立中断向量表
初始化代码必须建立好中断向量表,以备应用程序后续使用。如果系统的地址0处是rom,则中断向量表直接是一些跳转指令就可以了,他们转到相应的中断处理函数执行。如果系统的0地址处不是rom,则中断向量表是通过动态的方式创建的,这主要是通过存储器映射的方式来实现:即上电后,rom中的地址被映射到地址0,它首先开始执行以便完成环境的初始化,最重要的它会将中断向量表拷贝到ram中,然后通过地址映射将ram地址映射为0,这样ram中的中断向量就可以使用了。
初始化存储系统
对于有mmu的处理器,需要正确初始化mmu,没有的只需正确初始化存储控制器,为每个bank配置正确的参数就可以了。
初始化堆栈指针
初始化代码必须初始化处理器各个模式下的堆栈指针,所有系统或用户程序会涉及的处理器模式对应的堆栈指针都应该初始化。通常未定义指令和预取指终止异常对应模式的堆栈指针不需要配置,除非用户需要使用它们作为调试使用。
初始化堆栈指针
初始化代码必须初始化处理器各种模式下的堆栈指针,所有系统或用户程序会涉及的处理器模式对应的堆栈指针都应该被初始化。通常未定义指令和预取指终止异常对应模式的堆栈指针不需要配置,除非用户需要使用它们作为调试使用。
初始化i/o以及其他必要设备
关键的输入输出模块必须在中断打开之前被配置,例如看门狗,否则它们会在系统启动后产生复位信号。
改变处理器状态和模式
启动代码运行时,处理器状态认为管理模式,如果用户程序需要运行在用户模式,可以切换转入用户模式;所有处理器上电后是处于arm状态的,如果需要改变处理器状态,也可以在启动代码里切换到thumb态。
在执行环境建立起来后,接下来就是应用程序的初始化,简单点就是讲用户程序加载到他们相应的运行地址,初始化数据区等,这个阶段完成后,才能进入用户最终的c代码区域。用户应用程序的初始化过程包括:将rw段的数据拷贝到他们的运行地址处,同时在rw段后面初始化相应大小的zi段数据,把他们初始化为0,使用了库函数的程序(工程中有main函数)是在库函数_main中自动完成这些工作的。
https://mp.weixin..com/s/a7wV78NZLS81TCKpMQiQiw
对于熟悉电脑的伙伴们来说,BIOS(那个蓝色的界面)可能不会太陌生吧,这货就是电脑的启动代码。没有BIOS的电脑,那注定是一块板砖!BIOS主要是做一些开机前的准备工作,例如系统时间设定、启动顺序。。。扯远了!
其实电脑本身就是从单片机而来,那么单片机也是有启动代码的,只是我们绝大部分情况不去关心它。
启动代码究竟都干了些什么工作,为何需要它?想想你在c语言中用到了什么东西,而这些东西却是拿来就可以用的?堆、栈!没错,就是他们。我们知道堆和栈是内存中划分出的一块区域,那为什么我们没有亲自划分呢,因为启动代码帮了你的忙!!!再想想单片机工作的时候,有哪些配置被我们忽略了,而它却可以用?时钟,就是这货,我们可以不配置时钟,而你发现它竟然有默认值!还是启动代码帮了你!
接下来仔细研究一下STM32的启动代码,首先要知道启动代码藏在哪里:一个叫做startup的汇编文件。启动代码是对硬件的一个最初级的配置,它必须用汇编语言来实现,汇编是真正的硬件编程语言。
从上到下解读启动代码:
startup_stm32f103xe.s
1、定义栈大小
2、定义堆大小
讯享网
3、中断向量地址
ps:Cortex-M4内核要求内存的第一个地址是栈指针,第二个地址开始为中断向量。而中断向量的第一个必须是复位,因为代码是从上到下执行,开机首先遇到的就是复位。
4、复位中断处理函数
讯享网
ps1:仔细那看有两个东西需要注意:“SystemInit”这个是时钟初始,“main”这一句代表跳转到“main”函数。正因为复位这样的写法,所以程序都是从main函数开始执行的。
ps2:“SystemInit”具体实现过程在stm32fxxx.c,“main”也是在某个地方与真正的main关联起来。
5、其他中断处理函数
ps1:绝大部分中断的实现系统没有给出,等待程序员去写。但是中断的名字我们都可以看到的,每一个名字都和上面向量表中对应着。我们知道函数名其实就是一个地址,而中断的入口地址是固定的,只要找对名字,那地址也就找对了。
ps2:中断的入口地址由内核和芯片决定,无法更改。因此不要改启动代码中的中断向量表
6、初始化堆、栈
讯享网
ps:正因为堆和栈已经被初始化,所以你的C语言才可以顺利的执行。
启动代码是非常关键的,所有的CPU都有启动代码。这里完成了最基本的初始化功能,尤其是中断向量表,程序员每一个中断函数的名字都要与启动代码中的向量表对应,否则中断是无法进入的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/159474.html