GCC编译器+STM32的程序引导分析

程序引导分析

基于HAL库,学习STM32单片机的过程,实则是逐步熟悉和使用HAL库的过程。首先要熟悉的是STM32CubeMX 软件帮我们自动生成main.c文件,main.c文件是我们编写项目代码的入手点。

我们在计算机编程中,一致认为C程序的入口为main函数,main函数就在main.c文件中。我们很少有人问,为什么C程序是从main函数开始运行的呢?我们回到STM32中,系统上电瞬间,程序计数器PC复位。意味着开机时,单片机程序是从地址号为零的位置开始运行的。难道我们的main函数符号位置在地址0x00000000处?不可能的!单片机程序计数器PC指向mian函数入口地址的过程,是要在完成很多配置工作以后的事了,因此main函数只能算我们用户构建程序时的用户代码入口。我们把程序计数器PC指向main函数入口的过程称之为程序引导,也可以理解成:单片机是依靠汇编程序引导才进入C语言的main函数的。这一汇编程序过程由工程框架中的startup_stm32f429xx.s文件中的汇编程序代码来完成。

注意:使用makefile工具链的startup_stm32f429xx.s文件内容与Keil和IAR的是不一样的,且对应的system_stm32f4xx.c文件中的 SystemInit函数代码也是有区别的。但是引导过程中的代码意图是一致的。本教程基于makefile的startup_stm32f429xx.s文件进行讲解。

ARM各个版本架构中有三个指令集:Thumb指令集(16位)、ARM指令集(32位)和Thumb2指令集。Thumb2指令集是Thumb指令集的一个扩充,既包含了16位也包含了32位,几乎可以提供与ARM相同的功能,但是指令密度(单位内存所存放的指令数)与Thumb指令集相似。有了Thumb2指令集就不再需要在ARM与Thumb两者之间切换了,因为Thumb2就够了。

STM32F429使用了ARM Cortex-M4的内核基于ARMv7E-M架构,使用Thumb-2指令集。startup_stm32f429xx.s中的汇编代码切实反映了Thumb2指令集。即使程序进入C代码,汇编器最终也是将C程序汇编成了Thumb2汇编指令。

startup_stm32f429xx.s文件中内容中,主要任务有:

1. 声明数据段和代码段。

2. 初始化栈指针MSP=_initial_sp。

3. 初始化复位程序计数寄存器值=Reset_Handler。

4. 初始化数据段。

5. 系统初级初始化。

6. c 库的初始化。

7. 引导进main函数。

8. 初始化异常/中断向量表。

startup_stm32f429xx.s文件中的相关代码与伪代码不做具体分析。针对引导过程中的相关任务,作如下两点补充说明:

1)数据初始化

STM32单片机程序(C程序)中,存在两个要素:代码和数据,其中数据有变量和常量。这些要素被编译器编译后,形成bin、hex和elf文件,这些文件是我们计算机磁盘上的文件。我们可以选择其中的一个文件,下载进入单片机的FLASH中。单片机断电时,这些文件是静静的存储在FLASH中的;当单片机开机运行时,FLASH中的程序(要素为:代码)进入运行态。这就好比要唱戏,先得把戏台子搭好。搭台子第一步就是准备好服饰、道具和背景素材,有了这些,演员才能扮相,才能完成登台前的心理建设和角色带入。接下来就是舞台捯饬,这样戏台子搭好以后,才能开罗唱戏,生旦净末丑才能粉墨登场。

所以单片机开始运行态时,首先,代码需要将程序要素变量,分配在单片机的SRAM中。我们在C语言程序中,数据并不是一个模式的,至少作用域就有很大的区别。因此有些变量放在数据段上,有些会在堆栈中。其中数据段的数据代表着全局的,意味着程序在启动伊始,就得规划它们的存储位置。但是有些变量是函数内部变量,只有在该函数被调用时,才会产生,我们将这样的数据临时存放在堆中。一旦函数退出执行,局部变量的其生命结束,将局部变量从堆中剔除。故局部变量在,代码运行之初是不需要考虑的。但是全局变量必须准备好。

全局变量一般分为两种,一种是带有初始化值的变量,一种是初始化未赋初始值的变量。C语言教程中,往往会说未初始化的变量,编译器自动设为0,依据是什么呢?其实单片机程序中,这种未作初始化的全局变量,代码在开始启动时,将他们都赋值为0了,且这个行为,必须在引导代码中发生。

这样一来,两种情况就可以确定下来了。第一种:初始化有值的全局变量,在程序引导过程中,将其初始值拷贝给该变量;第二种,将初始化未赋值的变量,全部写入数据0。为了引导代码易于实现这一任务,我们把数据存储器分为两个数据段:.data和.bss。其中.data存放初始化有值的全局变量;.bss存放初始化为0的全局变量。

另外,静态变量(关键字static修饰的)包括局部静态变量和全局静态变量,都是存储在.data段。

.data和.bss,以及FLASH(代码段.text)的大小和起始等存储空间描述,是由链接文件STM32F429ZETx_FLASH.ld,来告知GCC编译器的,因此GCC才能有组织的去规划所有程序要素,以至于能解决明确程序要素所存在的地理位置问题。另外,还得补充一点。我们程序中的常量也是存放在FLASH中的代码段(.text)中。

2)系统初始化

我们把系统初始化理解成舞台捯饬,这种捯饬也仅仅是初级的处理,具体到戏剧的某一节,背景至少还得作应景处理。所谓初级处理,涉及到:要不要开启浮点处理单元?要不要在单片机外面扩展SRAM或SDRAM数据存储器?要不要重新定位中断向量表?

这些初级初始化,都是通过全局宏,来告知GCC编译器。GCC编译器便通过宏条件编译(可以理解成一种选择性编译行为) ystem_stm32f4xx.c文件中的SystemInit函数的内部代码。从而实现单片机系统初配。完成系统初配,就可以把程序执行权限交给main程序了。

在进入main函数伊始,我们的单片机还是工作在内部高速晶振提供的16MHz系统时钟频率下。因此进入main函数以后,首先完成HAL库的初始化,以致在HAL库方式中实现系统时钟的初始化。这样,单片机就可以按时钟树配置,在外部晶振倍频180MHz的内核频率下运行了。至此,我们的程序才真正完成代码引导过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值