STM32单片机示例:Bootloader(UART & STM32H743VIT6 & STM32CubeIDE)

简介

这是一个简单的单片机的 Bootloader 程序示例(基于STM32H743VIT6 & STM32CubeIDE)。提供了 bootapp 程序,提供了固件生成和固件下载需要的工具和源码。

例程地址:https://github.com/NaisuXu/STM32_Bootloader_Demo

API 说明

Format

本 Bootloader 示例通讯基于 UART ( 115200, 8N1 )。大于一字节数据以小端形式排列。

TypeByte0~Byte1Byte2Byte3Byte4~Byte5… ( None when length == 0 )
Request0xA5 0x5ARolling counterCMDLength (Data)Data
Response0xA5 0x5ARolling counterCMDLength (Data)Data
Negative
Response
0xA5 0x5ARolling counter0x00Length (Data)Data

Rolling counter 用于丢帧检测,不过例程中实际并未使用。

如果需要通讯通讯可靠,通常还需要在数据帧的结尾加上校验数据,这里偷了个懒。

Get Information

Request:  A5 5A 00 11 00 00
Response: A5 5A 00 11 01 00 Mode

Mode : 0=FBL, 1=APP

Go into FBL

Go into FBL(APP) or stay in FBL(BOOT).

Request:  A5 5A 00 10 03 00 46 42 4C
Response: A5 5A 00 10 00 00

Update

BEGIN:
Request:  A5 5A 00 0D 08 00 app_size(4bytes) app_crc(4bytes)
Response: A5 5A 00 0D 04 00 pack_max_size(4bytes)

WRITE:
Request:  A5 5A 00 0E 00 04 data(1024bytes)
Response: A5 5A 00 0E 00 00

END:
Request:  A5 5A 00 0F 00 00
Response: A5 5A 00 0F 00 00

pack_max_size : for this demo is fixed at 1024.

程序说明

boot_proj

这是例程的 boot 程序,程序使用C/C++混合开发。

Boot 和 APP 的 Flash 分布如下:

/* Boot占用STM32H743VIT6 Flash Bank1 第一个扇区,即用户程序起始地址 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
/* App占用STM32H743VIT6 Flash Bank1 第二个扇区开始的三个扇区 */
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 384K

程序启动后会检查是否有合法的 APP 程序,然后注册 API 接口回调函数,启动串口监听:

void boot_setup(void){
	__enable_irq();

	check_if_app_ready();

	uproto.add(0x0D, uart2pc_rx_cb_update_begin);
	uproto.add(0x0E, uart2pc_rx_cb_update_write);
	uproto.add(0x0F, uart2pc_rx_cb_update_end);
	uproto.add(0x10, uart2pc_rx_cb_stay_in_boot);
	uproto.add(0x11, uart2pc_rx_cb_get_info);

	uart2pc_rx.start();
}

主循环中就是处理串口收发以及 APP 跳转。当有合法 APP 以及满足跳转条件后跳转 APP :

#define STARTUP_DELAY_MS  (50)

void boot_loop(void){
	for(;;){
		uart2pc_rx.poll();
		uproto.poll();
		uart2pc_tx.poll();

		if(goto_app_flag && (bsp_sys_get_time_ms() > STARTUP_DELAY_MS)){
			goto_app();
		}
	}
}

本例程中 APP 合法的检查条件有下面三条:

  • APP 起始四字节数据符合栈指针地址范围;
  • APP 栈指针之后四字节数据符合程序入口指针范围;
  • 在 BOOT 的 NVM 数据中有 APP 就绪标志;
// 检查堆栈指针和程序入口是否合法
static bool check_app_sp_pc(void){
	uint32_t main_stack_pointer = *((volatile uint32_t*)APP_PART_START_ADDR);
	if((main_stack_pointer < RAM_START_ADDR) || (main_stack_pointer > (RAM_START_ADDR + RAM_PART_SIZE))) return false;

	uint32_t app_entry_addr = *((volatile uint32_t*)(APP_PART_START_ADDR + 4));
	if((app_entry_addr < APP_PART_START_ADDR) || (app_entry_addr > (APP_PART_START_ADDR + APP_PART_SIZE))) return false;

	return true;
}

#define APP_READY_FLAG    (0xA5A55A5A)

// 检查是否有合法APP
static void check_if_app_ready(void){
	#ifndef DEBUG_MODE
		uint32_t flag = *((volatile uint32_t*)BOOT_NVM_START_ADDR);
		if(flag != APP_READY_FLAG) return;
	#endif

	if(!check_app_sp_pc()) return;

	goto_app_flag = true;
}

除了上面三个条件外 BOOT 还设置了一个延时时间,在该时间内也不会进行跳转,以防 APP 程序不当处理或跑飞导致无法进入 BOOT 再次刷程序。

从 BOOT 跳转到 APP 主要就是设置堆栈指针然后跳转到程序入口:

