stlink 升级固件以后失败_STM32固件升级的一点经验

上面理论+实践当初花了3天时间弄完的,但是,当你真正做项目的时候,你会发现,只有上面的这些知识还不够,还有更多的细节要去处理:

  1. APP程序怎么跳转到BootLoader程序?
  2. APP程序和BootLoader之间是否会互相影响?
  3. APP和BootLoader之间如何传递参数?
  4. 固件更新一到一半,因为某种原因失败了(通信错误、掉电),该如何处理?
  5. 如何确保更新的APP是你需要的APP,而不是别的一个APP?

经过一个项目的固件升级功能洗礼,以上问题都得到了较好的解决,为了避免以后忘记,在此记录一下。芯片为:STM32F103ZET6

第一个问题,APP程序怎么跳转到BootLoader程序?看似很简单,因为这是基本的功能,但是实际情况并不简单。

由前面的小节了解到,从BootLoader跳转到APP可以通过指针进行跳转,但是当你从APP通过指针跳转到BootLoader时,发现会出现问题(具体原因不明,有机会的话去研究一下)。那么又该怎么办?

可以通过复位的方式,让程序重新从开始地址运行,有以下几种方式复位:

  1. 内核复位
  2. 系统复位
  3. 上电复位

第一、第二种方式都是通过设置相关寄存器使单片机发生复位的,两者的区别就是,内核复位只复位芯片的内核,但对单片机的片上外设并不进行复位,比如USART、SPI、USB等外设是不会进行复位的。

系统复位的话,就会对整个芯片进行复位,不管是外设还是内核,都会回到最初始的状态,就如按下复位按键一样。

最后一种上电复位,其实和系统复位、按键复位的效果差不多,都是会进行全部复位的,不过这个需要外部硬件控制单片机的电源的开启与关闭,增加了额外的硬件。

一开始鱼鹰准备采用系统复位的,直接设置寄存器触发导致复位,因为这样更彻底,测试发现项目中的单片机根本无法复位,而我自己的开发板是能进行复位的,后来经过硬件工程师的查找,发现是看门狗电路导致无法复位,这样一来,系统复位这条路堵死了(因为项目的硬件已经确定,无法再更改了)。

那么是否有其它方法,前面提到的指针跳转的方式发现会出现问题,因为项目比较急,就没怎么花心思解决。后来在调试过程中,突然发现KEIL中的复位按钮是能进行复位的,那么问题就简单了,既然调试器能进行复位,那我应该也能进行复位才对,之前说了系统复位不好使,那么按下复位按钮时应该是采用的内核复位(实际上CMSIS-DAP调试器是有单独的一条复位线的,但是当时没考虑它可能采用了这种方式,只考虑可能采用了内核复位,阴差阳错)。

那么就试试内核复位吧,一试发现果然有效,但是因为内核复位不彻底,导致出现了问题。

这就到了第二个问题,两个程序之间是否会有影响?

第一,首先从BootLoader对APP的影响考虑,我们知道,BootLoader程序也是需要一些资源的,比如串口之类的用于固件的传输,如果说BootLoader的寄存器和APP的寄存器配置要求不一样,那么就可能出现问题(鱼鹰的项目中还用了一个定时器喂狗,发现一进入APP程序就挂了,后来才找到这个原因)。

比如BootLoader采用串口查询的方式接收数据,而APP为了提高效率,使用DMA+空闲中断的方式处理,那么两者的寄存器配置肯定不同,那么该怎么消除BootLoader程序对APP的影响呢?

有人说,让BootLoader程序用完串口之后自动复位串口外设即可,确实,这是一种方法,但是你是否考虑过两个程序是独立的,万一后面的人在BootLoader程序中忘记了复位串口呢?所以说,靠别人不如靠自己,与其担心害怕别人不靠谱,不如APP自己去复位串口,即APP在配置串口之前,可以先复位串口,再进行配置(从这里可以知道,为什么有些代码会使用XXX_DeInit()之类的函数在配置前复位片上外设,一开始以为是多余的,毕竟一般程序开始运行的时候一般都是上电之后才运行的,这个时候已经复位外设了,为什么还要多此一举,直到现在才明白这才是安全的做法)。

