> 技术文档 > STM32 BootLoader 原理及使用方法(内附示例C代码及注释)_stm32 bootloader源码

STM32 BootLoader 原理及使用方法(内附示例C代码及注释)_stm32 bootloader源码

目录

一、STM32 BootLoader 的基本概念

二、BootLoader 的使用场景

三、BootLoader 需要配置的核心功能

四、进入 BootLoader 的方式

五、自定义 BootLoader 的实现步骤

        1、Flash 分区设计:

        2、硬件初始化:

        3、 通信协议实现:

        4、固件验证与写入:

        5、实现步骤:

六、BootLoader代码示例及注释

如果这篇文章能帮助到你,请点个赞鼓励一下吧φ(≧ω≦*)♪~


一、STM32 BootLoader 的基本概念

        BootLoader 是 STM32 系列微控制器中的一段引导程序,负责在芯片启动时初始化硬件、加载主应用程序,并支持APP应用程序固件更新(IAP/OTA)。它通常存储在 Flash 的固定地址(如 0x08000000 或系统存储器 0x1FFF0000),是设备启动和升级的核心组件。

二、BootLoader 的使用场景

        1、出厂预置 BootLoader使用场景

                STM32 内置系统 BootLoader(System Memory BootLoader)区别于内部FLASH的常用存储区段,有独立的存储空间,支持通过串口、USB、CAN 等接口烧录程序,这个内置的BootLoader当作设备急救箱来使用,当我们自定义的BootLoader出错时,需要使用出厂BootLoader来对自定义的BootLoader程序进行修复更新。

        2、自定义 BootLoader使用场景

                用户自己开发的 BootLoader,通过它进行应用程序的下载和更新,实现APP应用程序的在线升级等功能,注意,自定义的BootLoader需要独立的FLASH存储空间,和APP部分的代码区分开来。

三、BootLoader 需要配置的核心功能

        1、硬件初始化:

                配置时钟、GPIO、串口等外设,并关闭中断响应。

        2、固件更新

                通过串口、USB、SD 卡等接口接收新固件程序,写入指定 Flash 区域。

        3、跳转执行

                根据标志位或超时判断是否进入APP应用程序升级模式,或直接跳转执行应用程序。

四、进入 BootLoader 的方式

        1、硬件引脚配置

                设置 BOOT0 和 BOOT1 引脚(如 BOOT0=1BOOT1=0),从系统存储器启动,进入系统内置的出厂BootLoader程序。

        2、软件跳转

                在应用程序中配置串口接收数据,当接收到自己定义的”BootLoader更新命令“时,将APP更新标志位置1,存储在FLASH等非易失性存储器中,调用 NVIC_SystemReset() 函数进行软件复位,进入BootLoader程序后通过判断APP更新标志位进行软件更新。

五、自定义 BootLoader 的实现步骤

        1、Flash 分区设计:

  • BootLoader 区:通常占用前 16KB(0x08000000~0x08004000),取决于BootLoader程序文件的大小
  • 应用程序区:剩余 Flash(0x08004000~0x0807FFFF)。
  • 标志位存储区:预留 Flash 或 EEPROM 用于保存升级标志(如 0x08003000)。

        2、硬件初始化:

  • 配置时钟(如 HSI/LSI)、GPIO、通信接口(UART/USB)、关闭中断。

        3、 通信协议实现:

  • 接收升级命令(如串口接收到 0x55AA判定为升级命令),设置标志位并触发复位。

        4、固件验证与写入:

  • 擦除应用程序 Flash 区域。
  • 写入新固件APP程序(有条件的可以在写入之前对固件程序进行备份,防止出错)。
  • 跳转到应用程序,设置向量表偏移(VTOR)和堆栈指针(MSP),运行更新后的APP程序。

        5、实现步骤:

            1、将自 定义的BootLoader程序下载到内部FLASH中,放在最前区域。

            2、开机,初始化时钟,GPIO和串口,屏蔽中断。

            3、根据串口接收到的指令,来判断是否需要更新下载APP应用程序。

            4、如果需要更新,进行APP代码部分FLASH备份与擦除,FLASH成功擦除后,将接收到的新APP程序写入内部FLASH原APP应用程序的固定地址中。

            5、完成4后,读入中断向量表地址,同时重新设置主堆栈指针MSP的地址(默认在程序最头部),设置APP函数入口地址(默认在MSP地址偏移量+4)。

            6、运行APP函数。 

