STM32固件升级设计——SD卡升级固件

目录

概述

一、功能描述 

1、BootLoader部分:

2、APP部分:

二、BootLoader程序制作

1、分区定义

2、 主函数

3、SD卡升级文件检测和更新

4、程序跳转

三、APP程序制作

四、工程配置(默认KEIL5)

五、运行测试 

结束语


概述

        IAP(In Application Programming)即在应用中编程,允许在应用程序运行时更新或切换固件。STM32通过修改MSP(主堆栈指针)和PC(程序计数器)实现从不同地址启动,包括Flash或RAM地址。默认情况下,嵌入式程序以连续二进制形式烧录到STM32的可寻址Flash区域。若Flash容量足够存储多个完整程序,每个程序独立且完整,上电后可通过修改MSP值选择不同程序入口,从而实现多固件切换或升级。

         BootLoader(引导加载程序)是嵌入式系统或计算机启动时运行的一段小型程序,负责初始化硬件、加载操作系统内核并将其控制权移交。它是系统从关机状态到操作系统完全运行之间的桥梁。

        所以,固件升级的基本思路是将stm32的flash划分为若干个区域,其中包括BootLoader区域和APP区等,将各自的程序写到对应的flash区域里,本文将采用读取SD卡的文件来更新固件,只重实践不讲原理。

一、功能描述 

        使用STM32的SDIO总线驱动microSD卡实现SD卡升级程序。将FLASH分为2个部分。
分区介绍:
        本文使用stm32f407zgt6,flash是1M,sector是不固定的(在读写擦除flash时需要特别注意),详细看下图STM32F4系列的FLASH结构。

        BootLoader整个代码编译下来有36K左右,所以使用0x08000000~0x0000FFFF,剩下的FLASH全部用来存放APP,使用0x08010000~0x080F0000。

区域起始地址区域大小功能
BOOT0x080000000x00010000(64k)存放BootLoader程序
APP0x080100000x000F0000(960k)存放产品主程序

tips: 选择以上分区方式的好处是在上电瞬间可以触发升级,只要SD卡里面根目录有正确的程序都可以更新固件,所以就没必要用到SETTING区域在存更新触发标志位了。

1、BootLoader部分:

        运行时首先会初始化SD卡,如果初始化成功并且检测到存在SD卡,就查找根目录是否有firmware.bin文件,如果有就升级,没有就直接进入APP,检测不到SD卡也会直接进入APP。

2、APP部分:

        该部分需要在程序起始设置中断向量跳转指针,剩下的就是自己写的主程序。

二、BootLoader程序制作

        主要包含了YMODEM协议的驱动代码。