第二,从APP对BootLoader的影响考虑,APP程序使用的资源一般比BootLoader的资源多,如果两者之间使用了相同的资源,比如串口,那么肯定得考虑两者的差异性,所以根据上面的考虑,也可以让BootLoader程序在使用串口之前先进行复位,然后再进行配置,这是比较安全的做法。但是仅仅如此就足够了吗?

在项目里的APP程序中,有一个加热过程,如果说APP跳转到BootLoader之前没有考虑这一点就盲目的运行到BootLoader,那么很可能出现APP正在加热,但是因为跳转到了BootLoader中运行,导致无法对温度进行控制,那么结果将是灾难性的,轻点的只是设备烧毁,重的可能就引发火灾了。

所以说,两者之间的影响一定要慎重考虑。

事实上,如果采用系统复位或者上电复位的方式,第二点关于APP对BootLoader的影响是可以不考虑的,因为系统复位或者上电复位自动将外设进行初始化了,但是你不能肯定你现在采用这些方式,以后就不会采用内核复位的方式,所以为了安全,还是要考虑进去。

现在说说第三个问题,APP和BootLoader之间如何传递参数?

首先思考为什么要传递参数?

在前面的小节中,选择让BootLoader程序在开始复位时等待一段时间再进入APP运行,在等待的过程中,就可以判断是否需要固件升级,比如等待时,由上位机发送一条特殊的命令确定是否升级,或者通过引脚电平等方式,反正就是要让BootLoader程序知道,下面我要开始升级了,别急着进入APP运行。

但这里有一个问题就是,这里需要一个冷启动的过程,即先上电后再接收命令,而且时间短暂,有一个好处就是,即使单片机中暂时没有APP程序,也能够实现固件升级过程,这样保证了由BootLoader接收升级命令而不是由APP接收,所以当初在无法解决升级到一半时如何恢复时有考虑使用这种强制升级的方式。

那么有没有更好一点方式,不需要冷启动过程,而是由APP决定是否升级?有的。

既然是APP决定是否升级,那么肯定需要在进入BootLoader之前给它传递一个参数,告诉它,这次复位需要升级,不能直接跳到APP中运行,那么BootLoader就会乖乖地等着升级了。

那么怎么传递呢?有人说往FLASH中写入参数,这样复位的时候就可以判断是否需要升级了,这确实是一个方法,但是我们知道,如果我们要往FLASH写入参数,那么必须先进行擦除工作才行,而擦除的往往是一个扇区,为了写入几个字节的参数,擦除几K的数据,鱼鹰感觉实在是太浪费了;还有这个参数保存地址也是需要好好考虑的,放在APP区还是BootLoader区?那么有没有更好的方式?

有的。还不只一种。

一开始鱼鹰想到的是利用后备域保存参数,因为如果有电池存在的话,它的数据是不会丢失的,但不巧的是,这个项目没有这个功能。

还有可以使用外部的FLASH空间,有些FLASH芯片是可以进行字节编程的,不需要整片擦除,挺合适的。但是缺点就是,你的项目要有这种芯片,而你的BootLoader需要写相应的代码驱动这个芯片,显然很麻烦。

最后鱼鹰采用的是RAM传参。鱼鹰在之前的小节说过,APP和BootLoader共用RAM,如果说能用RAM传递参数的话,只是操作一个变量,相当方便。

但是怎么保证两者之间顺利传递参数呢?

我们知道,C语言申请的变量空间是由编译器自动分配的,也就是说,同样申明一个同名变量,APP和BootLoader申请的变量地址不一定是一样的,而且还有一点就是,即使你申请的变量通过某些方法让它地址固定,也会有问题,因为申请的变量会在进入main函数之前会被初始化掉,当然你可以说通过某种方式让它不被初始化,但是鱼鹰想到了更好的方法。

通过指针直接操作RAM空间最后几个字节用于参数传递(之前有看到说STM32单片机中有个寄存器可以直接掉电不丢失,但具体不知道是哪一个)。

因为采用指针操作,所以编译器并不会对你指向的地址进行初始化,这样可以很方便的绕过编译器的处理。其次,通过操作最后几个字节,保证了这个空间不会被程序的其他变量占用(其实占用了也关系不大,只要你传递的参数足够特别,比如0x05055555,就问题不大)。

