STM32的启动流程和boot跳转
这期整理了一下STM32的启动流程和我在bootloader中遇到的一些小问题。
STM32的启动流程和boot跳转
1.启动文件是什么?
无论是是何种MCU,从简单的51,MSP430,到ARM9,ARM11,A7 都必须有启动文件,因为对于嵌入式开发,绝大部分情况都是使用C语言,而C语言一般都是从main 函数开始,但是对于MCU来说,他是如何找到并执行main函数的,就需要用到“启动文件”,就是各种 startup_xxxx.s 文件。
根据这个汇编文件里的一些汇编函数,系统经过一系列配置最终可以找到main函数并开始正常运行
2.启动流程
根据Cortex-M3 内核规定,起始地址必须存放栈顶指针而第二个地址则必须存放复位中断向量地址,这样在MCU复位后会自动从起始地址的下一个32位空间中读取复位中断函数并执行,由于我们stm32做了内存映射,那么默认情况下就是从0x80000000找到sp的值,从0x80000004这个地址找到复位中断函数。
启动文件起始就几个作用:
1.设置SP指针的值
2.设置PC指针的值
3.初始化系统时钟
4.调用__main函数,在__main函数中会把flash中的rw-data给拷贝到sram中,在__main函数的最后会调用C函数,剩下就是我们自己写的代码了。
3.启动文件分析
Stack_Size EQU 0x00000400这个相对于C语言中的define Stack_Size 0x400 和define一样这只是一个定义不会开辟实际的内存空间,这里就定义了我们这个栈空间是1KB,这个值是可以修改的。
ARER 后面的关键字表示这个段的属性: STACK : 表示这个段的名字,可以任意命名。 NOINIT: 表示此数据段不需要填入初始数据。 READWRITE:表示此段可读可写。
ALIGN=3:表示按2的3次方对齐,所以我们栈是8字节对齐的。
SPACE就真正开辟了内存空间 __initial_sp就是我们栈顶的地址
这里同样的,开辟了512B的内存给到了堆,其中__heap_base是堆的起始地址,__heap_limit是堆的结束地址。这里堆栈有区别是因为栈是从上往下生长的而堆是从下往上生长的,两个生长方向是相反的。
中断向量表:
其实向量表的本质就是一个元素大小为4字节的数组,而这个数组里面的元素就是这些中断服务函数的名字(也就是中断服务函数的地址),而这张向量表默认就放在flash起始地址处,注意向量表的第一个存储的是栈顶指针(栈顶的地址),开发板上电复位时由CPU将这个栈顶地址赋给CPU的栈寄存器SP
中断向量表的作用就是当中断来临时CPU会从这个表里读取对应中断函数的地址然后去执行这个中断函数,这个过程由硬件帮我们执行。所以当我们程序由boot跳转到app中时对应中断也应该去app的中断向量表读取值执行,不然app执行的过程中调用boot的中断这显然是不对的。我们可以通过重定向VTOR这个寄存器来进行对中断向量表的偏移SCB->VTOR = address; //这句话
中断复位函数:
这其实就是运行了SystemInit和__main两个函数,这个函数是用WEAK弱定义的,也就是说我们其实可以自己来构建这个Reset_Handle函数来实现启动(不过不建议这样)SystemInit中就是帮我们初始化了系统的时钟。
__main 标号表示 C/C++标准实时库函数里的一个初始化子程序 __main的入口地址。该程序的一个主要作用是初始化堆栈(跳转_user_initial_stackheap标号进行初始化堆栈),并初始化映像文件,最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点,因为这是由C/C++标准实时库所规定的。总之,在__main文件后就进入main函数,我们就可以来写代码了。
这就是STM32的启动流程了。
在GCC环境下启动文件差不多,但有一点需要注意,先看链接文件.ld
这里显示算出来了这个_estack是ram最高的地方
在启动文件的中断向量表中
这里将_estack的值赋给了_sp,所以GCC环境下编译出来栈是放在ram的最上面。而MDK不一样,MDK中_sp指针的位置是连接器通过sct文件推算出来,开头是 data多大,bss多大,heap大小就知道sp的头在哪,再根据启动文件里面栈空间大小就能推算出栈顶,也就是_initial_sp的地址,他俩的主要区别如下图所示
根据我们的启动流程,从boot跳到app中所需要干的事情就三个:
1.重新设置sp的值为app中flash的起始地址
2.设置pc的值为app中flash起始地址+4
3.重定义VTOR的值,将中断向量表偏移到app中的去。
这里有问题了,如果跳过去不设置sp会怎样?
答:不设置sp的话app会和boot共用一个栈,此时因为boot已经用了一部分栈空间了,app不知道,会当成还有规定好的空间去使用栈,这样很可能造成栈溢出导致系统崩溃,是我们极力要避免的错误。
最后附一个跳转app的函数~
下期不知道更啥了,随缘发吧~