1、分区定义
#define FLASH_SECTOR_SIZE           1024
#define FLASH_SECTOR_NUM            1024    // 1024K
#define FLASH_START_ADDR            ((uint32_t)0x8000000)
#define FLASH_END_ADDR              ((uint32_t)(0x8000000 + FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))//flash sector addr
#define ADDR_FLASH_SECTOR_0         ((uint32_t)0x08000000) 	//sector0 addr, 16 Kbytes  
#define ADDR_FLASH_SECTOR_1         ((uint32_t)0x08004000) 	//sector1 addr, 16 Kbytes  
#define ADDR_FLASH_SECTOR_2         ((uint32_t)0x08008000) 	//sector2 addr, 16 Kbytes  
#define ADDR_FLASH_SECTOR_3         ((uint32_t)0x0800C000) 	//sector3 addr, 16 Kbytes  
#define ADDR_FLASH_SECTOR_4         ((uint32_t)0x08010000) 	//sector4 addr, 64 Kbytes  
#define ADDR_FLASH_SECTOR_5         ((uint32_t)0x08020000) 	//sector5 addr, 128 Kbytes  
#define ADDR_FLASH_SECTOR_6         ((uint32_t)0x08040000) 	//sector6 addr, 128 Kbytes  
#define ADDR_FLASH_SECTOR_7         ((uint32_t)0x08060000) 	//sector7 addr, 128 Kbytes  
#define ADDR_FLASH_SECTOR_8         ((uint32_t)0x08080000) 	//sector8 addr, 128 Kbytes  
#define ADDR_FLASH_SECTOR_9         ((uint32_t)0x080A0000) 	//sector9 addr, 128 Kbytes  
#define ADDR_FLASH_SECTOR_10        ((uint32_t)0x080C0000) 	//sector10 addr,128 Kbytes  
#define ADDR_FLASH_SECTOR_11        ((uint32_t)0x080E0000) 	//sector11 addr,128 Kbytes  #define BOOT_SECTOR_ADDR            0x08000000
#define BOOT_SECTOR_SIZE            0x10000
#define APP_SECTOR_ADDR             0x08010000     // APP sector start address  
#define APP_SECTOR_SIZE             0xF0000        // APP sector size    #define BOOT_ERASE_SECTORS_NUM      4  // 64k
#define APP_ERASE_SECTORS_NUM       8  // 64k+128k+128k+128k+128k+128k+128k+128ktypedef enum 
{NONE,START_PROGRAM,UPDATE_PROGRAM,UPDATE_SUCCESS,
} process_status;
2、 主函数

        这部分包含了升级的所有状态,该框架可以说对比上一篇文章是完全不变的,主要是SD卡初始化没问题就成功一大半了,具体看代码。