这样,BootLoader在复位后只要检查这个地址的值,就可以轻松知道是否该升级了。

但是还有一个隐患就是,在上电那一刻,如果这个地址的值刚好是你设置的特殊值(因为上电后,RAM的值是随机的),那么必然会出现问题,但这种可能性微乎其微,因为要让四个字节在复位哪一刻刚好都变成你设置的特殊值,简直比中彩票还要困难。

不过即使你真的中彩票了,重新上电复位一下就好了,如果说第一次中彩票还能接受,第二次还如此,那就需要烧烧香、拜拜佛了。

需要注意的是,一旦使用完这个参数,必须清零,防止下次内核复位又进入升级了(比如在线调试时可能会使用KEIL中的复位按钮)。

第四个问题,固件更新一到一半,因为某种原因失败了(通信错误,掉电),该如何处理?

我们知道,升级过程中很大可能是会失败的,但是单片机升级不像电脑升级,这次升级不成功,恢复成原来的系统就是了。单片机空间有限,没办法同时保存两份APP程序的,那么又该如何处理呢?

现在换个角度思考,你如何确定你升级失败了?如果能做到这一点,那么你的BootLoader程序就可以在升级失败后继续运行BootLoader的程序,而不进入APP运行那只升级到一半的程序(运行这种半残程序,鬼知道会发生什么怪异事件呢)。

如果从这个角度来看,其实就简单了,只要上位机把bin文件的大小发下来,然后由BootLoader程序判断是否升级完成就可以了,而Ymodem刚好可以有这个功能。

但是这样一来,就出现了一个问题,需要一个掉电不丢失的参数来保存是否升级成功,否则下次上电又会继续运行APP。但是鱼鹰对整个扇区擦除的方式很反感,就是不愿使用这种方式,怎么办?

苦思冥想之下,终于找到了一个巧妙的方式去处理。

我们知道,运行APP之前,一般会对APP的前面8个字节的栈顶指针、复位地址的合法性进行判断,判断是否是有效的APP程序,毕竟随便拿一个程序去升级,还不乱套了。

不管怎样,APP程序都是要往FLASH更新程序的,那么我们是否能利用这个过程呢?

APP更新之前,必定把该擦除的扇区进行擦除了,如果说我们一开始,就对写入的工作进行特殊处理,那么是否可以达到我们想要的效果呢?

比如说,前面8个字节,本来是在一开始的时候就会被写入的,如果我们在一开始写入数据的时候,跳过这8个字节的写入,然后把剩余的代码全部写入,当判断已全部接收到(大于等于bin文件大小,因为Ymodem协议稍微有点特殊,最后一帧数据可能填充0)固件之后,最后再对前面8个字节写入,这样一来,就保证了程序的完整性,如果说你中途数据中断了(掉电或上位机中断),那么前面那8个字节肯定不会写入,也就无法正常进入APP中运行了。

第五个问题,如何确保更新的APP是你需要的APP,而不是别的一个APP?

前面的问题保证了更新的程序是完整的,但是完整的程序不一定就是你需要的程序,那么怎么确定是你需要的APP呢

通过bin文件名来确定吗?这是一个方法,但是bin文件名可以被用户轻易更改,那就只能从bin文件内容本身入手了。

常规方法是,通过某些工具,在bin文件中加几个字节标着文件的特殊性,但是众所周知的是,鱼鹰比较懒,看似简单的只是加入几个字节的事情,如果产品成型的话比较好说,更新次数比较少,但是一旦产品处于测试阶段,更新频繁,累人不说,还可能出错。

那么有什么办法呢?就从代码本身入手好了。

方案确定下来了,但是怎么处理呢,标志位放在哪里,怎么放?又是一番苦思冥想。

一开始想到的是想将标志位放在bin文件最后,但是bin文件的大小是不固定的,虽然说上位机可以把bin文件大小传下来,但是怎么放是个问题,开始打算通过修改链接文件实现,但是发现自己对链接过程不熟,对汇编语言(汇编可以指定地址)也不熟,怎么办?

