使用RT-Thread Studio配置BootLoader和App实现OTA功能【详细教程】

前言

由于项目需要实现OTA功能学习了一下具体实现方法,以备后期查看,有问题的地方随时指正修改

理论

1.什么是OTA
OTA是“over-the-air”的缩写,是一种无线技术,用于在不需要接触设备的情况下向移动设备或物联网设备提供更新、补丁或新版本的软件。OTA更新通常通过无线网络(如Wi-Fi或移动数据)进行,这使得设备可以在不需要任何物理连接的情况下获得更新。这种技术在智能手机、智能家居设备和车载系统等设备中得到广泛应用。
2.什么是BootLoader
BOOTloader是一段特殊的程序,在计算机启动时运行。它的主要作用是读取和加载操作系统。

当计算机启动时,BOOTloader首先加载并启动操作系统。它负责加载内存中的系统映像,并初始化基本硬件设置。BOOTloader还可以提供一些选项,如选择不同的操作系统版本或进入系统恢复模式。

BOOTloader通常存储在只读存储器(如ROM或flash存储器)中,因此不容易遭到破坏或更改。它是系统启动过程中非常重要的一部分,因为如果BOOTloader不能正常工作,则系统将无法启动。


所以我们想实现BootLoader的话要对flash分区,及对内部偏移地址进行指定,OTA主要是一种技术手段,我们主要是实现BootLoader。

正文

本次教程实现的功能是使用网线对STM32F407VGT6进行OTA升级。

BootLoader

  1. 新建工程
    根据自己的实际情况选择芯片及型号后编译下载初始代码,检查芯片及串口功能是否良好。

    2. 通过Cube配置功能
    我这里使用的SPI3,CSS脚PE1。

点击CubeMX Settings 配置时钟,SPI引脚(片外Flash),串口
3.配置RT-Thread
在项目资源管理器中点击drivers->board.h文件配置 使能SPI、片内Flash

#define BSP_USING_SPI3
#define BSP_USING_ON_CHIP_FLASH

点击RT-Thread Settings 使能SPI、SFUD、FAL、QBOOT驱动程序
在FAL下要勾选FAL使用SFDU驱动程序,这个主要是管理片外Flash
在QBOOT组件下根据自己的需求进行勾选相应的功能

  1. 编写SFUD驱动及FAL分区
    新建w25q.c文件添加如下代码,这个是使用SFUD驱动程序通过SPI对W25Q128抽象化管理。
#include <rtthread.h>
#include "drv_spi.h"
#include "spi_flash_sfud.h"

#define SPI_BUS_NAME                        "spi3"
#define W25Q_SPI_DEVICE_NAME                "spi30"
//#define W25Q_FLASH_NAME                      "W25Q128"




static int rt_hw_spi_flash_init()
{
    rt_err_t ree = RT_EOK;

    ree = rt_hw_spi_device_attach(SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, GPIOE, GPIO_PIN_1);
    //ree = rt_hw_spi_device_attach(SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, GPIOB, GPIO_PIN_6);
    /* 使用 SFUD 探测 spi10 从设备,并将 spi10 连接的 flash 初始化为块设备,名称 W25Q128 */
    if (RT_NULL == rt_sfud_flash_probe(FAL_USING_NOR_FLASH_DEV_NAME, W25Q_SPI_DEVICE_NAME))
    {
            return -RT_ERROR;
    }
    return ree;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);

新建fal_cfg.h文件,对flash引入及分配内存,这要注意芯片的flash大小及偏移地址可能不同,我这个是对F404VGT6的,所以要看数据手册进行修改。

#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#include <rtconfig.h>
#include <board.h>
#include <rtconfig.h>



#define FLASH_SIZE_GRANULARITY_16K      (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K      (64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K     (7 * 128 * 1024)

#define STM32_FLASH_START_ADRESS_16K    STM32_FLASH_START_ADRESS
#define STM32_FLASH_START_ADRESS_64K    (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)
#define STM32_FLASH_START_ADRESS_128K   (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)
/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32_onchip_flash_16k;
extern const struct fal_flash_dev stm32_onchip_flash_64k;
extern const struct fal_flash_dev stm32_onchip_flash_128k;
/* ===================== Flash device Configuration ========================= */
extern struct fal_flash_dev nor_flash0;


#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &stm32_onchip_flash_16k,                                         \
    &stm32_onchip_flash_64k,                                         \
    &stm32_onchip_flash_128k,                                        \
    &nor_flash0,                                                     \
}


/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */


