1.问题
bootloader写成后,需要处理传送过来的.Hex,或者等价的文件的烧录。MicroChip官方并未给出一个.Hex转为.Bin的处理策略。在它的Bootloader代码中,我们可以大致看到它的实现机制,它定义了一组命令。上位机在处理升级时,与Bootloader要通过这组指令进行信息传递。这是一个交互式的协议。如果我们抛开这个交互协议,希望自己处理升级过程,那么,需要对.Hex文件本身进行处理。然后才能让bootloader解析。
2.分析
2.1 Hex文件格式
Microchip给出了这个定义,参见:
MPLAB® IPE Intel HEX File Format | Microchip TechnologyLearn what records are contained within a hex file and where the records are within the file. https://www.microchip.com/en-us/education/developer-help/learn-tools-software/mcu-mpu/mplab-ipe/sqtp-file-format-specification/intel-hex
重点如下:
- Hex文件被拆分为一条条记录。
- 每条记录头部固定,分别是
- Byte0: ":"
- Byte1: 数据字段长度
- Byte2-3:数据要放置到的内存区域地址:两字节,这是英特尔固有的格式MSB,高位前,注意。
- Byte4:这是类型,对PIC而言,只有3种类型:0,1,4
- Byte5开始,是有效载荷。总长度= Byte1
- 最后一个字段是校验和,校验和从Byte1开始,至有效载荷结束。校验和要保证,从Byte1到它自身的值=0.
2.2 字节流分帧解析
//与Hex分帧相关的数据结构
#define FLASH_WRITE_BLOCK 64
#define HEX_MAX_RECORD 16
#define HEX_RECORD_STR_MAX (1+5*2+0x10*2)
struct _HexRecorderFilterObj
{
uint16_t targetAddr;
uint8_t write_block[FLASH_WRITE_BLOCK]; //注意類型
uint8_t write_block_offset;
uint8_t frame_cache[HEX_RECORD_STR_MAX+1];
uint8_t frame_cache_offset;
uint16_t debugCnt[4]; /*line, 0cnt, 1cnt, 4cnt*/
uint32_t file_len;
}hexRecorderFilterObj;
//Hex分帧数据结构初始化
void HexRecordFilter_Init(void)
{
//hexRecorderFilterObj.write_block_offset = 0;
//hexRecorderFilterObj.frame_cache_offset = 0;
memset(&hexRecorderFilterObj, 0, sizeof(hexRecorderFilterObj));
}
//辅助函数,将缓冲区中相邻的一个Hex字节读出来
//未考虑小写字符,未考虑容错
uint8_t Hex2Byte(uint8_t *buf)
{
uint8_t ret;
if((*buf>='0')&& (*buf<='9')) ret = (*buf - '0');
else ret = (*buf - 'A') + 10;
buf++;
ret<<=4;
if((*buf>='0')&& (*buf<='9')) ret |= (*buf - '0');
else ret |= (*buf - 'A')+ 10;
return ret;
}
//这里传入是已经是.hex的一个完整的帧
bool Decode_a_hex_frame(uint8_t *frame, uint32_t len)
{
//:1057E000FFFFFF00FFFFFF00FFFFFF00FFFFFF00C5
if(Hex2Byte(frame+1)*2+5*2+1 != len) return false;
hexRecorderFilterObj.file_len += len;
hexRecorderFilterObj.debugCnt[0]++;
switch(Hex2Byte(frame+7))
{
case 0x01: hexRecorderFilterObj.debugCnt[2]++; break;
case 0x04: hexRecorderFilterObj.debugCnt[3]++; break;
case 0x00: hexRecorderFilterObj.debugCnt[1]++; break;
default:
return false;
}
return true;
}
//一个统计函数
void PrintHexFileFilterDesc(void)
{
xlog("total rec:%d, f0:%d,f1:%d,f4:%d; filelen=%d\r\n",
hexRecorderFilterObj.debugCnt[0],
hexRecorderFilterObj.debugCnt[1],
hexRecorderFilterObj.debugCnt[2],
hexRecorderFilterObj.debugCnt[3],
hexRecorderFilterObj.file_len);
}
//顶层函数,传入Hex的分段读入的信息
//已适当考虑容错
HAL_StatusTypeDef HexRecordFilter(uint8_t *buf, uint32_t len)
{
//:1057F00000000000FFFF0000EE2300007F3E0000DD
do
{
if(hexRecorderFilterObj.frame_cache_offset>0)
{
if(hexRecorderFilterObj.frame_cache_offset>HEX_RECORD_STR_MAX)
{
hexRecorderFilterObj.file_len+=hexRecorderFilterObj.frame_cache_offset;
hexRecorderFilterObj.frame_cache_offset = 0;
}
else if((*buf=='\r') || (*buf == '\n'))
{
hexRecorderFilterObj.frame_cache[hexRecorderFilterObj.frame_cache_offset] = 0;
if(!Decode_a_hex_frame(hexRecorderFilterObj.frame_cache, hexRecorderFilterObj.frame_cache_offset))
{
//return HAL_ERROR;
}
hexRecorderFilterObj.file_len++;
hexRecorderFilterObj.frame_cache_offset = 0;
}
else hexRecorderFilterObj.frame_cache[hexRecorderFilterObj.frame_cache_offset++] = *buf;
}
else if(*buf == ':')
{
hexRecorderFilterObj.frame_cache[0] = *buf;
hexRecorderFilterObj.frame_cache_offset = 1;
}
else
{
hexRecorderFilterObj.file_len++;
}
buf++;
len--;
}while(len>0);
return HAL_OK;
}
3.实现结果
total rec:292, f0:289,f1:1,f4:2; filelen=13056
实际的文件: filelen:12956字节。记录292行。无误。