BIM工程代码分析

BIM工程代码分析

凡是涉及到升级功能的,都一定会有一段BootLoader代码!BLE的空中升级自然也不例外,它的BootLoader工程为BLE-CC254x-1.3.2\ble\util\BIM目录下的BIM(Image Boot Manager)工程。BIM工程管理着两段代码空间,分别是ImageA与ImageB的这两段固件运行空间,它默认会先运行ImageB的代码,如发现ImageB不存在或无效,再去运行ImageA的代码,如发现ImageA不存在或无效,则让系统进入睡眠状态。下面就来分析下BIM的工程代码。

1、固件文件相关定义

(1)ImageA在flash空间的布局

  a.ImageA的起始位置为flash的page1。

#define BIM_IMG_A_PAGE 1

  b.分配给ImageA的空间大小为62个页。

#define BIM_IMG_A_AREA 62

(2)ImageB在flash空间的布局

  a.ImageB的起始位置为flash的page8。

#define BIM_IMG_B_PAGE 8

  b.分配给ImageB的空间大小为62个页。

#define BIM_IMG_B_AREA (124 - BIM_IMG_A_AREA)

(3)CRC及固件信息首部在页中的偏移

  a.CRC在页中的偏移

#define BIM_CRC_OSET 0x00

  b.固件首部在页中的偏移

#define BIM_HDR_OSET 0x00

2.固件信息首部结构定义

typedef struct {

  uint16 crc0;       /*CRC must not be 0x0000 or 0xFFFF*/

  uint16 crc1;       /*CRC-shadow must be 0xFFFF.*/

  uint16 ver;          /*User-defined Image Version Number*/

  uint16 len;          /*Image length in 4-byte blocks*/ 

  uint8  uid[4];       /*User-defined Image Identification bytes.*/

  uint8  res[4];     /*Reserved space for future use.*、

} img_hdr_t;

其中crc0实际上不属于固件信息的首部,但是由于它的位置紧挨着固件首部的crc1,为了代码方便,这才将它包含进来。crc0是独立存在的,由编译器编译时计算得到的。在编译固件ImageA与ImageB工程时,在对应的链接文件cc254x_f256_imgA.xcl与cc254x_f256_imgB.xcl的最后分别有一句:-J2,crc=8005,0804-_BANK7_END和-J2,crc=8005,4004-_BANK4_END,这两句表示在编译这两个固件工程时计算固件的CRC16值,这里的crc0就是用于保存编译器计算得到的crc值。

crc1才是真正属于固件信息首部的crc成员,它是在代码中计算的,通过比较由代码中计算得到的crc1与由编译器得到的crc0,就可以判断当前固件是否有效。

ver用于保存当前固件的版本,在空中升级时,就是判断ver来决定是否要升级。

len用于保存固件的大小,需要注意的是,它以字HAL_FLASH_WORD_SIZE为单位。

uid[4]用来标志固件身份,如对于ImageA,用uid[4]={’A’,’A’,’A’,’A’}来标志ImageA的身份;对于ImageB来说,用uid[4]={’B’,’B’,’B’,’B’}来标志ImageB的身份。

res[4]保留给以后拓展用。

3、定义缓冲数组pgbuf,可以用于缓冲一个页的数据

__no_init uint8 pgBuf[HAL_FLASH_PAGE_SIZE];

4、定义用于保存当前运行固件种类的变量

__no_init __data uint8 JumpToImageAorB @ 0x09;

当JumpToImageAorB = 0时,表示当前正运行ImageA代码。

当JumpToImageAorB = 1时,表示当前正运行ImageB代码。

注意:__no_init __data表示不让编译器布局这个变量位置;而@ 0x09则指定这个变量放到IDATA的0x09地址处。

 

halSleepExec()  让系统进入睡眠

在这函数之前,有句#pragma optimize=none表示不对这个函数进行优化。要进入睡眠很简单,只要设置电源的供电模式寄存器PCON强让设备进入睡眠模式。

PCON = 0x01;

 

crcCalc() 计算crc值

参数:

page-要计算的起始页

1、读取起始页的数据,因为这个页包含固件信息的首部。

HalFlashRead(page, 0, pgBuf, HAL_FLASH_PAGE_SIZE);

2、读取固件信息首部

const img_hdr_t *pImgHdr = (const img_hdr_t *)(pgBuf + BIM_HDR_OSET);

3、获取固件的结束位置

(1)如果是ImageB

uint8 pageBeg = page;

uint8 pageEnd = pImgHdr->len / (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE); /*固件所占的页个数*/

uint16 osetEnd = (pImgHdr->len - (pageEnd * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE)))*HAL_FLASH_WORD_SIZE;/*结束位置在最后一页的偏移*/

pageEnd += pageBeg;/*结束位置所在页*/

(2)如果是ImageA,则还需要跳过ImageB的区域。

if (pageBeg == BIM_IMG_A_PAGE)

{

  pageEnd += BIM_IMG_B_AREA;

}

4、配置随机数发生器为计算CRC用

ADCCON1 &= 0xF3; /*随机数发生器配置成16为的线性反馈以为计寄存器(LFSR)*/

RNDL = 0x00;

RNDL = 0x00;/*通过连续两次写入RNDL寄存器来产生CRC需要的种子数*/

5、开始计算固件的 CRC值。

(1)读取每个页的值,写入RNDH开始计算

for (uint16 oset = 0; oset < HAL_FLASH_PAGE_SIZE; oset++)

{

...

RNDH = pgBuf[oset];

}

