STM32远程乒乓升级,升级流程源码详细说明

  • 2019 年 11 月 10 日
  • 筆記

前言

  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.程序太长不好截图,就粘贴出来

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的教程

这次的代码整改下了很大功夫

我呢就是力求所有的代码要有很强的应用性.

第一:代码是写给别人看的

第二:代码更是写给别人用的