STM32F103通过Ymodem协议更新程序带MD5校验
  p2dF89NsnfQB 2023年11月02日 57 0


一、先来几张靓图

STM32F103通过Ymodem协议更新程序带MD5校验_在线IAP


STM32F103通过Ymodem协议更新程序带MD5校验_文件写入_02


STM32F103通过Ymodem协议更新程序带MD5校验_数据_03


二、Xshell软件设置

STM32F103通过Ymodem协议更新程序带MD5校验_数据_04


STM32F103通过Ymodem协议更新程序带MD5校验_数据_05


STM32F103通过Ymodem协议更新程序带MD5校验_在线IAP_06


三、程序更新流程


waitStartVerInfo, //等待起始校验消息,确定协议

getProgInfoPack, //或去信息包

progFileDeal, //程序处理

downloadAndSaveProg, //下载并保存程序

verifyDownloadProg, //校验下载下来的程序

carryProgToMcu, //搬运程序进入MCU

verifyMcuProg, //校验搬运进入MCU的程序

recordProgInfo, //记录程序信息

jumpToAppProg, //跳转到应用程序

四、主要源码

#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();

}




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;

}


}
}
}

五、主要说明

1、我用的是RS485协议,可以更改为SPI、IIC、串口、等。
2、程序自己已经在项目中使用,下面工程下载下来后可以直接使用,软件设置参考上面。
3、需要技术支持的可以在我的主页添加我的QQ、
4、项目中用到了FAFTS文件系统、MD5校验算法、在线IAP等知识。
5、项目中使用的存储介质是SPIFLASH(W25Q64),可以改为其他容量的。
六、文件名格式
文件名格式为:$V002@21540&9eff132d92eb0b32ddbe3654d567568d#
解释:$版本号@字节数&MD5校验值#
MD5计算地址:http://www.metools.info/other/o21.html
七、需要完整工程的点击购买
​完整工程​​


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  eo9lmrKcoG9P   2023年12月11日   34   0   0 组播多点HCIP数据
p2dF89NsnfQB