单片机IAP固件升级分几步?(Qt上位机)

news2024/11/20 16:26:56

更新

0924,一些潜在的bug解决方案

前言

这周一直想做一个IAP固件升级的上位机,然后把升级流程全都搞懂。

我用的单片机型号是STM32F103VET6,FLASH容量是512K,FLASH单页是2K。

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

IAP原理

IAP的原理我就不多赘述了,这里贴上几位大佬的文章

STM32CubeIDE IAP原理讲解,及UART双APP交替升级IAP实现-CSDN博客

STM32 IAP升级固件 + 上位机 例程 | 码农家园

IAR环境下STM32+IAP方案的实现

之前做过IAP,也讲解了一些存在的问题,参考之前我写的博客

单片机IAP升级的一些问题与经验_iap更新_TianYaKe-天涯客的博客-CSDN博客


0923初版

Qt读取二进制文件

读取二进制文件,将内容放在binRawData里

void MainWindow::readFw()
{
    QFileDialog dlg(this);
    QString fileName = dlg.getOpenFileName(this, tr("Open"), "./", tr("Bin File(*.bin)"));
    if( fileName == "" )
    {
        return;
    }

    QFile file(fileName);
    QFileInfo fileInfo(fileName);
    fwFileLen = fileInfo.size();
    fwPackNum = fwFileLen/fwPackLength + 1;

    if(file.open(QIODevice::ReadOnly))
    {
        binRawData = file.readAll();
        ui->lineEdit_fwUpdateFile->setText(fileName);
        ui->textEdit_fwUpdateFile->append(binRawData.toHex());
        file.close();
        ui->pushButton_startFwUpdate->setEnabled(true);
        ui->pushButton_stopFwUpdate->setEnabled(false);
    }
    else
    {
        QMessageBox::warning(this, tr("Error"), tr("Fail to open file!"));
    }
}

将binRawData拆包,并调用串口发送

connect(fwUpdateTimer,&QTimer::timeout,[=](){
        if(fwUpdateState == 1)
        {
            QByteArray fwSendBuff = binRawData.mid(fwPackIndex*fwPackLength+1,fwPackLength);
            fwPackIndex++;
            serialPort->write(fwSendBuff);
            if(fwPackIndex>fwPackNum)
            {
                fwUpdateTimer->stop();
                fwUpdateState = 3;
            }
        }
    });

加上固件传输的协议

发送开始指令,发送固件包大小

void MainWindow::startFwUpdate()
{
    ui->pushButton_startFwUpdate->setEnabled(false);
    ui->pushButton_stopFwUpdate->setEnabled(true);
    fwUpdateState = fwStart;

    QByteArray startCmd;
    uchar startCmd1 = 0xAB;
    uchar startCmd2 = 0xf0;
    startCmd = loadTxMsg(startCmd1, startCmd2, &startCmd);
    serialPort->write(startCmd);
    delay_ms(1000);


    uchar cmd1 = 0xAB;
    uchar cmd2 = 0xf1;
    uchar uData[2];
    uint16_t u16FwPackNum = fwPackNum;
    *(uint16_t *)&uData[0] = *(uint16_t *)&u16FwPackNum;
    QByteArray  txFwData;
    txFwData.append(uData[0]);
    txFwData.append(uData[1]);
    txFwData = loadTxMsg(cmd1, cmd2, &txFwData);
    serialPort->write(txFwData);

    fwUpdateTimer->start(100);
}

通过定时器逐帧传输,传输结束后发送结束信号

connect(fwUpdateTimer,&QTimer::timeout,[=](){

        if(fwUpdateState == fwStart)
        {
            QByteArray fwSendBuff = binRawData.mid(fwPackIndex*fwPackLength,fwPackLength);
            fwSendBuff.insert(0,fwSendBuff.length());
            QByteArray fwSendProtocolBuff = loadFwPackData(&fwSendBuff);
            serialPort->write(fwSendProtocolBuff);

            fwPackIndex++;
            QString fwDataString = ByteArrayToHexString(fwSendProtocolBuff).toLatin1();
            ui->textEdit_fwInfo->clear();
            ui->textEdit_fwInfo->setWordWrapMode(QTextOption::WordWrap);
            ui->textEdit_fwInfo->insertPlainText(QString("["));
            ui->textEdit_fwInfo->insertPlainText(QString::number(fwPackIndex));
            ui->textEdit_fwInfo->insertPlainText(QString("]  "));
            ui->textEdit_fwInfo->insertPlainText(fwDataString);
            if(fwPackIndex>=fwPackNum)
            {
                fwUpdateState = fwComplete;
                fwUpdateTimer->stop();
                QByteArray stopCmd;
                uchar stopCmd1 = 0xAB;
                uchar stopCmd2 = 0xf3;
                stopCmd = loadTxMsg(stopCmd1, stopCmd2, &stopCmd);
                serialPort->write(stopCmd);
            }
        }
    });

