STM32 OTA应用开发——自制BootLoader

news2024/9/22 15:43:20

STM32 OTA应用开发——自制BootLoader

目录

  • STM32 OTA应用开发——自制BootLoader
    • 前言
    • 1 环境搭建
    • 2 BootLoader工作原理以及常见分区介绍
    • 3 BootLoader的制作
    • 4 烧录下载配置
    • 5 运行测试
    • 结束语

前言

什么是OTA?

百度百科:空中下载技术(Over-the-Air Technology; OTA),是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。经过公网多年的应用与发展,已十分成熟,网络运营商通过OTA技术实现SIM卡远程管理,还能提供移动化的新业务下载功能。

实际上,现在我们所说的OTA比百度百科的定义还要更广泛,OTA的形式已经不再局限于手机和SIM卡,只要涉及到远程下载升级程序的方式我们都可以称之为OTA。例如通过4G,5G,WiFI,蓝牙等无线通讯进行下载升级的可以称为OTA,通过U盘,RS485等串行接口进行升级的也可以称之为OTA。

OTA的作用?
OTA的意义在于它在一定程度上突破了距离的限制,在不借助烧录器的情况下完成固件的下载升级,极大的方便了产品的升级和维护,降低售后成本。

什么是BootLoader?

百度百科:在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。

实际上,BootLoader不仅仅在操作系统上使用,在一些内存小,功能应用较为简单的单片机设备上面也可以通过BootLoader来完成OTA升级。

我之前也有发过一些关于STM32远程OTA的文章,但用的是第三方BootLoader,而且是基于操作系统实现的。BootLoader占用的内存也比较大,而且不开源。
所以这一讲我就来介绍一下如何自己制作一个简单的BootLoader程序。

1 环境搭建

关于STM32以及Keil的环境这里就不具体介绍了,网上教程也很多,不懂的同学自行查阅资料。

2 BootLoader工作原理以及常见分区介绍

不管用的是什么MCU,要使用OTA都离不开BootLoader,BootLoader是一个统称,它其实只是一段引导程序,在MCU启动的时候会先运行这段代码,判断是否需要升级,如果不需要升级就跳转到APP分区运行用户代码,如果需要升级则先通过一些硬件接口接收和搬运要升级的新固件,然后再跳转到APP分区运行新固件,从而实现OTA升级。
请添加图片描述

常见分区方式介绍:
1.Application
没有加入Bootloader之前,我们单片机内部的flash就是一整块的,所有的应用代码都放在这。
请添加图片描述

2.Bootloader + Application
在原有的flash区域里面划分出两个区域,Bootloader和Application,这种分区方式的好处在于既可以OTA,Appd区又可以分到较大的空间,缺点是没有存放新固件的区域,需要从外部导入进来,而且一旦传输的过程被异常打断,那么原有的App代码也无法正常运行了,也就是传说中的“变砖”。
请添加图片描述

3.Bootloader + Application + Download
这种分区方式是比较万能的一种,优点是新固件是先存放到Download区的,哪怕搬运的过程中出现异常中断的情况,也不会“变砖”,缺点是需要单独划分一块内存跟APP区差不多的区域用来存放新固件,变相的减少了APP区的空间,对于内存较小的单片机来说压力就比较大了。
请添加图片描述

4.Bootloader + Application1 + Application2
这种方式可以同时存在两套App,优点在于升级了新固件以后,还保留了原来的旧版固件,必要的时候还可以进行版本的回退。
请添加图片描述

5.Bootloader + Setting + Application + Download
这种方式跟第3种基本一样,只是增加了一个区域用来存放升级相关的一些参数以及用户的一些配置。
请添加图片描述

3 BootLoader的制作

BootLoader的制作需要根据实际的需求来做,不同的运行方式或者升级方式在做法上都是有区别的,包括BootLoader所需要的内存空间也不尽相同。
不过不管是用什么方式,Bootloader都应该尽可能做的更小更简洁,这样的话内存的开销就更小,对于内存较小的MCU来说压力就没那么大了。

我下面要做的这个bootloader是上面讲的常见分区方式里面的第5种。
分区介绍:
我用的是STM32F103,内存是128K的(想用内存更小的MCU也是可以的,改下各个分区的内存分配就行了)。

分区表如下:

nameoffsetsize
boot0x080000000x00003000
setting0x080030000x00001000
app0x080040000x0000E000
download0x080120000x0000E000

