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
喜好的话,请关注鱼鹰哦,须要看前面内容的,请点击下面的链接。