本帖最后由 woai32lala 于 2024-3-25 20:55 编辑
#申请原创#@21小跑堂
深入了解STM32F103单片机的BootLoader启动加载器 BootLoader在嵌入式系统中起到了非常关键的作用,它是嵌入式系统启动的第一步,负责加载和执行固件程序,确保系统的正常运行。本文将以STM32F103单片机为例,详细介绍如何编写和应用BootLoader。 一、STM32F103单片机简介 STM32F103单片机是ST公司生产的一款基于ARM Cortex-M3内核的32位Flash微控制器。它具有高性能、低成本、低功耗的特点,广泛应用于工业控制、消费电子、医疗设备等领域。 二、BootLoader的工作原理和流程
- 系统上电:当STM32F103单片机上电时,BootLoader程序会被加载到内存中并开始执行。
- 硬件初始化:BootLoader首先进行硬件初始化,包括配置时钟、GPIO、中断等。
- 检测固件版本:BootLoader可以通过检测固件版本来判断是否需要更新固件程序。
- 加载固件程序:如果需要更新固件程序,BootLoader将从外部存储器(如SD卡、NAND Flash等)或内部Flash存储器中加载固件程序,在这里我们只通过跳转到应用程序来验证。
- 校验固件程序:BootLoader会对加载的固件程序进行校验,确保固件程序的完整性和正确性。
- 跳转到用户程序:如果固件程序校验通过,BootLoader将跳转到用户程序的入口地址,开始执行用户程序。
三、TM32F103单片机BootLoader的实现 以下是一个简单的STM32F103单片机BootLoader的实现示例,使用标准库。 一共又两个程序,一个是BootLoader引导程序,另一个是APP应用程序。 BootLoader引导程序我们用LED慢闪作为指示,APP应用程序我们用LED快闪进行指示。 BootLoader部分 1、内存介绍 我们选用的是大容量的芯片,主存储模块起始地址为0x0800 0000,即Bootloader起始地址,当然这个地址是可以通过BOOT引脚进行设置的,如下图所示。 当BOOT0引脚通过一个下拉电阻接地,程序才会从主闪存存储器启动。 该芯片FLASH容量为256x 2k = 512k = 512 x 2 x1024 = 0x80000 个字节 2、中断向量表 程序开始启动后,首先查找中断向量表,中断向量表是为了单片机执行中断跳转,比如发生定时器中断,他会根据定时器中断函数在向量表的地址去执行程序,中断向量表程序烧写在FLASH开头位置。 3、实现思路 bootloader其实就是一段位于存储器中的启动程序,它在芯片上电启动的时候首先被执行,它可以用来做一些硬件的初始化,当初始化完成之后跳转到对应的应用程序中去。因此,我们可以将内存分为两个区,一个是启动程序区,另一个应用程序区,启动程序区大小可以根据MAP中ROM的大小来设置,可以看到ROM大小为2K,我们可以设置稍微大一点,设置为4K,即启动程序区地址0x0800 0000,结束地址为0x0800 1000,大小为4096 Bytes = 4k,如下图所示。
这个大小我们也可以根据Keil 的Build Output窗口手动计算如上图,存在Code、RO-data、RW-data、ZI-data四个代码段大小。 其中Code就是代码占用大小,RO-data是只读常量、RW-data是已初始化的可读可写变量,ZI-data是未初始化的可读可写变量。 RAM和ROM的使用情况如何,那么我们就可以使用下面的公式计算。 RAM = RW-data +ZI-data ROM = Code + RO-data + RW-data 那么剩下的为应用程序区(0x0800 1000 - 0x0800 7FFF),大小为0x79000。 4、程序跳转 址直接写入PC寄存器,就可以跳转到对应的地址中去。
当我们调用一个函数的时候,单片机会将这段内存的首地址(函数名对应的地址)加载到PC寄存器中,从而跳转到这段代码来执行。那么我们也可以利用这个原理,定义一个函数指针,将这个指针指向我们想要跳转的地址,然后调用这个函数,就可以实现程序的跳转了。 #define APP_ADDR 0x08001000 //应用程序首地址定义
typedef void (*APP_FUNC)(); //函数指针类型定义
APP_FUNC Jump_To_Application;
uint32_t JumpAddress;
void go_to_app(void)
{
if (((*(volatile uint32_t*)APP_ADDR) & 0x2FFFE000 ) == 0x20000000)
{
JumpAddress = *(volatile uint32_t*)(APP_ADDR + 4);
Jump_To_Application = (APP_FUNC)(JumpAddress);
__set_MSP(*(volatile uint32_t*)APP_ADDR);
Jump_To_Application();
}
}
上面的代码实现了我们要的跳转功能, STM32在芯片上电的时候,首先会从内存地址位0x08000000(由启动模式决定)的地方加载栈顶地址(4字节),从0x0800 0004的地方加载程序复位地址(4字节),然后跳转到对应的复位地址去执行,即Reset_Handler程序。 __set_MSP(*(volatile uint32_t*)APP_ADDR)是设置栈顶指针指向。 if (((*(volatile uint32_t*)APP_ADDR) & 0x2FFFE000 ) ==0x20000000) 这句话是为了判断栈顶是否合法,APP_ADDR里面存着栈顶的地址,即为0x20000000。判断正确之后,然后再将复位中断函数地址赋给Jump_To_Application,所以上面的程序会中,Jump_To_Application()这个函数指针的地址为(APP_ADDR+4),调用这个函数指针的时候芯片内核会自动跳转到这个指针指向的内存地址。
下面我们通过一个程序来测试,BootLoader 程序先执行一段LED闪烁程序,LED每隔300ms闪烁一次,闪烁5次之后,跳转到APP程序,APP程序LED每隔30ms闪烁一次 #include "led.h"
#include "delay.h"
#include "sys.h"
//ALIENTEK miniSTM32开发板实验1
//跑马灯实验
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司
#define APP_ADDR 0x08001000 //应用程序首地址定义
typedef void (*APP_FUNC)(); //函数指针类型定义
APP_FUNC Jump_To_Application;
uint32_t JumpAddress;
void go_to_app(void)
{
if (((*(volatile uint32_t*)APP_ADDR) & 0x2FFFE000 ) == 0x20000000)
{
JumpAddress = *(volatile uint32_t*)(APP_ADDR + 4);
APP应用部分 #include "led.h"
#include "delay.h"
#include "sys.h"
//ALIENTEK miniSTM32开发板实验1
//跑马灯实验
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
delay_ms(30); //延时300ms
LED0=1;
LED1=0;
delay_ms(30); //延时300ms
}
}
修改烧录APP的起始地址为0x0800 1000,则剩余地址为0x79000 修改中断向量表映射
在启动文件中执行调用 SystemInit()函数,这个函数会配置 时钟初始化、中断向量表映射。 SCB->VTOR=0x1000+ FLASH_BASE | VECT_TAB_OFFSET。 因为我们应用程序的中断向量表相对于之前FLASH的存储地址偏移了0x1000个地址 四、测试 使用Keil MDK将编写好的BootLoader程序编译,用ST-Link工具将.hex文件烧录到STM32F103单片机的内部Flash存储器中,然后烧录APP应用程序,烧录完成,断电重启。 在上电启动时,观察STM32F103单片机是否按照预期执行了BootLoader程序。如果一切正常,BootLoader将加载和执行固件程序,从而完成整个系统的启动过程。 可以看到LED先慢闪5次,然后一直快闪,表示已经进入了APP程序。
五、总结 本文详细介绍了STM32F103单片机的BootLoader启动加载器的工作原理、使用场景、功能和实现流程。通过编写和应用BootLoader,我们可以实现对固件程序的灵活管理和更新,提高系统的可靠性和可维护性。希望本文能对您的嵌入式开发工作有所帮助。
|
以STM32F103为例,探究单片机的BootLoader。一个你可能会忽略缺很重要的知识点。