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

    技术2025-04-09  8

    参考地址https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/

    第一步,生成BootLoader。

    Bootloader 在线获取地址: http://iot.rt-thread.com

    1.注册账号、新建产品。

    点击固件升级、然后是生成BootLoader。

    2.根据自己板子配置情况填写硬件信息。

    我的板子上是用的STM32F407VGT6,ROM是1M,RAM是192K。

    串口1为调试口,有一个片外的SPI flash,W25Q128,大小是128MBit即16MByte。

    分区表配置

    按照图上配置即

    Bootloader的地址是0x08000000到0x08020000,大小是128K。

    APP程序的地址是0x08020000到0x080ffff,大小是896K。

    Download下载到片外SPI flash,片外SPI flash的起始地址是0x00000000,SPI flash空间足够大,分配上1024K。

    加密和压缩和logo这些根据个人需要配置吧。然后生成BootLoader。

    第二步,下载Bootloader程序。

    打开STM32 ST-LINK Utility软件,连接上电路板,打开下载好的Bootloader程序,下载的起始地址为0x08000000然后点击下载。

    程序运行起来后显示这样。

    当然   这只是Bootloader程序运行起来了,和我们的应用程序没啥关系,我们继续制作应用程序部分。

    第三步 在APP应用程序中添加spi flash驱动

    1.准备一个现成的带rt thread系统的程序,且网络部分可以使用的工程,打开menuconfig。

    进入目录Hardware Drivers Config-->Onboard Peripheral Drivers

    打开Enable SPI FLASH

    进入目录Hardware Drivers Config-->On-chip Peripheral Drivers

    打开Enable SPI BUS

    保存退出后使用pkgs --update更新一下软件包

    然后使用scons --target=mdk5重新生成一下工程。

    编译无错后添加一个spi_w25q.c文件,添加一下spi flash测试程序,看一下驱动是否正常。

    测试代码如下:

    #define SPI_BUS_NAME                    "spi1"

    #define W25Q_SPI_DEVICE_NAME                 "spi10" #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 spi_w25q_sample(void) {     struct rt_spi_device *spi_dev_w25q;     rt_uint8_t w25x_read_id = 0x90;     rt_uint8_t id[5] = {0};

        /* 查找 spi 设备获取设备句柄 */     spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);     if (!spi_dev_w25q)     {         rt_kprintf("spi sample run failed! can't find %s device!\n", W25Q_SPI_DEVICE_NAME);     }     else     {             rt_kprintf("spi sample run ok!\n");             {                 struct rt_spi_configuration cfg;                 cfg.data_width = 8;                 cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0*/                 cfg.max_hz = 50 * 1000 * 1000; /* 50M */                 rt_spi_configure(spi_dev_w25q, &cfg);             }             /* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */             rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);             rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:%x%x\n", id[3], id[4]);     } } MSH_CMD_EXPORT(spi_w25q_sample, spi w25q128 sample);

    编译下载后运行spi_w25q_sample函数,读取flash 的ID号。

    第四步 添加SFUD管理框架

    我们在menuconfig中打开了Enable SPI FLASH后会在rtconfig.h中增加如下定义

    默认使用了SFUD框架

    然后在spi_w25q.c文件中增加以下代码,编译下载。

    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);

     

    执行sfud_w25q_sample函数

    可以使用sf命令测试flash初始化、读写、擦除等。

    flash初始化、读写测试正常。

    第五步 添加fal管理框架

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

    打开menuconfig,进入RT-Thread online packages-->system packages

    打开fal

    SFUD设备名默认为norflash0,可以更改设备名为W25Q128,配置界面如下。

    然后进入Hardware Drivers config -->on-chip peripheral Drivers

    打开Enable on-chip FLASH

    保存退出后使用pkgs --update更新软件包到本地,再用scons --target=mdk5重新生成工程。

    打开工程编译后报错,找不到fal_cfg.h文件,我们添加一下fal_cfg.h的文件路径

    文件路径是packages\fal-v0.5.0\samples\porting

    然后打开fal_flash_sfud_port.c文件,

    我们的flash型号为W25Q128,大小为16M,这里改为16 * 1024 * 1024。

    编译一下,提示这些没有定义

    默认的fal_cfg.h文件里没有定义stm32_onchip_flash_16k这些,

    由于stm32f4系列的内部flash分布如下

    flash块的大小有16k、64k、和128k三种,所以需要定义三种块设备。

    我们打开fal_cfg.h文件更改成如下

    #ifndef _FAL_CFG_H_ #define _FAL_CFG_H_

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

    #define RT_APP_PART_ADDR                0x08020000

     

    #ifndef FAL_USING_NOR_FLASH_DEV_NAME #define NOR_FLASH_DEV_NAME             "norflash0" #else #define NOR_FLASH_DEV_NAME              FAL_USING_NOR_FLASH_DEV_NAME #endif

    #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 const struct fal_flash_dev stm32_onchip_flash; extern struct fal_flash_dev nor_flash0;

    /* flash device table */ #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_WROD, "bootloader",     "onchip_flash",                                    0,       128 * 1024, 0}, \     {FAL_PART_MAGIC_WROD,        "app",     "onchip_flash",                           128 * 1024,       896 * 1024, 0}, \     {FAL_PART_MAGIC_WROD,   "download", NOR_FLASH_DEV_NAME,                                            0,      1024 * 1024, 0}, \ } #endif /* FAL_PART_HAS_TABLE_CFG */

    #endif /* _FAL_CFG_H_ */

    然后再次编译,没有错误了,然后在main函数里添加fal_init();,编译下载。

    第六步 添加easyflash

    打开menuconfig

    进入RT-Thread online packages-->tools packages

    打开EasyFlash

    然后打开RT-Thread online packages-->IOT-internet of things

    打开ota_downloader

    保存退出后使用pkgs --update更新软件包,再使用scons --target=mdk5重新生成工程。

    然后在keil工程中EasyFlash目录下添加el_fal_port.c文件

    el_fal_port.c在packages\EasyFlash-v4.1.0\ports目录下。

     

    然后编译下载有个错误

    这个是在sockets.h和time.h里都定义了timeval这个结构体了。

    然后把time.h里的timeval结构体换个名字,在time.c里也顺便一换,然后编译没有错误了。

    然后在main.c文件里更改中断向量表和打印版本信息。

    然后在这里更改程序下载地址,

    然后重新下载bootloader,在下载keil编译的程序。

    版本为0.0.0,然后在keil里更改版本号为0.0.1,然后编译生成bin文件

    使用rt_ota_packaging_tool.exe工具生成rbl文件

    然后在Xshell 6里输入ymodem_ota

    然后使用Xshell里的ymodem工具将rbl文件发送给单片机

    然后芯片会自动重启并更新程序

    更新完成后版本号变成0.0.1   说明升级成功。

     

     

     

     

     

     

     

     

    进入目录RT_Thread online packages-->IoT-internet of things-->

    打开ota_downloader:The firmware downloader which using on RT-Thread

    然后选中Enable OTA downloader debug和Enable Ymoden OTA

    然后进入Hardware Drivers Config-->on-chip Peripheral Drivers-->Enable on-chip FLASH

    保存退出后使用scons --target=mdk5命令生成MDK5的工程。

    打开MDK5的工程,在Applications中添加进已经写好的应用程序,然后编译一下。

    提示没有找到fal_cfg.h

    添加一下fal_cfg.h文件路径

    在packages\fal-v0.5.0\samples\porting下

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

    然后打开fal_cfg.h文件,发现和官方文档里的不太一样

    然后编译了一下正点原子的bsp和官方给的一样,复制过来如下。

    源代码

    #ifndef _FAL_CFG_H_ #define _FAL_CFG_H_

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

    #define RT_APP_PART_ADDR                0x08040000

    #define NOR_FLASH_DEV_NAME             "norflash0"

    #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 table */ #define FAL_FLASH_DEV_TABLE                                          \ {                                                                    \     &stm32_onchip_flash_16k,                                         \     &stm32_onchip_flash_64k,                                         \     &stm32_onchip_flash_128k,                                                     \ } /* ====================== Partition Configuration ========================== */ #ifdef FAL_PART_HAS_TABLE_CFG /* partition table */ #define FAL_PART_TABLE                                                               \ {                                                                                    \     {FAL_PART_MAGIC_WORD,        "bl",     "stm32_onchip",         0,   64*1024, 0}, \     {FAL_PART_MAGIC_WORD,       "app",     "stm32_onchip",   64*1024,  704*1024, 0}, \     {FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME,         0, 1024*1024, 0}, \     {FAL_PART_MAGIC_WORD,  "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \ } #endif /* FAL_PART_HAS_TABLE_CFG */

    #endif /* _FAL_CFG_H_ */

    然后再编译没有错了,在main函数里添加如下内容。

    最重要的一步,修改APP固件的链接地址。

    官方给的文档里这里填的是0x08040000,我们这里改为0x08020000

    因为我们在BootLoader里配置的APP的起始地址是0x08020000,而不是0x08040000。这里和官方配置的不一样,一定要修改哦。

    然后在fal_cfg.h文件里定义的RT_APP_PART_ADDR也改为0x08020000

    然后编译下载,打印出当前版本号V0.0.0

    第四步  打包APP固件

    使用 RT-Thread OTA 固件打包器对 app 固件进行打包,制作可以被下载到 download 分区的升级固件。固件打包工具可以在 ota_downloader 软件包下的 tools 文件夹内找到。

    在对固件进行打包操作前,先修改 stm32f407-atk-explorer/applications/main.c 中 APP_VERSION 宏的值为 2.0.0 作为参照,然后重新编译一遍生成新的 rtthread.bin 文件,修改内容如下图所示:

    2.双击打开 tools\ota_packager\rt_ota_packaging_tool.exe 程序,使用 OTA 固件打包工具将上一步编译出的 rtthread.bin 文件打包成可被升级的 rtthread.rbl 文件,如下图所示:

    第五步 执行OTA升级

    使用mywebserver软件找到rbl文件所在目录,IP地址选择本机IP地址然后点击启动。

    开发板通电,等待获取IP地址成功后再shell里输入http_ota http://192.168.1.72/rtthread.rbl即可开始OTA升级

    我这里使用了{"cmd":"robotset2","model":"b1","robot_id":"1503090496","data":{"updata":"http://192.168.2.119/rtthread.rbl"}}

    这个命令通过TCP开始远程升级的。

    使用shell过程是一样的,下载完成后系统自动重启

    Processed: 0.010, SQL: 9