STM32固件升级设计——串口IAP升级(基于YMODEM协议)

目录

一、功能描述 

1、BootLoader部分:

2、APP部分:

二、BootLoader程序制作

1、分区定义

2、 主函数

3、YMODEM协议的实现

4、程序跳转

三、APP程序制作

四、工程配置(默认KEIL5)

五、运行测试 

结束语


概述

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

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

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

一、功能描述 

        使用STM32的串口总线和Ymodem协议实现串口IAP升级程序。将FLASH分为3个部分。
分区介绍:
        本文使用stm32f103vet6,flash是512k,sector是1k,BootLoader整个代码编译下来有11K左右,所以使用0x08000000~0x00002FFF,SETTING主要存放升级标志位,使用0x08003000~0x00003FFF,剩下的FLASH将分为两个部分都用来存放代码,APP使用0x08004000~0x0807C000。(我这里已经极致压缩FLASH了,如果大家要移植的话,肯定要修改各个区域大小的)

区域起始地址区域大小功能
BOOT0x080000000x00003000(12k)存放BootLoader程序
SETTING0x080030000x00001000(4k)存放升级标志位/其它掉电不丢失标志位
APP0x080040000x0007C000(496k)存放产品主程序

tips: 选择以上分区方式的好处是在上电瞬间可以触发升级,在进入主程序运行期间也可以触发升级,这样就算升级了错误的程序,只要复位了再次进行升级就行,就不会导致变砖了。如果选择在主程序执行接收升级文件步骤,那如果程序有误,就很大概率会变砖,当然也有办法,那就是加校验或者将bin文件加密,这部分就不做了。

1、BootLoader部分:
  • 运行程序时首先从SETTING区域读取升级标志位,如果需要升级就一直用串口发送字符C(Ymodem协议的一部分),否则就直接跳转到APP。
  • 上电长按KEY1并复位,直接设置升级标志位进入升级,一直发送字符C,用xshell来发送升级bin文件。先将APP区域的程序全部擦除掉,然后xshell会按照Ymodem协议发送128或1024字节给MCU,MCU分别写入到APP区域内后复位即可实现升级程序,具体请查看本文源码。
2、APP部分:

        该部分需要在程序起始设置中断向量跳转指针,为了符合需求我用串口接收到‘1’就会设置程序更新标志位,复位后进入BootLoader升级。(大家也可以设置一个串口命令或者其它触发方式,只要能写更新标志位和复位就行)

二、BootLoader程序制作

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

1、分区定义
#define FLASH_SECTOR_SIZE       1024   //MCU sector size
#define FLASH_SECTOR_NUM        512    // 512k
#define FLASH_START_ADDR        ((uint32_t)0x08000000)
#define FLASH_END_ADDR          ((uint32_t)(0x08000000 + FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))#define BOOT_SECTOR_ADDR            0x08000000      // BOOT sector start address
#define BOOT_SECTOR_SIZE            0x3000
#define SETTING_SECTOR_ADDR         0x08003000      // APP设置的boot升级标志位
#define SETTING_SECTOR_SIZE         0x1000
#define APP_SECTOR_ADDR             0x08004000      // APP sector start address 
#define APP_SECTOR_SIZE             0x7C000         // APP sector size 
#define APP_ERASE_SECTORS        (APP_SECTOR_SIZE / FLASH_SECTOR_SIZE)   //507904/1024=496#define SETTING_BOOT_STATE      0x08003000
#define START_PROGRAM_STATE     2
#define UPDATE_PROGRAM_STATE    3
#define UPDATE_SUCCESS_STATE    4typedef enum {NONE = 0,BUSY,START_PROGRAM,    //进入APP主程序或者有更新就执行更新UPDATE_PROGRAM,   //进入更新UPDATE_SUCCESS    //更新成功写标志位
}process_status;      //更新状态
2、 主函数

        这部分包含了升级的所有状态,该框架可以说对比上一篇文章是完全不变的,在UPDATE_PROGRAM需要一直发送字符C来请求数据,具体看代码。

static void iap_process(void)
{process_status process;process = get_boot_state();switch (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:ymodem_c();LED1_TOGGLE;delay_ms(1000);break;case UPDATE_SUCCESS:write_setting_boot_state(START_PROGRAM_STATE);NVIC_SystemReset();break;default:break;}
}int main(void)
{LED_GPIO_Config();ymodem_init();Key_GPIO_Config();printf("jin ru boot\r\n");set_boot_state(read_setting_boot_state());if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == 1){set_boot_state(UPDATE_PROGRAM);}while(1){iap_process();}
}
3、YMODEM协议的实现

        接收固件按帧来接收的,默认使用1024个字节每帧接收,起始帧和结束帧(YMODEM_SOH)都不传输数据,数据帧(YMODEM_STX)以1024个字节接收固件,所以只需要接收YMODEM_STX,至于串口收发、队列和定时器的配置就不说了,只可以意会不可言传,反正根据YMODEM协议发完一帧需要MCU给应答才会发第二帧。

