20-STM32+ESP8266+AIR202远程升级方案-扩展-移植远程更新方案包,实现STM32F072+DTU利用http或https远程更新STM32F072程序

2020-10-29 14:11:03 浏览数 (1)

说明

有些用户使用的非F103系列的单片机做项目,为方便用户移植使用

故写一节关于 STM32F072 DTU实现远程升级的移植教程!

前要

在移植到别的单片机之前,用户需要对自己的单片机有如下的了解:

1.会使用Flash存储数据,是非常的了解Flash的使用!

2.知道怎么跳转程序运行!(这个都是固定的几句程序,找找资料)

3.能用自己的单片机进行TCP访问,并模拟http协议下载数据了

4.会使用定时器.

以上缺一不可!

注:(基础知识不再重复,请用户从第一节开始看!)

因为是使用的DTU,请用户自行配置DTU连接自己的WEB服务器

可以配置成TCP模式,可以配置成我的服务器,先测试下

IP地址:  mnif.cn    端口号: 80

配置完成以后发送个指令

GET /1.txt HTTP/1.1rnHost: mnif.cnrnrn

然后会看到返回

开始移植(BootLoader程序制作)

提示:一般BootLoader程序只有保留和服务器通信的程序!

1.把移植文件放到自己的工程

2.建立两个分组,把文件添加到里面,包涵下.h路径

3.编译下工程

修改为自己单片机的头文件包含

4.屏蔽掉stmflash.c文件里面的所有实现程序

5.然后再编译一下,如果出现以下数据类型没有定义

6.最简单的方式是在自己的 stm32fxxxx.h的头文件里面加入

typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8;

7.编译一下(替换自己的复位重启程序)

8.关闭自己在BootLoader程序里面使用的所有中断

注:请自行根据自己使用的关闭,我就不写了.

9.修改检测数据某些位置是不是固定的那个数数据

注:我使用的STM32F072 和F103的一样

10.屏蔽掉以前的跳转程序,编写自己的跳转程序

11.把自己的延时函数放到此处调用

注:我就用for循环代替

12.替换自己的请求文件发送函数

注:假设DTU配置好了透传模式.(使用的串口3)

只要往串口发送数据,数据就直接发送给了服务器.

如果自己的程序文件是https访问的,请自行编写https方式发送数据的方式.

13.再次编译一下应该没有任何错误

如果有的话可能是这个地方(关闭 和 打开全局中断),请自行根据自己的单片机修改

13.重定向printf打印(可以没有,最好有!)

注:假设使用的串口1作为日志打印

14.如果自己的串口使用的阻塞方式发送的printf数据,需要屏蔽下面的部分

15.把 IAPTimerOut(); 函数放到1ms定时器中断函数里面

假设使用的定时器3

16.在主程序里面写上以下程序

代码语言:javascript复制
IAPInit();

/*  ÓиüбêÖ¾                     δÁ¬½ÓÉÏWeb·þÎñÆ÷*/
        if(IAPStructValue.UpdateFlag && !IAPStructValue.ConnectWebFlag)
        {
            if(AutoConnectTCP())//Á¬½ÓWeb·þÎñÆ÷,²»Í¬Ä£¿éÇëÌæ»»×Ô¼ºµÄÁ¬½Óº¯Êý(²»¶Ï³¢ÊÔÁ¬½Ó,Ö±ÖÁÁ¬½ÓÉÏ,»ò³ÌÐò³¬Ê±ÖØÆô)
            {
                IAPStructValue.ConnectWebFlag = 1;
            }
        }

        IAPLoadAPPProgram();//³¢ÊÔ¼ÓÔØÓû§³ÌÐò

        IAPDownloadTimeoutFunction();//³ÌÐòÏÂÔس¬Ê±,³¬Ê±ÖØÆô;
        IAPMainTimeoutFunction();//ÕûÌåÔËÐг¬Ê±,³¬Ê±ÖØÆô;
        IAPGetProgramFile();//·¢ËÍÖ¸Áî»ñÈ¡³ÌÐòÎļþ
        IAPWriteData();//°Ñ½ÓÊÕµ½µÄ³ÌÐòÎļþдÈëFlash

17.如果使用的是DTU,DTU已经连接了,所以屏蔽掉需要自己实现连接的部分

