Slan 士兰微SC32F5664 通过串口IAP,实现OTA


前言

越来越多的设备在出厂的时候,自带boot,以便在出现问题不拆壳的情况下实现对设备的软件升级。士兰微的IAP在线更新程序因为资料比较少,做的过程中原厂给的资料也是模糊其词,所以我在做这个项目的过程中就想着做完的时候整理一下发出来,供需要的人参考。


一、整体思路

1.单片机

程序上电运行boot程序(注意:士兰微的芯片和ST的不太一样的地方是boot的启动地址不一样,从最后1K开始运行boot的程序大小是依次往前排,flash的地址也不一样,真正从0x0开始),看得懂的看下图,在boot程序里面会判断是否需要升级,需要升级会接收升级指令并作出应答,升级结束会校验app的完整性,然后校验通过才会跳转至APP程序运行;不需要升级的时候会直接校验APP完整性然后跳转至APP程序运行,在APP程序里面会接收升级指令,然后软重启至boot运行,重复上面的升级流程。单片机的实现流程很简单,要是有不太理解的朋友,可以先看一下我之前的一篇博客,关于STM32F051R8T6的远程升级的文章,链接: 点我查看
使用脱机烧录器读出来的boot程序

2. 上位机(c#)

因为我们使用的芯片IAP功能,考虑到升级过程中对flash擦写错误造成的不可控因素,所以会对整个app进行加密,在boot里面校验APP的完整性,这个就不描述了,因为加密的方式有很多,只说一下这个思路。加密的软件是一个独立的,不会共享出来。还有一个就是升级的软件,界面如下,操作起来也比较简单,因为项目上使用的升级是双驱的,所以增加了升级设备地址选择项,正常打开串口后,选择地址,没有问题就点击开始升级,上面有进度条提示,下面会一直刷新发送的升级数据和收到的单片机设备返回的数据。
升级界面

二、整体流程

1.单片机

boot程序下载地址:链接 点我下载

1.1 boot程序

使用keil_mdk,先安装pack主持包,在boot程序里面选择带boot的芯片,如下图,在APP的程序里只需要选择不带boot的芯片,然后flashDownLoad的位置根据提示选择64K就可以了。
在这里插入图片描述

boot 代码如下(示例):

u8 dat=0;
	u16 FileCRCNum=0;
	uint8_t Erase_Cnt_8u = 0;
	uint8_t FlashSendBuff[8]={0};  
	
	ReadAddr = 0x0000CC00;
//	uint16_t i=0,j = 0;
	System_init();
//	MotorFile_Par_Set();
//	MAX_485_Send_Launch = 0;
	IRQ_Init();
	FlashUpdata=ReadFlashWord(0xcc00,8,FlashSendBuff);
	FlashUpdata=FlashSendBuff[0]+FlashSendBuff[1]*256;
	Updata_PackSize=FlashSendBuff[4]+FlashSendBuff[5]*256+(FlashSendBuff[6]+FlashSendBuff[7]*256)*65536;
	if(FlashUpdata==0xaa55)
	{
		Databit.Bit.SendDataReadyFlag = 1;
		Databit.Bit.EraseFlashFlag = 1;
		Program_Addr = APP_ADDR;
		MAX_485_TX_BUF[0]=0xa9;
		MAX_485_TX_BUF[1]=0x20;
		MAX_485_TX_BUF[2]=Slave_ID;
		MAX_485_TX_BUF[3]=0xfd;
		MAX_485_TX_BUF[4]=0x00;
		MAX_485_TX_BUF[5]=0x03;
		MAX_485_TX_BUF[6]=0x00;
		MAX_485_TX_BUF[7]=0x00;
		MAX_485_TX_BUF[8]=0x80;
		SendCRC_Value=crc16(&MAX_485_TX_BUF[2],7);
		MAX_485_TX_BUF[9]=SendCRC_Value%256;
		MAX_485_TX_BUF[10]=SendCRC_Value/256;
		MAX_485_Send_Launch=1;
		G_Send_Len=11;
		TimeStopFlag=1;
	}	
		while(1)
		{
			if(TimeBase_100ms==1)
			{
				TimeBase_100ms=0;
				TGLBit(LED_ALARM_PORT_ABBR->OUTTGL,LED_ALARM_PIN);
			}
			#if 1
			if(TimeStopFlag == 0)
			{
				if(L_TimeOutCnt_32u < 200*5000) L_TimeOutCnt_32u++;	//
				else
				{
					CheckApp();
					if(AppCheckPass)	//检查栈顶地址是否合法&APP是否完整正确
					{
						EraseFlashSector(0xcc00);
						CHIPCTL->CHIP_KEY =0x05fa659aul;
						CHIPCTL->REMAP_CTRL =0xa05f0000;			//REMAP清零,切换到从0地址开始运行
						SCB->AIRCR=((0x05FA0000ul)|(1<<2));   //系统软件复位
					}
					else
					{
						TimeStopFlag = 1;
						L_TimeOutCnt_32u = 0;
					}
				}		
			}	
			
			if(MAX_485_Receive_Sucess)
			{
				if(MAX_485_RX_BUF[2]==Slave_ID)
				{
					if(MAX_485_RX_BUF[3]==0xfd)
					{
						if((MAX_485_RX_BUF[4]==0x00)&&(MAX_485_RX_BUF[5]==0x07))
						{
							Soft_Version=(MAX_485_RX_BUF[6]<<16)+(MAX_485_RX_BUF[7]<<8)+(MAX_485_RX_BUF[8]);
							Updata_PackSize=(MAX_485_RX_BUF[9]<<24)+(MAX_485_RX_BUF[10]<<16)+(MAX_485_RX_BUF[11]<<8)+(MAX_485_RX_BUF[12]);
							Databit.Bit.SendDataReadyFlag = 1;
							Databit.Bit.EraseFlashFlag = 1;
							Program_Addr = APP_ADDR;
							MAX_485_TX_BUF[0]=0xa9;
							MAX_485_TX_BUF[1]=0x20;
							MAX_485_TX_BUF[2]=Slave_ID;
							MAX_485_TX_BUF[3]=0xfd;
							MAX_485_TX_BUF[4]=0x00;
							MAX_485_TX_BUF[5]=0x03;
							MAX_485_TX_BUF[6]=0x00;
							MAX_485_TX_BUF[7]=0x00;
							MAX_485_TX_BUF[8]=0x80;
							SendCRC_Value=crc16(&MAX_485_TX_BUF[2],7);
							MAX_485_TX_BUF[9]=SendCRC_Value%256;
							MAX_485_TX_BUF[10]=SendCRC_Value/256;
							MAX_485_Send_Launch=1;
							G_Send_Len=11;
							EraseFlashSector(0xcc00);
							WriteFlashWord(0xcc00,0xaa55);
//							CHIPCTL->CHIP_KEY =0x05fa659aul;
//							CHIPCTL->REMAP_CTRL =0xa05f0001;			//REMAP清零,切换到从0地址开始运行
//							SCB->AIRCR=((0x05FA0000ul)|(1<<2));   //系统软件复位
						}
					}
					else if(MAX_485_RX_BUF[3]==0xef)
					{
						Databit.Bit.SendDataReadyFlag = 1;
						Uart0_Data.FlashWriteCnt++;
						if((Uart0_Data.FlashWriteCnt - 8) == 0) 
						{
							Uart0_Data.FlashWriteCnt = 0;
							Databit.Bit.EraseFlashFlag = 1;
						}
						Uart0_Data.G_Offset_32u = (MAX_485_RX_BUF[6]<<24)+(MAX_485_RX_BUF[7]<<16)+(MAX_485_RX_BUF[8]<<8)+MAX_485_RX_BUF[9];
						if((Program_Addr == (APP_ADDR + (Uart0_Data.G_Offset_32u-1)*128))&&(Program_Addr <= 0x0000CC00))
						{
							Databit.Bit.WriteErrFlag=0;
							for(Uart0_Data.FlashWriteCnt_16u = 0;Uart0_Data.FlashWriteCnt_16u < (Uart0_Data.U0Data_Length-4);Uart0_Data.FlashWriteCnt_16u+=4)				//(ModbusRegAmount-1)
							{
								if(Uart0_Data.U0Data_Length<132)
								{
									if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)<4)
									{
										if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==3)
										{
											Byte_Word=((MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x00ffffff);
								
											WriteFlashWord(Program_Addr,Byte_Word);
											Program_Addr += 3;
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];
										}
										else if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==2)
										{
											Byte_Word=(MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x0000ffff);
								
											WriteFlashWord(Program_Addr,Byte_Word);
											Program_Addr +=2;
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];
										}
										else if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==1)
										{
											Byte_Word=(MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x000000ff);
								
											WriteFlashWord(Program_Addr,Byte_Word);
											Program_Addr += 1;
											dat = (FileCRCNum >> 8); 
											FileCRCNum <<= 8;
											FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];
										}
										
									}
									else 
									{
										Byte_Word=(MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u];
								
										WriteFlashWord(Program_Addr,Byte_Word);
										Program_Addr += 4;
										dat = (FileCRCNum >> 8); 
										FileCRCNum <<= 8;
										FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];
										dat = (FileCRCNum >> 8); 
										FileCRCNum <<= 8;
										FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];
										dat = (FileCRCNum >> 8); 
										FileCRCNum <<= 8;
										FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];
										dat = (FileCRCNum >> 8); 
										FileCRCNum <<= 8;
										FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]];
									}
									
								}
								else  if(Uart0_Data.U0Data_Length==132)
								{
									Byte_Word=(MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u];
								
									WriteFlashWord(Program_Addr,Byte_Word);
									Program_Addr += 4;
									dat = (FileCRCNum >> 8); 
									FileCRCNum <<= 8;
									FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];
									dat = (FileCRCNum >> 8); 
									FileCRCNum <<= 8;
									FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];
									dat = (FileCRCNum >> 8); 
									FileCRCNum <<= 8;
									FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];
									dat = (FileCRCNum >> 8); 
									FileCRCNum <<= 8;
									FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]];
								}
								
							}
						}
						else
						{
							Databit.Bit.WriteErrFlag =1;
						}
						if(Databit.Bit.WriteErrFlag==0)
						{
							MAX_485_TX_BUF[0]=0xa9;
							MAX_485_TX_BUF[1]=0x20;
							MAX_485_TX_BUF[2]=Slave_ID;
							MAX_485_TX_BUF[3]=0xef;
							MAX_485_TX_BUF[4]=0x00;
							MAX_485_TX_BUF[5]=0x04;
							MAX_485_TX_BUF[6]=(Uart0_Data.G_Offset_32u&0xff000000)>>24;
							MAX_485_TX_BUF[7]=(Uart0_Data.G_Offset_32u&0x00ff0000)>>16;
							MAX_485_TX_BUF[8]=(Uart0_Data.G_Offset_32u&0x0000ff00)>>8;
							MAX_485_TX_BUF[9]=(Uart0_Data.G_Offset_32u&0x000000ff)>>0;
							SendCRC_Value=crc16(&MAX_485_TX_BUF[2],8);
							MAX_485_TX_BUF[10]=SendCRC_Value%256;
							MAX_485_TX_BUF[11]=SendCRC_Value/256;
						}
						else  //错误,返回0,关闭升级
						{
							MAX_485_TX_BUF[0]=0xa9;
							MAX_485_TX_BUF[1]=0x20;
							MAX_485_TX_BUF[2]=Slave_ID;
							MAX_485_TX_BUF[3]=0xef;
							MAX_485_TX_BUF[4]=0x00;
							MAX_485_TX_BUF[5]=0x04;
							MAX_485_TX_BUF[6]=0;
							MAX_485_TX_BUF[7]=0;
							MAX_485_TX_BUF[8]=0;
							MAX_485_TX_BUF[9]=0;
							SendCRC_Value=crc16(&MAX_485_TX_BUF[2],8);
							MAX_485_TX_BUF[10]=SendCRC_Value%256;
							MAX_485_TX_BUF[11]=SendCRC_Value/256;
						}
						G_Send_Len=12;MAX_485_Send_Launch=1;
					}
					else if(MAX_485_RX_BUF[3]==0xff)
					{
						Databit.Bit.SendDataReadyFlag = 1;
						Uart0_Data.FileCRC_Num_16u= (MAX_485_RX_BUF[8]<<8)+MAX_485_RX_BUF[9];
						if(FileCRCNum==Uart0_Data.FileCRC_Num_16u)
						{
							CheckApp();
							if(AppCheckPass)
							{
								Databit.Bit.UpdataSussFlag=1;
								MAX_485_TX_BUF[0]=0xa9;
								MAX_485_TX_BUF[1]=0x20;
								MAX_485_TX_BUF[2]=Slave_ID;
								MAX_485_TX_BUF[3]=0xff;
								MAX_485_TX_BUF[4]=0x00;
								MAX_485_TX_BUF[5]=0x01;
								MAX_485_TX_BUF[6]=0;
								SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);
								MAX_485_TX_BUF[7]=SendCRC_Value%256;
								MAX_485_TX_BUF[8]=SendCRC_Value/256;
							}
							else
							{
								MAX_485_TX_BUF[0]=0xa9;
								MAX_485_TX_BUF[1]=0x20;
								MAX_485_TX_BUF[2]=Slave_ID;
								MAX_485_TX_BUF[3]=0xff;
								MAX_485_TX_BUF[4]=0x00;
								MAX_485_TX_BUF[5]=0x01;
								MAX_485_TX_BUF[6]=2;
								SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);
								MAX_485_TX_BUF[7]=SendCRC_Value%256;
								MAX_485_TX_BUF[8]=SendCRC_Value/256;
							}
							
						}
						else 
						{
							MAX_485_TX_BUF[0]=0xa9;
							MAX_485_TX_BUF[1]=0x20;
							MAX_485_TX_BUF[2]=Slave_ID;
							MAX_485_TX_BUF[3]=0xff;
							MAX_485_TX_BUF[4]=0x00;
							MAX_485_TX_BUF[5]=0x01;
							MAX_485_TX_BUF[6]=1;
							SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);
							MAX_485_TX_BUF[7]=SendCRC_Value%256;
							MAX_485_TX_BUF[8]=SendCRC_Value/256;
						}
						
						G_Send_Len=9;MAX_485_Send_Launch=1;
					}
				}
				MAX_485_Receive_Sucess = 0;
			}
			if(Databit.Bit.SendDataReadyFlag)
			{
				if(Databit.Bit.EraseFlashFlag)
				{
					Databit.Bit.EraseFlashFlag = 0;
					if(Erase_Cnt_8u < 102)		//51K=102/2
					{
						EraseFlashSector(APP_ADDR+(Erase_Cnt_8u*512)); 					//擦51K
						Erase_Cnt_8u++;
						EraseFlashSector(APP_ADDR+(Erase_Cnt_8u*512));
						Erase_Cnt_8u++;
					}
				}
				Uart0_Data.U0SendDelayTime=300;
				Databit.Bit.SendDataReadyFlag =0;
				
			}
			#endif
		}
	
}

1.1 APP程序

app程序牵扯到工程项目比较多,不方便外发,说一下具体的ota实现,正常运行程序,接收升级指令后会写升级标志位到flash,然后实现软重启至boot程序区运行。

2.c#

主要功能:串口通信,bin文件或者hex文件读取,数据重组等等,软件链接: 上位机软件下载

总结

以上是对文章的描述,希望可以帮助到需要的人,谢谢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值