量产级别代码,单片机通过WiFI进行OTA固件升级

原创 芯片之家 2025-05-15 17:50

在物联网设备快速迭代和远程维护需求日益增长的今天,OTA(Over-the-Air)远程升级技术已成为智能硬件开发中不可或缺的一部分。

OTA升级,指通过无线通信方式(如Wi-Fi、蓝牙、NB-IoT等)将新固件发送到设备,实现远程软件更新。它有以下几个显著优势:

  • 无需现场维护:节省人工成本,提高维护效率

  • 产品生命周期内灵活迭代:快速修复BUG、添加功能

  • 提高用户体验:实时响应用户反馈

本文将详细介绍如何基于涂鸦 IoT 平台,在STM32F103C8T6这款经典单片机上实现 OTA 升级,助力你的产品实现远程升级,运维起来更加。

当然,不止于涂鸦平台,原理都一样,可以在任何支持的平台上实现,包括MCU,其它型号的MCU也都大同小异。

核心思想非常简单,我们把整个FLASH分成4部分,bootloader,APP1,APP2,FLAG


STM32F103C8T6一共64K,FLASH一共64页,每页1K,bootloader分8K,FLAG分2K,APP1与APP2各27K,也就是我们的应用程序,编译出来不能超过27K的大小。

bootloader是一个独立的固件,在启动后负责检查FLAG区域的升级标志位以确定是否有新的固件需要升级,如果有就跳到升级部分,将APP2的部分copy到APP1,copy完之后再清空FLAG区域的升级标志,然后重启,就会运行新的APP1也就是升级后的程序了。

这里如果升级到一半断电了,下次重启还是会重新copy一次APP2到APP1,因为升级标志位还没被清空,所以这里不用担心升级的时候断电导致无法重启

bootloader在启动的时候,如果FLAG区域的升级标志位没有置位,那就直接启动APP1就可以了。

大概原理就是这样,下面我们结合代码看下:

  if(ReadFlashTest(UPFLAG) == 0x55555555)  //0x55555555
  {
     update_firmware(APP1ADDR,APP2ADDR);
     EarseFlash_1K(UPFLAG);
     WriteFlash(UPFLAG,UPFlagBuffer,4);
     printf("APP复制中\r\n");
     NVIC_SystemReset();
  }
  else 
  {
     Jump2APP(APP1ADDR);
  }

从APP2复制到APP1,先读一页,然后擦除,再写:

void update_firmware(uint32_t SourceAddress,uint32_t TargetAddress)
{
  //先读  后擦除  后写  
  uint16_t i;
  volatile uint32_t nK ;
  nK = (TargetAddress - SourceAddress)/1024;
  for(i = 0;i < nK;i++)
  {
    memset(cBuffer,0xff,sizeof(cBuffer));
    ReadFlash(TargetAddress+i*0x0400,cBuffer,1024); 
    EarseFlash_1K(SourceAddress+i*0x0400);
    WriteFlash(SourceAddress+i*0x0400,cBuffer,1024); 
  }
}

Jump2APP函数通过验证应用程序地址、设置堆栈指针以及跳转到应用程序的入口点,促进从引导加载程序到应用程序代码的转换。

void Jump2APP(uint32_t app_address)
{
  volatile uint32_t JumpAPPaddr;
if (((*(uint32_t*)app_address) &0x2FFE0000 ) == 0x20000000)
  {
    JumpAPPaddr = *(volatile uint32_t*)(app_address + 4);
    jump2app = (APP_FUNC) JumpAPPaddr;
    printf("APP跳转地址:%x\r\n",app_address);
    __set_MSP(*(__IO uint32_t*) app_address);
    jump2app();
  }
else{
    printf("输入地址不合法\r\n");
  }
}

volatile uint32_t JumpAPPaddr; 声明一个变量,用于存储应用程序的入口点地址(重置处理程序的地址)。volatile关键字确保编译器不会优化对此变量的访问,因为它将从内存中读取。

if ((((uint32_t)app_address) & 0x2FFE0000 ) == 0x20000000) 在 STM32 向量表中,第一个字(位于app_address处)是初始堆栈指针(MSP,即主堆栈指针)。检查掩码值是否指示堆栈指针位于 SRAM 中。如果为真,则该地址被视为有效,因为合法应用程序的堆栈指针应该位于 SRAM 中。

JumpAPPaddr = (volatile uint32_t)(app_address + 4); app_address + 4指向向量表中的第二个字,该字保存重置处理程序(应用程序的入口点)的地址。

