小言_互联网的博客

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

367人阅读  评论(0)

一、先来几张靓图



二、Xshell软件设置




三、程序更新流程

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
七、需要完整工程的点击购买
完整工程


转载:https://blog.csdn.net/qq_15181569/article/details/115762289
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场