功能描述:
运行bootloader的时候先从setting里面读一些参数,确定是否需要升级,如果需要,则把download分区的固件搬运到app分区,如果不需要升级则直接跳转到app分区.
至于新固件的下载传输过程,我放到App里面去处理了,这跟我的项目实际需求有关系,App部分这里就先不往下拓展了,后面我会专门写一篇博客来介绍。

各个功能模块的具体讲解:
1、分区定义
先把各个分区的内存地址以及大小定义好,方便后面使用。

#define FLASH_SECTOR_SIZE       1024
#define FLASH_SECTOR_NUM        128    // 128K
#define FLASH_START_ADDR        ((uint32_t)0x8000000)
#define FLASH_END_ADDR          ((uint32_t)(0x8000000 + FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))

#define BOOT_SECTOR_ADDR        0x08000000     // BOOT sector start address 
#define BOOT_SECTOR_SIZE        0x3000         // BOOT sector size
#define SETTING_SECTOR_ADDR     0x08003000     // SETTING sector start address 
#define SETTING_SECTOR_SIZE     0x1000         // SETTING sector size
#define APP_SECTOR_ADDR         0x08004000     // APP sector start address  
#define APP_SECTOR_SIZE         0xE000         // APP sector size
#define DOWNLOAD_SECTOR_ADDR    0x08012000     // Download sector start address
#define DOWNLOAD_SECTOR_SIZE    0xE000         // Download sector size   

2、程序跳转
Bootloader作为引导程序,最重要的工作之一就是通过内存跳转进入用户程序,下面这段代码可以跳转到任何一个内存地址。

uint8_t jump_app(uint32_t app_addr) 
{
    uint32_t jump_addr;
    jump_callback cb;
    if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20000000) 
    {  
        jump_addr = *(__IO uint32_t*) (app_addr + 4);  
        cb = (jump_callback)jump_addr;  
        __set_MSP(*(__IO uint32_t*)app_addr);  
        cb();
        return 1;
    } 
    return 0;
}

3、处理函数
从setting区里面读取process状态值,然后进行对应的处理,如果需要升级则把download区的固件搬运到app区,然后再运行新APP,如果不需要升级则直接跳转到APP。

process = get_boot_state();
switch (process) 
{
    case START_PROGRAM:
        printf("start app...\r\n");
        delay_ms(50);
        if (!jump_app(APP_SECTOR_ADDR)) 
        {
            printf("no program\r\n");
            delay_ms(1000);
        }
        printf("start app failed\r\n");
        break;
    case UPDATE_PROGRAM:
        printf("update app program...\r\n");
        app_addr = APP_SECTOR_ADDR;
        down_addr = DOWNLOAD_SECTOR_ADDR;

        printf("app addr: 0x%08X \r\n", app_addr);
        printf("down addr: 0x%08X \r\n", down_addr);

        printf("erase mcu flash...\r\n");
        mcu_flash_erase(app_addr, APP_ERASE_SECTORS);  
        printf("mcu flash erase success\r\n");
    
        printf("write mcu flash...\r\n");
        // memset(down_buf, 0, sizeof(down_buf));
        for (i = 0; i < APP_ERASE_SECTORS * 8; i++)
        {
            mcu_flash_read(down_addr, &down_buf[0], 128);
            delay_ms(5);
            mcu_flash_write(app_addr, &down_buf[0], 128);
            delay_ms(5);
            down_addr += 128;
            app_addr += 128;
        }
        printf("mcu flash write success\r\n");

        set_boot_state(UPDATE_SUCCESS);
        break;
    case UPDATE_SUCCESS:
        printf("update success\r\n");
        boot_state = UPDATE_SUCCESS_STATE;
        write_setting_boot_state(boot_state);
        set_boot_state(START_PROGRAM);
        break;
    default:
        break;
}

完整代码下载地址:https://download.csdn.net/download/ShenZhen_zixian/87462312

4 烧录下载配置

我们的Bootloader做好以后需要烧录到MCU里面,可以直接用Keil uVison来下载,也可以用J-Flash或者其他,这个都没关系,但是要注意内存的分配,要把固件烧到对应的内存地址上。
我这里做出来的bootloader bin只有8K,不过为了方便后续在这部分增加新功能,我实际分配了12K的空间,地址区间是0x08000000-0x08003000。

如果是用keil下载的话,需要注意flash的配置,具体如下:
请添加图片描述
请添加图片描述
如果是用J-Flash或者STlink的工具烧录的话注意烧录的起始地址是0x08000000就好了。