STM32代码部分

iap.h

#ifndef __IAP_H
#define __IAP_H

#include "includes.h"

#define __APP_START_ADDR	0x08010000U
#define __APP_SIZE			0x10000U

typedef enum 
{
	IAP_START,
	IAP_TRANFER,
	IAP_COMPLETE,
} IAP_Status;

typedef struct
{
	u8	u8Length;		// 当前接受到数据帧的帧长
	u8	u8Data[64];		// 当前接受到的数据
} RcvFrame_S;

typedef struct 
{
	IAP_Status state;		// ipa升级当前状态
	RcvFrame_S stRcvFrame;	// 接受到的数据
	u16	u16FwFrameNum;		// 固件数据帧总量
	u16 u16FwFrameIndex;	// 固件数据帧偏移
	u32 u32WriteAddrIndex;	// 写地址偏移
} IAP_S;

extern IAP_S stIap;
void IapRcvDataProc(u8 *MsgData);
typedef void (*Application)(void);
void JumpToApplication(void);
#endif //__IAP_H

iap.c

#include "includes.h"

IAP_S stIap;

void IapRcvDataProc(u8 *MsgData)
{
	u8 cmd = MsgData[3];
	u8 i = 0;
	switch(cmd)
	{
		case 0xF1:
			EraseFwSpace(__APP_START_ADDR,__APP_SIZE/__FLASH_PAGE_SIZE);
			memcpy(&stIap.u16FwFrameNum, &MsgData[4], 2);
			break;
		case 0xF2:
			stIap.u16FwFrameIndex++;
			stIap.stRcvFrame.u8Length = MsgData[6];	
			memcpy(&stIap.stRcvFrame.u8Data, &MsgData[7], stIap.stRcvFrame.u8Length);
			for(i = 0; i < stIap.stRcvFrame.u8Length; i += 4)  //一次写入是4个字节
            {
				FlashWriteWord(__APP_START_ADDR+stIap.u32WriteAddrIndex, *(u32 *)&stIap.stRcvFrame.u8Data[i]);
				stIap.u32WriteAddrIndex += 4; //写入的地址加4
			}			
			break;
		case 0xF3:
			JumpToApplication();
	}
	
}

void JumpToApplication(void)
{	
	Application application;
	__set_FAULTMASK (1);
	application = (Application)(*(__IO u32*)(__APP_START_ADDR+4));
	__set_MSP(*(__IO u32*)(__APP_START_ADDR));
	SCB->VTOR = __APP_START_ADDR;
	application();	
}


视频演示

IAP固件升级(Qt上位机)最初版0923_哔哩哔哩_bilibili

IAP固件升级(Qt上位机)最初版0923


0924更新

之前的演示,终于能把新固件下载到单片机,并在单片机上面跑新固件。

可是有很多问题待解决

1.上位机怎么知道单片机接收到了正确的数据?

2.如果单片机接收的固件大于APP空间,该怎么处理?

3.升级过程,如果数据传输的通路断掉了(单片机挂了,上位机崩溃了,连接断开了),该怎么处理?

5.如果待传输的新固件与当前的固件是同一个版本,还有必要传输吗?

6.之前的演示只能跑新固件,但是没有预留升级的接口,如何在APP写一份接口,支持可重复升级?

7.上位机怎么知道单片机固件升级结束?

待我一一道来

1.上位机怎么知道单片机接收到了正确的数据?

这个问题可难可简,最常见的是加校验,之前已经做了串口数据帧的校验,基本上是不会出错了。但如果要保证数据传输的准确性,还需要加一种校验,可以是CRC32、奇偶校验。如果还要进一步保证整个固件的准确性,需要再加一个文件校验可以是 MD5、SHA1等等。

这部分的工作由网上各位大神来做,我这里只提一嘴。

[CRC校验]手算与直观演示_哔哩哔哩_bilibili

MD5为何不再安全_哔哩哔哩_bilibili

2.如果单片机接收的固件大于APP空间,该怎么处理?

