打印
[应用相关]

STM32 BootLoader 原理及使用方法

[复制链接]
62|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、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=1,BOOT1=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_ADDR                                0x08000000        //BootLoader的首地址
#define APP_ADDR                                        0x08008000        //自定义的APP程序头地址
#define FLASH_APP_CODE_SIZE                        0x18000                //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();                                                //解锁FLASH
        FLASH_EraseInitTypeDef EraseInitStruct;        //配置FLASH结构体
        uint32_t PageError = 0;                                        //页错误默认为0
        EraseInitStruct.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)
                ;
}




————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/DearJULY/article/details/148410053

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

51

主题

150

帖子

0

粉丝