打印
[应用相关]

STM32F103通过Ymodem协议更新程序带MD5校验

[复制链接]
709|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
观海|  楼主 | 2021-7-2 09:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、先来几张靓图




使用特权

评论回复
沙发
观海|  楼主 | 2021-7-2 09:42 | 只看该作者

二、Xshell软件设置





使用特权

评论回复
板凳
观海|  楼主 | 2021-7-2 09:42 | 只看该作者
三、程序更新流程


waitStartVerInfo, //等待起始校验消息,确定协议
getProgInfoPack, //或去信息包
progFileDeal, //程序处理
downloadAndSaveProg, //下载并保存程序
verifyDownloadProg, //校验下载下来的程序
carryProgToMcu, //搬运程序进入MCU
verifyMcuProg, //校验搬运进入MCU的程序
recordProgInfo, //记录程序信息
jumpToAppProg, //跳转到应用程序


使用特权

评论回复
地板
观海|  楼主 | 2021-7-2 09:44 | 只看该作者
四、主要源码



#include "main.h"

void $Sub$$main(void)
{       
        u8 res=0;                        
        FATFS fs;//逻辑磁盘工作区.               
        extern int main(void);
        extern int $Super$$main(void);
       
        delay_init();            //延时函数初始化          
        uart_init(115200);
        rs485_init(115200);
        rs485_timer_init(2000,71);
        key_init(); //IO初始化
        W25QXX_Init();                                //初始化W25Q128
        sys_data_read();
       
        res=f_mount(&fs,"0:",1);                                 //挂载FLASH.       
        if(res==0X0D)//FLASH磁盘,FAT文件系统错误,重新格式化FLASH
        {
                printf("Flash Disk Formatting...\r\n");        //格式化FLASH
                res=f_mkfs("0:",1,4096);//格式化FLASH,1,盘符;1,不需要引导区,8个扇区为1个簇
                if(res==0)
                {
                        f_setlabel((const TCHAR *)"0:PROG");        //设置Flash磁盘的名字为:ALIENTEK
                        printf("Flash Disk Format Finish\r\n");        //格式化完成
                }
                else
                        printf("Flash Disk Format Error \r\n");        //格式化失败
        }               
       
       
        $Super$$main();
       
}




使用特权

