2541 OTA固件升级。

 

 第一步:OTA升级原理解释

        TI官方WIKI详细介绍

        http://processors.wiki.ti.com/index.php/OAD

 1       解释:
 2       第一步:红色方框 1 Boot就像PC的BIOS,负责选择要运行的Image,是Image-A,还是Image-B.就像PC装了双系统,选择启动哪一个系统。Boot程序需要额外烧录。
 3 
 4       第二步:红色方框 2Boot会首先判断Image-B是否存在,如果存在则直接运行Image-B,绿色圆圈 5;如果不存在,红色方框 3检测ImageA是否存在,如果存在,则直接运行Image-A,绿色圆圈5;
 5 
 6       第三步:如果都不存在,则进入PM3模式,也即休眠模式。
 7
 8        伪代码实现如下:
 9          unsigned char image = boot_get_image_Type();
10          if(image == 'B')
11          {
12             Jump(B);
13          }
14          else if(image == 'A')
15        {
16             Jump(A);
17          }
18          else 
19          {
20            Jump(PM3);
21          }

 

 

 

 

第二步:校验源码分析

     (注:协议栈版本 v1.4,需要懂BLE相关知识)

     OAD升级的关键就是获取目前正在运行的是Image,然后升级不同的Image.这一步就是固件校验部分。

           ***如果正在运行的是Image-A,则升级Image-B;

           ***如果正在运行的是Image-B,则升级Image-A//协议栈Oad_target.c文件源码