jump2app = (APP_FUNC) JumpAPPaddr; 将程序地址转换为函数指针。

__set_MSP((__IO uint32_tapp_address); __set_MSP是一个 CMSIS 函数,它使用从app_address (应用程序的初始堆栈指针)读取的值更新 MSP 寄存器。

jump2app(); 调用函数指针jump2app,实际上将控制权转移到应用程序的入口点。这将启动应用程序代码的执行。

bootloader部分的设置,IROM1的起始地址为0x8000000,大小为0x2000。

选择Erase Sectors,Reset and Run,下载。

bootloader部分就介绍到这里。

下面介绍APP部分。

用户的代码逻辑都在APP部分,APP部分需要额外设置的比较简单,我们只需要在代码启动后重新设置一下应用程序的向量表地址

    __set_FAULTMASK(1);
    SCB->VTOR = FLASH_BASE | APP1;  
    __set_FAULTMASK(0);

在引导加载程序跳转到应用程序后执行(例如,通过Jump2APP)。它确保应用程序将 Cortex-M CPU 配置为其自己的向量表,设置前关闭中断,确保向量表重定位不会被中断。

这里中断向量表的起始地址就是0x08002000了。

这里需要注意的是,应用程序的下载地址需要设置为0x8002000,大小为0x6C00

在Keil中,需要配置下编译选项fromelf.exe --bin -o "$L@L.bin" "#L",以生成bin文件。

bin文件不同于hex文件,hex文件中包含烧录地址,bin文件中不含,所以我们可以指定bin文件的烧录地址。这样,在烧录的时候,就可以直接烧录APP到0x08002000了。

也就是开始需要先烧录bootloader文件,再烧录app文件,后续量产的时候,看可以把bootloader文件与app文件合并成一个文件进行烧录。

关于如何合并,我们之前发过一篇文章可以参考。

MCU OTA方案中,BootLoader与APP如何合并?(点击阅读)

APP1运行的时候,如果检测到服务器有新的版本号,就开始执行升级指令,就会将新版本的bin文件,分包向APP1发送,APP1接收到之后,就原封不动的将其存储到APP2区域,最后一包接收完之后,就将FLAG区域的升级标志位设置为相应的标志数据,然后重启,就回到上面的升级流程了。

#ifdef SUPPORT_MCU_FIRM_UPDATE
        case UPDATE_START_CMD:                                //升级开始
            //获取升级包大小全局变量
            firm_flag = PACKAGE_SIZE;
            if(firm_flag == 0) {
                firm_size = 256;
            }elseif(firm_flag == 1) {
                firm_size = 512;
            }elseif(firm_flag == 2) { 
                firm_size = 1024;
            }

            firm_length = wifi_data_process_buf[offset + DATA_START];
            firm_length <<= 8;
            firm_length |= wifi_data_process_buf[offset + DATA_START + 1];
            firm_length <<= 8;
            firm_length |= wifi_data_process_buf[offset + DATA_START + 2];
            firm_length <<= 8;
            firm_length |= wifi_data_process_buf[offset + DATA_START + 3];
            
            upgrade_package_choose(PACKAGE_SIZE);
            firm_update_flag = UPDATE_START_CMD;
        break;
    
        case UPDATE_TRANS_CMD:                                //升级传输
            if(firm_update_flag == UPDATE_START_CMD)
            {
                //停止一切数据上报
                stop_update_flag = ENABLE;
      
                total_len = (wifi_data_process_buf[offset + LENGTH_HIGH] << 8) | wifi_data_process_buf[offset + LENGTH_LOW];
      
                dp_len = wifi_data_process_buf[offset + DATA_START];
                dp_len <<= 8;
                dp_len |= wifi_data_process_buf[offset + DATA_START + 1];
                dp_len <<= 8;
                dp_len |= wifi_data_process_buf[offset + DATA_START + 2];
                dp_len <<= 8;
                dp_len |= wifi_data_process_buf[offset + DATA_START + 3];
      
                firmware_addr = (unsigned char *)wifi_data_process_buf;
                firmware_addr += (offset + DATA_START + 4);
      
                if((total_len == 4) && (dp_len == firm_length))
                {
                    //最后一包
                    ret = mcu_firm_update_handle(firmware_addr,dp_len,0);
                    firm_update_flag = 0;
                }
                elseif((total_len - 4) <= firm_size)
                {
                    ret = mcu_firm_update_handle(firmware_addr,dp_len,total_len - 4);
                }
                else
                {
                    firm_update_flag = 0;
                    ret = ERROR;
                }
      
                if(ret == SUCCESS)
                {
                    wifi_uart_write_frame(UPDATE_TRANS_CMD, MCU_SEND_VER, 0);
                }
                //恢复一切数据上报
                stop_update_flag = DISABLE;    
            }
        break;
#endif     

基本原理就是这样,关于STM32一些启动过程原理与涂鸦的SDK逻辑,本文不做详细的介绍,大家自行看一下就都懂了。

以上OTA代码逻辑,在实际产品中,已经量产并持续在使用,有需要的小伙伴可以参考,欢迎大家在评论区与老宇哥进行交流。

代码源文件全部工程:

通过网盘分享的文件:tuya-OTA-lightDemo1.0.0.rar

链接: https://pan.baidu.com/s/1z0EaHj3iRFYIGCbngpY5hg?pwd=8k17 提取码: 8k17

看到这里的小伙伴,就点个赞支持一下老宇哥吧,不然,都没动力码字了。

↑↑↑ 点击领取6层板打样券 ↑↑↑ 
往期推荐

写出漂亮规范的代码!分享一份嵌入式C编码规范(收藏细读)

老板说,单片机,Flash模拟EEPROM,16字节,算法轮询存储给我做到100万次的存储次数

玩转PD步进电机(一)硬件篇

电子漫画系列更新...(第22期)

评论
  • 贞光科技作为业内知名的车规及工业元器件供应商,现已成为紫光国芯存储芯片的授权代理商。在半导体存储芯片国产化的关键时期,这一合作为推动DRAM等关键器件的国产替代开辟了新的渠道。紫光国芯在存储芯片领域的技术积累,加上贞光科技在车规和工业应用方面的专业优势,正在为客户提供更可靠的国产化解决方案。产品技术实力:从DDR到LPDDR的全面布局紫光国芯在存储芯片方面的技术积累确实令人印象深刻。该公司的DRAM产品覆盖标准SDR、DDR、DDR2、DDR3、DDR4和移动用LPDDR、LPDDR2、LPDD
    贞光科技 2025-06-13 16:01 50浏览
  • FPU 是“Floating Point Unit”的缩写,中文意思是“浮点运算单元”。它是一种专门用于处理浮点数运算的硬件组件,通常集成在计算机的处理器(CPU)中,也可以是一个独立的芯片。以下是关于 FPU 的一些详细信息:  1. 功能    浮点数运算 :FPU 主要用于执行浮点数的加、减、乘、除等基本运算,以及更复杂的数学运算,如平方根、对数、三角函数等。    提高计算效率 :与整数运算相比,浮点运算在处理小数和高精度计算时更加高效。
    丙丁先生 2025-06-13 09:33 39浏览
  • 0.6"HDMI单目方案 在 AR 产业浪潮席卷全球的当下,Micro OLED 凭借高分辨率、低延迟等特性成为显示技术新宠。从消费级智能设备到专业级行业应用,Micro OLED 商业化进程也在全速推进。面对瞬息万变的市场环境,终端应用厂商亟需高效解决方案加速产品验证与落地。冠显光电最新推出的0.6"HDMI单目驱动板方案,以小型化设计和灵活开发能力,助力企业抢占市场先机。方案组成 该方案主要包括0.6”硅基显示屏,HDMI单目显示屏驱动板。驱动板以 Micro HDMI
    冠显光电MicroOLED代理视涯 2025-06-13 13:54 751浏览
  • 全球第一大PC厂商联想Lenovo,近期发布Lenovo Air X AI元启版,Lenovo YOGA 360 14 Aura Al元启翻转轻薄本、Lenovo YOGA 360 14 AI元启版、Lenovo YOGA Air 15 Aura Al元启版等多款笔记本电脑,均INBOX标配65W 49cc氮化镓快充充电器,这是迄今为止联想笔记本电脑最小体积的65W INBOX电源适配器。据悉,该款产品得益于镓未来G1N65R150PB-N GaN方案以及平面变压器模块的加持,设计得相当小巧,尺
    电子热文焦点 2025-06-13 14:17 58浏览
  • 夸克发布首个高考志愿大模型,为考生提供了智能化、个性化的志愿填报服务,这一创新对考生群体和传统报考机构产生了截然不同的影响。对于考生而言,夸克高考志愿大模型无疑是一大利好。该模型整合了教育官网、省招生办官网等权威信息源,确保数据准确可信,同时将高校毕业生就业信息、产业趋势等纳入知识库,为考生提供更全面的参考。通过深度搜索、志愿工具和志愿报告三大服务,夸克能精准理解考生需求,提供从专业适配性、地域优势到就业前景的全方位分析,并生成专属志愿报告和多种填报策略。这种智能化、个性化的服务大大减轻了考生和
    curton 2025-06-12 17:24 6454浏览
  • 随着数据中心对于网络带宽和延迟的要求日益增长,传统的TCP/IP网络已无法满足性能要求,RDMA网络则凭借其高带宽、低延时的特性脱颖而出。相较于传统TCP/IP协议,RDMA具有零拷贝、不需要CPU接入、消息基于事务等特点 。RDMA协议与传统TCP/IP协议在通信过程中的区别如图1所示。图1TCP/IP协议与RDMA协议区别图在图1中,左侧部分为传统TCP/IP协议的通信过程,当需要发送数据包时,应用程序将要传输的数据拷贝到TCP/IP协议栈的BUFFER中,TCP/IP协议栈组包后,再经由驱
    zzbwx_326664406 2025-06-14 23:28 79浏览
  • 随着游戏和影音需求的快速增长,市场上越来越多屏幕主打「高刷新率」,号称能为用户带来更流畅的视觉体验。不论是追求实时反应的游戏玩家,还是偏好画面清晰稳定的影视爱好者,高刷新率屏幕都成为热门的选择。然而,流畅的画面不只取决于刷新率,常被忽略的掉帧(Frame Drop)也是影响用户体验的关键因素。若PC系统无法稳定输出对应的FPS(每秒帧数),即使具备高刷新率,画面仍然会因掉帧而导致卡顿、撕裂、延迟等问题,让用户感受大打折扣。掉帧问题影响到游戏流畅示意图本次案例是笔电厂商,近期推出的一款支持高刷新率
    百佳泰测试实验室 2025-06-12 18:02 46浏览
  • 七年前买了个远程控制开关,想想那个时候应该物联网才兴起的时候吧。如今因为控制麻烦且经常出现连接掉线问题,于是给淘汰了。这个设备我是拿来控制吊灯,特别麻烦的是,当晚上关灯后,会有一点灯点亮着,掉线的时候还会闪,想想睡梦中醒来往天花板一看,一个东西在那闪多吓人,关键还是绿色的。而且二次匹配需要打开灯罩,按那个黑色的按钮才能重新配网。种种原因,让我今天给他拆了,结构也简单,拆开外壳就只有一个主板正面正面电路看起来还是很简单的:220V经过整流桥(背面U1),通过变压器将市电转化低压直流电一个继电器,这
    二月半 2025-06-12 14:32 321浏览
  • 本文教你从开发到部署,用 Docker 将 Python Flask 应用容器化,引入反向代理 Nginx,实现 HTTPS 支持、环境隔离与持续部署准备,是现代开发者必备的实战技能。https://txc.qq.com/products/732159/blog/1106857 https://txc.qq.com/products/732159/blog/1106856 https://txc.qq.com/products/732160/blog/1106855 https://txc
    小菜菜编程 2025-06-12 16:36 369浏览
  • 在金融 AI 赛道一路疾驰的百融云创,向来是资本市场的焦点。2021年上市以来,其营收曾一路高歌猛进,从2021年的16.23亿元,到2022年的20.54亿元,再到2023年的26.81亿元,增长率颇为亮眼。净利润也随之水涨船高,分别为1.41亿元同比增长76%、2.94亿元同比增幅108%、3.4亿元同比增长42%。不曾想,2024年风云突变,百融云创虽营收达29.29亿元,仍有 9% 的增长,但净利润却骤降至2.66亿元,同比下滑21.82%,上市以
    用户1742991715177 2025-06-15 21:36 908浏览
  • 《从磁能管理到开关电源设计》+认识电感 很幸运能获得这次《从磁能管理到开关电源设计》试读机会。让我能通过这本书更全面的了解电感,了解电感的生产,应用。更全面的了解电感。是的,电感,电阻,电容在电路应用中是基本的三大被动器件。在各领域都有所应用。 电感在开关电路中应用较为常见,是不可或缺的器件,他把电路中的电能转化为磁能阻碍电流的变化。电感的单位是H,它不同于磁珠。磁珠是应用在高频电路中,高频下成电阻产生热降低EMI.而电感式工作在低频中。在电源输入有差分电感,共模电感,等等不同种类。对EMI有各
    zhusx123 2025-06-14 20:29 851浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