static void iap_process(void)
{switch (bootupdate_process) {case NONE:break;case START_PROGRAM:printf("start app...\r\n");delay_ms(50);if ((((*(vu32*)(APP_SECTOR_ADDR+4))&0xFF000000)==0x08000000)&&(!iap_load_app(APP_SECTOR_ADDR))) {printf("no program\r\n");delay_ms(1000);}printf("start app failed\r\n");break;case UPDATE_PROGRAM:if(SD_Init()==0){printf("检测到SD卡\r\n");show_sdcard_info();	//打印SD卡相关信息sd_update();}else{printf("没检测到SD卡\r\n");}bootupdate_process = UPDATE_SUCCESS;break;case UPDATE_SUCCESS:bootupdate_process = START_PROGRAM;
//			NVIC_SystemReset();break;default:break;}
}int main(void)
{        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2delay_init(168);  //初始化延时函数uart_init(115200);		//初始化串口波特率为115200LED_Init();					//初始化LED bootupdate_process=UPDATE_PROGRAM;while(1){delay_ms(200);		 			   LED0=!LED0;iap_process();} 
}
3、SD卡升级文件检测和更新

        关于SD卡和FATFS文件系统的移植我是参考正点原子的,主要是将malloc用全局变量代替,其它需要注意的是SD卡的扇区需要选择512字节。

本来是存放在堆区的,现改为存放在栈区

#define    _USE_LFN    2        /* 0 to 3 */

        挂载SD卡,判断sd卡根目录下是否有firmware.bin文件,有的话就获取一下该固件占用了多少个扇区,对应去擦除扇区,然后就可以复制固件到APP起始地址了。不存在该固件的话就直接退出进入APP。

void sd_update(void) 
{u8 file_buffer[512]={0};   uint32_t flash_addr = APP_SECTOR_ADDR;unsigned long total_bytes_read = 0;UINT bytes_read;fres=f_mount(&fs[0],"0:",1); 				//挂载FLASH.	if(fres==FR_OK)                             //FLASH磁盘,FAT文件系统正常{printf("Flash disk OK!\r\n");}// 判断sd卡根目录下是否有firmware.bin文件fres = f_stat("0:/firmware.bin", &fno);if(fres == FR_OK){printf("0:/firmware.bin”文件信息:\n");printf("》文件大小: %ld(字节)\n", fno.fsize);printf("sector_num=%d\r\n",get_mcu_flash_sector(flash_addr+(u32)fno.fsize)/8-4+1);mcu_flash_erase(flash_addr, get_mcu_flash_sector(flash_addr+(u32)fno.fsize)/8-4+1);}else{printf("firmware.bin not found!\r\n");}if(fres == FR_OK) {// 文件存在printf("firmware.bin found, size: %lu bytes\r\n", fno.fsize);printf("开始更新固件...\r\n");// 打开固件文件fres = f_open(&firmware_file, "0:/firmware.bin", FA_READ);if(fres == FR_OK) {// 循环读取文件内容while (total_bytes_read < fno.fsize){// 读取数据块到缓冲区fres = f_read(&firmware_file, file_buffer, 512, &bytes_read);if (fres != FR_OK || bytes_read == 0){// 读取出错或到达文件末尾printf("读取文件失败或文件已结束,错误码: %d\r\n", fres);break;}// 写入到FLASHprintf("正在写入地址 0x%08X,大小: %u 字节\r\n", flash_addr, bytes_read);mcu_flash_write(flash_addr, file_buffer, bytes_read);// 更新计数器和地址total_bytes_read += bytes_read;flash_addr += bytes_read;// 显示进度printf("更新进度: %lu/%lu bytes\r\n", total_bytes_read, fno.fsize);}// 关闭文件f_close(&firmware_file);if (total_bytes_read == fno.fsize) {printf("固件更新完成! 共写入 %lu 字节\r\n", total_bytes_read);} else {printf("固件更新未完成! 已写入 %lu/%lu 字节\r\n", total_bytes_read, fno.fsize);}}}	else {// 文件不存在printf("firmware.bin not found!\r\n");}
}
4、程序跳转

        跳转这部分网上也很多,基本没什么区别,语句很少,建议每一步都去查一下大概意思。

tips:本文的开发环境有128k ram,所以下列语句需要判断跳转地址是否超出。

if (((*(__IO uint32_t*)appxaddr) & 0x2FFE0000 ) == 0x20000000)

详细参考以下文章:

关于STM32单片机IAP升级中if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)语句的理解-CSDN博客

iapfun jump2app;uint8_t iap_load_app(uint32_t app_addr) 
{uint32_t jump_addr;if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20000000) {  jump_addr = *(__IO uint32_t*) (app_addr + 4);  jump2app = (iapfun)jump_addr;  __set_MSP(*(__IO uint32_t*)app_addr);  jump2app();return 1;} return 0;
}

三、APP程序制作

        这部分只需要设置一下flash的偏移量。

NVIC_SetVectorTable (NVIC_VectTab_FLASH, 0x08010000);	//向量重定向	

四、工程配置(默认KEIL5)

BootLoader部分:0x08000000~0x0800FFFF

 

APP部分:0x08010000~0x080F0000

五、运行测试 

(1)首先将firmware.bin固件存放到SD卡的根目录。      

(2)没插上SD卡的情况,直接进入APP程序。

(3)上电前插入SD卡后启动的情况,可以看到检测到SD卡并且打印SD卡的基础信息,找到firmware.bin文件后就开始按512字节写入APP的起始地址,写完后进入新的APP程序。之后需要拔出SD卡,不然每次上电都会执行更新程序步骤。 

 

结束语

        以上SD卡升级stm32单片机功能已实现,思路和我前面写的文章一模一样,思路理解了就能一直套娃了,所以便一股脑移植SD卡了,建议大家在移植的时候对照着文档或者SDK直接移植过来,当然用HAL库更好。后面大家看到的也希望可以得到大家的指点或者互动,感谢各位亦菲彦祖们了。

完整代码下载地址:SD卡升级stm32固件资源-CSDN下载

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

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

相关文章

基于Python的图像文字识别系统

