gd32f103vbt6 串口OTA升级5-combin部分

news2024/10/7 20:34:14

一. 简介


本文主要是bin文件的组成进行一些简单介绍,方便理解升级的过程。

二.硬件部分


2.1 rk3399cpu+gd32f103

2.2 连接方式:串口(115200,8N1)或者iic(本文没有介绍iic)

三、其他需要说明的软件部分


3.1 单片机端分两个部分:iap(用于升级)和app(自己的应用)部分(这两个部分本文不做介绍)。
3.2 linux端做一个升级的app软件,这里称为update_app,本文主要是介绍该软件。
3.3 升级单片机用的bin文件,由iap的bin与app的bin的一个组合文件。由combin.exe在keil编译时调用完成。(本文不介绍该软件)
3.4 单片机flash的分区情况
3.4.1  0 ~(0x5c00-1) : iap程序区,用于存放iap程序
3.4.2  0x5c00~(0x6000-1) : 这个1k用于存放一些标志位,以及程序的md5
3.4.3  0x6000 ~(0x13000-1): app程序区,单片机的实际功能程序区
3.4.4  0x13000 ~ (0x20000-1) : app程序下载时的缓存区,程序下载时不直接下载到app区,而是先缓存到下载区,下载成功(数据完成后,会进行md5校验)后,才会更新到app区。
 

四、combin程序部分

4.1 main函数,程序需要三个参数

参数1: iap的bin文件名

参数2:app的bin文件名

参数3:合并后的文件名。本来准备设计为可以不要第三个参数的,此时传入NULL,但是没有测试。

int main(int argc,char* argv[])
{
	char* outfilename = NULL;

	if(argc < 3){
		printf("Usage : %s <file1.bin> <file2.bin> [outfilename]\n",argv[0]);
		return -1;
	}

	if(argc >= 4)
	{
		outfilename = argv[3];
	}

	//第一个文件,与第二个文件合并,生成第三个文件,第三个可以不指定。
	combin_file(argv[1],argv[2],outfilename);

	return 0;

}

4.2 主要的combin_file函数

4.2.1 两个bin文件打开后,都进行了头部的识别工作,非bin文件应该不能正常合并。

4.2.2 这里还考虑了通用单片机合并的情况,就是对偏移的值是进行读取,并不是固定值。

4.2.3 我使用的时候都是传入了参数3的,所以如何去组合出输出文件名没有测试,可能功能不正常。这里要考虑的问题是相对路径和绝对路径,取出文件名这些,好像有点复杂。

