赞
踩
平台:STM32F103ZET6
开发工具:STM32CUBEMX / RTTHREAD STUDIO / xshell 6
RT-OTA目前只支持STM32:F1 F4 L4 三个系列 且不开源
功能可以,希望能支持到STM32H7 AT32F4 GD32F3这些
层级从低到高是:W25Q16+内部flash -> SFUD -> FAL
IAP大致思路:在APP里烧写固件到SPI flash,然后软件reboot,bootloader去读SPI flash里的代码,覆盖APP区域,然后跳转到APP地址执行
尚未完善:划分一个备份区域,存放出厂代码。(考虑到自己的设计很难实现按键外露,加上RTT没开源OTA代码,就暂时没搞)
正文开始:
移植SFUD主要是为了简单读取W25QXX
新建RTT工程
打开cubemx 设置,在里面配置时钟 uart spi等外设 (如果只需要串口1,做调试的,那就不需要配置,RTT会自动配置好)
先保存cubemx再点生成代码,才会出现SConscript脚本,会减少匹配过程(如果直接生成代码,不会出现这个脚本文件)生成完成后点close,别点打开工程
编译一下,看会不会报错。
然后找到SPI的初始化文件,将以下SPI.C的硬件设置代码复制到board.c
- void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
- {
-
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- if(spiHandle->Instance==SPI2)
- {
- /* USER CODE BEGIN SPI2_MspInit 0 */
-
- /* USER CODE END SPI2_MspInit 0 */
- /* SPI2 clock enable */
- __HAL_RCC_SPI2_CLK_ENABLE();
-
- __HAL_RCC_GPIOB_CLK_ENABLE();
- /**SPI2 GPIO Configuration
- PB13 ------> SPI2_SCK
- PB14 ------> SPI2_MISO
- PB15 ------> SPI2_MOSI
- */
- GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_15;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- GPIO_InitStruct.Pin = GPIO_PIN_14;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- /* USER CODE BEGIN SPI2_MspInit 1 */
-
- /* USER CODE END SPI2_MspInit 1 */
- }
- }