18.把 IAPPutDataToLoopList(char Res); 和 IAPHttpHead(char Res);

函数放到和模块通信的串口中断接收里面

19.设置下默认的固件程序下载地址(根据自己的服务器修改)

20.设置工程生成bin文件,然后编译下工程.

21,根据bin文件大小在 stmflash.h中调整下flash分配

STM32_FLASH_SIZE  根据自己的单片机容量调整

如果使用的是128KB Flash的单片机:

FLASH_IAP_SIZE XX   根据BootLoader生成的bin文件大小设置(该值需要大于生成的bin文件大小)

FLASH_UPDATE_SIZE 1  //存储更新相关数据所有FLASH大小,不需要改动.

FLASH_USERDATA_SIZE XX  如果用户存储的数据量比较大,增加该值即可

如果使用的是256KB及其以上 Flash的单片机:

FLASH_IAP_SIZE XX   根据BootLoader生成的bin文件大小设置(该值需要大于生成的bin文件大小,设置为4的倍数)

FLASH_UPDATE_SIZE 4  //存储更新相关数据所有FLASH大小,设置为4

FLASH_USERDATA_SIZE XX  如果用户存储的数据量比较大,增加该值即可(设置为4的倍数)

我使用的单片机是STM32F072RBT6 ,所以设置的  STM32_FLASH_SIZE 是128KB

我刚才的BootLoader程序用到了16KB,所以我设置了18KB

22.最后一个工作是完善Flash里面的函数

读写半字

代码语言:javascript复制
//¶ÁÈ¡Ö¸¶¨µØÖ·µÄ°ë×Ö(16λÊý¾Ý)
//faddr:¶ÁµØÖ·(´ËµØÖ·±ØÐëΪ2µÄ±¶Êý!!)
//·µ»ØÖµ:¶ÔÓ¦Êý¾Ý.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
    return *(__IO uint16_t*)faddr; 
}
#if STM32_FLASH_WREN    //Èç¹ûʹÄÜÁËд   
//²»¼ì²éµÄдÈë
//WriteAddr:ÆðʼµØÖ·
//pBuffer:Êý¾ÝÖ¸Õë
//NumToWrite:°ë×Ö(16λ)Êý   
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{                       
    u16 i;
    for(i=0;i<NumToWrite;i  )
    {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
        WriteAddr =2;//µØÖ·Ôö¼Ó2.
    }  
}

指定地址写入指定长度的半字数据

