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

目录

一、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_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)
		;
}


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

        

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值