这种要看情况,如果只是用户给APP分配过小,可以考虑增大APP空间;如果是有两个APP代码,可以考虑空间合并,只跑一份APP。

3.升级过程,如果数据传输的通路断掉了(单片机挂了,上位机崩溃了,连接断开了),该怎么处理?

参考我之前写的博客

单片机IAP升级的一些问题与经验_iap更新_TianYaKe-天涯客的博客-CSDN博客

5.待传输的新固件与当前的固件是同一个版本,还有必要传输吗?

首先这个问题很容易回答,没必要,但这不是关键。

这里有几个问题,

怎么知道新老固件的版本号?

版本号如何比对?

如何在传输过程中附带版本号信息?

我的解决办法是,在固件传输前,也就是上位机给单片机发开始传输的起始信号时,附带上版本号信息。然后单片机接受到后,跟存放在用户FLASH空间的版本号进行比对。

6.如何在APP写一份接口,支持可重复升级?

最简单的办法是,APP只做固件版本号判断和跳转到IAP的升级接口。

7.上位机怎么知道单片机固件升级结束?

最保险的办法是,在IAP跳转到APP后,由APP发送升级结束的信号。

不然可能出现IAP全部接收到固件,但是出现跳转失败的情况。

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

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

相关文章

Rabbit消息的可靠性

Confirm模式简介 消息的confirm确认机制&#xff0c;是指生产者投递消息后&#xff0c;到达了消息服务器Broker里面的exchange交换机&#xff0c;则会给生产者一个应答&#xff0c;生产者接收到应答&#xff0c;用来确定这条消息是否正常的发送到Broker的exchange中&#xff…

黑马JVM总结(二十二)

&#xff08;1&#xff09;类的结构-field 成员变量信息 类字节码里的一些简单表示&#xff1a; &#xff08;2&#xff09;类文件结构-method-init &#xff08;3&#xff09;类文件结构-method-main &#xff08;4&#xff09;类文件结构-附加属性

Java基础(六)

前言&#xff1a;本篇博客学习Junit单元测试框架的使用以及常见的注解。 目录 单元测试 Junit单元测试框架 常见注解 单元测试 什么是单元测试&#xff1f; 针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;编写测试代码对其进行正确性测试。 Junit单元测试框…

十六、MySql的MVCC机制CONNECT(收官!)

文章目录 一、数据库并发的场景有三种&#xff1a;二、读-写&#xff08;一&#xff09;3个记录隐藏列字段&#xff08;二&#xff09;undo 日志&#xff08;三&#xff09;模拟 MVCC&#xff08;四&#xff09;一些思考&#xff08;五&#xff09;Read View 一、数据库并发的场…

interview6-jvm篇

JVM(Java Virtual Machine)Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 在JVM中共有四大部分&#xff0c;分别是ClassLoader&#xff08;类加载器&#xff09;、Runtime DataArea&#xff08;运行时数据区&#xff0c;内存分区&#xff09;、Execu…

整合车辆出险报告Api接口,轻松管理车险理赔!

随着车辆保有量的不断增加&#xff0c;车辆出险的情况也越来越普遍。对于车主来说&#xff0c;如何高效地管理车险理赔&#xff0c;处理保险事故是非常重要的。这时候我们就可以借助整合车辆出险报告API接口&#xff0c;实现快速定位理赔信息&#xff0c;轻松管理车险理赔。 一…

【C++面向对象侯捷】8.栈,堆和内存管理

文章目录 栈&#xff0c;堆stack object的生命周期static local object的生命周期global object的生命周期heap objects 的生命期new&#xff1a;先分配memory&#xff0c;再调用构造函数delete: 先调用析构函数&#xff0c;再释放 memory动态分配所得的内存块&#xff0c;in V…

VirtualBox解决VERR_SUPDRV_COMPONENT_NOT_FOUND错误

简述 最近使用VirtualBox时发现其增强功能不能用了&#xff0c;也就是不能双向拖拉文件&#xff0c;整了很久不知所以&#xff1b;看到有网友说跟新其VBoxGuestAdditions.ios文件&#xff0c;所以直接把我的VirtualBox从6.x升级到了7.x&#xff0c;然后就发生了眼前的一幕&…

040_小驰私房菜_MTK平台,添加camera客制化size

