STM32 IAP技术 bootloader设计

devtools/2024/12/28 15:23:04/

介绍

IAP,即在应用程序内编程,就是在Flash中预留一套升级固件的boot程序,以实现通过串口/CAN总线实现 “程序升级”。
为什么要做这个boot程序?SWD接口不够用吗?
工程师在程序开发调试阶段肯定是用SWD接口,但是在产品已经完成,进行装机后,交付给客户的设备上的SWD接口是要屏蔽掉的。客户想要升级程序的话,就要依赖这个bootloader
(一般来说还需要一个交付给客户使用的上位机程序,本篇只讲bootloader)

Flash空间分配

单片机型号为STM32F407ZET6,flash空间分配如我写的这一篇所述 STM32项目开发Flash空间分配_stm32 flash空间分配-CSDN博客

bootloader设计

主要包括四个核心内容:通信协议,程序跳转,程序接收,程序写入

通信协议

通信协议就是boot和上位机约定的一个规则:收到什么信号时接收数据包,什么时候接收完成,什么时候跳转应用,什么时候恢复出厂设置等等,这个可以按照canopen协议来,也可以自己规定

我这边规定的如下:大致思路就是先握手,握手完成就开始接收程序,接收完成之后写入flash

    if(CAN2_Receive_Flag){CAN2_Receive_Flag = 0;// 握手if(CAN_RxHeader.StdId == 0x0F0)  {update_begin_flag = 1;                   CAN2_Transmit(0x0F0, Shake_Hands);  // 握手完成}// 开始接收程序if(CAN_RxHeader.StdId == 0x0F1){}// 接收完成,开始写入if(CAN_RxHeader.StdId == 0x0F2){update_finish_flag = 1;}}

程序跳转

包括两个步骤:不更新时从boot跳转到APP,以及更新完后跳转到APP,原理都是一模一样的,直接放代码:

    if(!update_begin_flag){if(wait_ms >= WAITTIME)         // 等待超时{        if(((*(uint32_t*)(APP_ADDRESS+4))&0xFF000000)==0x08000000) // 检查地址{Jump_to_APP(APP_ADDRESS); // 跳转}                        }}

其中Jump_to_APP代码:

void Jump_to_APP(uint32_t app_address)
{uint8_t i = 0;if(((*(uint32_t*)app_address)&0x2FFE0000)==0x20000000)	// 检查地址{ // 地址+4是复位中断服务程序地址jump_to_app = (iapfun)*(uint32_t*)(app_address+4);// 关闭全局中断__set_PRIMASK(1); // 关闭滴答定时器SysTick->CTRL = 0;SysTick->LOAD = 0;SysTick->VAL = 0;// 设置所有时钟到默认状态HAL_RCC_DeInit();    // 关闭所有中断for (i = 0; i < 8; i++){NVIC->ICER[i]=0xFFFFFFFF;NVIC->ICPR[i]=0xFFFFFFFF;}// 使能全局中断__set_PRIMASK(0);// 这条在RTOS中比较重要,先照搬过来,不深究__set_CONTROL(0);__set_MSP(*(uint32_t*)app_address);					// APP堆栈地址jump_to_app();									    // 跳转// 跳转失败才会继续往下运行while(1){;}}
}

总之上面这段代码里面最关键的就是__set_MSP(*(uint32_t*)app_address);和jump_to_app = (iapfun)*(uint32_t*)(app_address+4);前者设置堆栈地址,即SP指针,后者设置程序入口。其余的都是一些安全措施,防止跳转过程收到乱七八糟的影响。

其中iapfun是需要定义的

// 用于跳转到特定地址的函数入口
typedef void (*iapfun)(void);		

要实现完整的程序跳转,还要APP也配合在main函数起始位置加一条语句,并在target中进行设置:

SCB->VTOR = FLASH_BASE | 0x00020000;

这段代码将 Cortex-M 核心的中断向量表基址重定位到 Flash 存储的地址0x08020000,没有这条语句的话就算boot成功跳转了也没法运行的

程序接收

实现方式就是定义一个很大的uint8_t类型的数组,然后通过CAN总线的报文一个一个字节接收过来

      if(CAN_RxHeader.StdId == 0x0F1){if(APPLength < APP_LEN_MAX){for(uint8_t i = 0; i < CAN_RxHeader.DLC; i++){APP_RX_BUF[APPLength] = CAN2_Rx_data[i];APPLength++;}}else                                      // 数组容量不足{;}}

这部分代码很简单,主要是上位机和boot程序要配合好,还要注意在 250 kbit/s 的波特率下,CAN总线每秒可以发送约 2212 条报文(8字节),所以数据发送周期最好不要设置太大。我设置10ms发一条报文,这样一个50k字节的程序差不多64秒发送完(实际项目程序也就30k左右的样子),还算可以接受。

程序写入

这个过程就是把接收的数组写到flash的对应位置中,之前介绍过flash读写的相关内容了,可以看一下STM32 使用HAL库实现flash读写_stm32halflash程序-CSDN博客

这里直接贴一下代码

if(((*(uint32_t*)(APP_ADDRESS+4))&0xFF000000)==0x08000000) 
{Write_APP(APP_ADDRESS, APP_RX_BUF, APPLength);           update_finish_flag = 1;                                  
}void Write_APP(uint32_t WriteAddr, uint8_t* pBuffer, uint16_t Num)
{static FLASH_EraseInitTypeDef EraseInitStruct;static uint32_t SECTORError;// Unlock the ROMHAL_FLASH_Unlock();// Erase the ROMEraseInitStruct.TypeErase    = FLASH_TYPEERASE_SECTORS;EraseInitStruct.Sector       = FLASH_SECTOR_5;EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;EraseInitStruct.NbSectors    = 1;if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK){Error_Handler();}// Writefor(uint16_t i=0; i<Num; i++){if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, WriteAddr, *pBuffer) != HAL_OK){Error_Handler();}WriteAddr += 1;pBuffer ++;}// Lock the ROMHAL_FLASH_Lock();	
}

注意一下EraseInitStruct.Sector需要根据自己写入程序的地址来选,不要擦除错了。还要注意HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, WriteAddr, *pBuffer)中使用的是FLASH_TYPEPROGRAM_BYTE,按字节写入。

总结

这样一个简单的bootloader就算设计完了,当然要实现完整的IAP功能的话还需要上位机程序的配合。如果仅仅是需要单纯的IAP,也可以使用一些可以直接发送bin文件的CAN总线调试程序。可以参考一下STM32的IAP技术,基于CAN总线的STM32F103 BootLoader设计_哔哩哔哩_bilibili这位大佬的教程,介绍的非常详细

由于工作需要我还要在上位机中设计一些特定的功能,所以我还需要自己来写(╥╯^╰╥)


http://www.ppmy.cn/devtools/146145.html

相关文章

IPv6的报头

IPv6报文格式 整个IPv6包包括&#xff1a;基本报头、拓展包头、上层协议 基本报头&#xff1a; 基本报头一共8个字段&#xff0c;固定大小为40字节&#xff0c;每一个IPv6数据包都必须包含包头 Traffic Class&#xff1a;区分服务代码点&#xff0c;和v4的一样用于标识服务类…

无人机巡检大疆智图测绘技术详解

无人机巡检结合大疆智图测绘技术&#xff0c;为巡检工作带来了革命性的变化。以下是对这一技术的详细解析&#xff1a; 一、无人机巡检技术概述 无人机巡检是利用无人机对目标对象或区域进行巡检和监测的一种技术。通过无人机搭载的传感器&#xff0c;如高清相机、红外热像仪…

运算符 - 算术、关系、逻辑运算符

引言 在编程中&#xff0c;运算符是用于执行特定操作的符号。C 提供了多种类型的运算符&#xff0c;包括算术运算符、关系运算符和逻辑运算符等。理解这些运算符及其用法对于编写高效且无误的代码至关重要。本文将详细介绍 C 中的这三种基本运算符&#xff0c;并通过实例帮助读…

常见的限流算法

常见的限流算法 限流的定义固定窗口算法滑动窗口算法漏桶算法&#xff08;推荐&#xff09;令牌桶算法(推荐)限流粒度本地限流&#xff08;单机限流&#xff09;分布式限流&#xff08;多机限流&#xff09;分布式限流的实现 限流的定义 限流&#xff0c;也称流量控制。是指系统…

JavaScript 前端开发 是什么?

一、引言 JavaScript 作为前端开发领域的核心语言&#xff0c;在构建现代 Web 应用程序中发挥着至关重要的作用。从创建交互性网页元素到构建复杂的单页应用&#xff08;SPA&#xff09;和 Progressive Web App&#xff08;PWA&#xff09;&#xff0c;JavaScript 的应用无处不…

AIGC与娱乐产业:颠覆创意与生产的新力量

个人主页&#xff1a;云边有个稻草人-CSDN博客 目录 引言 第一部分&#xff1a;AIGC技术概述 1.1 AIGC的基本原理 1.2 AIGC在娱乐产业中的应用 第二部分&#xff1a;AIGC在娱乐产业的实际应用案例 2.1 自动生成音乐&#xff1a;AIGC如何创作旋律 示例代码&#xff1a;使…

【自信息、信息熵、联合熵、条件熵、互信息】

文章目录 一、自信息 I(X)二、信息熵&#xff1a;衡量系统的混乱程度信息熵 H(X)联合熵 H(X,Y) 三、条件熵H(Y|X) 联合熵H(X,Y) - 信息熵H(X)四、互信息 I(X,Y)五、总结References 一、自信息 I(X) 自信息(Self-information) 是由香农提出的&#xff0c;用来衡量单一事件发生…

PDF书籍《手写调用链监控APM系统-Java版》第5章 插桩插件与bytebuddy字节码增强

本人阅读了 Skywalking 的大部分核心代码&#xff0c;也了解了相关的文献&#xff0c;对此深有感悟&#xff0c;特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式&#xff0c;看本书时一定要跟着敲代码。 作者…