1 固件升级
在一些项目交期比较急的情况下,可以先把基本功能做出来,加入固件升级的功能,后续即使发现重大bug,也不用返厂更新固件,只需要把加密固件发给客户自行更新,也可以使用物联网的方式进行远程固件升级。
2 flash划分
2.1 内部flash
以stm32f103zet6为例,内部flash的扇区大小是2kb
2.2 外部flash
整个flash作为fat32使用,以w25q128为例,有16mb的空间
3 生成加密文件
3.1 加密算法
用的是tinycrypt中的aes加密算法修改iv和key的定义
#define test_tiny_aes_iv
0123456789abcdef
#define test_tiny_aes_key
0123456789abcdef0123456789abcdef
在使用前选择是作为加密还是解密
tinycrypt_enc_init();
tinycrypt_dec_init();
3.2 压缩算法
用的是quicklz压缩算法,压缩率比较低,154kb的固件压缩之后是118kb
3.3 app再加工
在keil编译出app固件后,使用外部应用程序来对app进行压缩,再进行加密,再为整个固件增加crc32校验,外部程序在linux平台下同样适用,只需要在目标主机上编译生成目标文件即可。
3.3.1 参数检查
主要有flash总大小、flash扇区大小、bootloader大小、bootloader输入文件名、app输入文件名、输出app+crc文件名、输出app加密压缩后的文件名等
if (argc == 13) { combine_file.size_total = atoi(argv[1]); combine_file.size_sector = atoi(argv[2]); combine_file.size_bootloader = atoi(argv[3]); combine_file.check_type = atoi(argv[4]); sprintf(combine_file.fn_bootloader,%s,argv[5]); sprintf(combine_file.fn_app_in,%s,argv[6]); sprintf(combine_file.fn_app_out,%s,argv[7]); sprintf(combine_file.fn_app_mask,_upfw_app.bin,argv[7]); sprintf(combine_file.fn_product,%s,argv[8]); get_debug_switch = atoi(argv[9]); combine_file.size_app_max = atoi(argv[10]); combine_file.flash_base = atoi(argv[11]); sprintf(combine_file.fn_user,%s,argv[12]); }3.3.2 文件读取和加工
file *p_bootloader_file_source=null; file *p_app_file_source=null; file *p_app_file_out=null; file *p_app_file_out_mask=null; file *p_product_file_out=null; file *p_user_file_out=null; p_bootloader_file_source = fopen(combine_file.fn_bootloader,rb+); if (!p_bootloader_file_source) { if (get_debug_switch > 0) { printf(read bootloader file error=%s\\r\\n,combine_file.fn_bootloader); } goto error; } p_app_file_source = fopen(combine_file.fn_app_in,rb+); if (!p_app_file_source) { if (get_debug_switch > 0) { printf(p_app_file_source error=%d\\r\\n,p_app_file_source); } goto error; } //读取bootloader文件 fseek(p_bootloader_file_source, 0, seek_end); uint32_t get_bootloader_file_size = ftell(p_bootloader_file_source); if (get_debug_switch > 0) { printf(bootloader file size=%d\\r\\n,get_bootloader_file_size); } if ((0 == get_bootloader_file_size) || (get_bootloader_file_size > combine_file.size_total)) { if (get_debug_switch > 0) { printf(bootloader file size invalid=%d\\r\\n,get_bootloader_file_size); } goto error; } fseek(p_bootloader_file_source, 0l, seek_set); uint8_t *bootloader_data_buffer = (uint8_t *)malloc(sizeof(uint8_t)*get_bootloader_file_size); if (!bootloader_data_buffer) { if (get_debug_switch > 0) { printf(bootloader buffer malloc error\\r\\n); } goto error; } fread(bootloader_data_buffer,1,get_bootloader_file_size,p_bootloader_file_source); //读取app来源文件 fseek(p_app_file_source, 0, seek_end); uint32_t get_app_file_size = ftell(p_app_file_source); if (get_debug_switch > 0) { printf(file size[app]=%d\\r\\n,get_app_file_size); printf(file size[app + crc]=%d\\r\\n,get_app_file_size+4); } if ((0 == get_app_file_size) || (get_app_file_size > (combine_file.size_app_max-8))) { if (get_debug_switch > 0) { printf(app file size invalid\\r\\n); } goto error; } fseek(p_app_file_source, 0l, seek_set); uint8_t *app_data_buffer = (uint8_t *)malloc(sizeof(uint8_t)*get_app_file_size); if (!app_data_buffer) { if (get_debug_switch > 0) { printf(app buffer malloc error\\r\\n); } goto error; } fread(app_data_buffer,1,get_app_file_size,p_app_file_source); //把app内容进行校验生成带校验的app文件 uint32_t get_crc_value = crc32_customized(config_crc32_poly,config_crc32_init_value,config_crc32_out_xor,app_data_buffer,get_app_file_size); if (get_debug_switch > 0) { printf(get_crc_value = %x\\r\\n,get_crc_value); } //缓存大小是 bootloader+sector+app //新建一个bootloader大小+sector+app大小的缓存 //生产用的文件需要带crc校验和字节大小,客户用的app文件只包含crc校验没有大小,大小在升级的时候自动加上去 //后续再优化appsize和appcrc的存储位置,节省flash空间 uint32_t size_product = combine_file.size_bootloader+combine_file.size_parameter+get_app_file_size; uint8_t *product_data_buffer = (uint8_t *)malloc(sizeof(uint8_t)*(size_product)); if (get_debug_switch > 0) { printf(product file size = %d\\r\\n,size_product); } if (!product_data_buffer) { if (get_debug_switch > 0) { printf(bootloader buffer malloc error\\r\\n); } goto error; } //填充0xff printf(fill 0xff\\n); memset(product_data_buffer,0xff,size_product); printf(write bootloader\\n); //写入 bootloader memcpy(product_data_buffer,bootloader_data_buffer,get_bootloader_file_size); //加入 crc和size信息 memcpy(&product_data_buffer[combine_file.size_bootloader],&get_app_file_size,4); printf(write app\\n); //写入app memcpy(&product_data_buffer[combine_file.size_bootloader+combine_file.size_parameter],app_data_buffer,get_app_file_size); if (get_debug_switch > 0) { printf(app at=[%08x]\\r\\n,combine_file.flash_base+combine_file.size_bootloader+combine_file.size_parameter); } //输出 app_out p_app_file_out = fopen(combine_file.fn_app_out,wb+); if (!p_app_file_out) { if (get_debug_switch > 0) { printf(write app crc file error\\r\\n); } goto error; } fwrite(app_data_buffer,1,get_app_file_size,p_app_file_out); //输出 加密后的文件 uint8_t *app_mask_data_buffer = 0; printf(combine_file.check_type=[%d]\\r\\n,combine_file.check_type); p_app_file_out_mask = fopen(combine_file.fn_app_mask,wb+); if (!p_app_file_out_mask) { if (get_debug_switch > 0) { printf(write app mask file error\\r\\n); } goto error; }3.3.3 文件加密和压缩处理
if (t_crypt == combine_file.check_type) { int res = 0; uint8_t *set_data_out = 0; tinycrypt_enc_init(); uint8_t *buffer_out_temp = (uint8_t *) malloc(sizeof(uint8_t)*get_app_file_size); if (!buffer_out_temp) { printf(buffer_out_temp malloc error \\r\\n); } uint32_t size_out = 0; if (quicklz_compress_buffer(buffer_out_temp,app_data_buffer,get_app_file_size,&size_out) >= 0) { if (!size_out) { printf(size_out error \\r\\n); goto error; } set_data_out = (uint8_t *) malloc(sizeof(uint8_t)*size_out); if (!set_data_out) { printf(set_data_out malloc error \\r\\n); res = -1; goto error; } memset(buffer_data,0,compress_buffer_size); aes_en_buffer(set_data_out,buffer_out_temp,size_out); fwrite(set_data_out,1,size_out,p_app_file_out_mask); uint32_t aes_crc32 =crc32_customized(config_crc32_poly,config_crc32_init_value,config_crc32_out_xor,set_data_out,size_out); if (get_debug_switch > 0) { printf(aes_crc32 mask = %x\\r\\n,aes_crc32); } fwrite(&aes_crc32,1,4,p_app_file_out_mask); } if (buffer_out_temp) { free(buffer_out_temp); } if (set_data_out) { free(set_data_out); }}3.4 bootloader的设计
在启动后挂载文件系统,检查是否存在xxx.bin的文件,如果存在则检查文件是否合法,合法就进入解密和解压缩阶段,最后把文件的crc和size写入flash,复位跳转到app对应的地址上运行
int result = dfs_elm_mount(&g_fat,0,0); printf_app_main(result=%d\\r\\n,result); //if (0 == check_boot(config_uboot_waiting_time_s)) { uint8_t get_firmware_name[32]={0}; if (check_firmware_file(get_firmware_name)) { printf_app_main(find the firmware file name=%s\\r\\n,get_firmware_name); fresult res = f_open(&_update_file,get_firmware_name,fa_open_existing|fa_read); uint32_t get_crc_result = 0; printf_app_main(res=%d\\r\\n,res); if (fr_ok == res) { uint32_t read_bytes = 0; uint32_t total_bytes = _update_file.fsize-4; uint32_t left_bytes = total_bytes; uint32_t read_actual_bytes = 0; uint32_t index_read = 0; printf_app_main(update_file.fsize=%d\\r\\n,_update_file.fsize); printf_app_main(total_bytes=%d\\r\\n,total_bytes); /**/ if (total_bytes 0) { if (left_bytes > config_flash_sector_size) { read_bytes = config_flash_sector_size; } else { read_bytes = left_bytes; } fresult read_res = f_read(&_update_file,read_file,read_bytes,&read_actual_bytes); if (0 == read_actual_bytes) { break; } check_crypt_crc = hardware_crc32_short(read_file,read_actual_bytes); left_bytes = left_bytes - read_actual_bytes; } check_crypt_crc &= 0xffffffff; (check_crypt_crc ^= 0x00000000); printf_app_main(check_crypt_crc=%x\\r\\n,check_crypt_crc); fresult read_last_res = f_read(&_update_file,read_file,4,&read_actual_bytes); if (fr_ok == read_last_res) { uint32_t source_crc = 0; memcpy(&source_crc,read_file,4); f_close(&_update_file); memset(read_file,0,config_flash_sector_size); printf_app_main(source_crc=%x\\r\\n,source_crc); if (check_crypt_crc == source_crc) { res = f_open(&_update_file,get_firmware_name,fa_read); if (fr_ok == res) { printf_app_main(crypt_decode_part\\r\\n); if (crypt_decode_part(&_update_file,config_flash_app_address,&get_crc_result,&total_bytes) >= 0) { f_close(&_update_file); printf_app_main(crypt_decode_part after\\r\\n); printf_app_main(total_bytes=%d\\r\\n,total_bytes); printf_app_main(get_crc_result=%x\\r\\n,get_crc_result); uint8_t set_data_buffer[20]; set_data_buffer[0] = (uint8_t)(total_bytes); set_data_buffer[1] = (uint8_t)(total_bytes>>8); set_data_buffer[2] = (uint8_t)(total_bytes>>16); set_data_buffer[3] = (uint8_t)(total_bytes>>24); set_data_buffer[4] = (uint8_t)(get_crc_result); set_data_buffer[5] = (uint8_t)(get_crc_result>>8); set_data_buffer[6] = (uint8_t)(get_crc_result>>16); set_data_buffer[7] = (uint8_t)(get_crc_result>>24); memset(&set_data_buffer[8],0xff,12); uint8_t idx_check=0; printf_app_main(app info[); for (idx_check=0; idx_check 0) && (get_app_size<= config_flash_app_size)) { uint32_t read_bytes,data_offset,calc_crc_val = 0; uint32_t left_bytes = get_app_size; uint32_t flash_addr_index = config_flash_app_address; printf_app_main(left_bytes=%d\\r\\n,left_bytes); calc_crc_val = crc32_customized(config_crc32_poly,config_crc32_init_value,config_crc32_out_xor,config_flash_app_address,get_app_size); printf_app_main(calc_crc_val=%x\\r\\n,calc_crc_val); if (get_app_crc == calc_crc_val) { printf_app_main(run app\\r\\n); iap_load_app(config_flash_app_address); } } }可以加个后门,比如上电后某个脚为某个电平停留在bootloader阶段,进行ota响应。
3.5 在keil上的应用
3.5.1 准备资源
编写脚本文件另存为
firmware_update.bat
@echo offset /a config_debug_switch=1set /a config_flash_base=0x08000000set /a config_flash_sector_size=(2*1024)set /a config_flash_total_size=(512*1024)set /a config_bootloader_size=(40*1024)set /a config_app_max_size=(235*1024)set /a config_check_type=3set config_fn_bootloader=bootloader.binset config_fn_app_source=app_.binset config_fn_app_out=_upfw_.binset config_fn_product=product_.binset config_fn_user=sys.bin::call clean.batcopy %config_fn_bootloader% .\\bincopy data_crypt.exe .\\bincopy %config_fn_user% .\\bincd bin/data_crypt %config_flash_total_size% %config_flash_sector_size% %config_bootloader_size% %config_check_type% %config_fn_bootloader% %config_fn_app_source% %config_fn_app_out% %config_fn_product% %config_debug_switch% %config_app_max_size% %config_flash_base% %config_fn_user%start %~dp0\\bin3.5.2 bootloader的keil配置
在keil的user 标签中添加afterbuild/rebuild脚本
$k\\arm\\armcc\\bin\\fromelf.exe --bin --output=bin@l.bin ! l
将bootloader.bin存放到mdk项目文件目录下
3.5.3 app的keil配置
3.5.4 编译后生成以下目标文件
upfw_app.bin是压缩和加密后的文件
app .bin是未加工的应用程序
product_.bin是用于量产时的烧录文件
4 应用场景
4.1 通过电脑升级固件
枚举成大容量存储设备,通过usb线连接到电脑后会枚举出一个u盘,把_upfw_app.bin复制到u盘根目录下后重启即可
可以参考这篇文章做一个usb大容量存储设备stm32大容量存储设备
4.2 通过u盘升级
工作在usb主机模式下,
把_upfw_app.bin复制到一个u盘根目录下,插入u盘后,主机识别到u盘根目录下的文件,拷贝到文件系统,复位重启即可
4.3 通过离线烧录器烧录
在量产时可以使用合并好的未加密的product_.bin
4.4 ota升级
可通过ymodem协议把加密文件传输到文件系统根目录下,再重启即可,
可以参考涂鸦智能的串口协议的设计,可以参考rtthread自带的ymodem例子,也可以参考小米用到的xmodem来设计自己的ota
5 总结
加入ota后,分配给app的flash大小可能会少很多,把运用在实际项目上的经验复盘到自己的开发板上,总结好理论经验,多做几次实际操作,按照这样的方法,应该可以内化吸收,把经验转换成我们内在的东西,再刻意地运用在新的项目上。
做任何事应该都有方法,赚钱有方法,工作有方法,帮助他人有方法,教育孩子有方法,努力是没有用的,有了正确的方法和方向的努力才有用。
关于并网光伏逆变器的基本设计
索尼首席执行官表示不会拆分图像传感器业务
对于电动汽车的电池更换,我们该如何判断
鲲游光电AR衍射光波导产品助力MYVU智能眼镜全面量产
谷歌升级浏览器中的虹膜追踪模型
固件升级的设计
杰华特IPO:持续加码研发投入,回报成效显著
SIMATIC S7-1500 PLC S7-SCL SCL块详解
OPPO Reno6 颜值出众 个性与美感兼具
分析工业机器人存在的安全隐患
3700系列多通道数据采集解决方案的性能特点和应用
鸿蒙星河版亮相,满天星光终汇成璀璨星河
多链宇宙将如何解决区块链缩放问题
【重要】第四届深圳国际半导体展会延期通知
在项目中怎样选择超高频RFID标签?
智慧停车有怎样的发展前景
中国信通院发布“2023云计算十大关键词”
光宝表示将持续积极向多领域转型
编码标准,有必要吗
一次消谐器的优势:从经济性和适用性两个方面来分析