通过W5500的网络功能,到文件服务器下载STM32要更新的固件(可执行bin文件),存储到STM32片内FLASH的APP备份区中,以待bootloader拷贝到APP代码执行区,以实现OTA在线升级的功能。

我使用的芯片是STM32F103RCT6,48Kbyte的RAM内存和256Kbyte的片内FLASH。

片内FLASH区域划分

对于片内FLASH应用的划分,包括4个区域:bootloader区(36Kbyte),App代码执行区(108Kbyte),App代码备份区(108Kbyte),用户数据记录区(4Kbyte),共256Kbyte,如图所示:

那么片内FLASH各个区域对应的索引地址为:

Bootloader功能设计与实现

bootloader和普通的app程序类似,只不过它在STM32上电后开始执行的,它负责检测用户数据数据记录区是否有已经下载好的固件要更新。

如果用户数据数据记录区没有下载好的固件数据要更新,则直接跳转到App代码执行区;

如果用户数据数据记录区有下载好的固件数据要更新,那么根据用户数据记录区的下载固件文件长度,将App代码备份区的数据拷贝到App代码执行区,如果拷贝完成,则将用户数据记录区和App代码备份区数据擦除,再跳转到App代码执行区去执行更新好的新程序。

在Keil工程中,bootloader的配置

