1.0 固化固件
在开发HT46的代码时候,由于需要整合好几个固件,之前一直使用const数组的方式将其他设备的固件文件存放在STM32的固件中
const uint8_t boot_60_image[] = { ... }
但实际使用的时候用**sizeof(boot_60_image)**表示数据大小,这样就可以访问完整的数据
1.1 固化固件地址
实际在使用中经常需要修改其他版本的固件,此时需要修改就要重新编译,为了解决这个问题,使用固定的地址编译的方式来将固件编译到特定的地址:
const uint8_t BOOT_IMAGE_VER[] __attribute__((at(0x080A0004))) = { 0, 1, 14 };
const char BOOT_IMAGE_NAME[] __attribute__((at(0x080A0010))) = "RP555_BOOT_v0114_201907031.bin";
const uint8_t BOOT_IMAGE_DATA[] __attribute__((at(0x080A0100))) = { ... }
这个修改确实能将数据定位到特定的位置,也能将直接在数据中修改固件。
1.2 固化固件大小
虽然这样能替换固件,但是定数据固定到特殊的位置字后就会让固件变得比较大,原先 13xK 的固件,变成了 6xxK ,这样在下载数据的时候会升所需要的时间变长了好几倍。为了让数据地址边得紧凑,于是想到直接分析map文件:
const char boot_image_name[128] = "DTQ_RP554A_BOOT_v0113_201905291.bin";
const uint8_t boot_image_ver[3] = {0, 1, 13};
const uint32_t boot_image_size = 38896;
const uint8_t boot_image_data[1024*40] = { ... }
通过定义大的数组来定义大的数据空间,分析map文件的函数
def map_decode_image(image_tag, map_f0):
# 申明一个系统管理变量
image_tcb = OrderedDict()
tag_list = []
tag_list.append("%s_image_name" % image_tag)
tag_list.append("%s_image_ver" % image_tag)
tag_list.append("%s_image_size" % image_tag)
tag_list.append("%s_image_data" % image_tag)
# 解析map文件,获取系统常量变量地址
map_file = open(map_f0, "rU")
map_lines = map_file.readlines()
map_file.close()
for line in map_lines:
items = line.split()
if items:
if items[0] in tag_list and "Data" == items[2]:
image_tcb[items[0]] = int(items[1], 16)
return image_tcb
这样就将数据的地址分析出来了,替换数据的时候直接将数据写到特定的偏移即可;
1.3 修改bin文件数据样例
下面演示调整软件版本与设备类型数据的代码:
解析镜像信息的代码:
def map_decode_base(image_addr, map_f0, bin_f1, bin_f1_base):
# 申明一个系统管理变量
sys_tcb = OrderedDict()
# 解析map文件,获取系统常量变量地址
map_file = open(map_f0, "rU")
map_lines = map_file.readlines()
map_file.close()
for line in map_lines:
items = line.split()
if items:
if "sys_tcb_con" == items[0] and "Data" == items[2]:
sys_tcb["addr"] = int(items[1], 16) - bin_f1_base
sys_tcb["size"] = int(items[3], 10)
# 读取配置信息
pfile = open(bin_f1, "rb")
pfile.seek(sys_tcb["addr"], 0)
rdata = pfile.read(sys_tcb["size"])
# typedef struct
# {
# uint8_t dev_type; // 定义出来的不同功能的接收器
# uint8_t En_audio; // 解码芯片的电路的支持
# uint8_t hw_ver; // 硬件版本
# uint8_t port_num; // 总共的端口数目
# uint8_t hardware[30]; // 硬件版本名称
# uint8_t company[9]; // 公司名称
# uint8_t software[3]; // 软件版本
# const rfch_item_t rfch_arr[RF_CH_NUM];
# }sys_tcb_con_t;
r_pos = 0
sys_tcb["dev_type"],sys_tcb["En_audio"] = struct.unpack("BB", rdata[r_pos:r_pos+2])
image_addr["dev_type"] = sys_tcb["addr"] + r_pos
image_addr["En_audio"] = sys_tcb["addr"] + r_pos + 1
r_pos += 2
sys_tcb["hw_ver"], sys_tcb["port_num"] = struct.unpack("BB", rdata[r_pos:r_pos+2])
image_addr["hw_ver"] = sys_tcb["addr"] + r_pos
image_addr["port_num"] = sys_tcb["addr"] + r_pos + 1
r_pos += 2
sys_tcb["hardware"],sys_tcb["company"] = struct.unpack("30s9s", rdata[r_pos:r_pos+30+9])
image_addr["hardware"] = sys_tcb["addr"] + r_pos
image_addr["company"] = sys_tcb["addr"] + r_pos + 30
r_pos += 39
sys_tcb["software"] = struct.unpack("BBB", rdata[r_pos:r_pos+3])
image_addr["software"] = sys_tcb["addr"] + r_pos
pfile.close()
return sys_tcb
调整数据的代码:
# 检测是否合成新版本TX的固件
if self.image_idx1_btn.isChecked():
tmp_file = open(self.dtb_tcb["APP_NAME"].split(".")[0]+"_NEW_TX.bin", "w+b")
tmp_file.write(rdata)
tmp_file.seek(self.image_addr["dev_type"], 0)
wdata = struct.pack("B", 0x10)
tmp_file.write(wdata)
tmp_file.seek(self.image_addr["software"], 0)
wdata = struct.pack("BB", 0, 8)
tmp_file.write(wdata)
tmp_file.close()
调整的效果:
后记
在后面实际使用的时候发现,出现当后续更新的景象大小比编译默认的镜像的时候出现无法升级的问题,后来发现是编译器的原因导致,部分地方引用这个常量的时候被优化了,直接放入的常量的值
const char boot_image_name[128] = "DTQ_RP554A_BOOT_v0113_201905291.bin";
const uint8_t boot_image_ver[3] = {0, 1, 13};
// const uint32_t boot_image_size = 38896;
const uint8_t boot_image_size[4] =
{
(38896) & 0xFF,
(38896 >> 8) & 0xFF,
(38896 >> 16) & 0xFF,
(38896 >> 24) & 0xFF,
};
const uint8_t boot_image_data[1024*40] = { ... }
有地方直接访问这个常量会被编译器优化而直接使用常量而非访问这个地址,使用数组的方式存储就没有问题