前言
1.BootLoader程序,升级简要流程图
2.其实主要的就是把程序文件写入环形队列,然后环形队列取出来数据写入Flash
3.用户程序,简要流程图
细节说明
Flash调整(保持三份程序的stmflash文件一样即可)
一,Flash调整(可根据自己使用的f103系列的芯片进行设置),兼容f103全系列
1.1 可在stmflash.h 调整Flash存储位置
1.2 一般,用户只需要修改
1.3 可在串口打印查看两份程序文件的配置信息
user1ROMStart: 0x8004000 用户程序1 Flash存储的开始地址 user1ROMSize : 0x5c00 用户程序1 程序大小
user2ROMStart: 0x8009c00 用户程序2 Flash存储的开始地址 user2ROMSize : 0x5c00 用户程序2 程序大小
1.4 用户程序1配置
1.5 用户程序2配置
程序升级前是先擦除
1.如果获取的版本和当前版本不一致,擦除,获取另一份程序,允许数据写入Flash
2.STM32的擦除函数
串口处理http数据部分
1.程序太长不好截图,就粘贴出来
代码语言:javascript复制void USART1_IRQHandler(void)//串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
Usart1ReadBuff[Usart1ReadCnt] = Res; //接收的数据存入数组
Usart1ReadCnt ;
if(Usart1ReadCnt > Usart1ReadLen -10)//防止数组溢出
{
Usart1ReadCnt = 0;
}
Usart1IdleCnt = 0;
if(HttpDataStartFlage)//下次来的是真实数据
{
if(IAPStructValue.PutDataFlage && (IAPStructValue.PutDataFlage^IAPStructValue.Overflow))//可以往环形队列里面写数据,同时没有溢出
{
if(PutData(&rb_tIAP,NULL,&Res,1) == -1)
{
IAPStructValue.Overflow = 1;//环形队列溢出
}
}
}
//解析http数据-------------------------------Start
//HTTP/1.1 200 OK
if(!HttpHeadOK && IAPStructValue.PutDataFlage)
{
if(Res=='H' && HttpHeadCnt==0)HttpHeadCnt ;
else if(Res=='T' && HttpHeadCnt==1)HttpHeadCnt ;
else if(Res=='T' && HttpHeadCnt==2)HttpHeadCnt ;
else if(Res=='P' && HttpHeadCnt==3)HttpHeadCnt ;
else if(Res=='/' && HttpHeadCnt==4)HttpHeadCnt ;
else if(Res=='1' && HttpHeadCnt==5)HttpHeadCnt ;
else if(Res=='.' && HttpHeadCnt==6)HttpHeadCnt ;
else if(Res=='1' && HttpHeadCnt==7)HttpHeadCnt ;
else if(Res==' ' && HttpHeadCnt==8)HttpHeadCnt ;
else if(Res=='2' && HttpHeadCnt==9)HttpHeadCnt ;
else if(Res=='0' && HttpHeadCnt==10)HttpHeadCnt ;
else if(Res=='0' && HttpHeadCnt==11)HttpHeadCnt ;
else if(Res==' ' && HttpHeadCnt==12)HttpHeadCnt ;
else if(Res=='O' && HttpHeadCnt==13)HttpHeadCnt ;
else if(Res=='K' && HttpHeadCnt==14){HttpHeadOK = 1;HttpHeadCnt=0;HttpDataLength=0;}
else
{
HttpHeadCnt=0;
}
}
#ifdef UserContentLength
//Content-Length: XXXXXXXX
if(HttpHeadOK && !HttpDataLengthOK)//获取http发过来的数据个数
{
if(Res=='-' && HttpHeadCnt==0) HttpHeadCnt ;
else if(Res=='L' && HttpHeadCnt==1)HttpHeadCnt ;
else if(Res=='e' && HttpHeadCnt==2)HttpHeadCnt ;
else if(Res=='n' && HttpHeadCnt==3)HttpHeadCnt ;
else if(Res=='g' && HttpHeadCnt==4)HttpHeadCnt ;
else if(Res=='t' && HttpHeadCnt==5)HttpHeadCnt ;
else if(Res=='h' && HttpHeadCnt==6)HttpHeadCnt ;
else if(Res==':' && HttpHeadCnt==7)HttpHeadCnt ;
else if(Res==' ' && HttpHeadCnt==8)HttpHeadCnt ;
else if(HttpHeadCnt>=9 && HttpHeadCnt<=16 )//最大99999999个字节. 16:99999999 17:999999999 18:9999999999
{
if(Res!=0x0D)
{
HttpDataLength = HttpDataLength*10 Res - '0';
HttpHeadCnt ;
}
else
{
HttpDataLengthOK = 1;
HttpHeadCnt = 0;
}
}
else
{
HttpHeadCnt = 0;
}
}
if(HttpHeadOK && HttpDataLengthOK && HttpDataLength && !HttpHeadEndOK)
#else
if(HttpHeadOK && !HttpHeadEndOK)
#endif
{//0D 0A 0D 0A
if(Res==0x0D && HttpHeadCnt==0)HttpHeadCnt ;
else if(Res==0x0A && HttpHeadCnt==1)HttpHeadCnt ;
else if(Res==0x0D && HttpHeadCnt==2)HttpHeadCnt ;
else if(Res==0x0A && HttpHeadCnt==3){HttpHeadEndOK = 1;}
else HttpHeadCnt = 0;
}
if(HttpHeadEndOK == 1)//http数据的head已经过去,后面的是真实数据
{
HttpHeadEndOK=0;
HttpHeadCnt = 0;
HttpDataLengthOK=0;
HttpDataStartFlage=1;
Usart1IdleTime = 3000;//GPRS判断空闲时间需要3S左右,因为GPRS有延迟
}
//解析http数据-------------------------------end
}
}
2.处理程序主要就是去掉http数据的head,同时程序里面获取了总共的数据长度
3.根据自己的服务器,选择是不是可以获取,获取的这个长度在写入Flash的时候使用了
把数据写入Flash部分
1.写入的时候加了判断和限制
2.写入Flash函数,为保证写入的正确,写入之后再读一下.
写入完成
1.环形队列里面的数据写完了,同时空闲时间到了,然后判断有没有其他错误
2.其实我以前在这里还有个判断写入Flash的数据 和 Web服务器返回的一致才可以
后来改到了写入Flash里面,但是也不是判断的相等
说下原因:
在接收完程序数据以后,正常来讲,此时只等着串口空闲时间到,然后判断写入完成就好了
其实此时网络模块不应该给单片机发送任何数据了
但是呢!保证不了这段时间里网络模块不会往单片机再发一些数据!
其实就是说,单片机已经写入完成了,但是有可能由于模块或者服务器的种种原因,可能还会追加多余的数据.
有可能有人会说,把空闲时间设置的短一点不就好了,假设设置个 10ms
首先,确实设置10ms基本上就解决了上面说的,但是
GPRS特殊,本身GPRS数据延迟就有可能好几秒.....(更新的时候大家自己看数据,基本上一直有延迟)
我已经设置了GPRS更新的时候空闲时间是3S
综合考虑以后我才会
关于程序里面的两个定时器
一,下载超时定时器
1.1下载程序的时候该定时器开始累加
1.2.调用
1.3.该定时器在写入Flash的时候清零
二,整体运行超时定时器
1.该定时器只要执行BootLoader程序,就开始运行,除非是Wi-Fi配网的时候才会清零,是强制的.
2.调用
用户程序
1.获取上次更新的信息
2.下面这个最好放到感觉程序没有问题了的地方
比如:在后面的章节是程序连接上MQTT以后,才执行这个.然后把更新状态传给客户端
结语
我的程序看似写的相当复杂,但是只要理清思路其实也不是那么的难
只是有很多很多的细节,毕竟我要应对升级过程中的各种问题!
但是呢!这次的升级程序实际上很容易移植到其它单片机
F103系列的就不说了,只是改改Flash地址的事情
我呢后面会提供一节移植到F103RET6的教程
还有一节移植到 F407的教程
这次的代码整改下了很大功夫
我呢就是力求所有的代码要有很强的应用性.
第一:代码是写给别人看的
第二:代码更是写给别人用的