全网最具价值的Android Camera开发学习系列资料~ 作者:8年Android Camera开发,从Camera app一直做到Hal和驱动~ 欢迎订阅,相信能扩展你的知识面,提升个人能力~ 【问题背景:】mtk8195平台,录像需要添加一组自定义size 2560 * 1600。 添加一组自定义size,我们需要确认一…

数据湖在爱奇艺数据中台的应用

01 我们眼中的数据湖 作为爱奇艺的数据中台团队&#xff0c;我们的核心任务是管理和服务公司内的大量数据资产。在实施数据治理的过程中&#xff0c;我们不断吸收新的理念&#xff0c;引入尖端的工具&#xff0c;以精细化我们的数据体系管理。“数据湖”作为近年来数据领域广泛…

为您的SSH提提速

SSH是运维和开发人员接触比较多的工具&#xff0c;一般用SSH来连接远程服务器&#xff0c;这个是我的一些免费客户和企业客户经常使用的场景&#xff0c;当然SSH除了远程连接之外&#xff0c;还有很多额外的用途&#xff0c;比如SSH本身是具备代理功能的&#xff0c;我们也有一…

HarmonyOS开发:封装一个便捷的Log工具类

前言 日志打印&#xff0c;没什么好说的&#xff0c;系统已给我们提供&#xff0c;且调用也是非常的简单&#xff0c;我们封装的目的&#xff0c;一是扩展&#xff0c;打印一些不常见的类型&#xff0c;比如格式化json&#xff0c;使得日志看起来比较好看&#xff0c;二是&…

【Java 基础篇】Java Consumer 接口详解

在Java编程中&#xff0c;有时需要对某个对象进行操作或者处理&#xff0c;而这个操作可能是非常灵活的。Java 8引入了函数式编程的特性&#xff0c;其中的一个重要接口就是Consumer接口。本文将详细介绍Consumer接口&#xff0c;包括它的定义、用法以及示例。 什么是 Consume…

【Java 基础篇】Java Supplier 接口详解

在Java中&#xff0c;Supplier接口是一个重要的函数式接口&#xff0c;它属于java.util.function包&#xff0c;用于表示一个供应商&#xff0c;它不接受任何参数&#xff0c;但可以提供一个结果。Supplier通常用于延迟计算或生成值的场景。本文将详细介绍Supplier接口的用法以…

矩阵的c++实现

在大学数学课程《线性代数》中&#xff0c;就有矩阵和行列式的出现&#xff0c;这篇文章主要讲矩阵在c中的实现和一些用途&#xff08;目前我知道的&#xff09; 此篇文章只写c的内容&#xff0c;不具体写到数学中矩阵的一些公式、性质。 本篇文章中一部分图片来自百度百科。…

Android 12,调用系统库libft2.so 遇到的各种问题记录

问题前提,Android 12系统,vendor静态库中调用 libft2.so。(vendor静态库中调用libft2.so会简单点,没这么麻烦) 【问题1】 (native:vendor) can not link against libft2 (native:platform) 本地debug尝试修改: 为了本地环境debug调试方便,我找了个 mk文件,在里面添加了…

《富足》—没有完善的个人,但是可以有完善的团队

摘要&#xff1a;在吴军老师《富足》一书上&#xff0c;阅读到一句话&#xff1a;“没有完善的个人&#xff0c;但是可以有完善的团队”。很认同这句&#xff0c;目前听见最多的可能是“没有完美的个人&#xff0c;只有完美的团队”&#xff0c;这句长挂在嘴边的话在社会工作多…

Win7开启触摸键盘方法

在Win7系统中&#xff0c;自带有触摸屏幕键盘&#xff0c;能够在屏幕上显示虚拟键盘&#xff0c;让用户可以用指针设备或触屏等进行输入操作&#xff0c;那么Win7系统怎么开启触摸键盘呢&#xff1f;想知道的小伙伴可以跟着我一起来学习一下。 1、首先打开Win7系统的开始菜单&a…

计算机竞赛 深度学习YOLO抽烟行为检测 - python opencv

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习YOLO抽烟行为检测 该项目较为新颖&#xff0c;适合作为竞赛课…

方案:TSINGSEE青犀智能分析AI皮带撕裂算法的场景应用

在工地矿山等现实场景中&#xff0c;皮带运输在生产过程中是必不可少的&#xff0c;然而&#xff0c;由于长时间高强度的运转&#xff0c;皮带很容易发生撕裂、破损、跑偏等问题。这些问题会严重影响生产速度&#xff0c;甚至会导致严重的安全事故。为了有效预防此类安全事故发…