rtthread通过SFUD、FAL、RT--YMODEM-OTA实现在线升级IAP

平台: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代码

iot_admin (rt-thread.com)

配置如下,这里要对应上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,有需要请下载

看完觉得有用,请点赞。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值