主要语言&#xff1a;Python数据库&#xff1a;SQLiteUI界面&#xff1a;PYQT5文字识别模型&#xff1a;Tesseract OCR&#xff08;本地搭建&#xff09;主要功能&#xff1a;登录注册&#xff1a;登录注册功能。图片管理&#xff1a;单张/多张上传、图片列表、预览、删除、切换…

028_分布式部署架构

028_分布式部署架构 概述 本文档介绍如何设计和实现Claude应用的分布式部署架构&#xff0c;包括负载均衡、缓存策略、服务发现、容错机制等。 微服务架构设计 1. 服务拆分策略 from abc import ABC, abstractmethod from typing import Dict, Any, Optional import asyncio im…

duckdb和pyarrow读写arrow格式的方法

arrow格式被多种分析型数据引擎广泛采用&#xff0c;如datafusion、polars。duckdb有一个arrow插件&#xff0c;原来是core插件&#xff0c;1.3版后被废弃&#xff0c;改为社区级插件&#xff0c;名字改为nanoarrow, 别名还叫arrow。 安装 D install arrow from community; D…

机器人位姿变换的坐标系相对性:左乘法则与右乘法则解析​

文章目录1. 全局坐标系下机器人位姿更新的左乘法则​2. 局部坐标系下机器人位姿增量更新的右乘法则​3. 相对位姿的计算3.1. 基于世界坐标系&#xff08;全局变换&#xff09;3.2. 基于 t1t_1t1​ 时刻相机的局部坐标系&#xff08;局部变换&#xff09;3.3. 两者区别设机器人当…

代码随想录算法训练营65期第20天

代码随想录算法训练营65期第20天 本文中使用到一些代码随想录里面的图片或者链接&#xff0c;在这里致敬程序员Carl 二叉搜索树的最近公共祖先 相对于 二叉树的最近公共祖先 本题就简单一些了&#xff0c;因为 可以利用二叉搜索树的特性。 题目链接&#xff1a;代码随想录&…

LLaMA.cpp HTTP 服务参数: --pooling 嵌入模型 池化类型详解

LLaMA.cpp HTTP 服务参数: --pooling 嵌入模型 池化类型详解 --pooling {none,mean,cls,last,rank} 在 llama.cpp 的 embedding server 中&#xff0c;--pooling {none,mean,cls,last,rank} 参数用于指定 如何将输入文本的 token 级嵌入向量聚合为句向量。以下是各选项的详细解…

「日拱一码」027 深度学习库——PyTorch Geometric(PyG)

目录 数据处理与转换 数据表示 数据加载 数据转换 特征归一化 添加自环 随机扰动 组合转换 图神经网络层 图卷积层&#xff08;GCNConv&#xff09; 图注意力层&#xff08;GATConv&#xff09; 池化 全局池化&#xff08;Global Pooling&#xff09; 全局平均池…

IoC容器深度解析:架构、原理与实现

&#x1f31f; IoC容器深度解析&#xff1a;架构、原理与实现 引用&#xff1a; .NET IoC容器原理与实现等巫山的雲彩都消散撒下的碧色如何看淡 &#x1f50d; 一、引言&#xff1a;从服务定位器到IoC的演进 #mermaid-svg-BmRIuI4iMgiUqFVN {font-family:"trebuchet ms&…

从零开始学前端html篇3

表单基本结构表单是 HTML 中用于创建用户输入区域的标签。它允许用户输入数据&#xff08;例如文本、选择选项、文件等&#xff09;&#xff0c;并将这些数据提交到服务器进行处理。<form>&#xff0c;表单标签&#xff0c;用于创建表单常用属性&#xff1a;action&#…

Linux系统调优和工具

Linux系统调优和问题定位需要掌握一系列强大的工具&#xff0c;涵盖系统监控、性能分析、故障排查等多个方面。以下是一些核心工具和它们的典型应用场景&#xff0c;分类整理如下&#xff1a; 一、系统资源监控&#xff08;实时概览&#xff09;top / htop 功能&#xff1a; 实…