bootloader的代码实现:

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif#ifndef __Z_HARDWARE_FLASH_H
#define __Z_HARDWARE_FLASH_H
#include "z_hardware_flash.h"
#endif#include <string.h>#define APP_FLASH_ADDRESS     (0x8009000)
#define BACKUP_FLASH_ADDRESS (0x8024000)
#define RECORD_FLASH_ADDRESS (0x803F000)typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;void func_jump2app(void)
{if (((*(__IO uint32_t*)APP_FLASH_ADDRESS) & 0x2FFC0000 ) == 0x20000000){__disable_irq();JumpAddress = *(__IO uint32_t*) (APP_FLASH_ADDRESS + 4);Jump_To_Application = (pFunction) JumpAddress;__set_MSP(*(__IO uint32_t*) APP_FLASH_ADDRESS);Jump_To_Application();}
}void func_led_onoff_cross(void);u16 flash_buf[1024];
int main()
{u8 i;u32 len_backup;init_led();func_flash_read_datas(RECORD_FLASH_ADDRESS, flash_buf, 128);delay_ms(10);if(flash_buf[0] == 0x0002)// to update{u8 i, page, suc;len_backup = flash_buf[2]*65536 + flash_buf[3];page = len_backup / SECTOR_SIZE + 1;if(page <= 54)//108kbyte{for(i = 0; i < page; i++){memset(flash_buf, 0xFFFF, sizeof(flash_buf));func_flash_read_datas(BACKUP_FLASH_ADDRESS + i*2048, flash_buf, 1024);suc = func_flash_write_datas(APP_FLASH_ADDRESS + i*2048, flash_buf, 1024);if(suc != 0){break;}}if(suc == 0)//copy success{//clear backupfor(i = 0; i < page; i++){func_flash_erase_page(BACKUP_FLASH_ADDRESS + i*2048);}func_flash_erase_page(RECORD_FLASH_ADDRESS);}}        }for(i = 0; i < 5; i++){func_led_onoff_cross();}func_jump2app();}void func_led_onoff_cross()
{func_led1_on();func_led2_off();delay_ms(200);func_led2_on();func_led1_off();delay_ms(200);
}

App功能设计与实现

App的功能,业务逻辑部分不作为本文描述重点。关于网络或其他方式下载固件,通过一个外部触发去执行,我这里使用了一个拨码开关来触发下载固件的事件。

在App代码执行开始的地方,做一个中断向量的配置

NVIC_SetVectorTable(NVIC_VectTab_FLASH, (FLASH_APPCODE_ADDR - FLASH_BASE_ADDR));

然后初始化各个GPIO的操作,由于我是通过W5500的Http Get方式下载可执行文件bin,初始化一下网络的配置,DHCP动态获取IP等。还有一个地方,是在网络下载数据的过程,通过一个钩子函数进行注册存储函数,以实现下载的方法和业务分离。

//hook
init_hooks_http_download_file_save(func_custom_http_downloading_file_save);

文件下载的内容,存储到App代码备份区,下载完成后会存储升级固件的标记以及下载了的固件文件的长度。

至于 STM32 W5500 Http Get方式下载固件的思路和实现,可参考 《STM32 W5500 Http Client Get请求 下载bin文件思路和实现》。

下载完成后,程序跳转到bootloader去执行。

关于App的工程配置

App的测试代码:

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif#ifndef __Z_HARDWARE_FLASH_H
#define __Z_HARDWARE_FLASH_H
#include "z_hardware_flash.h"
#endif#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif#ifndef __Z_HARDWARE_SWITCHKEYS_H
#define __Z_HARDWARE_SWITCHKEYS_H
#include "z_hardware_switchkeys.h"
#endif#ifndef __W5500_H
#define __W5500_H
#include "w5500.h"
#endif#ifndef __SOCKET_H
#define __SOCKET_H
#include "socket.h"
#endif#ifndef __W5500_CONF_H
#define __W5500_CONF_H
#include "w5500_conf.h"
#endif#ifndef __DHCP_H
#define __DHCP_H
#include "dhcp.h"
#endif#ifndef __HTTPC_H
#define __HTTPC_H
#include "httpc.h"
#endiftypedef  void (*pFunction)(void);
pFunction Jump_To_Bootloader;
uint32_t JumpAddress;void func_jump2bootloader(void)
{if (((*(__IO uint32_t*)FLASH_BASE_ADDR) & 0x2FFC0000 ) == 0x20000000){__disable_irq();JumpAddress = *(__IO uint32_t*) (FLASH_BASE_ADDR + 4);Jump_To_Bootloader = (pFunction) JumpAddress;__set_MSP(*(__IO uint32_t*) FLASH_BASE_ADDR);Jump_To_Bootloader();}
}void func_led_onoff_cross(void);
void func_led_onoff_sametime(void);void func_custom_http_downloading_file_save(u8 state, u8 pageno, u8* cache, u32 len_cache_cont)//pageno start from 1
{
//  func_usart2_dma_send_bytes(cache, len_cache_cont);if(state == 0)//downloading{//write FLASH Backup file contentfunc_flash_write_datas(FLASH_APPBACKUP_ADDR + (pageno-1)*2048, (u16*)cache, SIZE_DOWNLOAD_CACHE/2);}else if(state == 1)//done{//write FLASH Backup file contentfunc_flash_write_datas(FLASH_APPBACKUP_ADDR + (pageno-1)*2048, (u16*)cache, (len_cache_cont%2) == 0 ? (len_cache_cont/2) : (len_cache_cont/2+1));//write FLASH Record{u16 arrs[4];u32 downloadsize = (pageno-1)*SIZE_DOWNLOAD_CACHE + len_cache_cont;arrs[0] = 2;arrs[1] = 0xFFFF;arrs[2] = downloadsize >> 16;arrs[3] = (u16)downloadsize;func_flash_write_datas(FLASH_RECORD_ADDR, arrs, 4);           }}
}u8 buf[2048];int main()
{u8 state_k1_init;u8 mac[6]={0, };DHCP_Get dhcp_get;//FIXME your file server ipu8 srv_ip[] = {192, 168, 1, 109};u16 srv_port = 8888;NVIC_SetVectorTable(NVIC_VectTab_FLASH, (FLASH_APPCODE_ADDR - FLASH_BASE_ADDR));init_led();init_switchkeys();init_system_spi();func_w5500_reset();   //  init_hardware_usart2_dma(115200);getMacByLockCode(mac);setSHAR(mac);sysinit(txsize, rxsize);setRTR(2000);setRCR(3);state_k1_init = func_get_switchkey1();//USART DMA problem: 2 bytes missing
//  func_usart2_dma_send_bytes(mac, 2);
//  delay_ms(100);//hookinit_hooks_http_download_file_save(func_custom_http_downloading_file_save);//DHCPfor(;func_dhcp_get_ip_sub_gw(buf, sizeof(buf), 1, mac, &dhcp_get, 500) != 0;);    if(func_dhcp_get_ip_sub_gw(buf, sizeof(buf), 1, mac, &dhcp_get, 500) == 0){setSUBR(dhcp_get.sub);setGAR(dhcp_get.gw);setSIPR(dhcp_get.lip);close(1);}for(;;){if(state_k1_init != func_get_switchkey1()){u8 res;//downlaod bin file and rebootmemset(buf, 0 , sizeof(buf));res = func_http_get_download_file(0, srv_ip, srv_port, "/file/APP_Download.bin_1.1.8", 5000, buf, sizeof(buf));if(res == 0){//jump to bootloaderfunc_jump2bootloader();}state_k1_init = func_get_switchkey1();}
//      func_led_onoff_cross();func_led_onoff_sametime();}}void func_led_onoff_cross()
{func_led1_on();func_led2_off();delay_ms(500);func_led2_on();func_led1_off();delay_ms(500);
}void func_led_onoff_sametime()
{func_led1_on();func_led2_on();delay_ms(500);func_led2_off();func_led1_off();delay_ms(500);
}

测试与结果

将bootloader编译并生成bin文件,通过STM32 ST-LINK Utility工具,将bootloader下载到0x08000000的位置上,如图:

将App编译一版LED1和LED2交叉亮灭的固件,通过STM32 ST-LINK Utility工具,将App下载到0x08009000的位置上,如图:

再编译一版LED1和LED2同时亮灭的固件,上传到文件服务器。

复位板子后,程序先执行bootloader的LED交叉快速闪烁,随后进入到原始App的LED慢速交叉闪烁。扳动拨码开关,随后几秒,LED交叉快速闪烁,然后两个LED同时亮,同时灭。App固件升级完成!

总结

bootloader和App的思路比较简单,但是实现的过程中,可能会遇到一些坑。说说我遇到过的坑:

1、bootloader工程target的FLASH大小配置

2、bootloader工程的startup_stm32f10x_hd.s文件的堆栈大小配置过小,导致Debug不好用,配置大一些就好了。

3、bootloader工程操作FLASH的代码问题,导致bin文件内容串位-通过STM32 ST-LINK Utility工具检查 FLASH中实际的内容是什么,从而排查解决了问题。

4、App工程也是因为FLASH的操作不当,导致用户数据记录区数据串位。

STM32 W5500 OTA功能 - bootloader及app的设计和实现相关推荐

  1. 基于STM32的多功能MP3设计 毕业设计(论文)开题报告

    中国计量学院 毕业设计(论文)开题报告 学生姓名:卢杰学 号:XXXXXXXXX 专    业:电子科学与技术 班    级:10电子1 设计(论文)题目: 基于STM32的多功能MP3设计 指导教师 ...

  2. 宠物电商社区APP的设计与实现

    摘  要:为了设计并实现具有宠物产品商城和问答社区功能的APP,通过比较国内外宠物行业电商发展现状和国内已有的宠物电商平台的优缺点,分析可行性和需求,从而进行详细设计和实现.该APP后台采用SSM框架 ...

  3. stm32 esp8266 ota升级-自建mqtt和文件服务器全量升级

    stm32 esp8266 ota系列文章: stm32 esp8266 ota-快速搭建web服务器之docker安装openresty stm32 esp8266 ota升级-tcp模拟http ...

  4. stm32 通用bootloader_STM32通用Bootloader

    STM32 通用 Bootloader 简介 为了能让开发者快速掌握 OTA 升级这把利器,RT-Thread 开发团队提供了通用的 Bootloader.开发者通过该 Bootloader 即可直接 ...

  5. esp32 Flash分区与OTA功能简析

    升级功能对于所有的嵌入式产品都是非常重要的.尤其是当产品量产/销售阶段,已经没有条件让厂家对产品升级,因此升级方式的设计必须防呆防错以及稳定. 乐鑫ESP32作为蓝牙WIFI合一的物联网芯片,开发成产 ...

  6. 我的STM32 IAP BOOT跳转到APP进入HardFault_Handler解决方案

    客户要求实现OTA功能,于是程序分BOOT和APP,因需要添加一个浮点型全局变量gfHtTmpValue,发现只要调用这个全局变量,BOOT跳转APP后,APP初始化外设结束进入HardFault_H ...

  7. 如何使用STM32F4的BootLoader和APP程序

    源:如何使用STM32F4的BootLoader和APP程序 STM32 BootLoader升级固件

  8. stm32+W5500+阿里物联网平台

    前提: 非物联网专业出身,网络协议一知半解(就是没学过),最近调试一块stm32+w5500开发板,为了学习知识,实现以个依靠阿里云物联网平台控制开发板上LED开关功能.(2020年4月24日) 1: ...

  9. STM32+W5500以太网模块

    一:w5500以太网模块介绍: W5500 是一款全硬件 TCP/IP 嵌入式以太网控制器,为嵌入式系统提供了更加简易的互联网连接方 案.W5500 集成了 TCP/IP 协议栈,10/100M 以太 ...

最新文章

  1. American Heritage
  2. LNMP环境搭建——PHP篇
  3. 推荐一款Java开发的精美个人博客
  4. 一、cocos2dx概念简介
  5. Android之自定义View实现带4圆角或者2圆角的效果
  6. python提取包含特定字符串的行_python语言----txt中搜索特定字符串所在行
  7. 常用中后台交互设计控件使用场景与规范总结
  8. centos8 装docker 问题
  9. urllib2:URLError与HTTPError
  10. 解决方法 svn checkout 更改用户名密码/断网续传
  11. start_kernel之前的调用流程(head.s)
  12. def在python中什么意思_《python中的def是什么意思》
  13. kernel - 制作linux开机logo
  14. 获取手机机身和外置SD卡存储容量的方法
  15. 15个常用excel函数公式_【Excel公式函数】一大波常用的日期公式来袭,强烈建议收藏!...
  16. 工厂设计模式 - 详解
  17. 涂鸦智能赴美上市:2年亏损1.4亿美元,腾讯、高瓴等参与认购
  18. 牙齿松动怎么办|成年人牙齿松动怎么办
  19. 结交各大云平台 IoT 专家的机会来啦!
  20. 项目需求说明书中的假定和约束

热门文章

  1. 2023年,如何自学通过PMP?(含pmp资料)
  2. 产品经理有必要考个 PMP吗?(含PMP资料)
  3. 十分钟python入门教程
  4. ad15原理图中变压器种类_简单高效1.5v升压电路图大全(七款1.5v升压电路设计原理图详解)...
  5. 【智能制造】奔向智能制造;什么样的人能引领工厂智能制造发展?
  6. 2021-03-23 - 高性能 Redis 实战
  7. android studio真机调试华为手机
  8. 在html中写js打开是乱码,javascript脚本中文乱码如何解决?
  9. 2016c语言模拟试卷A,2016C语言习题模拟试卷一.doc
  10. 极光推送设置别名错误码6002