rt thread studio使用QBOOT和片外flash实现OTA升级

单片机开发 专栏收录该内容
47 篇文章 1 订阅

我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系

PB3-->SPI3_CLK

PB4-->SPI3_MISO

PB5-->SPI3_MOSI

PE1-->SPI3_CSS

第一步

使用rt thread studio新建一个bootloader的工程,我这里使用的是stm32f407vgt6的芯片

使能SPI驱动和SFUD驱动

打开board.h文件,取消#define BSP_USING_SPI3的注释

然后在stm32f4xx_hal_conf.h文件中打开对 SPI 的支持,也就是取消掉 HAL_SPI_MODULE_ENABLED 这个宏定义的注释

将CubeMx 生成的 void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)函数复制到board.c文件中

从上图可以看出FAL抽象层位于SFUD框架的上层,可以将多个Flash硬件(包括片内Flash和片外Flash)统一进行管理,并向上层比如DFS文件系统层提供对底层多个Flash硬件的统一访问接口,方便上层应用对底层硬件的访问操作。

所以我们先要实现SFUD的flash驱动

与SFUD相关的文件在rt-thread下components下drivers下spi下sfud里

我们在applications新建w25qxx.c文件

添加以下代码

#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"

 


rt_uint8_t wData[4096] = {"SPI bus write data to W25Q flash."};
rt_uint8_t rData[4096];

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);
    /* 使用 SFUD 探测 spi10 从设备,并将 spi10 连接的 flash 初始化为块设备,名称 W25Q128 */
    if (RT_NULL == rt_sfud_flash_probe(W25Q_FLASH_NAME, W25Q_SPI_DEVICE_NAME))
    {
            return -RT_ERROR;
    }
    return ree;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
static void sfud_w25q_sample(void)
{
    rt_spi_flash_device_t flash_dev;
    sfud_flash_t sfud_dev;
    struct rt_device_blk_geometry geometry;

    // 1- use sfud api
    rt_kprintf("\n 1 - Use SFUD API \n");

    sfud_dev = rt_sfud_flash_find_by_dev_name(W25Q_FLASH_NAME);
    if(sfud_dev == RT_NULL){
        rt_kprintf("sfud can't find %s device.\n", W25Q_FLASH_NAME);
    }else{
        rt_kprintf("sfud device name: %s, sector_count: %d, bytes_per_sector: %d, block_size: %d.\n",
                    sfud_dev->name, sfud_dev->chip.capacity / sfud_dev->chip.erase_gran,
                    sfud_dev->chip.erase_gran, sfud_dev->chip.erase_gran);

        if(sfud_erase_write(sfud_dev, 0x002000, sizeof(wData), wData) == SFUD_SUCCESS)
            rt_kprintf("sfud api write data to w25q128(address:0x2000) success.\n");

        if(sfud_read(sfud_dev, 0x002000, sizeof(rData), rData) == SFUD_SUCCESS)
            rt_kprintf("sfud api read data from w25q128(address:0x2000) is:%s\n", rData);
    }

    // 2- use rt_device api
    rt_kprintf("\n 2 - Use rt_device API \n");

    flash_dev = (rt_spi_flash_device_t)rt_device_find(W25Q_FLASH_NAME);
    if(flash_dev == RT_NULL){
        rt_kprintf("rt_device api can't find %s device.\n", W25Q_FLASH_NAME);
    }else{
        rt_device_open(&flash_dev->flash_device, RT_DEVICE_OFLAG_OPEN);

        if(rt_device_control(&flash_dev->flash_device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry) == RT_EOK)
            rt_kprintf("spi flash device name: %s, sector_count: %d, bytes_per_sector: %d, block_size: %d.\n",
                    flash_dev->flash_device.parent.name, geometry.sector_count, geometry.bytes_per_sector, geometry.block_size);

        if(rt_device_write(&flash_dev->flash_device, 0x03, wData, 1) > 0)
            rt_kprintf("rt_device api write data to w25q128(address:0x3000) success.\n");

        if(rt_device_read(&flash_dev->flash_device, 0x03, rData, 1) > 0)
            rt_kprintf("rt_device api read data from w25q128(address:0x3000) is:%s\n", rData);

        rt_device_close(&flash_dev->flash_device);
    }
}
MSH_CMD_EXPORT(sfud_w25q_sample, sfud w25q128 sample);

然后编译下载

可以看到W25Q128的flash芯片初始化成功了,测试一下sfud_w25q_sample命令

可以看到使用SFUD的API和rt_device API读写flash都是可以的,那么SFUD设备驱动成功了。

按照这个图,flash硬件层移植完毕

第二步,添加fal软件包

右键详细配置

编译一下,发现找不到fal_cfg.h文件

在工程目录下的porting文件夹里是有fal_cfg.h文件的,只是没有添加到编译目录下

我们把这个文件放到inc文件夹里

再编译一下,还有个错误,找不到stm32f2_onchip_flash

通过fal_cfg.h可以看到里面使用了芯片内部flash,所以在board.h里需要打开BSP_USING_ON_CHIP_FLASH

编译后提示STM32_FLASH_START_ADRESS_16K这些没有定义

然后在fal_cfg.h文件里添加以下定义

