bootloader编写——MCU固件升级系列2(STM32)

news2024/9/19 10:37:11

本系列将从升级流程、boot代码编写、APP代码编写以及固件打包来介绍,硬件选用STM32F407ZGT6(手里只有),来完成这系列教程。

前言

开发STM32固件升级并编写Bootloader时,需要注意以下几个关键点:

  1. 熟悉硬件和数据手册:在开发过程中,确保充分理解STM32微控制器的特性和功能。阅读相关数据手册,了解其内存布局、外设接口以及其他重要信息。

  2. 选择合适的通信接口:根据项目需求选择合适的通信接口进行固件升级,如串口、I2C、SPI、USB等。确保所选接口可以与外部设备(如PC)正常通信。(后续会使用CAN UART)

  3. 定义固件升级协议:设计一个简单且可靠的通信协议,用于在Bootloader和外部设备之间传输数据。协议应包括命令、地址、数据长度、数据包校验等信息。

  4. 保留足够的Bootloader空间:为Bootloader预留足够的程序存储空间。Bootloader的大小可能会随着功能的增加而增大,因此预留一定的余量非常重要。

  5. 安全和鲁棒性:确保Bootloader代码具有良好的异常处理和错误检测能力。避免因意外情况导致的设备损坏或不可恢复状态。

  6. 可扩展性:在设计Bootloader时考虑到未来可能的功能扩展。保持代码结构清晰,易于维护和升级。

  7. 测试与验证:在实际硬件上对Bootloader进行充分测试,确保其下载、擦除、写入等操作的正确性和稳定性。

遵循以上关键点,在开发过程中保持耐心和细致,能有效编写出一个可靠、高效的STM32 Bootloader。

编写Bootloader程序

在编写前我需要确定几个地方:

  • bootloader

    • 确定bootloader存放地址 0x08000000
    • 配置bootloader中断向量表
    • 实现串口或USB等通信接口 UART
    • 编写flash擦除、编程函数
    • 确定应用程序存放地址 0x08000000 + Boot_size + PARAM_SIZE
    • 跳转到应用程序入口 跳转指令
  • 注意事项

    • 确认芯片型号和数据手册,了解芯片的Flash大小和布局
    • 确定应用程序和bootloader的存放地址
    • 确定bootloader的触发方式,如按键触发、超时触发等
    • bootloader需要配置中断向量表,以便跳转到应用程序时正确执行
    • bootloader需要实现串口或USB等通信接口,以便与上位机进行通信
    • bootloader需要编写flash擦除、编程函数,以便将应用程序下载到Flash中
    • bootloader需要检查应用程序的合法性,如校验和、签名等
    • bootloader需要跳转到应用程序入口,启动应用程序

在MCU中,bootloader主要作用引导进入APP1程序,检测升级标注位是否需要将备份APP2覆盖到APP1中(升级新程序)或者接收升级包进行升级(可以是CAN、UART通信或者读取SD卡获取升级包)。bootloader程序尽量保持简洁,不需要用到资源统统去掉(很容易出现问题,往往初始化的外设资源,最好都去初始化(保留原来的状态)),保证小巧、稳定和扩展性。
需要实现的功能:flash擦除接口和通信接口,主要这两个,还需要添加状态和标注位。

Flash读写和擦除

Flash 功能接口

u32 STMFLASH_ReadWord(u32 faddr)
{
	return *(vu32*)faddr; 
}  