六、BootLoader代码示例及注释

#include \"stm32g070xx.h\"#include \"main.h\"#define BOOTLOADER_ADDR0x08000000//BootLoader的首地址#define APP_ADDR0x08008000//自定义的APP程序头地址#define FLASH_APP_CODE_SIZE0x18000//APP程序大小typedef void (*pFunction)(void);//函数指针,用来调用同一类型的函数void ERROR_Process();//错误处理函数/*****************************************************************************[函数名称]BootLoader_JumpToApp[函数功能]BootLoader跳转到APP函数[参 数]app_addr:APP程序入口地址*****************************************************************************/void BootLoader_JumpToApp(uint32_t app_addr){pFunction jumo_to_application;//跳转函数指针,指向APP运行函数头地址后调用函数uint32_t jump_address;//跳转地址变量jump_address = *(__IO uint32_t*)(app_addr + 4);//计算APP运行函数头地址,为MSP主堆栈指针+4个地址偏移量jumo_to_application = (pFunction)jump_address;//函数指针指向APP运行函数头地址__set_MSP(*(__IO uint32_t*)app_addr);//设置主堆栈指针jumo_to_application();//跳转到APP应用程序,开始运行应用主程序}/*****************************************************************************[函数名称]BootLoader_UpdateApp[函数功能]BootLoader的APP更新程序[参 数]app_addr:APP程序入口地址*****************************************************************************/void BootLoader_UpdateApp(uint32_t app_addr){//如果有足够的FLASH空间,记得备份原始APP数据,防止BootLoader升级出错,可以回退版本//擦除APP应用程序内存部分的FLASH空间HAL_FLASH_Unlock();//解锁FLASHFLASH_EraseInitTypeDef EraseInitStruct;//配置FLASH结构体uint32_t PageError = 0;//页错误默认为0EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//配置按页擦除EraseInitStruct.Page = (FLASH_APP_CODE_SIZE + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;;//配置擦除页起始地址,保证页地址对齐EraseInitStruct.NbPages = 60;//配置擦除多少页数,根据自己的APP代码大小而定EraseInitStruct.Banks = FLASH_BANK_1;//配置块区域为1(F4系列芯片存在两个BANK区域,需要选择)HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);//开始擦除存储APP源代码的FLASH内存if(status != HAL_OK)//擦除失败处理{ERROR_Process();//擦除失败错误,进入死循环,可增加自定义的处理代码}//假设使用串口DMA来接收新的APP应用数据,串口自定义开启配置,本文不再展现uint32_t u32DatsSize = 10;//假设u32DatsSize为串口接收缓存数据个数uint8_t u8ReceiveBuffer[u32DatsSize] = {0};//假设u8ReceiveBuffer为串口接收缓存static uint32_t APP_Write_Addr = APP_ADDR;//记录APP应用程序写入实时地址while(u32DatsSize-- > 0)//将接收到的APP应用程序数据写入固定位置的FLASH内存中{HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, APP_Write_Addr++, u32ReceiveBuffer);//地址偏移1个字节量}HAL_FLASH_Lock();//写入完成,锁定FLASH}/*****************************************************************************[函数名称]main[函数功能]主函数[参 数]*****************************************************************************/int main(){//正常情况下,APP更新标志位需要存在FLASH中,掉电不丢失uint8_t IsCodeUpdate = 0;//假设IsCodeUpdate为BootLoader更新判断标志位//初始化程序HAL_Init();//HAL库初始化SystemClock_Config();//时钟初始化MX_GPIO_Init();//GPIO口初始化MX_UART_Init();//串口初始化__disable_irq();//关闭中断//通过串口接收到的命令设置IsCodeUpdate标志位,判断是否需要更新APP程序if(IsCodeUpdate == 1)//IsCodeUpdate标志位应该存在内部FLASH非易失性存储器中,防止数据丢失{IsCodeUpdate = 0;//清除APP更新标志位(实际应该通过FLASH擦除清除,这里只是演示)BootLoader_UpdateApp(APP_ADDR);//更新APP应用程序}else//如果没有更新指令BootLoader_JumpToApp(APP_ADDR);//跳转到APP运行函数//更新指令通过串口接收,如果有更新指令,将IsCodeUpdate标志位置1,然后运行软件复位}//错误处理函数void ERROR_Process(){while(1);}

如果这篇文章能帮助到你,请点个赞鼓励一下吧φ(≧ω≦*)♪~