推荐以下两篇文章学习YMODEM协议

Ymodem协议详解-CSDN博客

Ymodem文件传输协议_ymodem协议-CSDN博客

#define YMODEM_SOH		0x01  // start of 128
#define YMODEM_STX		0x02  // start of 1024
#define YMODEM_EOT		0x04  // end of transmission
#define YMODEM_ACK		0x06  // positive acknowledge
#define YMODEM_NAK		0x15  // negative acknowledge
#define YMODEM_CA		0x18  // cancel终止
#define YMODEM_C		0x43  // control character 'C'
#define YMODEM_END      0x4F  // control character 'O'关闭传输
void ymodem_ack(void) 
{uint8_t buf;buf = YMODEM_ACK;Usart_Send_Data(&buf, 1);
}void ymodem_nack(void) 
{uint8_t buf;buf = YMODEM_NAK;Usart_Send_Data(&buf, 1);
}void ymodem_c(void) 
{uint8_t buf;buf = YMODEM_C;Usart_Send_Data(&buf, 1);
}void ymodem_end(void)
{uint8_t buf;buf = YMODEM_END;Usart_Send_Data(&buf, 1);
}void ymodem_start(ymodem_callback cb) 
{if (ymodem.status == 0) {ymodem.cb = cb;}
}void ymodem_recv(download_buf_t *p) 
{uint8_t type = p->data[0];switch (ymodem.status) {case 0:if (type == YMODEM_SOH) {ymodem.process = BUSY;ymodem.addr = APP_SECTOR_ADDR;mcu_flash_erase(ymodem.addr, APP_ERASE_SECTORS);ymodem_ack();ymodem_c();ymodem.status++;}break;case 1:if (type == YMODEM_SOH || type == YMODEM_STX) {if (type == YMODEM_SOH)//起始帧、结束帧这里可以不操作flash{mcu_flash_write(ymodem.addr, &p->data[3], 128);ymodem.addr += 128;}else {mcu_flash_write(ymodem.addr, &p->data[3], 1024);ymodem.addr += 1024;}ymodem_ack();}else if (type == YMODEM_EOT) {ymodem_nack();ymodem.status++;}else {ymodem.status = 0;}break;case 2:if (type == YMODEM_EOT) {ymodem_ack();ymodem_c();ymodem.status++;}break;case 3:if (type == YMODEM_SOH) {ymodem_ack();ymodem_end();ymodem.status = 0;ymodem.process = UPDATE_SUCCESS;}}p->len = 0;
}
4、程序跳转

        跳转这部分网上也很多,基本没什么区别,关于中断可能要注意一下,可能跳转之前,某些外设中断是开启的,跳转之后,中断产生了,但是APP代码中没有处理对应该中断的中断处理函数,所以就可能会直接死机。

tips:本文的开发环境只有64k ram,所以需要特别注意这里,虽然bootloader的ram不太可能会超。

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

详细参考以下文章:

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

uint8_t iap_load_app(uint32_t appxaddr)
{uint8_t i;uint32_t jump_addr;if (((*(__IO uint32_t*)appxaddr) & 0x2FFF0000 ) == 0x20000000) {  jump_addr = *(__IO uint32_t*) (appxaddr + 4);  jump2app = (iapfun)jump_addr;  /* 关闭所有中断,清除所有中断挂起标志 */  for (i = 0; i < 8; i++){NVIC->ICER[i]=0xFFFFFFFF;NVIC->ICPR[i]=0xFFFFFFFF;}	__set_MSP(*(__IO uint32_t*)appxaddr);  jump2app();return 1;}return 0;
}	

三、APP程序制作

        这部分需要设置一下flash的偏移量,为了满足需求,还需要在串口中断函数添加触发更新标志位。

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);
// 串口中断服务函数
void USART1_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET){		ucTemp = USART_ReceiveData(DEBUG_USARTx);USART_SendData(DEBUG_USARTx,ucTemp);  if(ucTemp=='1'){write_setting_boot_state(UPDATE_PROGRAM_STATE);NVIC_SystemReset();}}	 
}

四、工程配置(默认KEIL5)

BootLoader部分:0x08000000~0x08002FFF

