前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >20-STM32+ESP8266+AIR202远程升级方案-扩展-移植远程更新方案包,实现STM32F072+DTU利用http或https远程更新STM32F072程序

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

作者头像
杨奉武
发布2020-10-29 14:11:03
8990
发布2020-10-29 14:11:03
举报
文章被收录于专栏:知识分享

说明

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

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

前要

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

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

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

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

4.会使用定时器.

以上缺一不可!

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

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

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

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

配置完成以后发送个指令

GET /1.txt HTTP/1.1\r\nHost: mnif.cn\r\n\r\n

然后会看到返回

开始移植(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.1\r\nHost: %s\r\n\r\n",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地址进行存放

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-10-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 说明
  • 前要
  • 注:(基础知识不再重复,请用户从第一节开始看!)
  • 开始移植(BootLoader程序制作)
  • 开始移植(APP用户程序制作)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档