最终鱼鹰选择把程序标志放在向量表的后面,即通过指定变量地址的方式保证地址唯一性(需要注意一点的就是,通过指定地址的方式,不一定就确保最终的地址就是你设定的地址,如果和其它地址冲突了的话,可能不一致,需要看map文件确定)。而且在设置APP程序的时候,一般都会重新定义向量表的位置,那么可以在定义标志地址时利用这个地址进行偏移。

当BootLoader在写前面8个字节前,只要再判断这个地址的标志位是否正确即可。

到此,固件升级方面的知识应该比较完善了,但还有一个问题,如何对bin文件加密与解密呢?

这个问题只有下次项目需要的时候再研究了。

------------------------------------------------------------------------------2019-06-30 Osprey

喜欢就来关注鱼鹰吧!

如果对你有帮助,点个赞再走呗!

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

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

相关文章

linux搭建Django环境,Linux (ubuntu 12.04)下搭建Python Django环境

1. 检查python是否安装:直接在shell里输入python,如果已经安装了python,即可进入python bash,并看到版本号(如Python 2.7.3)——在ubuntu中python应该是已经默认安装好了2. 安装Django:sudopythonsetup.pyinstall检查Django是否安…

comsol分析时总位移代表什么_超弹性材料模型的压缩分析

为了表征超弹性材料,需要进行各种测试获取实验数据,包括承受单轴拉伸和压缩、双轴拉伸和压缩以及扭转测试。今天,我们向大家介绍如何使用通过单轴和双轴测试获得的拉伸和压缩测试数据,模拟由弹性泡沫材料制成的球体的压缩。通过案…

linux函数计时,Linux 中的计时——gettimeofday函数

1.使用C语言进行计时在用户空间中可以使用C语言函数gettimeofday 得到时间,它的调用格式是:#include int gettimeofday(struct timeval *tv, struct timezone *tz);int settimeofday(const struct timeval *tv , const struct timezone *tz);…

由对称性知定点一定在x轴上_线上优秀教学案例(九)|计算机科学与工程学院刘钊:“延期不延教”之“1+X课堂”...

【编者按】受新冠肺炎疫情影响,按照教育部和河北省教育厅统一部署,学校延迟春季开学时间。为最大程度减少疫情和延期开学对我校教育教学工作的影响,本学期,我校以线上教学的形式拉开序幕。面对新的教学模式,各学院、广…

cxf restful_使用Apache CXF开发RESTful服务

cxf restful介绍 如您所知,有两种开发Web服务的方法 简单对象访问协议(SOAP) 代表性状态转移(REST) 在继续学习如何使用Apache CXF创建基于REST的Web服务之前,我们将了解什么是REST。 REST不是一种技术&…

linux adc测试程序,基于Qt4.7的ADC测试程序

所使用的开发板:友善之臂tiny6140widget.h#ifndef WIDGET_H#define WIDGET_H#include #include namespace Ui {class Widget;}class TMainForm : public QWidget{Q_OBJECTpublic:TMainForm(QWidget * parent 0, const char * name 0, Qt::WFlags f 0);virtual ~T…

和显卡驱动要配套吗_天天学渲染,你的显卡驱动用对了吗?

大家好,最近一直有很多人和我反馈说,自己在使用Octane或者Redshift渲染器渲染的时候经常容易崩溃,甚至是闪退了,其实这个问题有很大一部分原因是由于你的显卡驱动没用对导致的。接下来就和大家说下如何解决。首先我们大部分人买了…

网络研讨会:Java 9的第一印象–构建可伸缩企业应用程序的新方法

在此网络研讨会上听我们对新Java版本的一些初步想法 Java 9的新版本引起了很多炒作。在Mark Reinhold领导的专门团队的带领下,将Java平台迁移到模块花费了近十年的辛苦工作。 除了备受期待的Project Jigsaw,此版本还包含许多其他令人兴奋的功能。 现在是…

linux cat 文本颜色,linux文本文件查看、展示命令 :cat head tail grep more less nl

linux文本文件查看、显示命令 :cat head tail grep more less nllinux文本文件查看、显示命令 :cat head tail grep more less nl1、cat 显示文件内容命令。命令格式:#cat [[选项]] 常用选项:-b,计算所有非空输出行,开始行编号为1。-n,计算…

noj数据结构稀疏矩阵的加法十字链表_一个算法毁了一款好游戏?算法和数据结构到底有多重要?...

