ZigBee OTA镜像页升级

    (转载请指明出处:http://blog.csdn.net/d4l6c8)

        最近很多人问ZigBee-OTA的镜像页升级方式如何实现,本人整ZigBee的时间也不短了,并且优化了协议栈自带的OTA升级,由ZigBee-OTA镜像页升级网上的资料很少很少,因此我决定将自己设计实现镜像页升级的方案提供出来供大家参考。


        首先分析一下镜像块升级和镜像页升级的区别,镜像页升级的可行性和升级方案。看下图,镜像块升级在协议栈上已经实现,其升级方式通过下图可以明显的看出。这是一种请求数据,获得需要数据包的可靠传输,确保每一包数据都能传输到终端。不过这种传输需要花费大量的时间在客户端想服务器发送的镜像请求上,对于有用的数据“镜像”来说耗费掉了一般的通讯量。


        因此镜像页的方式就是减少客户端请求的次数,或者增加每次镜像传输的字节数。每包数据块的长度是有一定限制的,因此我们主要从减少客户端请求来考虑。OTA镜像页传输的过程如下:


        镜像页和镜像块的主要区别就是在于客户端发送了1次请求后由服务器向上位机控制台发送多次请求数据块的命令,控制台收到命令后将数据包发送给服务器,服务器将这些数据包依次传输给客户端,有客户端将镜像存储起来。

        当然这样是会存在很多问题的,比如说客户端接收后丢失1包或者多包数据怎么办?服务器如何多次请求控制台数据,这样还能保证兼容多点升级吗?每包传输的数据长度多少,每个数据包的间隔为多少?客户端未收到重发时间及次数怎么确定?当然这些问题都是要在OTA镜像页升级中处理的,其中的一些参数我会直接通过我的测试结果给出最合理的值。 

     (转载请指明出处:http://blog.csdn.net/d4l6c8)

        对于OTA客户端,启动代码位于bank0的0x0000~0x0800地址区域,大小为2 KB。其余的254 KB的Flash空间,用来存储当前固件和其他信息。值得注意的是,0x0888~0x088B区域存放了CRC校验信息,0x088C~0x0897区域存放了PREAMBLE,包括镜像大小、制造商ID、镜像类型和镜像版本号信息。另外,bank7最后的14 KB空间(0x7C800~0x7FFFF)用作非易失性(None Volatile,NV)变量区(12 KB)和特定信息保留区(2 KB)。OTA系统升级方案有两种,分别是片内Flash升级和片外Flash升级。考虑到一般程序固件大小都超过128KB和以后程序功能升级的扩展性,本文采用片外Flash的方案。采用的片外Flash(M25PE20)容量为256 KB,通过SPI总线与CC2530之间传输数据。片内升级同样是可以的,不过片内升级OTA在处理时有一些问题没有解决,解决了这几个问题后片内升级同样支持,在本文最后我将就这几个问题阐述一下如何进行片内的OTA升级。


        镜像块请求的一个周期包括:节点发送镜像块请求,服务器收到请求后发送给控制台,控制台将请求的镜像块发送值服务器,之后服务器传送给节点,节点写入校验无误后写入Flash。这种方式是最基本的传输方式,具有很高的准确性,但是其由于其在传输过程中有很多的等待时间,因此传输效率并不高,如果充分利用传输过程中的间隙,则会大大增加无线升级的效率。

        在ZigBee协议栈上TI官方提供了镜像页传输的框架,但是没有具体的实现过程,只有镜像页的函数,因此在该基础上通过完善镜像页请求代码来实现OTA的镜像页请求升级。

        镜像块每次请求会回复一个镜像块,和镜像块请求不同,镜像页提出了页的概念,即一定数量的镜像块视为一页(该数量可调),每一次的镜像页请求,都会回复一个镜像页,即将镜像块分次数发送值节点,从而完成了镜像页的请求传输。由于镜像页请求的锐减,减少了镜像页请求的时间,并且在无线传输过程中继续想控制台索取下一包镜像块,因此大大提高了无线升级的效率。

        Z-Stack运行在一个OSAL操作系统上,OSAL是一种基于任务事件调度机制的操作系统。每个任务包含若干事件,每个事件对应一个事件号。当一个事件需要产生时,可以通过API函数设置相应的事件号,然后提交给操作系统调度触发。本文设计的镜像页请求功能正是基于这种机制。OTA服务器的镜像页请求处理流程如图5所示,OTA服务器为每一个请求更新的节点分配一个事件号,并通过请求节点的短地址索引,设置特定的事件。进入事件后,OTA服务器通过串口向OTA应用控制台请求镜像数据块,并向节点发送镜像块数据。通过把事件添加到定时器链表,就能够以响应间隔为时间单位,循环发送镜像块数据,直到累计的发送镜像块大小等于节点的请求镜像页大小,从而完成一次镜像页请求的传输过程。

        (转载请指明出处:http://blog.csdn.net/d4l6c8)

        Z-Stack协议栈有一个MAC定时器为操作系统提供计时。该定时器以每1 ms为单位,更新系统的定时器事件链表。定时器事件链表如图6所示,链表的每一个结点记录了任务号(task_id)、事件号(event_flag),计时时间(timeout)和下一个结点地址(*next)。图中的ZCL_OTA_MT_ READ n定义为每个请求节点对应的事件号,Response Spacing即为节点请求的响应间隔,把两者添加到链表当中。当计时时间减为0后,系统自动设定对应的事件号,从而使OTA服务器循环地向OTA应用控制台索取镜像块数据,并向节点发送镜像块响应。

        镜像页请求的难点在于,OTA升级支持多点同时升级,因此如果采用镜像页升级,则需要保存不同节点的信息,在节点升级完成为了节省空间需要删除该条信息,因此如何处理好节点信息,将节点信息和定时器链表以及系统任务一一对应是本次设计的难点。另外对于客户端的数据请求,在出现某个数据包校验出错的处理问题也是本设计的难点。



1、Boot Loader工作流程 

        对于基于任务事件轮询机制的Z-Stack工程,默认没有添加OTA功能。如果节点需要开启OTA功能,首先需要烧写OTA的启动代码。当节点完成镜像接收之后,对新镜像进行CRC校验,并清空当前镜像的CRC信息,然后重启。当节点重启后,首先跳转到启动代码的地址,开始执行如图1所示的工作流程。



2、OTA工作流程 

对于OTA的工作流程,可以简单地分为如下过程,首先控制台发送传输镜像的通知,ZigBee服务器收到后转发该条数据包给节点,节点收到后发送镜像请求给服务器(请求特定制造商和类型的镜像信息),之后服务器通过串口给控制台,控制台响应镜像请求(将镜像大小返回给节点),由服务器转发给节点,节点接收到该条命令后记录镜像信息并且初始化传输参数,之后便开始了镜像块的请求传输,控制台开始传送镜像块给客户端直到传输完成。这是OTA工作的大概流程,在OTA无线升级的过程中,有很多的处理例如未收到数据包的处理,写Flash失败的处理,重复收到数据包的处理等等都有对应的处理过程。下面分别简单的叙述一下客户机和服务器的工作流程。

(1)客户机的工作流程如图2所示:(忽略次要功能)


        OTA协议较OAD协议更为全面,在升级过程中有很多参数可控,升级时需要判断制造商,协议类型,协议版本,请求是否成功,请求是否为下一次需要写入的数据包等等,如果这些都不正确,则需要重新请求数据包,每次请求的数据包的写入地址用一个全局变量控制,保证每次请求的为下一次需要写入的数据包,方便扩展。同时这种设计可以避免重复升级,造成不必要的资源浪费,另外该协议还有数据包超时判断,定时重新请求数据包等异常处理功能,提高了无线升级的成功率。

        转载请指明出处:http://blog.csdn.net/d4l6c8

(2) 服务器工作流程:(忽略次要功能)

无线端


串口端


        服务器作为一个中间设备为节点和PC控制台提供服务,将接收到的节点的无线数据经过重新组织转发到串口给PC控制台,将接收到的PC控制台的串口数据经过重新组织通过无线转发给节点。在服务器的过程中还有其他处理过程没有介绍到,包括当前节点镜像属性的读取,网络发现,节点发现,网络加入,网络离开等等功能。这些功能虽不是镜像传输过程中用到的功能,但是镜像升级过程是在这些功能的基础上实现的。

  (转载请指明出处:http://blog.csdn.net/d4l6c8)

3、客户端处理流程:

首先客户端发送镜像页请求,并且保存当前请求的地址偏移AddrOffset,启动超时定时器,之后等待接收镜像数据信息,在接收信息后将数据信息写入FLASH,并且判断该镜像块是否为镜像页的最后一块,如果为最后一块,则停止超时定时器,继续发送下一次镜像页请求。如果在规定时间内还没有接收到镜像页的最后一块,则重新发送镜像页请求。(由于镜像块偏移地址为全局变量,没写一次就要移动镜像块偏移地址,因此在重新发送镜像页请求时是从最后一个镜像块偏移地址开始请求,保证了不会发生重复请求的情况。)。


4、服务器处理流程:

服务器在接收到节点的镜像页请求后,将节点发送请求的信息保存在一个全局结构体数组中(该结构体数组中每一条记录的生命周期为一个页请求周期),之后发送镜像块请求至控制台,并且启动定时器定时请求控制台镜像块数据。当收到控制台镜像块后将镜像块通过无线发送至节点。当服务器定时处理请求控制台镜像块数据时,首先计算需要请求的镜像块大小,然后判断该镜像块偏移地址是否已经超出镜像大小,如果超出,则直接返回,没有超出则向控制台请求一包镜像块,并且启动定时器请求下一包镜像块。如果本次请求为该镜像最后一块镜像块,则将结构体数组中该条信息记录清空,使得有新节点时仍可以使用该存储空间。


        由于任务事件掩码的影响,目前该OTA镜像页升级应用仅支持10个节点同时升级,多余10个节点就可能造成升级延时严重甚至出现升级失败的情况。主要原因是每个镜像页请求需要占用一个OTA任务事件,目前使用的事件掩码为16位,并且OTA任务已经使用了5个事件,因此在使用该事件可支持10个节点同时升级,如果有第11个节点,则需要在10个节点镜像页请求间隙插入到其中(每条记录的生存周期为一个镜像页请求周期)。这样会导致升级出现延时,如果是个事件持续占用事件资源,则会导致第11个节点升级超时,造成升级失败。(注:由于服务器内存分配的问题,导致申请的全局变量只能申请到5个数组,多余5个则造成内存不够的情况,目前还没有解决此问题,因此现阶段仅支持5个节点同时升级,理论上可以多余5个,不过这样会造成升级变得缓慢)

 

服务器存储节点信息的结构体如下所示:

typedef struct

{

  afAddrType_t  sourceAddress; //OTA客户端地址

  zclOTA_FileID_t  fileID;   //镜像信息:包括镜像类型、制造商与版本号

  uint32  ServerFileOffset; //服务器读取镜像的偏移量,与客户端同步

  uint16   pageSize;   //镜像页大小

  uint8   maxDataSize; //镜像块大小

  uint16  responseTime; //响应间隔时间

} zclOTA_Address_Index;   //镜像页请求属性结构体


    下面给出一组测试结果:



通过大量测试得到的OTA升级的最佳性能及参数:


  (转载请指明出处:http://blog.csdn.net/d4l6c8)

    ZigBee-OTA片内升级存在的问题,主要包括以下两个问题,解决了这两个问题便可以进行片内升级:

    1、ZigBee片内升级需要将镜像存入CC2530片内FLASH,文章刚开始分析了CC2530片内FLASH的分配情况,前2K用于Boot,最后的14 KB空间(0x7C800~0x7FFFF)用作非易失性(None Volatile,NV)变量区(12 KB)和特定信息保留区(2 KB),因此想要使用片内FLASH需使得升级镜像小于(256-2-14)/2=120K,这就要看你的精简代码的功能了;

    2、OTA的镜像头为62字节,由于OTA镜像头的问题导致OTA在操作片FLASH时会出现一些问题,OTA镜像头如下所示:

    :10E800001EF1EE0B000138000000785634120300B0

    :10E8100000000200000000000000000000000000F6

    :10E8200000000000000000000000000000000000E8

    :10E8300000000000C8D5010000008AD501000213C5

    其中红色部分为镜像头数据,包括了镜像ID,镜像版本,镜像制造商,镜像大小等等信息,该数据头有62个字节,最后空出两个字节,正是由于这个镜像头的62个字节,导致OTA使用片内FLASH进行无线升级产生错误。

    升级产生的问题如图所示:


        第一个问题精简代码后便可解决,第二个问题则要复杂一些。

        CC2530的片内FLASH读写问题:CC2530读和写每次都是读写4个字节,每次擦除一个扇区,正常情况下在读写的时候都是从4的整数倍开始读写的,这样不会产生错误,但是由于上述OTA的镜像头的出现,导致了boot在读取镜像数据的时候是从63个字节开始读取的,这样就会导致一个问题的产生,就是在跨BankBoot在读取4个字节时头两个字节在上一个Bank,后两个字节在下一个Bank,这样Boot在读取时只能读取两个字节,后两个字节由于跨Bank而导致数据丢失,因此在使用Boot下载镜像时导致了某些数据的丢失数据

        丢失的数据在60823885071618104386这四个地方,其对应的镜像位置分别为Bank30xFFFE-Bank40x0001Bank40x7FFE-Bank50x8001Bank50xFFFE-Bank60x0001Bank60x7FFE-Bank70x08001四个位置,正好是Bank的相邻位置。

        因此造成无线升级失败的根本原因就在于OTA镜像头不是4的倍数导致镜像在读写Flash时跨Bank读取致使后两个字节丢失,因此解决的方法就是在读写Flash时如果该次读写为跨Bank读写,则需要读写两次方可操作正确。




    由于镜像头62字节的原因,造成Flash读取的时候(每次4字节)Bank最后0xFFFE时,读取的4个字节在上一Bank的后两个和下一Bank的前两个。因此读取时只有前两个可以读取成功,后两个无法读取出来。因此在写Flash时写到RC的为无效的两个字节,所以镜像重启校验失败。

    经过分析后修改Boot启动代码的读写Flash驱动,经测试后OTA升级采用片内Flash无线升级成功。












展开阅读全文

没有更多推荐了,返回首页