5 运行测试

注:这里我还没讲解App部分代码,你们只看Bootloader部分的log就好了,不影响的,也可以下载完整的代码实际去跑一下。
不需要升级时直接跳转到App区,如下图:
请添加图片描述

需要升级时先从download区搬运新固件到app区,然后再跳转到App区,如下图:
请添加图片描述

结束语

好了,关于自制BootLoader的介绍就讲到这里,本文只是提供一个思路,不是唯一的方法,关键还是看你自己实际的需求。
还有App那部分这里没详细讲,后续我会专门写一篇关于App部分的博客,到时候合并到一起就比较清晰了。或者你也可以下载完整的源码自己去跑一下,下面的源码我把BootLoader和APP都上传了。

完整代码下载地址:https://download.csdn.net/download/ShenZhen_zixian/87462312

如果你有什么问题或者有更好的方法,欢迎在评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/359278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java基础】IO流

IO流 最后一定要关闭流&#xff0c;防止资源泄露 字节流 一次读取1字节&#xff0c;8比特 FileInputStream import org.junit.jupiter.api.Test;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;public class CopyBytes {pub…

2023最新网络工程师面试大全,全都答得上offer绝对拿到手软

一、目录 TCP和UDP都可以实现客户端/服务端通信&#xff0c;这两个协议有何区别&#xff1f; 是第几层的协议&#xff0c;其作用是什么&#xff1f; 请说一下FTP&#xff0c;SSH&#xff0c;TELNET&#xff0c;DNS&#xff0c;HTTP&#xff0c;HTTPS&#xff0c;SMTP协议的端口…

我的三周年创作纪念日——学习不止,创作不停

机缘 最开始写文章博客&#xff0c;是为了用输出倒逼自己输入。 从校园离开后&#xff0c;才逐渐意识到学习的不容易。没有写好的教材课程、没有画好的考点重点&#xff0c;没有一起学习的同学&#xff0c;更没有明确的学习方向和路径。 数据分析方向可以学的东西太多了&…

SUMO安装并实现交通仿真Demo

sumo简介sumo是一种开源&#xff0c;微观&#xff0c;多模态的交通模拟仿真软件&#xff0c;每辆车都是明确建模的&#xff0c;有自己的路线&#xff0c;并在网络独立移动下载安装sumo前往sumo官网下载,如下图红色标注所示&#xff0c;一键安装。实现Demo3.1加载道路文件.net.x…

软件工程学习

文章目录前言软件特点分类软件工程软件危机项目管理工具总结前言 本博客仅做学习笔记&#xff0c;如有侵权&#xff0c;联系后即刻更改 科普&#xff1a; 软件 软件的定义 软件不是程序&#xff0c;而是程序、数据以及开发、使用和维护程序需要的所有文档的完整集合。 特点 …

100份简历才找一个合适的,2023,软件测试岗位饱和了吗?

各大互联网公司的接连裁员&#xff0c;政策限制的行业接连消失&#xff0c;让今年的求职雪上加霜&#xff0c;想躺平却没有资本&#xff0c;还有人说软件测试岗位饱和了&#xff0c;对此很多求职者深信不疑&#xff0c;因为投出去的简历回复的越来越少了。 另一面企业招人真的…

为什么启动一个线程不用run()方法,而是用start()方法

在使用java多线程时&#xff0c;有三种方式创建线程 复习传送门 当使用继承Thread来实现多线程时&#xff0c; 我们会把线程执行的代码写在run() 方法中&#xff0c; 使用Thread的start()方法来启动一个线程。 代码如下&#xff1a; public class ThreadDemo extends Thread{O…

RocketMQ快速入门:消息发送、延迟消息、消费重试

一起学编程&#xff0c;让生活更随和&#xff01; 如果你觉得是个同道中人&#xff0c;欢迎关注博主gzh&#xff1a;【随和的皮蛋桑】。 专注于Java基础、进阶、面试以及计算机基础知识分享&#x1f433;。偶尔认知思考、日常水文&#x1f40c;。 目录1、RocketMQ消息结构1.1…

算法训练营 day50 动态规划 单词拆分 多重背包理论基础

算法训练营 day50 动态规划 单词拆分 多重背包理论基础 单词拆分 139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词…

佳能iC MF645CX彩色激光多功能打印机报E302-0001故障码检修

