背景
由于项目应用(服务器-APP-下位机)中,
1)服务器限制只能上传hex文件
2)APP中通过应用读取的数据为bin文件
所以需要APP中将bin文件转成hex文件,,正好做个bin转hex的功能
注:应用读取的bin文件实际是MCU的存放在Flash中的数据
转换逻辑
Hex文件格式:(以下对着hex文件一看便知)
【1】Hex是文本文件,所有数据都是以字符串方式读取
【2】文件有若干行字符串数据,每行都采用如下的固定格式:
MARK | LEN | OFFSET | TYPE | DATA | CHKSUM
【2.1】MARK 即起始符,以“:”开始,以"\r\n"结尾
【2.2】LEN,即本行有效数据的长度
【2.3】OFFSET,即地址偏移量
【2.4】TYPE,即类型,
Hex文件起源于英特尔定义的Inter Hex文件,每行数据被称为一条数据记录,TYPE也就是本行数据记录的类型
04,表示扩展地址记录
00,表示数据记录,真正的有效数据
01,文件结束标志
【2.5】DATA,有效数据
【2.6】CHKSUM,单字节的校验和
我们可以任意打开一个keil工程生成的hex文件进行查看:
基本只用了3种数据记录类型:
开始是04写地址,然后是00写数据,00写满65536字节时 - 地址递增重新写地址然后继续写数据,最后是01结束。
我们就按照这种逻辑实现bin转hex。
实现方法
1、获取bin文件路径,新建一个相同文件名的Hex文件
string fileName = System.IO.Path.GetFileName(path);
string write_path = path.Replace(".bin", ".hex");
2、用FileStream读取bin文件数据,用StreamWriter写字符串到Hex文件
byte[] read_buf = new byte[16];
int read_num, offset = 0;
FileStream f_read = File.OpenRead(path);//打开文件 准备读取
StreamWriter s_write = File.CreateText(write_path);//新建文件 进行写入
//写入首地址
//例如芯片为XMC4500 存放数据的地址为0x0C08 0000,则写入地址为0C08,也即地址的地址单位是0x10000 = 65536个字节
s_write.Write(WriteExternAddr(address));
//读取数据,每次读取16个字节,读不够16字节表明文件读完了
read_num = f_read.Read(read_buf, 0, 16);
while (read_num == 16)
{
//将读取到的数据和索引组成数据记录写入文件
s_write.Write(WriteDataRecord((UInt16)offset, read_buf, read_num));
offset += 16;
if (offset >= 65536)
{
offset = 0;
address += 1;
//读取超过65536字节,地址递增,索引置0,写入地址记录
s_write.Write(WriteExternAddr(address));
}
//继续读取
read_num = f_read.Read(read_buf, 0, 16);
}
//写入剩余字节
if (read_num > 0)
{
s_write.Write(WriteDataRecord((UInt16)offset, read_buf, read_num));
}
//写入结束记录
s_write.Write(WriteEndRecord());
s_write.Flush();
//关闭文件
s_write.Close();
f_read.Close();
3、数据记录的写入接口实现
1)写入地址记录
如下图,“ :020000040801F1 ” 即地址记录
:开头
02 表示有效长度为2 对应数据 0801
0000 地址记录中索引固定为 0000
04 即类型,表示本条为地址记录
0801 有效数据 表示地址为0x0801 0000
F1 为校验值,计算方法如下:(hex文件每行的校验值的计算方法都是如此)
Sum = 02 + 04 + 08 + 01 = 0x0F
Sum = 0xFF - Sum + 1 = 0xF1
如下是实现2章节代码里的方法
简单总结:
1、写地址记录
可变参数只有地址,传参为无符号16位类型
2、写数据记录
传参有 有效数据长度 、偏移量 和有效数据
3、写结束记录
完全固定
private static string WriteExternAddr(UInt16 address)//写扩展地址记录
{
byte sum = (byte)(2 + 4 + address / 256 + address % 256);
sum = (byte)(0xff - sum);
sum = (byte)(sum + 1);
string ret = ":02000004" + address.ToString("X4") + sum.ToString("X2") + "\r\n";
return ret;
}
private static string WriteDataRecord(UInt16 offset, byte[] bytes, int length)//写数据记录
{
string ret = ":" + length.ToString("X2") + offset.ToString("X4") + "00";
//byte sum = (byte)(length + offset / 256 + offset % 256);
byte sum = (byte)(length);
sum += (byte)(offset >> 8);
sum += (byte)(offset & 0xFF);
for (int i = 0; i < length; i++)
{
sum += bytes[i];
ret += bytes[i].ToString("X2");
}
sum = (byte)(0xff - sum);
sum = (byte)(sum + 1);
ret += sum.ToString("X2") + "\r\n";
return ret;
}
private static string WriteEndRecord() //写终止序列
{
string ret = ":00000001FF\r\n";
return ret;
}
Over
在STM32/GD32/XMC4500这种单片机中,读取Flash原始数据,保存为bin文件。
再实现按钮和文件选择框,将该bin文件转成hex文件,和keil生产的完全一致。
但是,该例子只用于地址连续的数据,比如还有地址分段的数据或者多个bin文件或地址,组成一个hex文件,需要自己再去封装逻辑了。