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

2019-11-10 23:10:56 浏览数 (1)

前言

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

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

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

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

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

0 人点赞