一、 需求引入
现有嵌入式设备:基于arm cortex-m3处理器、带以太网通讯功能。为降低设备维护成本节省宝贵的时间和金钱,需要设计网口升级固件功能。
本文描述了基于iap和网口升级该嵌入式系统的方法,其中处理器为nxp公司的arm cortex-m3,开发环境为iar embedded workbench for arm。
iap( in applicatin programming)在应用编程,一般指mcu可以通过通信端口(uart口、网口等)从外部接收应用程序镜像并烧录到flash中实现固件升级。
图1网口升级固件
二、 原理介绍
先温习下小知识:一个典型的嵌入式软件地址空间如图2所示,程序代码(ro段)和初始化数据(rw段)都是存储在rom(常见为flash)中,当系统上电运行时,bootloader程序会把rw段数据从rom中拷贝到ram中(.data),同时它会清零未初始化数据段(.bss),设置栈(.stack)和堆(.heap),之后系统就可以正常运行了。
这里会有2个疑问:为什么要拷贝rw段呢?还有bootloader程序是什么?第一个问题很简单,既然是rw(read&write)数据,那就只能保存在ram中,因为rom是无法执行“写数据”的操作。第二个问题很容易让程序员忽视,它的实现有2种,一种是由芯片的boot代码(固化在rom中)来执行拷贝,另一种是由编译器自带的代码(如iar就有__iar_program_start函数)
图2软件地址空间
基于iap固件升级一般会把软件设计成2部分:boot和应用程序,其中boot相当于pc机的bios负责升级固件和引导应用程序,它对于用户是不可见的;应用代码就是常见的嵌入式软件。这2个软件的存储如图3所示:
图3存储地址视图
我们再一起来看看boot和app启动时序。当系统上电时,它首先从0地址找到中断向量表,取出reset_handler中断服务代码,该代码先初始化芯片(如pll和中断寄存器),然后调用bootloader代码执行搬运工作,把存储空间布置成图2右边的“运行地址视图”,之后跳转到用户代码的main()函数,此时boot软件启动完成。
boot代码开始检测是否需要升级固件,如果需要就从外部取app镜像文件并烧录flash,最后一步都是启动app软件。
那么boot代码如何启动app代码呢?其实很简单,因为app代码它自身包含中断向量表和bootloader代码,boot代码只需要告诉mcu新的中断向量表地址,然后跳转到app代码区。之后,app的bootloader会把自己的rw数据搬运到ram中,同样也会布置存储空间如图2右边所示,最终跳转到app代码的用户main()函数,这样app代码完成了启动。
三、 iap关键技术
首先,app代码需要将程序地址重定向,在本例中需要把代码地址重定向到0x0001 0000。为什么要执行如此操作呢?我们看一个实例,假设代码中有调用f1(),如果没有执行重定向,那么f1()可能被链接器分配在0x1234,当pc寄存器导入该地址并解析指令执行时,致命的错误发生了——该地址根本没有f1()代码,因为app代码是从0x0001 0000开始存储的。重定向就是告诉链接器,请从0x0001 0000开始定位程序代码,这样在本例中f1()将分配在0x0001 1234,才能正确调用f1()。
在iar环境下程序空间重定向操作如下:打开“options”-》“linker”-》“config”,点击“edit”,在弹出的窗口中设置如图4所示地址空间。
图4链接器重定向代码地址
然后,boot代码在使用外设后,一定要de-initialize该外设再启动app代码,即要让app代码认为mcu只是刚上电运行,而不是跑完一个系统再调用自己。如果boot代码没有执行该动作,当app代码运行时,mcu的外设处于不确定状态(尤其是中断未关闭),可能会带来一些预料不到的错误。
再次,boot代码操作flash是通过调用iap函数来实现的(nxp公司的iap库函数提供erase()/write()/compare()等),其中写flash函数一次操作只能接收256/512/1024/4096字节,如果不足也需要填充。
千万要注意的是调用iap函数期间中断需要特别处理,一起看看nxp官方的说明文档(原为英文,翻译如下)“当iap函数调用期间,芯片的bootloader会暂时禁止访问用户rom空间数据。用户rom空间被映射到一些配置数据区以便于iap调用。因此原来的中断向量地址没有包含正确的中断向量。所以当iap调用正在处理时如果一个中断发生,该中断将不能被正确对待同时mcu的行为是不确定的。在一些情况下,当中断不能被正确处理时mcu将会复位。”
解决该问题有2种方法:如果中断是至关重要的(任何时候都不能禁止),那么需要把中断向量表和isr重定向到sram中;另外一个简单的办法是调用iap函数之前先禁止中断,调用完成之后再使能中断。在本固件升级中,采用第二种方法,毕竟短暂地关闭中断对于本设计是可以接受的。
最后,当app代码被引导运行时,中断向量表不再位于0地址了,在本例中位于0x0001 0000,因此需要将这个新地址告诉mcu,有一专门寄存器vtor(vector table offset register)用来存储该地址。该工作必须由boot代码来完成,因为一旦跳转到app代码mcu第一件事情就是访问中断向量表。
千万小心的一个错误是:app代码不要再对vtor寄存器进行任何操作,否则mcu将因为无法访问中断向量表而紊乱。(本人就遇到这个错误,app代码中的汇编文件startup_lpc17xx.s启动时“静悄悄地”调用了nxp库函数systeminit,该函数会重置vtor导致app的中断不能使用,这个错误查了整整一天呀!)。
四、 网口下载镜像
本部分内容是偏向策略进行设计,因此很多地方值得商榷,在这里坚持的原则是——简单就是美。
首先,通信帧直接建立在802.3以太网协议上,这样保证简单化;然后,不管镜像文件实际长度,一律向1kb取整,不足则填充0;其次,因为以太网mtu为1518字节,通信帧每次传输1kb镜像文件;再次,嵌入式系统与pc机通讯采用“停等+ack”机制,即pc机只有接收到第i帧确认后才能传输第i+1帧;最后,为确保镜像文件在传输中不受损,每帧都包含crc校验码。
图5较好地描述了升级固件时pc与嵌入式设备的通讯逻辑。当设备发出握手帧连续10次无应答后,boot代码将直接引导原app程序启动,即无需升级固件;正常升级固件时,首先有3次握手,接下来是分片传输镜像文件,最后嵌入式设备会回应“升级成功”帧;如果某分片通讯时发生错误,嵌入式设备会回应“错误原因”帧,当重传达到5次仍出错时pc机需要提醒用户,最好还能说明错误原因。
图5升级固件通讯时序
嵌入式设备回应pc的数据帧直接封装字符串,这样做的好处是可以通过截取数据包查明通信内容;而pc机传输给设备的数据帧采用二进制,主要是照顾嵌入式系统较弱的计算和存储能力,该二进制通讯帧格式如图6所示。
图6二进制通讯帧格式
flash一般是由sector组成的,并且在写操作之前需要擦除该sector。本系统中使用的flash共30个sector,前16个均为4kb,后14个为32kb,为了简化设计,将boot放置在前16个sector中,共计64kb,app镜像放置在后14个32kb的sector中,共448kb。
这样安排程序依赖如下设定:镜像文件起始位置为0x0001 0000,每接收与写入flash字节数为1024,当写入flash地址为sector之首时需要擦除该分区,逻辑流程如图7所示。
图7分片写镜像文件
荣耀官方9月5日正式发布荣耀8X和8X MAX两款新机
边缘生成人工智能推理技术面临的挑战有哪些
汽轮机凝汽器结垢原因及处理方法知识详解
LED驱动电源一定需要铝电解电容?
徕卡新一代双摄面世,华为P10进阶黑白情怀
基于IAP的嵌入式系统升级方案
一文解析PLC模拟量的原理
Molex公司推出MX150L工业密封连接器系统
如何防范区块链带来的融资风险
物联网技术为制药行业带来了哪些好处
浅析HarmonyOS基于AI的通用文字识别技术
运放-3. 基本电路
电源设计过程中的EMI抑制要素讲解
农药残留检测仪的作用原理是怎样的
广东大力推动4K电视及智能交通的发展
什么存储器速度最快
基于谐波补偿的逆变器波形控制技术研究
汽车安全与保养知识:手动挡比自动挡好在哪里?
三星SK海力士加码半导体设备投资与产量,以缓解行业压力
无人机的优势以及大数据环境下的无人机技术改革分析