来源 | 异步前段时间大火的国产游戏——《太吾绘卷》,由于创新的玩法和精良的制作一度广受好评,然而随着玩家游戏的深入和时长的积累,发现该游戏在玩的过程中游戏外的问题很多很多。首先是存档速度慢,然后是密集的计算导致功耗大量…

arm-linux-gcc libstdc .so.6,mini2440编译内核:usr/lib/libstdc++.so.6 not found

买了块mini2440开发板,按照光盘中的mini2440用户手册编译内核时,#make zImage出现错误:usr/lib/libstdc.so.6 not found,在网上百度了下,有很多不同的解决办法,我试了一个,编译成功 ^ ^注&#…

前端实现可绘制的canvas画布_前端图形学基础(五)——Canvas状态管理

点击右上角的关注,不定期前端干货分享!!欢迎来到我的前端图形学系列文章:前端图形学基础(一)——Canvas基础入门前端图形学基础(二)——Canvas基础前端图形学基础(三)——Canvas绘制图片前端图形学基础(四)——Canvas绘制曲线我们…

sudo su su_Spring Security应用程序中的su和sudo

sudo su su很久以前,我从事的项目具有很强大的功能。 有两个角色:用户和主管。 主管可以以任何方式更改系统中的任何文档,而用户则更受工作流约束的限制。 当普通用户对当前正在编辑并存储在HTTP会话中的文档有疑问时,主管可以介入…

linux如何判断同名进程个数,Linux下判断是否存在多个同名进程

Linux 下如何判断同名进程的个数,这个可以通过shell命令ps -e | grep -c "所查进程名字",就可以得到进程的个数。这里给出代码实现。bool processNumber(char *name){FILE *fptr;bool bret false;char cmd[255] {\0};char buf[255] {\0};sp…

having和where可以同时使用吗_阴、阳离子聚丙烯酰胺可以同时混合溶解使用吗?...

聚丙烯酰胺是一种水处理药剂,需要溶解在水中制成浓度为0.1%-0.5%的聚丙烯酰胺水溶液。一般来说,当聚丙烯酰胺实际使用时,阴离子聚丙烯酰胺和阳离子聚丙烯酰胺可以同时使用。但是,阳离子聚丙烯酰胺和阴离子聚丙烯酰胺不能混合在一起…

Lombok,AutoValue和Immutables,或如何编写更少,更好的代码返回

在上一篇有关Lombok库的文章中 ,我描述了一个库,该库有助于处理Java中的样板代码( 是的,我知道这些问题已经在Kotlin中解决了 ,但这是现实生活,我们不能一味地坐下来,一旦出现较新或更简单的语言…

noj大作业c语言扫雷,noj大作业.doc

作业名称:算法演示程序学 院:航海学院班 级:学 号:2013300951姓 名:苏和团队组成:西北工业大学2015年11月11日1、问题与背景(描述程序所要解决的问题或应用背景)C语言经过几十年的发展已经发展出多种多样的…

福州PHP讲师招聘_“艺”起就业|招聘信息汇总

亲爱的美术学院毕业生们在全国上下万众一心抗击新冠肺炎疫情的关键时期学院党委高度重视并时刻关注着每一位毕业生的就业工作为进一步做好疫情防控工作也为进一步确保同学们顺利求职就业保障毕业生就业工作的时效性学院公众号特别设立“艺”起就业—招聘信息发布栏目 由院学工…

哪个app最费电_关于石墨烯养生地暖,业主最关心的几大问题,答案附上!

很多人对石墨烯地暖有些许误解,不敢轻易接受。其实不然,石墨烯地暖是一种供热效果好、安全性高、运用寿命长、节能环保的新型健康采暖方式,不仅适用于住宅小区、别墅等家庭采暖,也可用于办公楼、医院等各类公共建筑采暖。在众多对…

双机之间的串行通信设计 c语言编程,双机串行通信设计精品.doc-资源下载人人文库网...

双机串行通信设计 精品.doc所属课程名称 单片机原理与接口技术 题 目 双机串行通信 二 总体设计2.1 设计目的 通过本次课题设计,应用单片机原理及其接口技术等所学相关知识及查阅资料,完成简易双机串行通信设计,以达到理论与实践更好的结合、…