HalFlashRead(page, 0, pgBuf, HAL_FLASH_PAGE_SIZE);

(2)开始计算时,跳过固件首部与CRC相关的4个字节(上面的crc0与crc1)

if ((page == pageBeg) && (oset == BIM_CRC_OSET))

{

  oset += 3;  // Skip the CRC and shadow.

}

(3)如果到了结束为止在,则计算出最后的CRC16的值

else if ((page == pageEnd) && (oset == osetEnd))

{

  uint16 crc = RNDH;

  crc = (crc << 8) | RNDL;

  return crc;

}

(4)如果在计算ImageA的CRC时,需要跳过中间的ImageB空间,在继续继续计算下去。

if (++page == BIM_IMG_B_PAGE)

{

  page += BIM_IMG_B_AREA;

}

注意:对于ImageA,由于它分成了两部分,所以需要特别处理;而ImageB位于ImageA两部分中间的连续空间中,不需要做什么特殊处理。ImageA与ImageB的布局如下:

BLE空中升级——BIM工程代码分析 - ziye334 - ziye334的博客


crcCheck() 检验固件的CRC是否有效

参数:

page-固件的起始页

crc-传递过来的crc值

1、设置DMA通道0,用做烧写flash用

HAL_DMA_SET_ADDR_DESC0(&dmaCh0);

2、计算固件的CRC值,并与编译器得到的CRC值比较,如果相同,则将计算好的CRC值写到flash空间中去,并复位。

if (crc[0] == crcCalc(page))

{

  uint16 addr = page * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE) +BIM_CRC_OSET / HAL_FLASH_WORD_SIZE;

  crc[1] = crc[0];

  crc[0] = 0xFFFF;

  HalFlashWrite(addr, (uint8 *)crc, 1);

  HAL_SYSTEM_RESET();

}

 

main() 主函数

1、先运行ImageB的代码

(1)获取ImageB的CRC值

HalFlashRead(BIM_IMG_B_PAGE, BIM_CRC_OSET, (uint8 *)crc, 4);

(2)判断ImageB的CRC是否正确

if ((crc[0] != 0xFFFF) && (crc[0] != 0x0000))

if (crc[0] == crc[1])

(3)如果CRC检验正确,则跳转到Image程序入口处执行,并设置标志位。

JumpToImageAorB = 1;

asm("LJMP 0x4030");

HAL_SYSTEM_RESET(); /*不能执行到这句*/

(4)如果ImageB得到CRC未校验,则开始计算CRC值。

else if (crc[1] == 0xFFFF)

{

crcCheck(BIM_IMG_B_PAGE, crc);

}

2、ImageB不存在或无效,再运行ImageA的代码。

(1)获取的ImageA的CRC值

HalFlashRead(BIM_IMG_A_PAGE, BIM_CRC_OSET, (uint8 *)crc, 4);

(2)判断ImageA的CRC值是否有效

if ((crc[0] != 0xFFFF) && (crc[0] != 0x0000))

  if (crc[0] == crc[1])

(3)如果CRC检验正确,则跳到ImageA代码入口处执行,并设置标志位。

JumpToImageAorB = 0;

asm("LJMP 0x4030");

HAL_SYSTEM_RESET(); /*不能执行到这句*/

(4)如果ImageA的CRC未检验,则开始计算CRC值。

else if (crc[1] == 0xFFFF)

{

crcCheck(BIM_IMG_A_PAGE, crc);

}

3、如ImageA与ImageB都无效,则进入睡眠,以节省电量。

SLEEPCMD |= 0x03; /*设置供电模式3,PM3*/

halSleepExec();/*进入睡眠*/

HAL_SYSTEM_RESET(); /*不应执行到该处*/

 


在理解BIM工程代码时,可能会有很多容易理解错误或理解不透的地方。下面给出我理解遇到的两个误区。


1、crcCalc()函数中,在计算ImageA的结束位置时,需要跳过ImageB的空间。如果固件的大小pImgHdr->len小于7个page时,即ImageA代码空间小于原先分配给ImageA-Part1的7个页空间,这时还要跳过ImageB空间(pageEnd += BIM_IMG_B_AREA;),那岂不是要占用ImageB空间了。

其实这种情况是不会发生的,ImageA的大小肯定超过7个page空间,因为Image包含的关于蓝牙协议部分代码就不止7个页大小了,所以这里就直接忽略这种情况,也就没有做任何预防处理了。


2、在crcCheck()中,如果固件的校验值有效,则写入flash空间:

crc[1] = crc[0];

crc[0] = 0xFFFF;

HalFlashWrite(addr, (uint8 *)crc, 1);

这里将crc0 = 0xFFFF烧写进flash空间中,那岂不是要破坏掉原来由编译器计算得到的CRC值了?

举例说,在校验ImageA时,flash的0x0800-0x0801保存着ImageA编译时计算得到的CRC值,0x0802-0x0803保存着由BIM工程调用crcCheck(BIM_IMG_A_PAGE, crc)计算得到的CRC值。在校验前,flash中CRC的值如下:

BLE空中升级——BIM工程代码分析 - ziye334 - ziye334的博客

在调用crcChanck()后,crc在flash的内容变成:ff ff d1 cf。这是种误区,要知道要写flash可以按字为单位写,但是已经写过的空间除非擦除过,否则就不能在重新写入。这里就是这样,因为原先crc0所在的flash位置已经被写如果,所以这里再将0xffff写入不会改变原先的值,所以调用crcChanck()后crc在flash中的值应该是:

BLE空中升级——BIM工程代码分析 - ziye334 - ziye334的博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值