代码语言:javascript复制
//´ÓÖ¸¶¨µØÖ·¿ªÊ¼Ð´ÈëÖ¸¶¨³¤¶ÈµÄÊý¾Ý
//WriteAddr:ÆðʼµØÖ·(´ËµØÖ·±ØÐëΪ2µÄ±¶Êý!!)
//pBuffer:Êý¾ÝÖ¸Õë
//NumToWrite:°ë×Ö(16λ)Êý(¾ÍÊÇҪдÈëµÄ16λÊý¾ÝµÄ¸öÊý.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //×Ö½Ú
#else 
#define STM_SECTOR_SIZE    2048
#endif         
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//×î¶àÊÇ2K×Ö½Ú
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)    
{
    FLASH_EraseInitTypeDef f;
    f.TypeErase = FLASH_TYPEERASE_PAGES;
    f.NbPages = 1;
    uint32_t PageError = 0;
    
    u32 secpos;       //ÉÈÇøµØÖ·
    u16 secoff;       //ÉÈÇøÄÚÆ«ÒƵØÖ·(16λ×Ö¼ÆËã)
    u16 secremain; //ÉÈÇøÄÚÊ£ÓàµØÖ·(16λ×Ö¼ÆËã)       
     u16 i;    
    u32 offaddr;   //È¥µô0X08000000ºóµÄµØÖ·
    if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE 1024*STM32_FLASH_SIZE)))return;//·Ç·¨µØÖ·
    HAL_FLASH_Unlock();                        //½âËø
    offaddr=WriteAddr-STM32_FLASH_BASE;        //ʵ¼ÊÆ«ÒƵØÖ·.
    secpos=offaddr/STM_SECTOR_SIZE;            //ÉÈÇøµØÖ·  0~127 for STM32F103RBT6
    secoff=(offaddr%STM_SECTOR_SIZE)/2;        //ÔÚÉÈÇøÄÚµÄÆ«ÒÆ(2¸ö×Ö½ÚΪ»ù±¾µ¥Î».)
    secremain=STM_SECTOR_SIZE/2-secoff;        //ÉÈÇøÊ£Óà¿Õ¼ä´óС   
    if(NumToWrite<=secremain)secremain=NumToWrite;//²»´óÓÚ¸ÃÉÈÇø·¶Î§
    while(1) 
    {    
        STMFLASH_Read(secpos*STM_SECTOR_SIZE STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//¶Á³öÕû¸öÉÈÇøµÄÄÚÈÝ
        for(i=0;i<secremain;i  )//УÑéÊý¾Ý
        {
            if(STMFLASH_BUF[secoff i]!=0XFFFF)break;//ÐèÒª²Á³ý        
        }
        if(i<secremain)//ÐèÒª²Á³ý
        {
            f.PageAddress = secpos*STM_SECTOR_SIZE STM32_FLASH_BASE;
            
            HAL_FLASHEx_Erase(&f, &PageError);//²Á³ýÕâ¸öÉÈÇø
            
            for(i=0;i<STM_SECTOR_SIZE;i =2)//ÔÙ´ÎÅжÏÊÇ·ñÓвÁ³ýʧ°Ü
            {
              if(STMFLASH_ReadHalfWord(secpos*STM_SECTOR_SIZE STM32_FLASH_BASE i)!=0XFFFF)
                {
                    f.PageAddress = secpos*STM_SECTOR_SIZE STM32_FLASH_BASE;
                  HAL_FLASHEx_Erase(&f, &PageError);//²Á³ýÕâ¸öÉÈÇø
                    break;
                }
            }
            
            for(i=0;i<secremain;i  )//¸´ÖÆ
            {
                STMFLASH_BUF[i secoff]=pBuffer[i];      
            }
            STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//дÈëÕû¸öÉÈÇø  
        }
        else 
        {
             STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//дÒѾ­²Á³ýÁ˵Ä,Ö±½ÓдÈëÉÈÇøÊ£ÓàÇø¼ä. 
        }
                
        if(NumToWrite==secremain)break;//дÈë½áÊøÁË
        else//дÈëδ½áÊø
        {
            secpos  ;                //ÉÈÇøµØÖ·Ôö1
            secoff=0;                //Æ«ÒÆλÖÃΪ0      
            pBuffer =secremain;      //Ö¸ÕëÆ«ÒÆ
            WriteAddr =secremain;    //дµØÖ·Æ«ÒÆ       
            NumToWrite-=secremain;    //×Ö½Ú(16λ)ÊýµÝ¼õ
            if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//ÏÂÒ»¸öÉÈÇø»¹ÊÇд²»Íê
            else secremain=NumToWrite;//ÏÂÒ»¸öÉÈÇø¿ÉÒÔдÍêÁË
        }     
    };    
    HAL_FLASH_Lock();//ÉÏËø
}

写一个半字,并加入判断写入的是否正确

代码语言:javascript复制
char WriteFlashHalfWord(uint32_t WriteAddress,u16 data)
{
    HAL_FLASH_Unlock();
    FlashStatus = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddress,data);//дÈëÊý¾Ý

    if(FlashStatus == HAL_OK)//²Ù×÷Íê³É
    {
        if(STMFLASH_ReadHalfWord(WriteAddress) == data)//¶Á³öµÄºÍдÈëµÄÒ»ÖÂ
            FlashStatus = 0;//¶Á³öºÍдÈëµÄÒ»ÖÂ
        else
            FlashStatus = 5;
    }
    HAL_FLASH_Lock();//ÉÏËø
    
    return FlashStatus;
} 

指定地址读取指定长度的半字数据

代码语言:javascript复制
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)       
{
    u16 i;
    for(i=0;i<NumToRead;i  )
    {
        pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//¶ÁÈ¡2¸ö×Ö½Ú.
        ReadAddr =2;//Æ«ÒÆ2¸ö×Ö½Ú.    
    }
}

擦除页

注意:假设需要擦除20KB,BootLoader会传进来20.

BootLoader里面只是传进来需要擦除的KB数.

如果自己的单片机是以1024字节一页,那么此函数直接控制擦除20页即可.

如果自己的单片机是以2048作为一页,那么此函数就应该擦除10页!