如何快速有效地在WordPress中添加Instagram动态

在当今社交媒体的时代&#xff0c;通过展示Instagram的最新动态&#xff0c;可以有效吸引读者的目光&#xff0c;同时丰富网站内容。很多人想知道&#xff0c;如何把自己精心运营的Instagram内容无缝嵌入WordPress网站呢&#xff1f;别担心&#xff0c;操作并不复杂&#xff0c…

spring容器加载工具类

在Spring框架中&#xff0c;工具类通常不需要被Spring容器管理&#xff0c;但如果确实需要获取Spring容器中的Bean实例&#xff0c;可以通过静态方法设置和获取ApplicationContext。下面是一个典型的Spring容器加载工具类的实现&#xff1a;这个工具类通过实现ApplicationConte…

定时器更新中断与串口中断

问题&#xff1a;我想把打印姿态传感器的角度&#xff0c;但是重定向的打印函数突然打印不出来。尝试&#xff1a;我怀疑是优先级的问题&#xff0c;故调整了串口&#xff0c;定时器&#xff0c;dma的优先级可是发现调了还是没有用&#xff0c;最终发现&#xff0c;我把定时器中…

用Python向PDF添加文本:精确插入文本到PDF文档

PDF 文档的版式特性使其适用于输出不可变格式的报告与合同。但若要在此类文档中插入或修改文本&#xff0c;常规方式难以实现。借助Python&#xff0c;我们可以高效地向 PDF 添加文本&#xff0c;实现从文档生成到内容管理的自动化流程。 本文将从以下方面介绍Python实现PDF中…

Quick API:赋能能源行业,化解数据痛点

随着全球能源结构的转型和数字化的深入推进&#xff0c;能源行业正面临前所未有的机遇与挑战。海量的实时数据、复杂的业务系统、以及对数据安全和高效利用的迫切需求&#xff0c;都成为了能源企业在数字化转型道路上的核心痛点。本文将深入探讨麦聪Quick API如何凭借其独特优势…

Google Chrome V8< 13.6.86 类型混淆漏洞

【高危】Google Chrome V8< 13.6.86 类型混淆漏洞 漏洞描述 Google Chrome 是美国谷歌&#xff08;Google&#xff09;公司的一款Web浏览器&#xff0c;V8 是 Google 开发的高性能开源 JavaScript 和 WebAssembly 引擎&#xff0c;广泛应用于 Chrome 浏览器和 Node.js 等环…

力扣经典算法篇-23-环形链表(哈希映射法,快慢指针法)

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

HarmonyOS DevEco Studio 小技巧 42 - 鸿蒙单向数据流

在鸿蒙应用开发中&#xff0c;状态管理是构建响应式界面的核心支柱&#xff0c;而 单向数据流&#xff08;Unidirectional Data Flow, UDF&#xff09;作为鸿蒙架构的重要设计原则&#xff0c;贯穿于组件通信、状态更新和界面渲染的全流程。本文将结合鸿蒙 ArkUI 框架特性&…

【LeetCode 3136. 有效单词】解析

目录LeetCode中国站原文原始题目题目描述示例 1&#xff1a;示例 2&#xff1a;示例 3&#xff1a;提示&#xff1a;讲解化繁为简&#xff1a;如何优雅地“盘”逻辑判断题第一部分&#xff1a;算法思想 —— “清单核对”与“一票否决”第二部分&#xff1a;代码实现 —— 清晰…

前端面试专栏-算法篇:24. 算法时间与空间复杂度分析

&#x1f525; 欢迎来到前端面试通关指南专栏&#xff01;从js精讲到框架到实战&#xff0c;渐进系统化学习&#xff0c;坚持解锁新技能&#xff0c;祝你轻松拿下心仪offer。 前端面试通关指南专栏主页 前端面试专栏规划详情 算法时间与空间复杂度分析&#xff1a;从理论到实践…