基于IAP和网口升级固件
一、 需求引入
现有嵌入式设备:基于ARM Cortex-M3处理器、带以太网通讯功能。
为减少设备维护成本节省宝贵的时间和金钱,须要设计网口升级固件功能。
本文描写叙述了基于IAP和网口升级该嵌入式系统的方法,当中处理器为NXP公司的ARM Cortex-M3,开发环境为IAR Embedded Workbench for ARM。
IAP( In Applicatin Programming)在应用编程,一般指MCU能够通过通信port(UART口、网口等)从外部接收应用程序镜像并烧录到FLASH中实现固件升级。
图1网口升级固件
二、 原理介绍
先温习下小知识:一个典型的嵌入式软件地址空间如图2所看到的。程序代码(RO段)和初始化数据(RW段)都是存储在ROM(常见为FLASH)中,当系统上电执行时,BootLoader程序会把RW段数据从ROM中复制到RAM中(.data),同一时候它会清零未初始化数据段(.bss),设置栈(.stack)和堆(.heap),之后系统就能够正常执行了。
这里会有2个疑问:为什么要拷贝RW段呢?还有BootLoader程序是什么?第一个问题非常简单,既然是RW(Read&Write)数据。那就仅仅能保存在RAM中。由于ROM是无法运行“写数据”的操作。第二个问题非常easy让程序猿忽视。它的实现有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代码呢?事实上非常easy,由于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分片写镜像文件
附录:源码清单
限于篇幅源码另行打包,位于“上传资源”之中。
作者简单介绍:
蒋俊,男。硕士研究生,现任长沙市锐米通信科技有限公司CEO。
从事通信研究与嵌入式开发10年,主攻微功率无线网络。
精通LoRa无线扩频通信,无线星型/树型/MESH网络设计;
通晓Contiki,Linux,uC/OS-II,OSAL等操作系统。
熟悉ARM,DSP,STM8,PIC,PC104等处理器;
擅长AD,RF等集成IC开发。
Web: www.rimelink.com
EMail: jiangjunjie_2005@126.com
QQ群:35212129