一、将版本号放在代码固定的地方
/* 将版本号转化为int类型,保存在.bin偏移0x1000处 */
#define APP_VERSION_ADDR (APP_FLASH_START_ADDR + 0x1000)
#define VERSION_MAKE(vh, vm, vl) ((vh << 16) | (vm << 8) | vl)
static const uint32_t version __attribute__((at(APP_VERSION_ADDR))) = VERSION_MAKE(1, 1, 2);
二、编写程序读取编译后的.bin文件,并读取到版本号
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include "stdio.h"
#include "stdint.h"
#include "string.h"
void show_usage()
{
printf("usage: rename_bin -i \"xxx.bin\" -a \"xxxx\"");
printf("\r\n");
printf("\t-i bin_file"
"\t\t input file\r\n");
printf("\t-a"
"\t\t version store addr offset \r\n");
printf("\r\n");
}
int main(int argc, char *argv[])
{
char file_name[128] = {0};
char file_name_with_version[128] = {0};
uint32_t addr = 0;
uint8_t vh = 0;
uint8_t vm = 0;
uint8_t vl = 0;
char version[128] = {0};
int fd = 0;
// test
remove("FILENAME_WITH_VERSION.txt");
if (argc != 5)
{
show_usage();
return 0;
}
/* parase input file */
if (memcmp(argv[1], "-i", strlen("-i")) != 0)
{
printf("please use -i to add input file\r\n");
show_usage();
return 0;
}
else if (strncmp(argv[2] + (strlen(argv[2]) - strlen(".bin")), ".bin", strlen(".bin") != 0))
{
printf("the input file is not a bin file\r\n");
show_usage();
return 0;
}
strncpy(file_name, argv[2], 128);
/* parase offset addr */
if (memcmp(argv[3], "-a", strlen("-a")) != 0)
{
printf("please use -a to add offset addr\r\n");
show_usage();
return 0;
}
sscanf(argv[4], "%x", &addr);
printf("offset addr is %x\r\n", addr);
/* open in file */
fd = open(file_name, O_RDWR | O_BINARY);
if (fd <= 0)
{
printf("cant open file %s, line %d\r\n", file_name, __LINE__);
return 0;
}
lseek(fd, addr, SEEK_SET); // set to version byte
read(fd, &vl, 1);
read(fd, &vm, 1);
read(fd, &vh, 1);
close(fd);
strncpy(file_name_with_version, file_name, strlen(file_name) - strlen(".bin"));
snprintf(version, 128, "V%d.%d.%d", vh, vm, vl);
strcat(file_name_with_version, "_");
strcat(file_name_with_version, version);
strcat(file_name_with_version, ".bin");
printf("rename file name %s to %s\r\n", file_name, file_name_with_version);
remove(file_name_with_version);
rename(file_name, file_name_with_version);
// 将文件名输出到FILENAME_WITH_VERSION.txt文件中,方便外部引用
int fd_file_name_with_version = -1;
fd_file_name_with_version = open("FILENAME_WITH_VERSION.txt", O_CREAT | O_RDWR | O_TRUNC | O_TEXT, 0777);
if (fd_file_name_with_version > 0)
{
printf("create FILENAME_WITH_VERSION.txt\r\n");
write(fd_file_name_with_version, file_name_with_version, strlen(file_name_with_version));
close(fd_file_name_with_version);
}
return 0;
}
编译以上代码为FilenameAddVersion.c.exe
三、编译后自动合并bootloader和App
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stdio.h"
#include "stdint.h"
#include "string.h"
uint8_t check_sum_add8(uint8_t *data_buf, uint8_t len)
{
uint8_t sum = 0;
for (uint32_t i = 0; i < len; i++)
{
sum += data_buf[i];
}
return (0x100 - sum);
}
// HEX格式:|:1|长度2|地址2|数据类型1|数据长度32|校验和1|
int32_t output_hexline_to_file(uint32_t fd, uint32_t addr, uint8_t *date_buf, uint32_t date_len, uint8_t data_type)
{
uint32_t i = 0;
uint8_t hex_buf[128] = {0};
char hex_file_buf[8] = {0};
uint8_t hex_len = 0;
hex_buf[0] = date_len;
switch (data_type)
{
case 0x00: /* 用来记录数据, HEX文件的大部分记录都是数据记录*/
hex_buf[1] = (addr >> 8) & 0xFF;
hex_buf[2] = addr & 0xFF;
hex_buf[3] = 0x00;
for (i = 0; i < date_len; i++)
{
hex_buf[4 + i] = date_buf[i];
}
hex_buf[4 + i] = check_sum_add8(hex_buf, 4 + i);
hex_len = 4 + i + 1;
break;
case 0x01: /* 用来标识文件结束,放在文件的最后, 标识HEX文件的结尾*/
hex_buf[1] = 0x00;
hex_buf[2] = 0x00;
hex_buf[3] = 0x01;
hex_buf[4] = 0xFF;
hex_len = 4 + 1;
break;
case 0x02: /* 用来标识扩展段地址的记录*/
break;
case 0x03: /* 开始段地址记录*/
break;
case 0x04: /* 用来标识扩展线性地址的记录*/
hex_buf[1] = 0x00;
hex_buf[2] = 0x00;
hex_buf[3] = 0x04;
hex_buf[4] = date_buf[1];
hex_buf[5] = date_buf[0];
hex_buf[6] = check_sum_add8(hex_buf, 6);
hex_len = 7;
break;
case 0x05: /* 开始线性地址记录*/
break;
default:
break;
}
write(fd, ":", 1);
for (i = 0; i < hex_len; i++)
{
sprintf(hex_file_buf, "%02X", hex_buf[i]);
write(fd, hex_file_buf, 2);
}
write(fd, "\n", 1);
return 0;
}
int merage_bootload_app_to_hex(char *bootload_file_name, uint32_t bootload_start_addr,
char *app_file_name, uint32_t app_start_addr, char *hex_file_name)
{
uint32_t cur_hex_addr = 0; // 当前 hex 文件地址
int32_t bootload_fd = -1;
int32_t app_fd = -1;
int32_t hex_fd = -1;
uint16_t addr_high = 0;
uint8_t read_num = 0;
uint8_t read_buf[64] = {0};
bootload_fd = open(bootload_file_name, O_RDWR | O_BINARY);
if (bootload_fd < 0)
{
printf("Open bootload file failed!\r\n");
goto __exit;
}
app_fd = open(app_file_name, O_RDWR | O_BINARY);
if (app_fd < 0)
{
printf("Open app file failed!\r\n");
goto __exit;
}
hex_fd = open(hex_file_name, O_RDWR | O_CREAT | O_TRUNC);
if (hex_fd < 0)
{
printf("Create merge hex file failed!\r\n");
goto __exit;
}
// 写入Bootload
cur_hex_addr = bootload_start_addr;
addr_high = ((cur_hex_addr >> 16) & 0xFFFF);
output_hexline_to_file(hex_fd, 0, (uint8_t *)&addr_high, 2, 0x04);
while (1)
{
// 更新标识扩展线性地址
if (((cur_hex_addr >> 16) & 0xFFFF) != addr_high)
{
addr_high = ((cur_hex_addr >> 16) & 0xFFFF);
output_hexline_to_file(hex_fd, 0, (uint8_t *)&addr_high, 2, 0x04);
}
read_num = read(bootload_fd, read_buf, 32);
if (read_num > 0)
{
output_hexline_to_file(hex_fd, cur_hex_addr & 0xFFFF, read_buf, read_num, 0x00);
cur_hex_addr += read_num;
}
else
{
break;
}
}
// 写入App
cur_hex_addr = app_start_addr;
while (1)
{
// 更新标识扩展线性地址
if (((cur_hex_addr >> 16) & 0xFFFF) != addr_high)
{
addr_high = ((cur_hex_addr >> 16) & 0xFFFF);
output_hexline_to_file(hex_fd, 0, (uint8_t *)&addr_high, 2, 0x04);
}
read_num = read(app_fd, read_buf, 32);
if (read_num > 0)
{
output_hexline_to_file(hex_fd, cur_hex_addr & 0xFFFF, read_buf, read_num, 0x00);
cur_hex_addr += read_num;
}
else
{
output_hexline_to_file(hex_fd, 0, 0, 0, 0x01);
break;
}
}
printf("Merage %s and %s to %s success!\n", bootload_file_name, app_file_name, hex_file_name);
__exit:
if (bootload_fd > 0)
{
close(bootload_fd);
}
if (hex_fd > 0)
{
close(hex_fd);
}
return 0;
}
void show_usage(void)
{
printf("usage: MergerBootloadApp2Hex path/bootloader.bin 0x08000000 path/app.bin 0x08010000");
}
int main(int argc, char const *argv[])
{
char bootload_file_name[512] = {0};
char app_file_name[512] = {0};
char hex_file_name[512] = {0};
uint32_t bootload_start_addr = 0x08000000; // bootload文件地址
uint32_t app_start_addr = 0x08010000; // app文件地址
if (argc < 5)
{
show_usage();
return 0;
}
strcpy(bootload_file_name, argv[1]);
sscanf(argv[2], "%x", &bootload_start_addr);
strcpy(app_file_name, argv[3]);
sscanf(argv[4], "%x", &app_start_addr);
strncpy(hex_file_name, app_file_name, strlen(app_file_name) - strlen(".bin"));
strcat(hex_file_name, ".hex");
printf("Bootload(%08X): %s\r\n", bootload_start_addr, bootload_file_name);
printf("App(%08X): %s\r\n", app_start_addr, app_file_name);
printf("Merge hex: %s\r\n", hex_file_name);
merage_bootload_app_to_hex(bootload_file_name, bootload_start_addr, app_file_name, app_start_addr, hex_file_name);
}
编译以上代码为MergerBootloadApp2Hex.exe
四、编写自动执行脚本
set BOOTLOAD_FILENAME=.\bootload.bin
set BOOTLOAD_ADDR=0x08000000
set APP_FILENAME=.\%1.bin
set APP_ADDR=0x08008000
set APP_VERSION_ADDR=0x1000
:: 在bin文件后添加添加版本号
.\tools\FilenameAddVersion.exe -i %APP_FILENAME% -a %APP_VERSION_ADDR%
:: 将bin文件名称读取到FILENAME_WITH_VERSION
@echo off
if not exist "FILENAME_WITH_VERSION.txt" (
@echo on
echo FILENAME_WITH_VERSION.txt not exist, return right now!
exit /b 1
)
@echo on
set /p FILENAME_WITH_VERSION=<FILENAME_WITH_VERSION.txt
:: 将app和bootload合并成一个hex文件
.\tools\MergerBootloadApp2Hex.exe %BOOTLOAD_FILENAME% %BOOTLOAD_ADDR% %FILENAME_WITH_VERSION% %APP_ADDR%
讲以上内容保存为AddVersion&MergeBootload.bat
五、在Keil执行编译后执行脚本
"AddVersion&MergeBootload.bat" @L