标签:
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文件的大小都是124k,如下图,
打开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.传输要升级的数据.
标签:
原文地址:http://www.cnblogs.com/alin-qu/p/4976601.html