然后打开board.h
去掉两个注释
然后点开rtt settings 添加软件包,spi libc sfud
然后新建一个spi_flash.c文件加入SPI 和SFUD初始化代码和测试代码
- #include <board.h>
- #include <drv_common.h>
- #include <rtthread.h>
- static int rt_hw_spi_flash_init(void)
- {
- __HAL_RCC_GPIOB_CLK_ENABLE();
- rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12); //对应CS脚
- //SFUD会去匹配表上支持的器件,然后读到各项参数,包括器件容量,扇区数等
- if (RT_NULL == rt_sfud_flash_probe("W25Q16", "spi20")) //这里注意容量标识 可以去看看SFUD支持的器件表
- {
- return -RT_ERROR;
- };
-
- return RT_EOK;
- }
- /* 导出到自动初始化 */
- INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
- #define W25Q_SPI_DEVICE_NAME "spi20" //SPI20代表 SPI2 第0个器件
-
- static void spi_w25q_sample(int argc, char *argv[]) //读W25Q的ID 打印
- {
- struct rt_spi_device *spi_dev_w25q;
- char name[RT_NAME_MAX];
- rt_uint8_t w25x_read_id = 0x90;
- rt_uint8_t id[5] = {0};
-
- if (argc == 2)
- {
- rt_strncpy(name, argv[1], RT_NAME_MAX);
- }
- else
- {
- rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX);
- }
-
- /* 查找 spi 设备获取设备句柄 */
- spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
- if (!spi_dev_w25q)
- {
- rt_kprintf("spi sample run failed! can't find %s device!\n", name);
- }
- else
- {
- /* 方式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 命令列表中 */
- MSH_CMD_EXPORT(spi_w25q_sample, spi w25q sample);
- #include "spi_flash.h"
- #include "./sfud/inc/sfud.h"
-
- static void spi_w25q_sf_sample(int argc, char *argv[]) //通过SFUD来测试W25Q的读写
- {
- uint8_t *read_data; // 读取到的数据
- uint8_t *write_data; // 将要写入的数据
- sfud_flash *sfud_dev = NULL;
- sfud_err ret;
- sfud_dev = rt_sfud_flash_find("spi20"); // 获取 sfud_dev
- // 或者 sfud_dev = rt_sfud_flash_find_by_dev_name("W25Q16");
-
- write_data = rt_malloc(32);
- rt_memset(write_data, '1', 32);
- ret = sfud_erase_write(sfud_dev, 0, 32, write_data); // 将数据 32 字节的 write_data 从 0 开始写入 flash
- if(ret == SFUD_SUCCESS)
- {
- rt_kprintf("sfud write data at 0 is:%s\n", write_data);
- }
- else
- {
- rt_kprintf("sfud write data failed\n");
- }
- read_data = rt_malloc(32);
- ret = sfud_read(sfud_dev, 0, 32, read_data); // 读取从 0 开始的 32 字节,存入 read_data
- if(ret == SFUD_SUCCESS)
- {
- rt_kprintf("sfud read data at 0 is:%s\n", read_data);
- }
- else
- {
- rt_kprintf("sfud read data failed\n");
- }
-
- }
- /* 导出到 msh 命令列表中 */
- MSH_CMD_EXPORT(spi_w25q_sf_sample, spi w25q sf sample);

编译 程序,下载,记得download选项里选择系统复位,然后看一下设备列表
看到flash和spi20都挂好了
开始测试SFUD读写
外部flash测试完成
添加FAL软件包
这里要选对应外部flash设备名字
复制fal_cfg.h,避免报错
编译工程提示未定义
这个是FAL软件包里demo的问题,这里需要将内部flash挂载的设备名字和FAL包里调用的内部flash设备名字对上
打开drv_flash_f1.c文件(要确定board.h里去掉了内部flash的注释,在最后,拉到底即可)
找到如下信息
const struct fal_flash_dev stm32_onchip_flash = { "onchip_flash", STM32_FLASH_START_ADRESS, STM32_FLASH_SIZE, FLASH_PAGE_SIZE, {NULL, fal_flash_read, fal_flash_write, fal_flash_erase} };
onchip_flash这个就是RTT对内部flash注册的设备名,stm32_onchip_flash是设备信息的结构体,这俩都有用
然后修改fal_cfg.h文件
修改spi_flash_sfud.c 主要是修改总容量 扇区长度 这里我用的是W25Q16 2MBIT 扇区4096bit
在main.c里添加FAL初始化函数和头文件,再添加一个跳转函数,对应APP的地址和中断向量表地址
编译,烧写
注意这里外部FLASH分区名字,后面OTA部分会用到->W25Q16
添加OTA软件包
修改APP程序烧写位置,此处要对应上前面FAL分区里app起始地址,容量大小到不是很重要,因为没用满,严谨算一下也可以。
main.c里加入跳转 里面APP地址要对应link里的APP地址
- static int ota_app_vtor_reconfig(void)
- {
- #define NVIC_VTOR_MASK 0x3FFFFF80
- #define RT_APP_PART_ADDR 0x08020000
- /* Set the Vector Table base location by user application firmware definition */
- SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
-
- return 0;
- }
- INIT_BOARD_EXPORT(ota_app_vtor_reconfig);
然后去RTT网站生成OTA-BOOT代码
配置如下,这里要对应上fal里的app区域和download区域,片外FLASH要引脚匹配(看看CUBEMAX)
然后点击生成bootload.bin,烧写进去看看BL的分区是否正常,烧写前点了个全片擦除,为了验证BL是否正常
然后再烧写APP,这次是通过RTT studio来下载的,串口打印如下,BL跳转正常
然后修改APP里打印的版本号,将该APP的BIN文件使用RTT-OTA打包工具,位置在工程目录下packages OTA/tools 大概位置,打包生成LDR文件,此处加密不加密都可以,要对应网页配置选项
打包完成,再次进入Xshell 6软件,在msh命令行输入ymodem_ota,进入传输等待过程。
在Xshell6 的文件→传输→ymodem发送→找到生成的ldr文件
等待传输完成,代码自动重启,然后查看版本号,版本号变化,确认成功升级
如果出现ymodem传输不正常,需要注意:时钟一定要用外部时钟
APP+BOOT代码已上传CSDN,有需要请下载
看完觉得有用,请点赞。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。