static void goto_app(void){
	__disable_irq(); 

	SCB_DisableICache(); // 程序中启用了 ICache ,这里需要关闭

	// 失能所有用到的外设
	LL_DMA_DeInit(DMA1, LL_DMA_STREAM_0);
	LL_DMA_DeInit(DMA1, LL_DMA_STREAM_1);
	LL_USART_DeInit(USART1);

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

	// 清中断
	for (int i = 0; i < 8; i++){
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	}

	uint32_t main_stack_pointer = *(volatile uint32_t*)APP_PART_START_ADDR;

	typedef void (*app_entry_func_tpte)(void);
	uint32_t app_entry_addr = *(volatile uint32_t*)(APP_PART_START_ADDR + 4);
	app_entry_func_tpte app_entry_func = (app_entry_func_tpte)app_entry_addr;

	__set_MSP(main_stack_pointer); // 设置栈指针
	app_entry_func(); // 跳转到程序入口
}

接收固件基于上文的串口 API ,擦除固件区域,然后再分包接受固件数据并写入Flash,最后检查数据,并设置标志。

本示例中的固件数据是经过了异或运算的,需要再次运算进行还原。异或运算提供一定程度的加密。本示例中只是使用一个字节的异或,实际中可以采用一个数组循环进行异或加强加密能力。

本示例固件生成工具中会计算原始固件的 CRC32 校验数据,但 BOOT 中偷了个懒并未进行校验核对。

可以注释掉 boot/config.h 中的 #define DEBUG_MODE 来启用读保护,防止固件被简单读取。

app_proj

这是例程的 app 程序,程序使用C/C++混合开发。

APP 程序非常简单,和 BOOT 部分代码是重复了,唯一不同的是跳转 BOOT 接口中擦除 BOOT 的 NVM (主要是其中的 APP 就绪标志)。

APP程序可以单独调试,需要如下设置:

app_proj/STM32H743VITX_FLASH.ld 文件:
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 384K

app/config.h 文件:
SCB->VTOR = 0x08000000;

APP程序也可以在有BOOT下调试,需要如下设置:

boot/config.h 文件:
#define DEBUG_MODE

app_proj/STM32H743VITX_FLASH.ld 文件:
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 384K

app/config.h 文件:
SCB->VTOR = APP_PART_START_ADDR;

另外说一句 BOOT 本身就是起始于 0x08000000 ,直接调试就行。

fw_gen.html

该工具用于将 app_proj 生成的 .bin 文件转换成用于通过 boot 升级的 .nxfw 文件。

程序中读取上传的 .bin 文件,将数据通过异或运算后填充,最后在结尾处填充 CRC32 校验数据。

fw_update.html

该工具用于更新固件到设备中,本身并没有什么特别难的地方,只是对上文 API 的简单操作。

该工具用到了 Web Serial API 需要 Windows 下最新的 Edge 或者 Chrome 浏览器支持。该技术相关使用说明可以参考下面文章:

《使用 Web Serial API 在浏览器中实现串口通讯(纯前端)》:https://blog.csdn.net/Naisu_kun/article/details/132522118

使用演示

固件生成

在这里插入图片描述

固件更新

在这里插入图片描述

带有 BOOT 时调试 APP

在这里插入图片描述

其他说明

Bootloader 程序的原理和实现方式是有很多种的,这里只是比较简单常见的一种实现而已。

### STM32CubeIDE 中实现 OTA 功能 #### 一、STM32 的程序存储位置 在探讨如何通过 STM32CubeIDE 实现 OTA 更新之前,了解 STM32 中的程序存放位置至关重要。通常情况下,STM32 微控制器内部集成了闪存(Flash),用于保存应用程序代码以及数据[^1]。 #### 二、STM32CubeMX 和 HAL 库的角色转变 随着 ST 对标准外设库支持力度减弱,HAL (Hardware Abstraction Layer) 库成为主流选择。STM32Cube 提供了一个图形化的界面来简化硬件初始化设置,并且能够生成基于 HAL 库的应用框架,这使得开发者可以更专注于应用逻辑而非底层驱动细节[^2]。 #### 三、OTA 技术概述及其实施路径 为了使设备具备远程更新能力,在线升级技术即 Over-The-Air (OTA),允许用户无需物理接触即可完成软件版本迭代。具体到 STM32 上,则涉及到 In-Application Programming (IAP), 即应用程序自我重写机制的设计与部署。此过程中需考虑多个方面: - **分区管理**:合理规划 Flash 存储空间,创建主副两个工作区域以便交替执行新旧版本切换操作; - **引导加载器设计**:构建一个安全可靠的 Bootloader 来控制启动流程并处理固件包下载解析任务; - **通信协议制定**:定义适合特定应用场景的数据传输格式及时序关系; 这些要素共同构成了完整的 IAP OTA 解决方案[^1][^3]。 #### 四、实践指导建议 当准备着手于 STM32CubeIDE 下开展上述工作的实际编码环节时,推荐遵循如下原则: - 使用 STM32CubeMX 工具配置项目参数,确保正确指定了目标芯片型号及相关资源分配情况; - 参考官方文档或开源社区提供的案例学习最佳实践经验,特别是有关双分区管理和 Bootloader 编写的部分; - 调试阶段务必重视异常状况下的恢复策略测试,比如断电保护措施等。 ```c // 示例 C 语言片段展示简单 Bootloader 结构 void bootloader_main(void){ if(check_new_firmware()){ update_application(); }else{ jump_to_application(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naisu Xu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值