//config_data分区必须是128k的整数倍,如果设置为1K,则在OTA升级后会擦除掉config_data分区的数据
//这种配置占用芯片flash太多,如果芯片flash存储空间不够可以考虑将config_data分区放在外部spi flash里

#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD,       "app",     "onchip_flash_128k",   0,  768*1024, 0}, \
    {FAL_PART_MAGIC_WORD,       "config_data","onchip_flash_128k",768*1024,  128*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "download", FAL_USING_NOR_FLASH_DEV_NAME,0, 1024*1024, 0}, \
}

/*
#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD,       "config_data",     "onchip_flash_128k",   0,  128*1024, 0}, \
    {FAL_PART_MAGIC_WORD,       "app","onchip_flash_128k",128*1024,  768*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "download", FAL_USING_NOR_FLASH_DEV_NAME,0, 1024*1024, 0}, \
}
*/

#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */

修改main.c文件,添加FAL头文件及初始化

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <fal.h>

int main(void)
{
    int count = 1;

    fal_init();
    while (count++)
    {
//        LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

5,下载验证
编译下载验证,只要开到这几条打印就代表BootLoader成功了

APP实现

  1. 创建工程
    复制bootl工程改名为App
    2. 测试APP跳转
    在RT-Thread Studio 中删除所有软件包,修改main函数,添加中断向量表跳转函数
#include <rtthread.h>
#include "fal.h"
#include <drivers/pin.h>




#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define RT_APP_PART_ADDR                0x08020000




int main(void)
{
    int count = 1;

 		fal_init();
//    LOG_D("Hello RT-Thread!");
    while (count++)
    {
        LOG_D("Hello RT-Thread!!!!!!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}


static int ota_app_vtor_reconfig(void)
{
    #define NVIC_VTOR_MASK   0xFFFFFF80
    /* 根据应用设置向量表 */
    SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;

    return 0;
}
INIT_BOARD_EXPORT(ota_app_vtor_reconfig);

然后在linkscripts下修改链接脚本起始地址,和下载地址这个地址是app的起始地址


下载测试后发现可以正常跳转到app程序就OK了

  1. 配置驱动
    接下来实现OTA功能
    点击CubeMX Settings,添加以太网外设(根据自己实际引脚配置)

    点击RT-Thread Settings添加ota_download、agile_console、agile_telnet、lwip组件
    lwip组件下设置为静态ipv4

设置如下:
ota_download

Lwip
agile_telnet
agile_console

到这里就添加结束了,保存后在board.h文件下使能ETH并添加网卡型号

#define BSP_USING_ETH
#ifdef BSP_USING_ETH
#define PHY_USING_LAN8720A
/*#define PHY_USING_DM9161CEP*/
/*#define PHY_USING_DP83848C*/
#endif

编译后会出现一个报错,将这个报错注释掉
3.修改main.c函数
这里添加app的偏移地址,让中断向量表跳转及定义phy复位函数

#include <rtthread.h>
#include "fal.h"
#include <drivers/pin.h>
#include <board.h>



#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define RT_APP_PART_ADDR                0x08020000


#define PHY_RESET_IO GET_PIN(A, 0)



void phy_reset(void)
{
    rt_pin_mode(PHY_RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(PHY_RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(PHY_RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(PHY_RESET_IO, PIN_HIGH);
}


int main(void)
{
    int count = 1;

     fal_init();
    LOG_D("Hello RT-Thread!v1.0");
    while (count++)
    {
        LOG_D("Hello RT-Thread!v1.0");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}


static int ota_app_vtor_reconfig(void)
{
    #define NVIC_VTOR_MASK   0xFFFFFF80
    /* 根据应用设置向量表 */
    SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;

    return 0;
}
INIT_BOARD_EXPORT(ota_app_vtor_reconfig);

烧入打印如下

4. 验证OTA功能
使用xshell和mywebServer
xshell配置如下IP地址是设备ip

点击连接后会连接上同时打印信息,用法和串口一样
修改main.c函数主要是为了验证 OTA不要下载进去。
将编译好的bin文件打包好,打包软件和打包方式在下图:
压缩算法要与bootloader的压缩方式一样,固件分区名要与fal分区相同,我要下载到app里,如果填写错误会导致QBoot搬运失败。

打开mywebserver,设置如下
服务目录是刚才打包好的的目录,IP地址是本机IP地址,要注意设备与本机IP在同一网段,端口号默认即可

在xshell中输入http_ota http://192.168.1.112:80/rtthread.rbl
这样mywebserver就侦听到了请求,然后将文件传输到指定分区。
如下看效果。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用RT-Thread Studio配置BootLoader和App实现OTA功能【详细教程】

发表评论