所以源码里面有一句 if(STM_SECTOR_SIZE==2048){PageCnt=PageCnt/2;}

代码语言:javascript复制
/**
* @brief  ²Á³ýFlash
* @waing  
* @param  EraseAddress  ²Á³ýµÄµØÖ·
* @param  PageCnt       ²Á³ýÁ¬ÐøµÄ¼¸Ò³(1024¼ÆËã)
* @param  None
* @retval 4:³É¹¦
* @example 
**/
char FlashErasePage(uint32_t EraseAddress,u16 PageCnt)
{
    FLASH_EraseInitTypeDef f;
    f.TypeErase = FLASH_TYPEERASE_PAGES;
    f.NbPages = 1;
    uint32_t PageError = 0;
    
    u32 i=0;
    u32 secpos;       //ÉÈÇøµØÖ·
    if(EraseAddress<STM32_FLASH_BASE||(EraseAddress>=(STM32_FLASH_BASE 1024*STM32_FLASH_SIZE)))return 0;//·Ç·¨µØÖ·
    secpos = EraseAddress-STM32_FLASH_BASE;//ʵ¼ÊµØÖ·
    secpos = secpos/STM_SECTOR_SIZE;//ÉÈÇøµØÖ· 
    if(STM_SECTOR_SIZE==2048){PageCnt=PageCnt/2;}
//    if(FLASH_GetStatus() == FLASH_COMPLETE)//¿ÉÒÔ²Ù×÷Flash
    {
        HAL_FLASH_Unlock();
        for(i=0;i<PageCnt;i  )
        {
            f.PageAddress = secpos*STM_SECTOR_SIZE STM32_FLASH_BASE;
            FlashStatus = HAL_FLASHEx_Erase(&f, &PageError);//²Á³ýÕâ¸öÉÈÇø
            
            if(FlashStatus != HAL_OK) break;
            secpos  ;
        }
        HAL_FLASH_Lock();//ÉÏËø
    }    
    return FlashStatus;
} 

23.编译下载到单片机,查看下打印的信息

开始移植(APP用户程序制作)

1.复制一份上面的BootLoader程序作为用户程序

2.修改IAP.h文件, #define IAPProgramSelect

3.屏蔽

4.取消屏蔽

5.修改型号和info.txt文件的下载地址(根据自己的情况修改)

6.根据BootLoader先前打印的信息调整用户程序配置

7.此时编译下工程

报错原因:

咱们把48个中断地址强制放在了RAM的 0x200000000的开始地址上了.

软件编译的时候,有些变量也放到了这个地址上,然后软件就报冲突错误.

设置下RAM的偏移,让程序里面的变量从偏移的地址开始存放

7.在主函数里面添加以下函数

IAPInfoPathInit();

IAPUpdateDispose();

注意:根据前面的说明,实际中最好应该确保用户程序执行一段时间没有问题以后

再调用 IAPUpdateDispose();函数

8.增加使用get指令访问info.txt文件命令

连接的服务器的IP地址 IAPStructValue.IP(字符串)   端口号 IAPStructValue.Port

文件路径 IAPStructValue.Path

以上信息是由 IAPInfoPathInit();函数解析而来

如果使用的是DTU,只需要 用到 文件路径 IAPStructValue.Path

获取 info.txt 文件的get指令拼接方式如下:

MainLen = sprintf((char*)MainBuffer,"GET %s HTTP/1.1rnHost: %srnrn",IAPStructValue.Path,IAPStructValue.IP);

然后把这个消息发给服务器即可

9.编写解析获取的文件信息

提示一下info.txt文件内容格式:

{"version":"0.0.1","size":15990,"url":"http://mnif.cn/ota/hardware/STM32ESP8266BK/user_crc.bin","info":"1.解决了部分BUG 2.优化了部分程序"}

注:具体的提取程序,请自行编写,可参照提供的源码.

10.修改下版本

11.编译下工程生成bin文件,打开OTA Tools 软件

选择bin文件,点击带有crc校验的bin文件

11.根据生成的信息修改 info.txt文件信息

12.然后把 info.txt 和 bin文件放到云端即可

注意: info.txt的路径要根据自己设置的地址进行存放

bin文件路径,要根据 info.txt里面的url地址进行存放

0 人点赞