评论回复
5
观海|  楼主 | 2021-7-2 09:44 | 只看该作者
int main(void)
{       
        bool isJumpToApp=true;
        YmodemSohPackage *ymodemSohPack = (YmodemSohPackage *)&rs485RecData.recDataBuf;//SOH包结构
        YmodemStxPackage *ymodemStxPack = (YmodemStxPackage *)&rs485RecData.recDataBuf;//STX包结构
        ProgUpdateProcessE progUpdateProcess=waitStartVerInfo;                                                        //程序更新流程
        CommitAgree commitProtoco=noAgree;                                                                                                //通信协议
        u16 rs485RecCntVal=0;                                                                                                                        //485接收到的数据
        u8 versionStrBuf[10];                                //旧版本
        u8 sizeStrBuf[10];                                        //程序大小
        u8 md5StrBuf[34];                                        //MD5值
        u32 programTotalSize;                                //程序大小
        u8 failCnt=0;                                                //失败计数
        char buf[200];                                        //临时缓冲器
        FRESULT fileRes=FR_OK;                                        //文件操作结果
        static FIL file; //文件
        bool isAck=true;                                //是否应答ACK
        u8 packNumCnt;
        u32 br;                        //程序移动指针
        u32 updatingProgSize;//程序读取字节
        u32 progWriteAddr;        //写MCU程序地址
        u8 readProgBuf[2048];//读取存储BUF
       
        if(get_update_key())//检测是否强制更新程序
        {
                MSG_OUT("开始更新程序");
                isJumpToApp=false;
        }
       
        while(1)
        {
                if(isJumpToApp)//跳转到Application程序
                {
                        if (strcmp((char *)sysParaSave.oldVersion, (char *)sysParaSave.newVersion) == 0)//程序有效
                        {
                                //执行跳转
                                MSG_OUT("程序无需更新,直接跳转");
                                __disable_irq();                                //关闭所有中断
                                iap_load_app(SAVE_PROGRAM_ADDR);//执行FLASH APP代码       
                        }
                        else
                        {
                                isJumpToApp=false;
                        }
                }
                else
                {
                       
                        switch(progUpdateProcess)
                        {
                                case waitStartVerInfo:                //等待起始校验消息,确定协议
                                {
                                        rs485RecCntVal = wait_rec_data(1000);
                                        if(rs485RecCntVal!=0)
                                        {                                               
                                               
                                                if(strstr(rs485RecData.recDataBuf,"ymodem")!=NULL)//查找协议
                                                {
                                                        commitProtoco=ymodem;
                                                }
                                                if(strstr(rs485RecData.recDataBuf,COMMIT_PASSWORD)!=NULL)//验证密码
                                                {
                                                        if(commitProtoco!=noAgree)//通信协议和密码都合适
                                                        {
                                                                MSG_OUT("包头提取成功");
                                                                progUpdateProcess=getProgInfoPack;//执行获去信息包
                                                        }
                                                               
                                                }
                                        }
                                }break;
                                case getProgInfoPack:                //获去信息包
                                {
                                        switch(commitProtoco)
                                        {
                                                case ymodem:
                                                {
                                                        ymodem_answer_c();
                                                        rs485RecCntVal = wait_rec_data(1000);
                                                       
                                                        if(rs485RecCntVal!=0)        //接收到了数据
                                                        {
                                                                //校验数据是否合适
                                                                if(ymodemSohPack->packHead==ymodem_soh&&\
                                                                        ymodemSohPack->ymodemPackNum==(u8)(~ymodemSohPack->ymodemPackNumNegation)&&\
                                                                        ymodem_crc16_cal(ymodemSohPack->ymodemData, sizeof(ymodemSohPack->ymodemData))\
                                                                                ==(ymodemSohPack->ymodemCrcH*256+ymodemSohPack->ymodemCrcL))
                                                                {

                                                                        memset(versionStrBuf,0x00,sizeof(versionStrBuf));
                                                                        memset(sizeStrBuf,0x00,sizeof(sizeStrBuf));
                                                                        memset(md5StrBuf,0x00,sizeof(md5StrBuf));
                                                                        programTotalSize=0;       
                                                                       
                                                                        //获取字符串
                                                                        if(copy_between_characters((char *)&ymodemSohPack->ymodemData,(char *)&versionStrBuf,"$","@")&&\
                                                                                copy_between_characters((char *)&ymodemSohPack->ymodemData,(char *)&sizeStrBuf,"@","&")&&\
                                                                                copy_between_characters((char *)&ymodemSohPack->ymodemData,(char *)&md5StrBuf,"&","#"))//获取版本
                                                                        {
                                                                               
                                                                                if(strcmp((char *)&versionStrBuf,(char *)sysParaSave.oldVersion)==0)//版本一样
                                                                                {
                                                                                        MSG_OUT("运行程序与当前更新程序为同一个文件");
                                                                                        failCnt=0;
                                                                                        ymodem_cancel_transport();//停止ymodem传输
                                                                                        progUpdateProcess=jumpToAppProg;//直接跳转运行       
                                                                                                       
                                                                                }
                                                                                else
                                                                                {
                                                                                       
                                                                                        programTotalSize = atol((char *)sizeStrBuf);
                                                                                       
                                                                                        MSG_OUT("文件信息获取成功");
                                                                                        failCnt=0;
                                                                                        progUpdateProcess=progFileDeal;//文件处理
                                                                                }

                                                                        }
                                                                        else
                                                                        {
                                                                                failCnt++;
                                                                                if(failCnt>=5)
                                                                                {
                                                                                        failCnt=0;
                                                                                        MSG_OUT("文件信息获取失败");
                                                                                        ymodem_cancel_transport();//停止ymodem传输
                                                                                        while(1);
                                                                                }                                                                               
                                                                        }
                                                                       
                                                                       
                                                                }
                                                                else
                                                                {                                                                       
                                                                        failCnt++;
                                                                        if(failCnt>=5)
                                                                        {
                                                                                failCnt=0;
                                                                                MSG_OUT("文件信息数据校验失败");
                                                                                ymodem_cancel_transport();//停止ymodem传输
                                                                                while(1);
                                                                        }
                                                                }
       
                                                        }       
                                                        else
                                                        {
                                                                failCnt++;
                                                                if(failCnt>=5)
                                                                {
                                                                        failCnt=0;
                                                                        MSG_OUT("文件信息数据获取失败");
                                                                        ymodem_cancel_transport();//停止ymodem传输
                                                                        while(1);
                                                                }
                                                        }
                                                }break;
                                        }                       
                                }break;
                                case progFileDeal:                        //程序处理
                                {
                                        //删除旧文件
                                        memset(buf,0x00,sizeof(buf));
                                        sprintf(buf,"%s%s%s%s",PROG_PATH,"/",sysParaSave.oldVersion,".bin");
                                       
                                        fileRes = f_unlink(buf);
                                        if(fileRes!=FR_OK&&fileRes!=FR_NO_PATH)
                                        {
                                                MSG_OUT("旧文件删除失败");
                                                ymodem_cancel_transport();//停止ymodem传输
                                                while(1);                                               
                                        }
                                       
                                        //新建根目录
                                       
                                        fileRes=f_mkdir(PROG_PATH);                //新建程序根目录
                                        if(fileRes!=FR_OK&&fileRes!=FR_EXIST)                                                        //文件打开失败
                                        {
                                                MSG_OUT("程序根目录新建失败");
                                                ymodem_cancel_transport();//停止ymodem传输
                                                while(1);
                                        }               
                                        //新建文件并打开
                                        memset(buf,0x00,sizeof(buf));
                                        sprintf(buf,"%s%s%s%s",PROG_PATH,"/",versionStrBuf,".bin");                                       
                                        fileRes=f_open(&file,buf,FA_CREATE_NEW|FA_WRITE);                //新建文件并打开
                                        if(fileRes!=FR_OK)
                                        {
                                                MSG_OUT("打开新文件失败");
                                                ymodem_cancel_transport();//停止ymodem传输
                                                while(1);                                               
                                        }
                                        progUpdateProcess=downloadAndSaveProg;//开始下载程序
                                        isAck=true;
                                        packNumCnt=1;                //从1开始计数
                                        failCnt=0;
                                        br=0;
                                       
                                }break;
                                case downloadAndSaveProg:        //下载并保存程序
                                {
                                        switch(commitProtoco)
                                        {
                                                case ymodem:
                                                {                                       
                                                        if(isAck)//应答ACK
                                                                ymodem_answer_ack();
                                                        else
                                                                ymodem_answer_nak();
                                                       
                                                        rs485RecCntVal = wait_rec_data(1000);
                                                        if(rs485RecCntVal<5)//有可能是结束信号
                                                        {
                                                                if(ymodemStxPack->packHead==YMODEM_EOT)//结束信号
                                                                {
                                                                        ymodem_answer_nak();
                                                                        rs485RecCntVal = wait_rec_data(1000);
                                                                        ymodem_answer_ack();
                                                                        ymodem_answer_c();
                                                                        delay_ms(200);
                                                                        packNumCnt=0;//结束包号是0
                                                                        isAck=true;
                                                                }
                                                        }
                                                        else if(ymodemStxPack->packHead==ymodem_soh)//128字节包
                                                        {
                                                                if(ymodemSohPack->ymodemPackNum==(u8)(~ymodemSohPack->ymodemPackNumNegation)&&\
                                                                        ymodem_crc16_cal(ymodemSohPack->ymodemData, sizeof(ymodemSohPack->ymodemData))\
                                                                                ==(ymodemSohPack->ymodemCrcH*256+ymodemSohPack->ymodemCrcL)&&
                                                                        ymodemSohPack->ymodemPackNum==packNumCnt)
                                                                {
                                                                        if(ymodemSohPack->ymodemPackNum!=0x00)//不是结束包
                                                                        {
                                                                                //读取成功了
                                                                                fileRes=f_write (&file, ymodemSohPack->ymodemData,sizeof(ymodemSohPack->ymodemData), &br);                //写入文件
                                                                                if(fileRes==FR_OK)
                                                                                {               
                                                                                        MSG_OUT("写入成功");
                                                                                        packNumCnt++;
                                                                                        isAck=true; //应答、发送下一包
                                                                                        failCnt=0;
                                                                                }
                                                                                else
                                                                                {
                                                                                        isAck=false;//非应答、重新发送此包       
                                                                                        failCnt++;
                                                                                        if(failCnt>=5)
                                                                                        {
                                                                                                failCnt=0;
                                                                                                MSG_OUT("文件写入失败");
                                                                                                ymodem_cancel_transport();//停止ymodem传输
                                                                                                while(1);
                                                                                        }                                                                       
                                                                                }                                                                       
                                                                        }
                                                                        else//是结束包
                                                                        {
                                                                                MSG_OUT("成功收到应答包");
                                                                                f_close (&file);//关闭文件
                                                                                progUpdateProcess = verifyDownloadProg;
                                                                        }
                                                                       
                                                                }
                                                                else
                                                                {
                                                                        isAck=false;//非应答、重新发送此包
                                                                        failCnt++;
                                                                        if(failCnt>=5)
                                                                        {
                                                                                failCnt=0;
                                                                                MSG_OUT("文件写入失败");
                                                                                ymodem_cancel_transport();//停止ymodem传输
                                                                                while(1);
                                                                        }                                                       
                                                                }                                                       
                                                        }
                                                        else//1024 STX包
                                                        {
                                                                if(ymodemStxPack->ymodemPackNum==(u8)(~ymodemStxPack->ymodemPackNumNegation)&&\
                                                                        ymodem_crc16_cal(ymodemStxPack->ymodemData, sizeof(ymodemStxPack->ymodemData))\
                                                                                ==(ymodemStxPack->ymodemCrcH*256+ymodemStxPack->ymodemCrcL)&&\
                                                                        ymodemStxPack->ymodemPackNum==packNumCnt)
                                                                {
                                                               
                                                                        //读取成功了
                                                                        fileRes=f_write (&file, ymodemStxPack->ymodemData,sizeof(ymodemStxPack->ymodemData), &br);                //写入文件
                                                                        if(fileRes==FR_OK)
                                                                        {                               
                                                                                MSG_OUT("写入成功");
                                                                                packNumCnt++;
                                                                                isAck=true; //应答、发送下一包
                                                                                failCnt=0;
                                                                        }
                                                                        else
                                                                        {
                                                                                isAck=false;//非应答、重新发送此包
                                                                               
                                                                                failCnt++;
                                                                                if(failCnt>=5)
                                                                                {
                                                                                        failCnt=0;
                                                                                        MSG_OUT("文件写入失败");
                                                                                        ymodem_cancel_transport();//停止ymodem传输
                                                                                        while(1);
                                                                                }
                                                                               
                                                                        }
                                                                }
                                                                else
                                                                {
                                                                        isAck=false;//非应答、重新发送此包
                                                                        failCnt++;
                                                                        if(failCnt>=5)
                                                                        {
                                                                                failCnt=0;
                                                                                MSG_OUT("文件写入失败");
                                                                                ymodem_cancel_transport();//停止ymodem传输
                                                                                while(1);
                                                                        }                                                       
                                                                }                                                       
                                                        }
                                                }break;
                                        }                               
                                }break;
                                case verifyDownloadProg:                //校验下载下来的程序
                                {               
                                        fileRes=f_open(&file,buf,FA_READ);                //打开新文件
                                        if(fileRes!=FR_OK)
                                        {
                                                MSG_OUT("校验文件打开失败");
                                                while(1);                                               
                                        }       
                                        MSG_OUT("校验文件打开成功");
                                       
                                        if(file.fsize<programTotalSize)
                                        {
                                                MSG_OUT("文件尺寸校验失败");
                                                f_close(&file);
                                                while(1);                                               
                                        }
                                        MSG_OUT("文件尺寸校验成功");
                                       
                                       
                                        progUpdateProcess=carryProgToMcu;
                                }break;
                                case carryProgToMcu:                        //搬运程序进入MCU
                                {
                                        MSG_OUT("开始更新程序");
                                        updatingProgSize=programTotalSize;//程序读取字节
                                        progWriteAddr=SAVE_PROGRAM_ADDR;        //程序写入MCU地址
                                        __disable_irq();                                //关闭所有中断
                                        while(1)//开始读取
                                        {
                                                memset(buf,0x00,sizeof(buf));
                                                sprintf(buf,"程序已更新: %d/%d",(programTotalSize-updatingProgSize),programTotalSize);


                                                MSG_OUT(buf);
                                                if(updatingProgSize==0)                  //程序复制完成
                                                {
                                                        MSG_OUT("程序更新完成");
                                                        progUpdateProcess=verifyMcuProg;//执行校验程序
                                                       
                                                        f_close (&file);
                                                        break;
                                                }                                               
                                               
                                                if(updatingProgSize<2048)
                                                {
                                                        if(f_read (&file, &readProgBuf,updatingProgSize, &br)==FR_OK) //读取文件
                                                        {
                                                                iap_write_appbin(progWriteAddr,readProgBuf,updatingProgSize);          //更新FLASH代码
                                                                updatingProgSize=0;
                                                        }
                                                        else
                                                        {
                                                                MSG_OUT("程序文件读取失败!");
                                                                f_close (&file);
                                                                while(1);
                                                        }
                                                }
                                                else
                                                {
                                                        if(f_read (&file, &readProgBuf,2048, &br)==FR_OK) //读取文件
                                                        {
                                                                iap_write_appbin(progWriteAddr,readProgBuf,2048);          //更新FLASH代码  
                                                                updatingProgSize-=2048;
                                                                progWriteAddr+=2048;
                                                        }
                                                        else
                                                        {
                                                                MSG_OUT("程序文件读取失败!");
                                                                f_close (&file);
                                                                while(1);
                                                        }
                                                }
                                               
                                        }       
                                        __enable_irq();                                //关闭所有中断
                                }break;
                                case verifyMcuProg:                        //校验搬运进入MCU的程序
                                {
                                        MSG_OUT("开始MD5校验程序");
                                        memset(buf,0x00,sizeof(buf));
                                        get_bin_md5(SAVE_PROGRAM_ADDR,programTotalSize,buf);                        //MD5校验程序文件
                                        if(strcmp((char *)md5StrBuf,buf)!=0)
                                        {
       
                                                MSG_OUT("程序MD5校验失败");
                                                while(1);
                       
                                        }                                       
                                        MSG_OUT("MD5校验程序成功");
                                        progUpdateProcess=recordProgInfo;//记录程序信息
                                }break;
                                case recordProgInfo:                        //记录程序信息
                                {
                                        MSG_OUT("开始记录数据");
                                       
                                       
                                        memset(sysParaSave.oldVersion,0x00,sizeof(sysParaSave.oldVersion));
                                        memset(sysParaSave.newVersion,0x00,sizeof(sysParaSave.newVersion));
                                        memset(sysParaSave.md5Val,0x00,sizeof(sysParaSave.md5Val));                                               
                                       
                                        memcpy(sysParaSave.oldVersion,versionStrBuf,sizeof(versionStrBuf));        //程序版本
                                        memcpy(sysParaSave.newVersion,versionStrBuf,sizeof(versionStrBuf));        //程序版本
                                        memcpy(sysParaSave.md5Val,md5StrBuf,sizeof(md5StrBuf));                                //MD5
                                       
                                        sysParaSave.programSize = programTotalSize;                                                        //程序大小
                                       
                                        if(sys_data_save()!=true)
                                        {
                                                MSG_OUT("数据保存失败");
                                                while(1);
                                        }
                                        MSG_OUT("数据保存成功");
                                        progUpdateProcess=jumpToAppProg;//执行跳转程序
                                }break;
                                case jumpToAppProg:                        //跳转到应用程序               
                                {
                                        MSG_OUT("执行跳转");
                                        __disable_irq();                                //关闭所有中断
                                        iap_load_app(SAVE_PROGRAM_ADDR);//执行FLASH APP代码                                               
                                }break;                               
                               
                        }
                       
                       
                }
        }
}


使用特权

评论回复
6
观海|  楼主 | 2021-7-2 09:44 | 只看该作者
五、主要说明


1、我用的是RS485协议,可以更改为SPI、IIC、串口、等。
2、程序自己已经在项目中使用,下面工程下载下来后可以直接使用,软件设置参考上面。
3、需要技术支持的可以在我的主页添加我的QQ、
4、项目中用到了FAFTS文件系统、MD5校验算法、在线IAP等知识。
5、项目中使用的存储介质是SPIFLASH(W25Q64),可以改为其他容量的。


使用特权

评论回复
7
yklstudent| | 2021-7-2 13:44 | 只看该作者
把固件版本和固件类型也加上最好

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

3962

帖子

1

粉丝