//主机在升级之前,需要发送要升级固件的"版本" "类型"和"大小(其实是固定的124k,后面会讲到)"。BLE外设收到数据会回调到下面的函数
static bStatus_t oadImgIdentifyWrite( uint16 connHandle, uint8 *pValue ) { img_hdr_t rxHdr;。//存储要升级固件的数据 img_hdr_t ImgHdr;//存储目前运行固件的信息
 //前两个字节的数据是要升级固件的版本号和类型。Byte2和Byte3是要升级固件的大小(124k) rxHdr.ver = BUILD_UINT16( pValue[0], pValue[1] );
rxHdr.len = BUILD_UINT16( pValue[2], pValue[3] ); (void)osal_memcpy(rxHdr.uid, pValue+4, sizeof(rxHdr.uid)); //读取Flash中目前运行固件的信息
HalFlashRead(OAD_IMG_R_PAGE, OAD_IMG_HDR_OSET, (uint8 *)&ImgHdr, sizeof(img_hdr_t));
//OAD 16个Byte为一块,算出一共要升级多少块数据,这个数据非常有用,因为下面升级的时候,会做二次校验 oadBlkTot = rxHdr.len / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE);
//#define OAD_IMG_ID( ver )    ( (ver) & 0x01 )
// OAD_IMG_VER( OAD_IMAGE_VERSION ), // 15-bit Version #, left-shifted 1; OR with Image-B/Not-A bit.
//上面的代码是从源码中复制的,注意阴影部分说的很清楚,高15bit位版本号,最后一位是判断Image-A还是Image-B if ( (OAD_IMG_ID( ImgHdr.ver ) != OAD_IMG_ID( rxHdr.ver )) && // TBD: add customer criteria for initiating OAD here. (oadBlkTot <= OAD_BLOCK_MAX) && (oadBlkTot != 0) ) {//只针对Image做了判断,只要不是相同的Image,并且升级的块不等于0,小于最大的升级块就可以
oadBlkNum = 0; oadImgBlockReq(connHandle, 0);
//当通过校验,就会发个0给数据传输通道,同时0也表示请求发送第0块要升级的数据 } else { oadImgIdentifyReq(connHandle, &ImgHdr);
//如果没有通过校验,那么会发送当前运行固件的信息, } return ( SUCCESS ); }

结论:
1.TI官方软件的升级也是必须要经过以上的校验,至于如何做的,不再介绍,串口打日志是可以得到结果的。
2.如果我们写自己的App,我的想法是发送0xffffffff,给要升级的固件,此时由于是错误的升级信息,那么BLE外设一定会回复目前正在运行固件的信息,我们获得正在运行固件的信息,就可以做出相应的选择了。
3.如果不想让TI官方的App升级我们的固件,只需要加一些判断标志位即可。

 

第三步:升级源码分析

     当数据传输通道收到数据0,则证明通过校验,也表示可以发送第0帧要升级的数据,数据升级的回调函数如下。

     升级是以序列块的形式发送的,App每次需要发送18字节的数据,前两个字节的数据是序列,后16字节的是要升级的数据。

 

 1 static bStatus_t oadImgBlockWrite( uint16 connHandle, uint8 *pValue )
 2 {
//收到数据的序列,取前两个字节 3 uint16 blkNum = BUILD_UINT16( pValue[0], pValue[1] ); 4 5 // make sure this is the image we're expecting 6 if ( blkNum == 0 ) 7 {//第一块数据非常关键,要说明的是数据的序列是程序里手动加上的,而数据则是直接读取bin文件获得的。bin文件里面保存了字节 的一些信息,这些信息被2541收到之后,会做二次校验 8 img_hdr_t ImgHdr; 9 uint16 ver = BUILD_UINT16( pValue[6], pValue[7] ); 10 uint16 blkTot = BUILD_UINT16( pValue[8], pValue[9] ) / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE); 11
//再次读取Flash中存储的信息, 12 HalFlashRead(OAD_IMG_R_PAGE, OAD_IMG_HDR_OSET, (uint8 *)&ImgHdr, sizeof(img_hdr_t)); 13 //对比第二步存储的数据和从Flash中读取的数据,如果错误,就返回 14 if ( ( oadBlkNum != blkNum ) || 15 ( oadBlkTot != blkTot ) || 16 ( OAD_IMG_ID( ImgHdr.ver ) == OAD_IMG_ID( ver ) ) ) 17 {// 18 return ( ATT_ERR_WRITE_NOT_PERMITTED ); 19 } 20 } 21 //如果收到的序列是对的,就是说我要升级第三块数据,那么收到的就是第三块数据 22 if (oadBlkNum == blkNum) 23 { 24 uint16 addr = oadBlkNum * (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE) + 25 (OAD_IMG_D_PAGE * OAD_FLASH_PAGE_MULT); 26 oadBlkNum++; 27 28 #if defined FEATURE_OAD_SECURE 29 if (blkNum == 0) 30 { 31 // Stop attack with crc0==crc1 by forcing crc1=0xffff. 32 pValue[4] = 0xFF; 33 pValue[5] = 0xFF; 34 } 35 #endif 36 37 #if defined HAL_IMAGE_B 38 // Skip the Image-B area which lies between the lower & upper Image-A parts. 39 if (addr >= (OAD_IMG_B_PAGE * OAD_FLASH_PAGE_MULT)) 40 { 41 addr += OAD_IMG_B_AREA * OAD_FLASH_PAGE_MULT; 42 } 43 #endif 44 if ((addr % OAD_FLASH_PAGE_MULT) == 0) 45 { 46 HalFlashErase(addr / OAD_FLASH_PAGE_MULT); 47 } 48 //将要升级的数据写入Flash 49 HalFlashWrite(addr, pValue+2, (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE)); 50 } 51 //如果数据升级完成,就是说所有的块都发完了 52 if (oadBlkNum == oadBlkTot) // If the OAD Image is complete. 53 { 54 #if defined FEATURE_OAD_SECURE 55 HAL_SYSTEM_RESET(); // Only the secure OAD boot loader has the security key to decrypt. 56 #else//校验数据 57 if (checkDL()) 58 { 59 #if !defined HAL_IMAGE_A 60 // The BIM always checks for a valid Image-B before Image-A, 61 // so Image-A never has to invalidate itself. 62 uint16 crc[2] = { 0x0000, 0xFFFF }; 63 uint16 addr = OAD_IMG_R_PAGE * OAD_FLASH_PAGE_MULT + OAD_IMG_CRC_OSET / HAL_FLASH_WORD_SIZE; 64 HalFlashWrite(addr, (uint8 *)crc, 1); 65 #endif//重启 66 HAL_SYSTEM_RESET(); 67 } 68 #endif 69 } 70 else // Request the next OAD Image block. 71 {
//上面的注释写到很清楚,发送要升级数据块的序列 72 oadImgBlockReq(connHandle, oadBlkNum); 73 } 74 75 return ( SUCCESS ); 76 }

结论:
1.由于已经通过身份校验,App直接读取要升级的bin文件即可,在这里要注意的是,固件类型Image-A还是Image-B,以及升级固件的大小一定不可以搞错。
2.在写数据到Flash涉及到更加底层的操作,具体的就不在说明。

 

第四步:bin文件分析

所有生成的bin文件的大小都是124k,如下图,
 第一个问题,在上面的第三步会对第0块数据做判断,那么第0块数据里面都有什么数据呢?
打开bin文件

第三步代码中的第9行
uint16 ver = BUILD_UINT16( pValue[6], pValue[7] );如果去掉加的序列,那么 0000 就是版本号和类型,007C就是要发送块的大小

第二个问题,bin文件的内容是我全部的应用的数据吗?答案:否,非应用的数据全部用FFFF填充。
但是我们升级的时候,还是要升级124数据的

数据块大小:124K * 1024 /(16字节) = 7936块数据,下面的截图也印证了这一点。


到此,所有的分析都已经完成了,下面是我总结BLE升级时候的流程图和一些数据。

 

 

  

 

 一:通信UUID:

   二:OTA升级流程图

          主要分为两个部分:

                1.身份校验,APP要发送要升级古剑的大小和类型;

                2.传输要升级的数据.

 

 

  

posted on 2016-05-30 09:53  qglddh  阅读(529)  评论(0编辑  收藏  举报

导航