uint16_t STMFLASH_GetFlashSector(u32 addr)
{
	if(addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
	else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
	else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
	else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
	else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
	else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
	else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
	else if(addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
	else if(addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
	else if(addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
	else if(addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10; 
	return FLASH_Sector_11;	
}

void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)	
{ 
  FLASH_Status status = FLASH_COMPLETE;
	u32 addrx=0;
	u32 endaddr=0;	
  if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;	//非法地址
	FLASH_Unlock();									//解锁 
  FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间,必须禁止数据缓存
 		
	addrx=WriteAddr;				//写入的起始地址
	endaddr=WriteAddr+NumToWrite*4;	//写入的结束地址
	if(status==FLASH_COMPLETE)
	{
		while(WriteAddr<endaddr)//写数据
		{
			if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)//写入数据
			{ 
				break;	//写入异常
			}
			WriteAddr+=4;
			pBuffer++;
		} 
	}
  FLASH_DataCacheCmd(ENABLE);	//FLASH擦除结束,开启数据缓存
	FLASH_Lock();//上锁
} 

void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)   	
{
	u32 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
		ReadAddr+=4;//偏移4个字节.	
	}
}

实现在SD卡寻找APP程序,进行升级。

	FIL fnew;                         /* 文件对象 */
	FRESULT res_sd = FR_OK;                   /* 文件操作结果 */
	UINT fnum;                        /* 文件成功读写数量 */
	BYTE ReadBuffer[512]={0};        /* 读缓冲区 */
	
	u8 checknum = 0;
	int writeSum = 0;
 	while(SD_Init())
	{

	}
 	exfuns_init();									 
  f_mount(fs[0],"0:",1); 					
	Flash_Erase(APP1_ADDRESS);
	while(1)
	{
		t++; 
		res_sd = f_open(&fnew, "0:APP2.bin", FA_OPEN_EXISTING | FA_READ);
		if(res_sd == FR_OK)//有升级文件
		{
			printf("open bootloader\r\n");
			fnum = 1;
			do
			{
				res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
				if(res_sd == FR_OK)
				{
						printf("##");
						if(fnum!=0)
						STMFLASH_Write(APP1_ADDRESS+writeSum,(u32*)ReadBuffer,fnum/4);
						writeSum += fnum;
						for(int i = 0 ; i < fnum;i++)
						{
							checknum ^= ReadBuffer[i];
							printf("0x%2x ",ReadBuffer[i]);
						}
						printf("\r\n");
						memset(ReadBuffer,0,sizeof(ReadBuffer));
				}
				else
				{
						
						printf("read file failth(%d)\n", res_sd);
				}
			}while(fnum > 0);
			/* 不再读,关闭文件 */
        f_close(&fnew);
				u8 readBuf[4];
				u8 readCheckCrc = 0;
				for(int i = 0;i < writeSum ; i++)
				{
					STMFLASH_Read(APP1_ADDRESS+i*4,(u32*)readBuf,1);
					for(int j = 0;j<4;j++)
					readCheckCrc ^= readBuf[j];
				}
				printf("boot jump app\r\n");
				if(readCheckCrc == checknum && readCheckCrc!=0)
				{
					printf("check sum success jump APP1\r\n");
					//跳转
					jump_to_app();
				}
				else
				{
					printf("check sum Failth\r\n");
				}
				while(1);
				
		}
		else
		{
			printf("not found bootloader \r\n");
		}
		LED0=!LED0;  //状态灯  bootloader状态
	}

上面还需要进行优化,需要添加一些标志位可以在一个扇区或者在备份寄存器标记,来做升级标记。

跳转指令 重点

#define  APP1_ADDRESS  0x8020000
__asm void start_app(uint32_t r0_msp, uint32_t r1_pc)
{
    MOV SP, R0    //R0的数值   其实就是参数r0_msp
    BX R1             //R1               其实就是r1_pc
}
void jump_to_app(void)
{
	start_app((*(uint32_t *)(APP1_ADDRESS)),(*(uint32_t *)(APP1_ADDRESS + 4)));
}

在实际操作过程中,遇到跳转,没能正常运行APP。
我进行分析,排除问题,把问题范围缩小,可能会出现的问题:1、Flash读写接口有问题 2、FAT32文件系统读取数据有问题。
看前面的代码,我都加了日志信息输出,把文件系统读到的数据输出,和真实的bin文件内容对比,对比问题是正确,说明问题出现在Flash擦除。
看下面的截图,左边是都是FF没做写入前做擦除,右边是擦除后再写,是写入成功的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/481995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构篇四:栈

文章目录 前言1.栈1.1 栈的概念及结构1.2 栈的实现 2.栈功能的解析及实现2.1 栈的创建2.2 初始化2.3 入栈2.4 出栈2.5 检查栈是否为空2.6 获取栈顶元素2.7 栈中的有效元素个数2.8 销毁 3.代码实现3.1 Stack.h3.2 Stack.c3.3 test.c 4.总结 前言 前面学习的一些结构都比较普通&a…

13 | visual studio与Qt的结合

1 前提 Qt 5.15.2 visual studio 2019 vsaddin 2.8 2 具体操作 2.1 visual studio tool 2.1.1 下载 https://visualstudio.microsoft.com/zh-hans/downloads/2.1.2 安装 开发

推荐算法实战项目:WideDeep原理以及案例实战(附完整 Python 代码)

本文要介绍的是Google于2016年提出的Wide&Deep模型&#xff0c;此模型的提出对业界产生了非常大的影响&#xff0c;不仅其本身成功地应用在多家一线互联网公司&#xff0c;而且其后续的改进工作也一直延续至今。 Wide&Deep模型正如其名&#xff0c;分别包含了Wide部分…

golang - 函数的使用

核心化编程 为什么需要函数&#xff1f; 代码冗余问题不利于代码维护函数可以解决这个问题 函数 函数&#xff1a;为完成某一功能的程序指令&#xff08;语句&#xff09;的集合&#xff0c;称为函数 在 Go 中&#xff0c;函数分为&#xff1a;自定义函数&#xff08;自己写…

Apache Solr Velocity模板注入RCE

Apache Solr Velocity模板注入RCE 一、Apache Solr介绍 Solr是一个独立的企业级搜索应用服务器,它对外提供类似于web-service的API接口,用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引,也可以通过http get操作提出查找请求,并得到XML格式的返回结果。…

【openFrameworks】跨平台的 C++ 开源框架 | oF 文件结构 | 图形基础介绍

&#x1f4ad; 写在前面&#xff1a;本章我们将介绍一个非常好用的跨平台的 C 开源框架 —— openFrameworks。它是一个开源的跨平台的 C工具包&#xff0c;方便开发者创建出一个更简单和直观的框架&#xff0c;擅长开发图像和动画。 &#x1f4dc; 本章目录&#xff1a; 0x0…

SpringBoot集成SpringSecurity从0到1搭建权限管理详细过程(认证+授权)

前言 最近工作需要给一个老系统搭建一套权限管理&#xff0c;选用的安全框架是SpringSecurity&#xff0c;基本上是结合业务从0到1搭建了一套权限管理&#xff0c;然后想着可以将一些核心逻辑抽取出来写一个权限通用Demo&#xff0c;特此记录下。 文章目录 前言1、SpringSecuri…

CSS中4个定位设计与实现

1.相对定位 说明&#xff1a;相对原来元素的定位。开启定位后&#xff0c;元素层级高&#xff0c;会置于最上层 作用&#xff1a;用于元素的微调&#xff0c;不会脱离文档流 1.1代码实现 <!DOCTYPE html> <html lang"zh"> <head><meta charset…

外卖项目优化-01-redis缓存短信验证码、菜品数据、Spring Cache(注解开发缓存)、(注解开发)缓存套餐数据

文章目录 外卖项目优化-01课程内容前言1. 环境搭建1.1 版本控制解决branch和tag命名冲突 1.2 环境准备 2. 缓存短信验证码2.1 思路分析2.2 代码改造2.3 功能测试 3. 缓存菜品信息3.1 实现思路3.2 代码改造3.2.1 查询菜品缓存3.2.2 清理菜品缓存 3.3 功能测试3.4 提交并推送代码…

每日一题133——环形链表

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;…

5. 操作系统基础

5. 操作系统基础 常考面试题 说说你对进程的理解⭐⭐⭐ 程序是指令、数据及其组织形式的描述,而进程则是程序的运行实例,包括程序计数器、寄存器和变量的当前值。 Linux的进程结构,一般分为三部分:代码段、数据段(.data与.bss)和堆栈段。 代码段用于存放程序代码,如果有…

【计算机图形学基础教程】面向对象程序设计基础

构造函数与析构函数 例1 设计一个长方形CRectangle类&#xff0c;调用类的成员函数计算长方形的周长和面积。 #include <iostream>class CRectangle { public:CRectangle(); // 声明默认构造函数CRectangle(int width, int height); // 声明带…

Python基础合集 练习21 (错误与异常处理语句)

‘’‘try: block1 except[ExceptionName]: block2 ‘’’ block1:执行代码,表示可能会出现错误的代码块 ExceptionName: 表示要捕获的异常名称,为可选参数.如果不指定异常名称,则表示捕获所有异常 block2:表示发生异常时执行的代码块 while True: try: num int(input(请输…

测试时,可快速调用 Mapper 的 Mapper Generator

项目 Gitee 地址&#xff1a;MapperGenerator (当前使用的是 JDK17&#xff0c;JDK8 的需改下 pom.xml 文件&#xff09; 解决的问题&#xff1a;SpringBootTest 启动太慢 使用方式 假设有这样一个数据库&#xff0c;名为 a SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;…

推荐 3 个令你惊艳的 GitHub 项目

昨日 GitHub Trending 上榜的开源项目&#xff0c;基于 AI 技术提高你的生产力。借助 AI 你能搭建自己的数字人、搭建自己的法律助手、文档分析助手。 本期推荐开源项目目录&#xff1a; 1. 数字人开源项目 2. AI 法律助手 3. 为 PDF 文档打招一个聊天机器人 01 数字人开源项目…

作业区域工服穿戴识别算法 yolov7

作业区域工服穿戴识别系统基于yolov7视频智能图像识别技术&#xff0c;作业区域工服穿戴识别算法模型利用深度学习技术&#xff0c;不需人为干预自动识别现场施工作业人员未按要求穿工作服行为&#xff0c;代替后台工作人员执勤时的人眼判断。YOLOv7 研究团队提出了基于 ELAN 的…

win10 全屏秒退

问题 程序比较老&#xff0c;而电脑配置很新窗口化无任何问题&#xff0c;但是一旦全屏就退出 解决方案 猜测可能是兼容性的问题。 定位发现&#xff1a;禁用全屏优化。 方式如下&#xff1a;右键配置。选择兼容性。 选择禁用全屏优化。以兼容性运行这个程序。

什么是图数据库Neo4j

什么是图数据库Neo4j 所谓的图数据库一般由节点和关系构成&#xff0c;neo4j是其中的一种 在寻求数据的关联性中优于传统数据库mysql 且neo4j支持上亿级别的节点和关系 传统图运算一般在内存中进行&#xff0c;无法处理整个知识图谱&#xff0c;neo4j可以在磁盘中完成图运算…

【官网解读】主页解读

1.简况 Quick Prototyping&#xff08;快速原型设计&#xff09; Build machine learning solutions on raw data&#xff08;原始数据&#xff09; in a few lines of code. State-of-the-art Techniques&#xff08;最先进的技术&#xff09; Automatically utilize SOTA…

如何进行物联网渗透测试?

渗透测试揭示了未知的安全漏洞&#xff0c;因为值得信赖的专业人员模拟威胁性攻击。他们深入挖掘固件和硬件&#xff0c;以查找漏洞和可访问性疏忽。 物联网(IoT)连接设备是严重且可预防的安全漏洞的意外来源&#xff0c;现在是时候像其他硬件一样对其进行渗透测试处理了。为什…