#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 */
#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD,        "bl",     "onchip_flash_128k",         0,   64*1024, 0}, \
    {FAL_PART_MAGIC_WORD,       "app",     "onchip_flash_128k",   64*1024,  704*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_ */

注意:::::

这里的onchip_flash_128k要和drv_flash_f4.c里的名字一样

有的文档里写的名字是stm32_onchip如果你照搬过来,那就错了

因为stm32f4系列的内部flash分布和其他型号的不一样,如图

所以在drv_flash_f4.c会有onchip_flash_16k、onchip_flash_64k、onchip_flash_128k这三种定义

这里是针对stm32f4系列的,其他型号可能不太一样。

再编译一下,没有报错了,然后在main.c里添加#include "fal.h"和fal_init();

再把w25qxx.c文件里的W25Q_FLASH_NAME改成FAL_USING_NOR_FLASH_DEV_NAME

编译、下载

各个分区都初始化成功

测试fal命令试一下,探测和读命令

读、写、擦除等

接下来向工程中添加qboot软件包,添加完qboot后会出现一些其他软件包,应该是qboot依赖这些软件包

配置QBoot组件,在RT-Thread Settings界面,双点qboot组件,进入选项配置界面

如果开启using shell command of qboot的话程序会在bootloader启动后等待一段时间,这个在调试阶段有用,正式应用场景下这个等待时间不可接受。建议关闭。

这里的一些配置根据自己需要修改吧,这里使用了syswatch所以还要添加syswatch相关的驱动

在软件包里搜索一些syswatch看下用法

打开syswatch_config.h去掉这两个的注释

syswatch使用了IWDG看门狗,打开stm32f4xx_hal_conf.h取消HAL_IWDG_MODULE_ENABLED的注释

编译一下没有错误了,看了下ROM占用112.76K了,有点大,后面根据需要再裁减吧。

下载,看到我们的bl分区设置的是64K,显然不够用了,打开fal_cfg.h文件把bl分区改为128k

总ROM是1024K,bl分区占了128k,剩余896K全部用作app分区。

再编译下载,可以看到打印出来的分区和我们设置的一致

我们这里只是只做了bootloader程序,还没加应用程序。

接下来开始做app的程序。

我们随便找一个程序,把下载地址改为0x08020000,下载到单片机试一下。

更改下载地址是在linkscripts下的link.lds里修改

下载完成后看到是可以跳转到应用程序的,但是系统在不停的重启,仔细想了一下,app程序里必须要重新设置中断向量表啊,把这个给忘了

在main.c文件里重新设置中断向量表

然后编译、下载。app程序启动成功,但运行了几秒钟又重启了,这个类似看门狗复位,仔细想了一下,在bootloader里加了syswatch,那么系统的看门狗肯定是启动了,那么就要在app程序里也加上syswatch。

添加方法和bootloader里一样,这里不再赘述了。

添加完syswatch后下载测试,发现app程序启动正常,没有再重启了。

app程序能正常启动后我们就要在app添加ota下载功能了,我们要把程序下载到外部flash就要有相关的fal驱动。

然后在应用程序里依照上面的方法添加SPI驱动,SFUD驱动,FAL驱动,并测试一下fal读写、擦除命令等

完成以后再软件包里搜索ota_downloader

双击后配置一下ota的默认下载地址,就是电脑的IP地址

注意:我的这个工程里是有网络通信的程序,如果没有则无法实现http的ota

编译、下载、我们在开机的时候打印一下当前版本号

系统启动成功后我们改一下这个版本号,如果ota升级成功了这个版本号就会变成新的版本号了

改完以后再编译一下,这次就不要用下载器下载程序了。

我们找到当前项目所在目录的Debug文件夹,查看是否有rtthread.bin文件,以及生成时间,是否是我们当前生成的。

再找到rt_ota_packaging_tool.exe这个工具,目录workspace\gaoliu0201\packages\ota_downloader-latest\tools\ota_packager

将刚才生成的bin文件压缩成可以ota升级的rbl文件

压缩算法要和bootloader里的压缩算法一致,我们这里没有加密

注意固件版本要和当前芯片里的固件版本不一样,如果一样的话就会跳过当前版本的升级。

打包后的程序在Debug文件夹下

然后打开mywebserver.exe,将rbl文件所在的目录添加到服务目录,IP地址为电脑的IP,点击启动,可以勾上访问日志和错误日志

然后将开发板插上网线,获取到IP后发送http_ota命令

开发板将从192.168.1.81处下载rtthread.rbl文件

mywebserver.exe里也可以看到访问记录,状态码200表示下载成功。

开发板开始下载升级程序

虽然ota程序下载成功了,但程序跳转启动的时候在擦除内部flash的时候出错了,擦除地址越界

stm32f407vgt6的flash是1024K,地址从0x08000000到0x080fffff

但擦除的时候地址到0x08120000肯定就出错了。

检查了很久也没找到哪里有问题,但可以肯定问题出在bootloader程序里。

试了下在http://iot.rt-thread.com网站上在线生成的BootLoader下载到单片机启动后打印出来的分区是这样的。

只有两个分区,且app分区的地址偏移是0x00000000,大小是0x000e0000

猜想应该是这里的问题,也是打开bootloader程序的fal_cfg.h,把里面的bl分区删掉。并将app分区的偏移地址改为0

然后用st-link或者j-link把单片机的flash全部擦除,重新编译下载bootloader

bootloader分区也改成两个了,和咱们预想的一样。

然后就看到开始从外部flash里往内部flash里写程序了

我们之前使用http_ota命令下载的固件是存放在外部flash里的,我们只是清空了单片机内部flash,所以开始向app空间拷贝程序了,新的程序也正常启动。我们再更改一下固件版本使用http_ota再试一遍。

更改系统版本为"V1.1.3"

编译、重新用bin文件生成rbl文件,然后发送http_ota命令

文件下载,更新、启动一气呵成,成功升级到V1.1.3版本。

整个过程还是遇到很多问题的,陆陆续续搞了好几天了,记录一下,以备后面查阅。

 

 

 

  • 3
    点赞
  • 4
    评论
  • 19
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 我行我“速” 设计师:Amelia_0503 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值