我们这里要使用单片机外部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版本。

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

rt thread studio使用QBOOT和片外flash实现OTA升级相关推荐

  1. RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程

    (1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...

  2. RT Thread Studio 配置IIC并读取AS5600角度

    RT Thread Studio 配置IIC并读取AS5600角度 一.RT Thread Studio 配置IIC 1.在RT Thread Seting 中开启IIC功能 并保存 一定要保存才能更 ...

  3. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  4. stm32f407单片机rt thread 片外spi flash OTA升级配置示例

    参考地址https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/ 第一步,生成Boot ...

  5. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  6. rt thread 使用FAL遇到fal_init() undefined reference

    rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...

  7. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

  8. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  9. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

最新文章

  1. Linxu终端gcc与gcc -c的区别
  2. 朴素贝叶斯分类器(Navie Bayesian Classifier)中的几个要点(一)
  3. Python练习题:批量删除多个文件夹内的相同文件
  4. ASP.NET MVC 学习之路-4
  5. 单碟1.75T 西数14TB充氦硬盘今年上
  6. Redis设计与实现笔记
  7. (1) 漂亮的日期控件
  8. oracle开窗函数是什么,ORACLE数据库(六)-----开窗函数
  9. 【渝粤教育】电大中专药事管理与法规 (2)作业 题库
  10. matlab评估边缘检测性能,【模糊推理】模糊逻辑图像边缘检测,原理+matlab代码~...
  11. 【转】9、XAML名称空间详解
  12. 深度优先,广度优先,拓扑排序(实战题解)
  13. 来自百度的71款开源项目
  14. 计算机组组内培训记录,计算机教研组活动记录
  15. 原生JS实现的跳一跳小游戏完整实例
  16. 华为手机更新EIMU10之后google play 商店消失
  17. 极致体验,解密微信背后的音视频通话技术
  18. 影视后期制作学习(AE)(父子级链接-表达式)
  19. 手机里竟然有这么多传感器!终于都搞懂了
  20. 超详细的Android so库的逆向调试

热门文章

  1. typescript基础学习
  2. 艾伟:Memcached深度分析
  3. 将App通过XCode上传到AppStore 出现这个错误“An error occurred uploading to the iTunes Store”的解决方法
  4. 金弘同创:拼多多怎么退保证金
  5. 通达信五点多组预测主图指标公式源码实测
  6. JeeSite系列之一_JeeSite简介
  7. Python学习:Python分析钉钉评论(一)爬取数据
  8. h5微信js-sdk分享接口php,H5 微信JSSDK自定义分享代码模板
  9. 熊海cms——代码审计
  10. Flask智能图书推荐系统