APP部分:0x08004000~0x0807C000

五、运行测试 

        用串口助手执行升级操作,我用Xshell 8来操作(用其它支持YMODEM协议的串口助手也可以)。

1、配置Xshell 8。点击文件新建->点击连接->修改名称(随便改)和协议(选SERIAL)->点击串口->修改常规设置确认(图不截了,不会自己百度了解一下)

2、直接跳转APP。根据下图看到首先进来boot,然后直接跳转APP了。

​3、进入更新。按下KEY1按键后复位板子会重新进入boot,然后进入更新,MCU会一直发送字符C请求数据,根据下图发送bin文件给MCU接收。

可以看到已经更新成功,根据需求,大家也可以在主程序发送字符1给单片机也可以实现该功能。

tips: 测试完有个瑕疵,每次更新程序都需要把APP的全部扇区都擦除掉,那样很费时间,当然也有解决的办法,就是YMODEM协议可以抓取数据包的长度,根据数据包的长度去擦除固定的扇区,但是需要琢磨一下,我懒得弄,在我看来这个瑕疵是可以接受的。

结束语

        以上基于YMODEM协议的串口IAP升级功能已实现,这只是其中的一种升级方式,后面大家看到的也希望可以得到大家的指点或者互动,感谢各位亦菲彦祖们了。下面给出了完整工程,也包含了另一种分区(BOOT+SETTING+APP+DOWNLOAD)的代码。

完整代码下载地址:

基于YMODEM协议的串口IAP升级固件资源-CSDN下载

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

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

相关文章

Cookie(搭配domain)/Session(搭配HttpServletRequest+HttpSession)

各位看官&#xff0c;大家早安午安晚安呀~~~如果您觉得这篇文章对您有帮助的话欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦今天我们来学习&#xff1a;Cookie/Session1.Cookie/Session的简述我们在讲解HTTP协议的时候已经讲解过Cookie了HTTP 协议自身是…

240.搜索二维矩阵Ⅱ

纯暴力有点太唐了&#xff0c;不过竟然能过&#xff1b;还有行和列的表示我一直搞反了。。。class Solution {public boolean searchMatrix(int[][] matrix, int target) {for(int i 0 ;i<matrix.length;i){for(int j 0 ;j<matrix[0].length;j){if(matrix[i][j]target)…

【计算机组成原理】-CPU章节学习篇—笔记随笔

计算机组成原理 CPU 章节知识点总结&#xff08;适用于 408 考研&#xff09;​ 一、CPU 的功能与基本结构​ 1.1 CPU 的功能​ CPU&#xff08;中央处理器&#xff09;是计算机的核心部件&#xff0c;主要功能包括&#xff1a;​ 指令控制&#xff1a;程序的顺序执行&#xff…

公用测控装置的功能

公用测控装置在电力系统中广泛应用于变电站的高压开关单元、变压器本体及低压侧等对象。它集测量、控制、保护于一体&#xff0c;确保电网的安全、稳定运行。公用测控装置采用高性能硬件架构&#xff0c;如32位微控制器和独立AD采样技术&#xff0c;结合软件算法&#xff0c;实…

数据库|达梦DM数据库配置实例步骤

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 最近因为一个信创项目&#xff0c;要使用达梦DM数据库&#xff0c; 因此安装了达梦DM数据库来学习使用&#xff0c; 上一节中记录了安装Windows版本达梦DM数据库的过程步骤&#xff0c; 这一节紧接着上一节《数据库…

三十一、【高级特性篇】接口用例参数化与关联:实现上下文数据传递

三十一、【高级特性篇】接口用例参数化与关联:实现上下文数据传递 前言准备工作第一部分:后端数据模型调整1. 升级 `TestCase` 模型2. 生成并应用数据库迁移3. 更新 `TestCaseSerializer`第二部分:后端测试执行器强化1. 修改 `execute_api_test_case` 函数2. 修改 Celery 任…

PCA通过“找最大方差方向”实现降维,本质是用更少的变量捕捉原始数据的主要模式

什么事 PCA(主成分分析) PCA(主成分分析)的原理与通俗举例 PCA 是什么? PCA(Principal Component Analysis)是一种常用的降维算法,核心目标是将高维数据映射到低维空间,同时尽可能保留原始数据的关键信息(方差最大的方向)。 核心原理:找“最能代表数据的方向”…

JAVA synchronized关键字涉及的Monitor对象中 EntryList和WaitSet工作机制

