参考地址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过程是一样的,下载完成后系统自动重启