故障现象: 一台佳能iC MF645CX彩色激光多功能一体机开机报E302-0001故障代码,如果设备未恢复,请联系经销商或客户支持中心。 维修分析: 佳能iC MF645CX彩色激光多功能一体机开机报E302-0001故障代码的

java中NumberFormat 、DecimalFormat的介绍及使用,java数字格式化,BigDecimal数字格式化

文章目录前言一、NumberFormat1、概述2、实例化方法3、货币格式化4、百分比格式化5、NumberFormat的坑5.1、不同的格式化对象处理相同数值返回结果不同问题源码分析&#xff1a;二、DecimalFormat1、概述2、常用方法3、字符及含义0与#的区别分组分隔符的使用“%” 将数字乘以10…

Python 基本数据类型(二)

1. 列表 列表是 Python 最常用的数据类型&#xff0c;它是有序元素的集合&#xff0c;元素之间以逗号分隔&#xff0c;用中括号括起来&#xff0c;可以是任何数据类型。同时它也是一种序列&#xff0c;支持索引、切片、加、乘和成员检查等。 **数组&#xff1a;**数据类型必须…

程序员的接单外卖平台

今天王同学给大家安利一款非常实用并且能接单的一款非常好的平台—— 独自开 独自开的功能非常之多 简直不要太香~ 集成第三方数学接口&#xff0c;形成标准化解决方案&#xff0c;提供开发者调用 支付分账功能电子签单功能税务接口硬件接口 独自开的开发功能简直不要太多~ 如…

LinkedList集合对元素进行增、查、删操作

ArrayList集合在查询元素时速度很快&#xff0c;但在增删元素时效率较低&#xff0c;为了克服这种局限性&#xff0c;可以使用List接口的另一个实现类LinkedList。LinkedList集合内部包含有两个Node类型的first和last属性维护一个双向循环链表&#xff0c;在链表中的每一个元素…

Python机器学习入门笔记(3)—— 线性回归

目录 线性回归 算法简述 LinearRegression() API SGDRegressor API LinearRegression() 和 SGDRegressor对比 过拟合与欠拟合 岭回归 应用场景 线性回归 算法简述 线性回归是一种基本的机器学习算法&#xff0c;它用于建立自变量和因变量之间的线性关系模型。它假设…

【Linux系统】第七篇:Linux调试器gdb的使用

文章目录一、gdb简介二、gdb的安装三、gdb使用3.1、release和debug版本3.2、gdb基本使用命令1、启动gdb2、调试命令3、显示代码&#xff08;list&#xff09;4、断点命令&#xff08;breakpoint&#xff09;5 、变量命令&#xff08;variable&#xff09;6、特殊调试命令7、调用…

企业微信的聊天机器人来了,免费下载(Python版)

大家好&#xff0c;这里是程序员晚枫&#xff0c;个人网址&#xff1a;python-office.com 上次分享了微信机器人的视频以后&#xff0c;视频下面有一个热门评论&#xff1a; 什么时候开发企业版微信机器人&#xff1f;自动回复、自动群发等等~ 在经历了一段时间的查找和开发以…

Github隐藏功能:显示自己的README,Github 个人首页的 README,这样玩儿

内容概览 前言创建仓库修改 README 的内容总结前言 大家最近有没有发现这个现象&#xff0c;有些名人的 Github 首页变得更丰富了&#xff1f;尤其是那个夺目的 README 板块&#xff01;&#xff01;&#xff01; 请看&#xff0c;这是 iOS 喵神 的 Github 首页&#xff1a; …

Flink-时间和窗口(水位线、窗口、迟到数据的处理等)

文章目录时间和窗口时间水位线&#xff08;Watermark&#xff09;时间和窗口水位线有序和无序流的插入水位线生成策略&#xff08;Watermark Strategies&#xff09;水位线的传递窗口&#xff08;Window&#xff09;窗口窗口的分类窗口API概述窗口分配器&#xff08;Window Ass…

“离开浪浪山”是假象,80%年轻人下班后还在学习,真实是想先上个山。

最近&#xff0c;又有一个关于年轻人与职场的新词横空出世—— 浪浪山。 什么是浪浪山&#xff1f; 每个人心中都有一座浪浪山。 浪浪山&#xff0c;其实是人生的一种状态&#xff0c;步入社会时满腔热血&#xff0c;然而很快就被现实给修理了一顿&#xff1b;想要辞职不干出去…