在Java的synchronized同步机制中&#xff0c;Monitor对象的EntryList和WaitSet是两个关键队列&#xff0c;它们分别管理不同状态的线程。下面我将详细解释它们的工作原理&#xff0c;并提供代码示例说明。 EntryList&#xff08;锁竞争队列&#xff09; 作用机制 EntryList保…

js-day10

JS学习之旅-day101. 作用域1.1 局部作用域1.2 全局作用域1.3 作用域链1.4 JS垃圾回收机制&#xff08;GC&#xff09;1.5 闭包1.6 变量提升2. 函数进阶2.1 函数提升2.2 函数参数2.3 箭头函数3. 解构赋值3.1 数组解构3.2 对象解构4. 数组遍历4.1 forEach4.2 filter1. 作用域 作…

智能数字式毫秒计在实际生活场景中的应用

在电力领域&#xff0c;SYN5307型数字毫秒表可精准监测特高压变电站断路器合闸时间差&#xff0c;定位继电保护装置信号延迟&#xff1b;工业自动化中&#xff0c;优化汽车焊装线时序、提升半导体晶圆切割良率&#xff1b;科研计量上&#xff0c;助力量子通信同步校准&#xff…

Java面试基础:概念

1. Java的特点跨平台性&#xff1a;Java的 “编写一次&#xff0c;运行无处不在” 是其最大的特点之一。Java编译器将源代码编译成字节码(bytecode)&#xff0c;该字节码可以在任何安装了Java虚拟机(JVM)的系统上运行。面向对象&#xff1a;Java是一门严格的面向对象编程语言&a…

PyQt5高级窗口控件详解:停靠窗口、多文档界面与滚动条

掌握PyQt5的高级窗口控件&#xff0c;让你的GUI应用具备专业级的布局与交互体验 在PyQt5应用开发中&#xff0c;高效管理窗口布局和实现复杂交互功能是提升用户体验的关键。本文将深入解析三个核心高级控件&#xff1a;停靠窗口&#xff08;QDockWidget&#xff09;、多文档界面…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DrawingApp(画板组件)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— DrawingApp组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 Composition API&#xff08;<script setup>…

Eureka、Nacos、LoadBalance、OpenFeign​之间的区别联系和协作 (附代码讲解)

这篇文章聊聊微服务里的这几个老伙计&#xff1a;Eureka、Nacos、LoadBalance、OpenFeign。咱们做微服务开发&#xff0c;总会跟这几个组件打交道&#xff1a;Eureka、Nacos、Spring Cloud LoadBalancer、OpenFeign。它们各司其职又互相配合&#xff0c;今天就把它们的关系、用…

JavaSE-继承

继承&#xff08;inheritance&#xff09;继承的意义我们首先来看下面两个类&#xff1a;public class Dog {public String name;public int age;public void eat(){System.out.println(this.name"正在吃饭");}public void bark(){System.out.println(this.name"…

第二届虚拟现实、图像和信号处理国际学术会议(VRISP 2025)

重要信息 官网&#xff1a;www.icvisp.net 时间&#xff1a;2025年8月1-3日 地点&#xff1a;中国-长沙 简介 近年来&#xff0c;虚拟现实技术取得了显著进步&#xff0c;与5G、云计算和物联网等新一代信息技术的融合加速&#xff0c;推动了其在硬件、软件和内容应用等方面…

SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分离版:整体布局、架构调整(二)

目录 一、前言 二、后端调整 1.实体类调整 2.菜单相关接口 3.用户相关接口 4.新增工具类 5.新增菜单树返回类 6.配置类、拦截器 三、前端调整 1.请求调整 2.页面布局、样式调整 1.user.vue 2.index.vue 3.请求拦截 四、开发过程中的问题 五、附&#xff1a…

vue3官方文档学习心得

这几天抽空把vue3的文档整个看了一遍。简介 | Vue.js 23年写过一个vue2的项目&#xff0c;24年写了一个vue3的项目&#xff0c;页面功能比较简单&#xff0c;用几个简单的API&#xff0c;watch、watchEffect、ref、reactive就能实现的业务功能。 写了几年的react的&#xff0…

Pycharm恢复默认设置,配置导致复制粘贴等不能使用

在file 种找到manage IDE settings在manage IDE settings中找到restore default settings

【王树森推荐系统】召回12:曝光过滤 Bloom Filter

概述 曝光过滤通常是在召回阶段做&#xff0c;具体的方法就是用 Bloom Filter 曝光过滤问题 如果用户看过某个物品&#xff0c;则不再把该物品曝光给用户。原因是同一个物品重复曝光给用户会损害用户体验&#xff0c;但也不是所有推荐系统都有曝光过滤&#xff0c;像 youtube 这…