void combin_file(char* file1,char* file2,char* outfilename)
{
	FILE *fin1,*fin2;// *fout;
    int size1 = 0,size2 = 0,total_size = 0;
    char outnamebuf[64] = {0};
    uint16_t posision = 0;   //位置
    int bw = 0;       
    int readcount = 0;
    int ret,i;
    int lcd_inch = 0;

    if(file1== NULL || file2 == NULL)
    {
    	printf("ERROR : file is NULL \n");
    	return ;
    }

    //1. 这一段是考虑没有指定输出文件名的情况,但是实际效果没有测试,可能有问题。
    //形成输出文件名
    if(outfilename == NULL)
    {
    	size1 = strlen(file1)-4;
    	if(size1 > 29)
    		size1 = 29;

    	size2 = strlen(file2)-4;
    	if(size2 > 29)
    		size2 = 29;
    	strncpy(outnamebuf,file1,size1); //不需要后缀
    	strcat(outnamebuf,"_");  //加入下划线
    	strncat(outnamebuf,file2,size2);
    	strcat(outnamebuf,".bin");
    }
    else
    {
    	size1 = strlen(outfilename);
    	if(size1 > 63)  //文件名太长了
    	{
    		size2 = size1;
    		size1 = 59;
    	}	
    	strncpy(outnamebuf,outfilename,size1);

    	if(size2 > 63)
    	{
    		strcat(outnamebuf,".bin");
    	}
    }	
    printf("outnamebuf = %s\n",outnamebuf);



    //2.得到app文件的md5值,存在全局变量中
    get_file_md5sum(file2);

    //3.打开iap的bin文件,全部读取处理
    fin1 = fopen(file1, "rb");
    if (fin1 != NULL)
    {
        /* 文件打开成功*/
        printf("open %s success\r\n",file1);
    }
    else
    {
        printf("open %s error\r\n",file1);
        return ;
    }

    //4.打开app的bin文件,全部读取处理
    fin2 = fopen(file2, "rb");
    if (fin2 != NULL)
    {
        /* 文件打开成功*/
        printf("open %s success\r\n",file2);
    }
    else
    {
    	fclose(fin1);
        printf("open %s error\r\n",file2);
        return ;
    }

    //5.得到app文件的大小
    fseek(fin2, 0, SEEK_END);
    size2 = ftell(fin2);
    
    //6.得到iap文件的大小
	fseek(fin1, 0, SEEK_END);
    size1 = ftell(fin1);
	fseek(fin1, 0, SEEK_SET); 


    printf("file2 size = %d\r\n", size2);
    total_size = size2+0x6000;  //7.需要分配的空间的大小
    char* buf = malloc(total_size);  //开一个空间
    if(buf == NULL)
    {
    	printf("error: total_size malloc %d\n",total_size);
    	fclose(fin1);
    	fclose(fin2);
    	return ;
    }

    //8.因为flash中没有数据的位置都是0xff,所以把缓存填充0xff
    memset(buf,0xff,total_size);   //先填充0xff

	
	//9.读取appbin文件的内容,先判断前8个字节的内容,是否符合bin文件的特征。
	fseek(fin2, 0, SEEK_SET);
	ret = fread(buf, 1, 8, fin2);   //从app文件中读取4个字节
	if(ret == 8)
	{
		if (((*(uint32_t*)buf) & 0xfFFE0000 ) != 0x20000000)
		{
			printf("image addr 0 != 0x20000000\n");
			printf("ERROR: bad image(app.bin)!!!!! combin cancle!!!,please check bin file!!!");
			fclose(fin1);
    		fclose(fin2);
			free(buf);
			return ;
		}
		else if(((*(uint32_t*)(buf+4)) & 0xfFFf0000 ) != 0x08000000)
		{
			printf("image  addr %#x != ApplicationAddress %#x\n",((*(uint32_t*)(buf+4)) & 0xfFFffc00 ),0x08000000);
			printf("ERROR: bad image(app.bin)!!!!! combin cancle!!!,please check again!!!");
			fclose(fin1);
    		fclose(fin2);
			free(buf);
			return ;
		}

		posision = ((*(uint16_t*)(buf+4)) & 0xfc00 );   //10.关键的一部,bin文件中可以知道app的偏移值,这里考虑的是不同单片机的偏移不同的问题
		printf("posision = %#x\n",posision);
	}
	else
	{
		printf("error: fread(buf, 1, 2, fin2)\n");
        fclose(fin1);
        fclose(fin2);
        free(buf);
		return;
	}

	//11.正常偏移是24k左右,但是综合考虑,选择了2k,怕有的程序的偏移过小。
    if(posision < 2048)
    {
        printf("ERROR: bad app image(bin)!!!!! ,please check app.bin!!!\n");
        fclose(fin1);
        fclose(fin2);
        free(buf);
        return;
    }

	//12. iap的大小超过了整个偏移区的地址
    if(size1 > posision-1024)
	{
		printf("ERROR: bad iap image(bin)!!!!! ,please check iap.bin!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
	}

	//13.偏移不是24k的情况,调整一下大小
	if(posision <= 0x6000) //
	{
		total_size = total_size - 0x6000 + posision;   //重新调整一下大小
		printf("total_size = %d\n",total_size);
	}
	else
	{
		printf("ERROR: bad app image(bin)!!!!! ,please check app.bin!!!\n");
		fclose(fin1);
        fclose(fin2);
        free(buf);
        return;
	}


	//14.文件2指针还原,指向文件开始的位置
	fseek(fin2, 0, SEEK_SET);

	//15.把iap的bin文件全部读出来
	printf("size1 = %d\n",size1);
	readcount = 0;
	do
    {
        bw = fread(buf+readcount, 1, size1-readcount, fin1);
        if(bw == 0)
        	break;
        readcount += bw;
    } while (readcount < size1);


    //16.判断bin的合法性,
    if (((*(uint32_t*)buf) & 0xfFFE0000 ) != 0x20000000)
	{
		printf("image addr 0 != 0x20000000\n");
		printf("ERROR: bad image(iap.bin)!!!!! combin cancle!!!,please check bin file!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
		return ;
	}
	else if(((*(uint32_t*)(buf+4)) & 0xfFFffc00 ) != 0x08000000)
	{
		printf("image  addr %#x != ApplicationAddress %#x\n",((*(uint32_t*)(buf+4)) & 0xfFFffc00 ),0x08000000);
		printf("ERROR: bad image(iap.bin)!!!!! combin cancle!!!,please check again!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
		return ;
	}



    //17.把app的bin文件全部读出来
    printf("size2 = %d\n",size2);
	readcount = posision;  //app的位置,在bin中已经指定
	do
    {
        bw = fread(&buf[readcount], 1, size2, fin2);
        if(bw == 0)
        	break;
        readcount += bw;
    } while (readcount-posision < size2);


    //18,缓存的特定位置加入下载标记,升级标记,文件大小和md5值
    posision -= 0x400;   //退1k字节
    printf("posion2 = %#x\n",posision);
    (*(uint32_t*)(buf+4+posision)) = size2;  //只保存app 的bin文件大小
    (*(uint16_t*)(buf+posision)) = 0xff;     //不需要升级

    //19.这个地方比较特殊,设置对不同屏幕设置不同标记,方便单片机区分屏幕
    if(strstr(file2,"old5") != NULL)
    {
    //    lcd_inch = 4;  
        (*(uint8_t*)(buf+posision-1)) = 5;
        printf("detect old5inch\n"); 
    }
    else if(strstr(file2,"7inch") != NULL)
    {
    //     lcd_inch = 5;  
         (*(uint8_t*)(buf+posision-1)) = 4; 
         printf("detect 7inch\n"); 
    }
    else if(strstr(file2,"new5") != NULL)
    {
    //    lcd_inch = 6;
        (*(uint8_t*)(buf+posision-1)) = 6; 
        printf("detect new5inch\n"); 
    }

    //20,缓存的特定位置md5值,后面以为要区分多个单片机的bin,所以不同的单片机进行了一些小的偏移
    //所以md5的位置也不会是固定的。同一个单片机肯定是固定在某个位置。
    posision += 512;   //写入md5的值。2023-06-12 hj的+8
    if(strstr(file2,"hj22134") != NULL)
        posision += 8; // hj的+8
    printf("posion3 = %#x\n",posision);
    for(i=0;i<32;i++)
    {
    	buf[i+posision] = md5_readBuf[i];
    }

    fclose(fin1);
    fclose(fin2);

    //21.将缓存的内容生成第三个文件
    fin1 = fopen(outnamebuf, "wb");
    if (fin1 != NULL)
    {
        /* 文件打开成功*/
        printf("3.open %s success \n",outnamebuf);
    }
    else
    {
        printf("3.open %s error \n",outnamebuf);
        free(buf);
        return ;
    }

    //22.写入文件
    readcount = 0;
	do
    {
        bw = fwrite(&buf[readcount], 1, total_size, fin1);
        readcount += bw;
    } while (readcount < total_size);

    fclose(fin1);
    free(buf);

    printf("combin complete!! size = %d\n",total_size);

    return;
}

 

五、总结

5.1 组合bin文件应该是一个比较简单的操作,但是说明一下,有助于理解升级过程。

5.2 该程序编译后,生成exe,由keil自动调用生成即可,比较方便。

 点击编译之后,自动就生成好了。

 5.3 全部代码如下:windows下使用mingw64编译即可

/*
* @Author: dazhi
* @Date:   2023-05-09 09:30:03
* @Last Modified by:   dazhi
* @Last Modified time: 2023-07-19 14:17:56
*
* 如果使用md5sum的命令,则在windows下编译时,无法计算md值
* 下载mingw64:
* https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-v11.0.0.zip
* D:\Programfile\mingw64\bin\gcc combin.c -o combin
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <unistd.h>
#include <string.h>
#include <libgen.h>
//#include "md5.h"

char md5_readBuf[64] = {0};


#if 1  

#define ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

/**
 * @desc: convert message and mes_bkp string into integer array and store them in w
 */
static void md5_process_part1(uint32_t *w, unsigned char *message, uint32_t *pos, uint32_t mes_len, const unsigned char *mes_bkp)
{
    uint32_t i; // used in for loop

    for(i = 0; i <= 15; i++)
    {
        int32_t count = 0;
        while(*pos < mes_len && count <= 24)
        {
            w[i] += (((uint32_t)message[*pos]) << count);
            (*pos)++;
            count += 8;
        }
        while(count <= 24)
        {
            w[i] += (((uint32_t)mes_bkp[*pos - mes_len]) << count);
            (*pos)++;
            count += 8;
        }
    }
}

/**
 * @desc: start encryption based on w
 */
static void md5_process_part2(uint32_t abcd[4], uint32_t *w, const uint32_t k[64], const uint32_t s[64])
{
    uint32_t i; // used in for loop

    uint32_t a = abcd[0];
    uint32_t b = abcd[1];
    uint32_t c = abcd[2];
    uint32_t d = abcd[3];
    uint32_t f = 0;
    uint32_t g = 0;

    for(i = 0; i < 64; i++)
    {
        if(i <= 15) //i >= 0 && 
        {
            f = (b & c) | ((~b) & d);
            g = i;
        }else if(i >= 16 && i <= 31)
        {
            f = (d & b) | ((~d) & c);
            g = (5 * i + 1) % 16;
        }else if(i >= 32 && i <= 47)
        {
            f = b ^ c ^ d;
            g = (3 * i + 5) % 16;
        }else if(i >= 48 && i <= 63)
        {
            f = c ^ (b | (~d));
            g = (7 * i) % 16;
        }
        uint32_t temp = d;
        d = c;
        c = b;
        b = ROTATELEFT((a + f + k[i] + w[g]), s[i]) + b;
        a = temp;
    }

    abcd[0] += a;
    abcd[1] += b;
    abcd[2] += c;
    abcd[3] += d;
}

static const uint32_t k_table[]={
    0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
    0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
    0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
    0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
    0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
    0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
    0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
    0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
    0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
    0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};

static const uint32_t s_table[]={
    7,12,17,22,7,12,17,22,7,12,17,22,7,
    12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
    4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
    15,21,6,10,15,21,6,10,15,21,6,10,15,21
};

int32_t cal_md5(unsigned char *result, unsigned char *data, int length){
    if (result == NULL)
    {
        return 1;
    }

    uint32_t w[16];

    uint32_t i; // used in for loop

    uint32_t mes_len = length;
    uint32_t looptimes = (mes_len + 8) / 64 + 1;
    uint32_t abcd[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};

    uint32_t pos = 0; // position pointer for message
    uint32_t bkp_len = 64 * looptimes - mes_len; // 经过计算发现不超过72

//    unsigned char *bkp_mes = (unsigned char *)calloc(1, bkp_len);
    unsigned char bkp_mes[80];
    for(int i = 0; i < 80; i++) //初始化
    {
        bkp_mes[i] = 0;
    }

    bkp_mes[0] = (unsigned char)(0x80);
    uint64_t mes_bit_len = ((uint64_t)mes_len) * 8;
    for(i = 0; i < 8; i++)
    {
        bkp_mes[bkp_len-i-1] = (unsigned char)((mes_bit_len & (0x00000000000000FF << (8 * (7 - i)))) >> (8 * (7 - i)));
    }

    for(i = 0; i < looptimes; i++)
    {
        for(int j = 0; j < 16; j++) //初始化
        {
            w[j] = 0x00000000;
        }

        md5_process_part1(w, data, &pos, mes_len, bkp_mes); // compute w

        md5_process_part2(abcd, w, k_table, s_table); // calculate md5 and store the result in abcd
    }

    for(int i = 0; i < 16; i++)
    {
        //result[i] = ((unsigned char*)abcd)[i];
		sprintf((char*)result+i*2,"%02x",((unsigned char*)abcd)[i]);   //2023-05-09 返回字符串
    }

    return 0;
}

#endif



int get_file_md5sum(const char * filename)
{
	FILE * fin1;
//	char cmd[128] = {"md5sum "};
	int size1 ;
	int ret;
	int readcount,bw;

    fin1 = fopen(filename, "rb");
    if (fin1 != NULL)
    {
        /* 文件打开成功*/
        printf("open %s success\r\n",filename);
    }
    else
    {
        printf("open %s error\r\n",filename);
        return 1;
    }

    fseek(fin1, 0, SEEK_END);
    size1 = ftell(fin1);
	fseek(fin1, 0, SEEK_SET); 


    printf("file size = %d\r\n", size1);

    char* buf = malloc(size1);  //开一个空间
    if(buf == NULL)
    {
    	printf("error: size1 malloc %d\n",size1);
    	fclose(fin1);
    	return 1;
    }


    readcount = 0;
	do
    {
        bw = fread(buf+readcount, 1, size1-readcount, fin1);
        if(bw == 0)
        	break;
        readcount += bw;
    } while (readcount < size1);

    ret = cal_md5(md5_readBuf, buf, size1);
    //md5(buf, size1, md5_readBuf);
    if(!ret) 
    	printf("cal_md5 = %s\n",md5_readBuf);


    free(buf);
    fclose(fin1);
// 	strcat(cmd,filename);

// 	filep = popen(cmd,"r");
// 	if(!filep)
// 		return -1;
//     ret = fread(md5_readBuf,32,1,filep);
 
// //    printf("get_file_md5sum = %s\n",md5_readBuf);

//     pclose(filep);

    return ret;
}





void combin_file(char* file1,char* file2,char* outfilename)
{
	FILE *fin1,*fin2;// *fout;
    int size1 = 0,size2 = 0,total_size = 0;
    char outnamebuf[64] = {0};
    uint16_t posision = 0;   //位置
    int bw = 0;       
    int readcount = 0;
    int ret,i;
    int lcd_inch = 0;

    if(file1== NULL || file2 == NULL)
    {
    	printf("ERROR : file is NULL \n");
    	return ;
    }

    //1. 这一段是考虑没有指定输出文件名的情况,但是实际效果没有测试,可能有问题。
    //形成输出文件名
    if(outfilename == NULL)
    {
    	size1 = strlen(file1)-4;
    	if(size1 > 29)
    		size1 = 29;

    	size2 = strlen(file2)-4;
    	if(size2 > 29)
    		size2 = 29;
    	strncpy(outnamebuf,file1,size1); //不需要后缀
    	strcat(outnamebuf,"_");  //加入下划线
    	strncat(outnamebuf,file2,size2);
    	strcat(outnamebuf,".bin");
    }
    else
    {
    	size1 = strlen(outfilename);
    	if(size1 > 63)  //文件名太长了
    	{
    		size2 = size1;
    		size1 = 59;
    	}	
    	strncpy(outnamebuf,outfilename,size1);

    	if(size2 > 63)
    	{
    		strcat(outnamebuf,".bin");
    	}
    }	
    printf("outnamebuf = %s\n",outnamebuf);



    //2.得到app文件的md5值,存在全局变量中
    get_file_md5sum(file2);

    //3.打开iap的bin文件,全部读取处理
    fin1 = fopen(file1, "rb");
    if (fin1 != NULL)
    {
        /* 文件打开成功*/
        printf("open %s success\r\n",file1);
    }
    else
    {
        printf("open %s error\r\n",file1);
        return ;
    }

    //4.打开app的bin文件,全部读取处理
    fin2 = fopen(file2, "rb");
    if (fin2 != NULL)
    {
        /* 文件打开成功*/
        printf("open %s success\r\n",file2);
    }
    else
    {
    	fclose(fin1);
        printf("open %s error\r\n",file2);
        return ;
    }

    //5.得到app文件的大小
    fseek(fin2, 0, SEEK_END);
    size2 = ftell(fin2);
    
    //6.得到iap文件的大小
	fseek(fin1, 0, SEEK_END);
    size1 = ftell(fin1);
	fseek(fin1, 0, SEEK_SET); 


    printf("file2 size = %d\r\n", size2);
    total_size = size2+0x6000;  //7.需要分配的空间的大小
    char* buf = malloc(total_size);  //开一个空间
    if(buf == NULL)
    {
    	printf("error: total_size malloc %d\n",total_size);
    	fclose(fin1);
    	fclose(fin2);
    	return ;
    }

    //8.因为flash中没有数据的位置都是0xff,所以把缓存填充0xff
    memset(buf,0xff,total_size);   //先填充0xff

	
	//9.读取appbin文件的内容,先判断前8个字节的内容,是否符合bin文件的特征。
	fseek(fin2, 0, SEEK_SET);
	ret = fread(buf, 1, 8, fin2);   //从app文件中读取4个字节
	if(ret == 8)
	{
		if (((*(uint32_t*)buf) & 0xfFFE0000 ) != 0x20000000)
		{
			printf("image addr 0 != 0x20000000\n");
			printf("ERROR: bad image(app.bin)!!!!! combin cancle!!!,please check bin file!!!");
			fclose(fin1);
    		fclose(fin2);
			free(buf);
			return ;
		}
		else if(((*(uint32_t*)(buf+4)) & 0xfFFf0000 ) != 0x08000000)
		{
			printf("image  addr %#x != ApplicationAddress %#x\n",((*(uint32_t*)(buf+4)) & 0xfFFffc00 ),0x08000000);
			printf("ERROR: bad image(app.bin)!!!!! combin cancle!!!,please check again!!!");
			fclose(fin1);
    		fclose(fin2);
			free(buf);
			return ;
		}

		posision = ((*(uint16_t*)(buf+4)) & 0xfc00 );   //10.关键的一部,bin文件中可以知道app的偏移值,这里考虑的是不同单片机的偏移不同的问题
		printf("posision = %#x\n",posision);
	}
	else
	{
		printf("error: fread(buf, 1, 2, fin2)\n");
        fclose(fin1);
        fclose(fin2);
        free(buf);
		return;
	}

	//11.正常偏移是24k左右,但是综合考虑,选择了2k,怕有的程序的偏移过小。
    if(posision < 2048)
    {
        printf("ERROR: bad app image(bin)!!!!! ,please check app.bin!!!\n");
        fclose(fin1);
        fclose(fin2);
        free(buf);
        return;
    }

	//12. iap的大小超过了整个偏移区的地址
    if(size1 > posision-1024)
	{
		printf("ERROR: bad iap image(bin)!!!!! ,please check iap.bin!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
	}

	//13.偏移不是24k的情况,调整一下大小
	if(posision <= 0x6000) //
	{
		total_size = total_size - 0x6000 + posision;   //重新调整一下大小
		printf("total_size = %d\n",total_size);
	}
	else
	{
		printf("ERROR: bad app image(bin)!!!!! ,please check app.bin!!!\n");
		fclose(fin1);
        fclose(fin2);
        free(buf);
        return;
	}


	//14.文件2指针还原,指向文件开始的位置
	fseek(fin2, 0, SEEK_SET);

	//15.把iap的bin文件全部读出来
	printf("size1 = %d\n",size1);
	readcount = 0;
	do
    {
        bw = fread(buf+readcount, 1, size1-readcount, fin1);
        if(bw == 0)
        	break;
        readcount += bw;
    } while (readcount < size1);


    //16.判断bin的合法性,
    if (((*(uint32_t*)buf) & 0xfFFE0000 ) != 0x20000000)
	{
		printf("image addr 0 != 0x20000000\n");
		printf("ERROR: bad image(iap.bin)!!!!! combin cancle!!!,please check bin file!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
		return ;
	}
	else if(((*(uint32_t*)(buf+4)) & 0xfFFffc00 ) != 0x08000000)
	{
		printf("image  addr %#x != ApplicationAddress %#x\n",((*(uint32_t*)(buf+4)) & 0xfFFffc00 ),0x08000000);
		printf("ERROR: bad image(iap.bin)!!!!! combin cancle!!!,please check again!!!\n");
		fclose(fin1);
		fclose(fin2);
		free(buf);
		return ;
	}



    //17.把app的bin文件全部读出来
    printf("size2 = %d\n",size2);
	readcount = posision;  //app的位置,在bin中已经指定
	do
    {
        bw = fread(&buf[readcount], 1, size2, fin2);
        if(bw == 0)
        	break;
        readcount += bw;
    } while (readcount-posision < size2);


    //18,缓存的特定位置加入下载标记,升级标记,文件大小和md5值
    posision -= 0x400;   //退1k字节
    printf("posion2 = %#x\n",posision);
    (*(uint32_t*)(buf+4+posision)) = size2;  //只保存app 的bin文件大小
    (*(uint16_t*)(buf+posision)) = 0xff;     //不需要升级

    //19.这个地方比较特殊,设置对不同屏幕设置不同标记,方便单片机区分屏幕
    if(strstr(file2,"old5") != NULL)
    {
    //    lcd_inch = 4;  
        (*(uint8_t*)(buf+posision-1)) = 5;
        printf("detect old5inch\n"); 
    }
    else if(strstr(file2,"7inch") != NULL)
    {
    //     lcd_inch = 5;  
         (*(uint8_t*)(buf+posision-1)) = 4; 
         printf("detect 7inch\n"); 
    }
    else if(strstr(file2,"new5") != NULL)
    {
    //    lcd_inch = 6;
        (*(uint8_t*)(buf+posision-1)) = 6; 
        printf("detect new5inch\n"); 
    }

    //20,缓存的特定位置md5值,后面以为要区分多个单片机的bin,所以不同的单片机进行了一些小的偏移
    //所以md5的位置也不会是固定的。同一个单片机肯定是固定在某个位置。
    posision += 512;   //写入md5的值。2023-06-12 hj的+8
    if(strstr(file2,"hj22134") != NULL)
        posision += 8; // hj的+8
    printf("posion3 = %#x\n",posision);
    for(i=0;i<32;i++)
    {
    	buf[i+posision] = md5_readBuf[i];
    }

    fclose(fin1);
    fclose(fin2);

    //21.将缓存的内容生成第三个文件
    fin1 = fopen(outnamebuf, "wb");
    if (fin1 != NULL)
    {
        /* 文件打开成功*/
        printf("3.open %s success \n",outnamebuf);
    }
    else
    {
        printf("3.open %s error \n",outnamebuf);
        free(buf);
        return ;
    }

    //22.写入文件
    readcount = 0;
	do
    {
        bw = fwrite(&buf[readcount], 1, total_size, fin1);
        readcount += bw;
    } while (readcount < total_size);

    fclose(fin1);
    free(buf);

    printf("combin complete!! size = %d\n",total_size);

    return;
}




int main(int argc,char* argv[])
{
	char* outfilename = NULL;

	if(argc < 3){
		printf("Usage : %s <file1.bin> <file2.bin> [outfilename]\n",argv[0]);
		return -1;
	}

	if(argc >= 4)
	{
		outfilename = argv[3];
	}

	//第一个文件,与第二个文件合并,生成第三个文件,第三个可以不指定。
	combin_file(argv[1],argv[2],outfilename);

	return 0;

}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/771170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

销售自动化如何提高团队生产力?从这5个方面发力

任何用于减少人工劳动和缩短销售流程相关任务时间的技术&#xff0c;都可定义为销售自动化。 对于忙碌的销售人员来说&#xff0c;流程自动化是真正的救星。它可以使他们的工作简化30%&#xff0c;让他们更专注于创收任务。这将显著提高团队的工作效率&#xff0c;并带来许多其…

第二十一章:CCNet:Criss-Cross Attention for Semantic Segmentation ——用于语义分割的交叉注意力

0.摘要 全图像依赖关系为视觉理解问题提供了有用的上下文信息。在这项工作中&#xff0c;我们提出了一种称为Criss-Cross Network&#xff08;CCNet&#xff09;的方法&#xff0c;以更有效和高效的方式获取这种上下文信息。具体而言&#xff0c;对于每个像素&#xff0c;CCNet…

JVM系列(8)——对象的内存布局

1、对象的创建过程 加载-验证-准备-解析-初始化-申请内存-成员变量赋初始值-加载构造方法。 前半段是JVM系列&#xff08;5&#xff09;——类加载过程&#xff0c;申请内存可参考&#xff1a;JVM系列&#xff08;3&#xff09;——内存分配与回收策略。 2、对象在内存中的存…

python使用Anconda安装Cartopy

安装 Cartopy的话官方推荐是使用conda安装&#xff0c;打开Anconda Prompt后&#xff0c;新建一个环境&#xff08;如果已有环境可跳过这步&#xff09;&#xff0c;然后激活环境&#xff1a; conda create -n newenv python3.9 conda activate newenv接着按照官网的推荐在Anc…

JavaScript——基础知识及使用

初识 JavaScript JavaScript (简称 JS) 是世界上最流行的编程语言之一.一个脚本语言, 通过解释器运行.主要在客户端(浏览器)上运行, 现在也可以基于 node.js 在服务器端运行. JavaScript 的能做的事情: 网页开发(更复杂的特效和用户交互)网页游戏开发服务器开发(node.js)桌…

使用Jenkins自由风格的软件项目实现接口自动化测试持续集成

这里写目录标题 一、JOB项目配置1、添加描述2、限制项目的运行节点3、源码管理4、构建触发器5、构建步骤6、构建后操作 一、JOB项目配置 1、添加描述 可选选项可填可不填 2、限制项目的运行节点 节点中要有运行环境所需的配置 节点配置教程&#xff1a;https://blog.csdn…

详解c++---特殊类设计

目录标题 设计一个不能被拷贝的类设计一个只能从堆上创建对象的类设计一个只能在栈上创建对象的类设计一个无法被继承的类什么是单例模式饿汉模式饿汉模式的缺点懒汉模式懒汉模式的优点懒汉模式的缺点特殊的懒汉 设计一个不能被拷贝的类 拷贝只会放生在两个场景中&#xff1a;…

Apache Doris (三十一):Doris 数据导入(九)Spark Load 4- 导入Hive数据及注意事项

目录 1. Spark Load导入Hive非分区表数据 2. Spark Load 导入Hive分区表数据 3. 注意事项 进入正文之前&#xff0c;欢迎订阅专题、对博文点赞、评论、收藏&#xff0c;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; 宝子们订阅、点赞、收藏不迷路&#xff01;抓紧…

这8种算法

一个程序员一生中可能会邂逅各种各样的算法&#xff0c;但总有那么几种&#xff0c;是作为一个程序员一定会遇见且大概率需要掌握的算法。今天就来聊聊这些十分重要的“必抓&#xff01;”算法吧~ 算法一&#xff1a;快速排序法 快速排序法是对冒泡排序的一种改进&#xff0c…

Spring Cloud Gateway - 新一代微服务API网关

Spring Cloud Gateway - 新一代微服务API网关 文章目录 Spring Cloud Gateway - 新一代微服务API网关1.网关介绍2.Spring Cloud Gateway介绍3.Spring Cloud Gateway的特性4.Spring Cloud Gateway的三大核心概念5.Gateway工作流程6.Gateway核心配置7.动态路由8.Predicate自定义P…

vue 集成tinymce2实现图片,视频以及文件的上传

vue 集成tinymce2实现图片&#xff0c;视频以及文件的上传 1. 安装插件 &#xff08;1&#xff09;安装tinymce npm install tinymce -S &#xff08;2&#xff09;安装tinymce-vue npm install tinymce/tinymce-vue3.0.1 -S 2. 复制静态文件到public目录 资源下载路径&…

day40-Mybatis(resultMap拓展)

0目录 Mybatis-resultMap拓展 1.2.3 1.数据库字段和javabean实体类属性不一致时 解决方案1&#xff1a;将sql语句中给予别名&#xff08;别名同javabean中实体类保持一致&#xff09; 解决方案2&#xff1a;使用resultMap 2.两表关联&#xff08;用户表和角色表关联查询&…

QGIS绘制一张地图——建立打印布局在地图中添加图例和比例尺后,将地图保存为图片(出图)

前言 本节所述内容,基于上节所绘制的北京市区地图为例,特此说明! 北京市区地图如图所示: 一、直接保存为图片 依次点击工程、导入/导出、导出地图为图片: 设置比例尺、像素等信息,点击保存: 保存出来的地图的显示区域是和QGIS中看到的地图区域一样的: 二、建立…

qiankun:react18主应用 + 微应用 react18 + vue3

一&#xff1a;主应用 搭建react项目 npx create-react-app react-qiankun-main安装Antd npm install antd –save在 index.js中引入 import { ConfigProvider } from "antd"; import zhCN from "antd/locale/zh_CN"; import "antd/dist/reset.css…

心电前置放大电路制作与原理详细分析(附电路板实物图)

1、软件平台:Multisim仿真软件、EDA原理图绘制软件、医学电子学开发平台 2、硬件平台:心电示教仪、示波器、信号发生器、除颤仪、电烙铁 3、元件清单: 实验电路图 1、心电放大器原理图 2、50Hz双T陷波滤波器原理图 原理解释与计算 (1)一级放大电路 一级放大电路由…

【PCB专题】如何在Allegro中定义字体及批量修改丝印

在PCB板上丝印往往包含了很多信息,比如元件边界、元件参数、元件编号、极性、静电标识、板号等,这些信息在生产、测试及后期维护等都需要使用。一个好的设计往往都能从丝印的布局、丝印的完整性上体现出来。如下所示PCB在电解电容旁有极性丝印、电阻旁有电阻的位号信息等。 …

前端 | (七)浮动 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;浮动介绍&#x1f407;元素浮动后的特点&#x1f407;浮动小练习&#x1f525;盒子1右浮动&#x1f525;盒子1左浮动&#x1f525;所有盒子都浮动&#x1f5…

EasyCVR视频融合平台能正常播放其他协议流,但无法播放HLS流的原因排查

EasyCVR基于云边端一体化架构&#xff0c;支持海量视频汇聚管理&#xff0c;平台支持多协议与多类型设备接入&#xff0c;具体包括国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、大华SDK、宇视SDK等&#xff0c;能对外分发RTMP、RTSP、HTTP-FLV、WS-FLV、HLS、WebRTC等。…

【Spring Boot】拦截器与统一功能处理:统一登录验证、统一异常处理与统一数据返回格式

前言 Spring AOP是一个基于面向切面编程的框架&#xff0c;用于将横切性关注点&#xff08;如日志记录、事务管理&#xff09;与业务逻辑分离&#xff0c;通过代理对象将这些关注点织入到目标对象的方法执行前后、抛出异常或返回结果时等特定位置执行&#xff0c;从而提高程序的…

浏览器显示ERR_NETWORK_ACCESS_DENIED,安全设置或防火墙可能正在阻止连接,无法上网

环境: Win10 专业版 HP台式机 问题描述: 浏览器显示ERR_NETWORK_ACCESS_DENIED,安全设置或防火墙可能正在阻止连接,无法上网 1.无线连接状态正常 打不开网站 2.可以ping通百度DNS解析正常 3.防火墙已关闭 这样的错误可能由于多种原因而发生 原因分析 1.防火墙/防…