基于rt-fota的stm32l431-bootloader实战学习

发布于 2020-05-19 15:39:40
RT-FOTA项目简介
由于RTT官方推出的bootloader名字叫RT-OTA,因此为了蹭点RTT的流量,我这个bootloader名字就叫RT-FOTA。
【RT-FOTA的需求分析】
开发基于ROTS的bootloader软件,网上很多牛人会说bootloader最好是裸机程序编写,就像u-boot一样稳定和可靠。但我个人认为目前32位单片机资源丰富,RT-Thread的稳定和可靠性不言而喻,加之RTT的组件丰富,后续功能拓展方便(比如加入网络和USB功能)。因此我使用RT-Thread的阉割版本rtt-nano实现。兼容RTT官方的rbl文件:使用RTT官方的打包软件生成下载文件,可以实现加密压缩功能。由于个人水平问题,只能做到尽可能的兼容,比如RBL文件里面的HASH_CODE我就不知道怎么计算出来的。移植方便:由于RT-FOTA基于RT-Thread开发,因此只要你的平台被RT-Thread支持,就可以很方便的移植到。【RT-FOTA主要的功能】
支持RTT官方的RBL打包软件,使用方式也一致。目前支持包括CRC32、AES256、quicklz和fastlz功能;支持命令行模式(FINSH组件)和出厂固件恢复;支持FLASH分区(FAL组件);支持功能扩展(RTT组件);其他功能可自行方便扩展;


RT-FOTA项目地址:


查看更多

关注者
1
被浏览
686
18 个回答
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
我公司有个项目使用的是STM32L431单片机,可惜不知道什么原因,这款芯片使用官网的网页配置生成的bootloader烧录文件不能使用,故借此机会可以移植一下王总的rt-fota。
rt-fota源项目使用的是STM32F407单片机,移植的虽然是nano版本,但是添加了完整版特有的 device 框架和finsh组件,这样的优点是很方便扩展更多的软件包,但是相应的也增加了nano的尺寸,这对于大flash的单片机来说没影响。我把rt-fota源项目移植到我STM32F429的开发板,然后进行重新配置,使用AC6编译,并开最大优化,编译后所占尺寸为1589875513(1).png。这样的尺寸,对于我只有256Kflash的STM32L431来说,还是有点太大了,故此有了这篇重新移植rt-fota的文章
MDK配置:
1589875613(1).jpg
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
在移植重新移植rt-fota之前,先说一下如何简单移植rt-fota项目到自己的板子上的方法:
第一步:下载rt-fota,使用mdk打开项目,根据自己单片机的型号重新选择
1589876113(1).jpg
第二步:使用cubmx生成自己板子的驱动,替换红框的两个文件
1589876201(1).jpg
第三步:替换配置
1589876333(1).jpg
第四步:配置rtconfig.h与你板子有关的信息
第五步:如果你的板子不是M4内核,还需要替换HAL库
最后:如果你使用的也是AC6编译器,编译会有个错误,更改一下cpuport.c代码:
#elif defined(__CLANG_ARM)
int __rt_ffs(int value)
{
__asm volatile(
"CMP r0, #0x00 \n"
"BEQ exit \n"

"RBIT r0, r0 \n"
"CLZ r0, r0 \n"
"ADDS r0, r0, #0x01 \n"

"exit: \n"

: "=r"(value)
: "r"(value)
);
return value;
}
#elif defined(__IAR_SYSTEMS_ICC__)
int __rt_ffs(int value)
{
if (value == 0) return value;

asm("RBIT %0, %1" : "=r"(value) : "r"(value));
asm("CLZ %0, %1" : "=r"(value) : "r"(value));
asm("ADDS %0, %1, #0x01" : "=r"(value) : "r"(value));

return value;
}
这样就可以愉快的体验rt-fota了~
如果不在乎代码尺寸,直接用rthread完整版,体验更佳
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
接下来开始正式重新移植rt-fota到STM32L431的板子上,对rt-fota进行瘦身。首先使用cubmx生成STM32L431的工程。RT-Thread 操作系统重定义 HardFault_Handler、PendSV_Handler、SysTick_Handler 中断函数,为了避免重复定义的问题,在生成工程之前,需要在中断配置中,代码生成的选项中,取消选择三个中断函数(对应注释选项是 Hard fault interrupt, Pendable request, Time base :System tick timer),最后点击生成代码,具体操作如下图中步骤
1589877289(1).jpg
MDK需要先获取 RT-Thread Nano pack 安装包并进行安装。RT-Thread Nano 离线安装包下载,下载结束后双击文件进行安装。
RT-Thread Nano pack安装完成后,勾选 kernel和shell
1589877151(1).png
1589877361(1).jpg

然后还需要移植finsh,移植官方提供的移植示例代码:中断示例


finsh移植完成后,测试如果能正常打印和接收命令,进行下一步移植软件包




Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
移植SFUD软件包。
SFUD全称Serial Flash Universal Driver,是一款开源的串行 SPI Flash 通用驱动库,由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计。


[p=26, null, left]SFUD的特点在于:[/p]* 支持 SPI/QSPI 接口

* 面向对象设计(同时支持多个 Flash 对象)

* 可灵活裁剪、扩展性强

* 支持 4 字节地址

项目地址:


移植思路:
[p=26, null, left]在移植过程中主要参考两个资料:项目的readme文档和demo工程。[/p][p=26, null, left]对于这些开源项目,其实移植起来也就两步:[/p]* ① 添加源码到工程中;

1589878293(1).png
* ② 实现需要的接口即可;

实现SFUD移植接口:

① 底层SPI/QSPI读写接口:
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size) {
sfud_err result = SFUD_SUCCESS;
spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
/**
* add your spi write and read code
*/
RT_ASSERT(spi);

HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_RESET);
if(write_size && read_size)
{
if(HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf, write_size, 1000)!=HAL_OK)
{
result = SFUD_ERR_WRITE;
}
/* For simplicity reasons, this example is just waiting till the end of the
transfer, but application may perform other tasks while transfer operation
is ongoing. */
while (HAL_SPI_GetState(spi_dev->spix) != HAL_SPI_STATE_READY);
if(HAL_SPI_Receive(spi_dev->spix, (uint8_t *)read_buf, read_size, 1000)!=HAL_OK)
{
result = SFUD_ERR_READ;
}
}else if(write_size)
{
if(HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf, write_size, 1000)!=HAL_OK)
{
result = SFUD_ERR_WRITE;
}
}else
{
if(HAL_SPI_Receive(spi_dev->spix, (uint8_t *)read_buf, read_size, 1000)!=HAL_OK)
{
result = SFUD_ERR_READ;
}
}
/* For simplicity reasons, this example is just waiting till the end of the
transfer, but application may perform other tasks while transfer operation
is ongoing. */
while (HAL_SPI_GetState(spi_dev->spix) != HAL_SPI_STATE_READY);
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin, GPIO_PIN_SET);
return result;
}
② 如果使用的是QSPI通信方式,还需要实现快速读取数据的接口:
static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
uint8_t *read_buf, size_t read_size)
③ SFUD底层使用的SPI/QSPI接口和SPI设备对象初始化接口:
sfud_err sfud_spi_port_init(sfud_flash *flash) {
sfud_err result = SFUD_SUCCESS;

/**
* add your port spi bus and device object initialize code like this:
* 1. rcc initialize
* 2. gpio initialize
* 3. spi device initialize
* 4. flash->spi and flash->retry item initialize
* flash->spi.wr = spi_write_read; //Required
* flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
* flash->spi.lock = spi_lock;
* flash->spi.unlock = spi_unlock;
* flash->spi.user_data = &spix;
* flash->retry.delay = null;
* flash->retry.times = 10000; //Required
*/
rt_mutex_init(&lock, "sfud_lock", RT_IPC_FLAG_FIFO);
MX_SPI_Init();
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F0) \
|| defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32G0)

SET_BIT(hspi2.Instance->CR2, SPI_RXFIFO_THRESHOLD_HF);
#endif
switch (flash->index) {
case SFUD_W25QXX_DEVICE_INDEX: {

/* 同步 Flash 移植所需的接口及数据 */
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &user_spi;
/* about 100 microsecond delay */
flash->retry.delay = retry_delay_100us;
/* adout 60 seconds timeout */
flash->retry.times = 60 * 10000;

break;
}
}
return result;
}
其他:
static void spi_lock(const sfud_spi *spi) {
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);

RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);

rt_mutex_take(&lock, RT_WAITING_FOREVER);
}

static void spi_unlock(const sfud_spi *spi) {
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);

RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);

rt_mutex_release(&lock);
}

static void retry_delay_100us(void) {
/* 100 microsecond delay */
rt_thread_delay((RT_TICK_PER_SECOND * 1 + 9999) / 10000);
}





20200416115206184.png
20200416115539942.png
1589879058(1).jpg
1589879205(1).jpg
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
添加SFUD读写测试命令
static void sf(uint8_t argc, char **argv) {

#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
#define HEXDUMP_WIDTH 16
#define CMD_PROBE_INDEX 0
#define CMD_READ_INDEX 1
#define CMD_WRITE_INDEX 2
#define CMD_ERASE_INDEX 3
#define CMD_RW_STATUS_INDEX 4
#define CMD_BENCH_INDEX 5

sfud_err result = SFUD_SUCCESS;
static const sfud_flash *sfud_dev = NULL;

size_t i = 0, j = 0;

const char* sf_help_info[] = {
[CMD_PROBE_INDEX] = "sf probe - probe and init SPI flash",
[CMD_READ_INDEX] = "sf read addr size - read 'size' bytes starting at 'addr'",
[CMD_WRITE_INDEX] = "sf write addr data1 ... dataN - write some bytes 'data' to flash starting at 'addr'",
[CMD_ERASE_INDEX] = "sf erase addr size - erase 'size' bytes starting at 'addr'",
[CMD_RW_STATUS_INDEX] = "sf status [ ] - read or write '1:volatile|0:non-volatile' 'status'",
[CMD_BENCH_INDEX] = "sf bench - full chip benchmark. DANGER: It will erase full chip!",
};

if (argc < 2) {
rt_kprintf("Usage:\n");
for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) {
rt_kprintf("%s\n", sf_help_info
    );
    }
    rt_kprintf("\n");
    } else {
    const char *operator = argv[1];
    uint32_t addr, size;

    if (!strcmp(operator, "probe")) {
    if (argc < 2) {
    rt_kprintf("Usage: %s.\n", sf_help_info[CMD_PROBE_INDEX]);
    } else {

    sfud_dev = sfud_get_device(SFUD_W25QXX_DEVICE_INDEX);;
    if (sfud_dev->chip.capacity < 1024 * 1024) {
    rt_kprintf("%d KB %s is current selected device.\n", sfud_dev->chip.capacity / 1024, sfud_dev->name);
    } else {
    rt_kprintf("%d MB %s is current selected device.\n", sfud_dev->chip.capacity / 1024 / 1024,
    sfud_dev->name);
    }
    }
    } else {
    if (!sfud_dev) {
    rt_kprintf("No flash device selected. Please run 'sf probe'.\n");
    return;
    }
    if (!rt_strcmp(operator, "read")) {
    if (argc < 4) {
    rt_kprintf("Usage: %s.\n", sf_help_info[CMD_READ_INDEX]);
    return;
    } else {
    addr = strtol(argv[2], NULL, 0);
    size = strtol(argv[3], NULL, 0);
    uint8_t *data = rt_malloc(size);
    if (data) {
    result = sfud_read(sfud_dev, addr, size, data);
    if (result == SFUD_SUCCESS) {
    rt_kprintf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\n",
    sfud_dev->name, addr, size);
    rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
    for (i = 0; i < size; i += HEXDUMP_WIDTH)
    {
    rt_kprintf("[%08X] ", addr + i);
    /* dump hex */
    for (j = 0; j < HEXDUMP_WIDTH; j++) {
    if (i + j < size) {
    rt_kprintf("%02X ", data[i + j]);
    } else {
    rt_kprintf(" ");
    }
    }
    /* dump char for hex */
    for (j = 0; j < HEXDUMP_WIDTH; j++) {
    if (i + j < size) {
    rt_kprintf("%c", __is_print(data[i + j]) ? data[i + j] : '.');
    }
    }
    rt_kprintf("\n");
    }
    rt_kprintf("\n");
    }
    rt_free(data);
    } else {
    rt_kprintf("Low memory!\n");
    }
    }
    } else if (!rt_strcmp(operator, "write")) {
    if (argc < 4) {
    rt_kprintf("Usage: %s.\n", sf_help_info[CMD_WRITE_INDEX]);
    return;
    } else {
    addr = strtol(argv[2], NULL, 0);
    size = argc - 3;
    uint8_t *data = rt_malloc(size);
    if (data) {
    for (i = 0; i < size; i++) {
    data
      = strtol(argv[3 + i], NULL, 0);
      }
      result = sfud_write(sfud_dev, addr, size, data);
      if (result == SFUD_SUCCESS) {
      rt_kprintf("Write the %s flash data success. Start from 0x%08X, size is %ld.\n",
      sfud_dev->name, addr, size);
      rt_kprintf("Write data: ");
      for (i = 0; i < size; i++) {
      rt_kprintf("%d ", data
        );
        }
        rt_kprintf(".\n");
        }
        rt_free(data);
        } else {
        rt_kprintf("Low memory!\n");
        }
        }
        } else if (!rt_strcmp(operator, "erase")) {
        if (argc < 4) {
        rt_kprintf("Usage: %s.\n", sf_help_info[CMD_ERASE_INDEX]);
        return;
        } else {
        addr = strtol(argv[2], NULL, 0);
        size = strtol(argv[3], NULL, 0);
        result = sfud_erase(sfud_dev, addr, size);
        if (result == SFUD_SUCCESS) {
        rt_kprintf("Erase the %s flash data success. Start from 0x%08X, size is %ld.\n", sfud_dev->name,
        addr, size);
        }
        }
        } else if (!rt_strcmp(operator, "status")) {
        if (argc < 3) {
        uint8_t status;
        result = sfud_read_status(sfud_dev, &status);
        if (result == SFUD_SUCCESS) {
        rt_kprintf("The %s flash status register current value is 0x%02X.\n", sfud_dev->name, status);
        }
        } else if (argc == 4) {
        bool is_volatile = strtol(argv[2], NULL, 0);
        uint8_t status = strtol(argv[3], NULL, 0);
        result = sfud_write_status(sfud_dev, is_volatile, status);
        if (result == SFUD_SUCCESS) {
        rt_kprintf("Write the %s flash status register to 0x%02X success.\n", sfud_dev->name, status);
        }
        } else {
        rt_kprintf("Usage: %s.\n", sf_help_info[CMD_RW_STATUS_INDEX]);
        return;
        }
        } else if (!rt_strcmp(operator, "bench")) {
        if ((argc > 2 && rt_strcmp(argv[2], "yes")) || argc < 3) {
        rt_kprintf("DANGER: It will erase full chip! Please run 'sf bench yes'.\n");
        return;
        }
        /* full chip benchmark test */
        addr = 0;
        size = sfud_dev->chip.capacity;
        uint32_t start_time, time_cast;
        size_t write_size = SFUD_WRITE_MAX_PAGE_SIZE, read_size = SFUD_WRITE_MAX_PAGE_SIZE;
        uint8_t *write_data = rt_malloc(write_size), *read_data = rt_malloc(read_size);

        if (write_data && read_data) {
        rt_memset(write_data, 0x55, write_size);
        /* benchmark testing */
        rt_kprintf("Erasing the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
        start_time = rt_tick_get();
        result = sfud_erase(sfud_dev, addr, size);
        if (result == SFUD_SUCCESS) {
        time_cast = rt_tick_get() - start_time;
        rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
        time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
        } else {
        rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
        }
        /* write test */
        rt_kprintf("Writing the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
        start_time = rt_tick_get();
        for (i = 0; i < size; i += write_size) {
        result = sfud_write(sfud_dev, addr + i, write_size, write_data);
        if (result != SFUD_SUCCESS) {
        rt_kprintf("Writing %s failed, already wr for %lu bytes, write %d each time\n", sfud_dev->name, i, write_size);
        break;
        }
        }
        if (result == SFUD_SUCCESS) {
        time_cast = rt_tick_get() - start_time;
        rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
        time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
        } else {
        rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
        }
        /* read test */
        rt_kprintf("Reading the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
        start_time = rt_tick_get();
        for (i = 0; i < size; i += read_size) {
        if (i + read_size <= size) {
        result = sfud_read(sfud_dev, addr + i, read_size, read_data);
        } else {
        result = sfud_read(sfud_dev, addr + i, size - i, read_data);
        }
        /* data check */
        if (memcmp(write_data, read_data, read_size))
        {
        rt_kprintf("Data check ERROR! Please check you flash by other command.\n");
        result = SFUD_ERR_READ;
        }

        if (result != SFUD_SUCCESS) {
        rt_kprintf("Read %s failed, already rd for %lu bytes, read %d each time\n", sfud_dev->name, i, read_size);
        break;
        }
        }
        if (result == SFUD_SUCCESS) {
        time_cast = rt_tick_get() - start_time;
        rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
        time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
        } else {
        rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
        }
        } else {
        rt_kprintf("Low memory!\n");
        }
        rt_free(write_data);
        rt_free(read_data);
        } else {
        rt_kprintf("Usage:\n");
        for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) {
        rt_kprintf("%s\n", sf_help_info
          );
          }
          rt_kprintf("\n");
          return;
          }
          if (result != SFUD_SUCCESS) {
          rt_kprintf("This flash operate has an error. Error code: %d.\n", result);
          }
          }
          }
          }
          MSH_CMD_EXPORT(sf, SPI Flash operate.);
1589879205(1).jpg

Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
使用SFUD
初始化SFUD的API如下,该函数会初始化 Flash 设备表中的全部设备:
sfud_err sfud_init(void);
在QSPI模式下,SFUD 对于 QSPI 模式的支持仅限于快速读命令,通过该函数可以配置 Flash 所使用的 QSPI 总线的实际支持的数据线最大宽度,例如:1 线(默认值,即传统的 SPI 模式)、2 线、4 线:
sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width);
所以,在main函数中编写如下初始化函数:
/* SFUD初始化 */if(sfud_init() != SFUD_SUCCESS){ rt_kprintf("SFUD init fail.\r\n");}
编译、下载之后,可以在串口终端中看到SFUD打印的日志:
楼上最后的两幅图

Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
对了,STM32L431的SPI速率不能配置为最高的40K,实测有读写错误,配置为20k没有出现过读写错误
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
移植FAL软件包。
FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API (框架图如下所示),并具有以下特性:
  • )支持静态可配置的分区表,并可关联多个 Flash 设备;
    (
  • )分区表支持 自动装载 。避免在多固件项目,分区表被多次定义的问题;
    (
  • )代码精简,对操作系统 无依赖 ,可运行于裸机平台,比如对资源有一定要求的 Bootloader;
    (
  • )统一的操作接口。保证了文件系统、OTA、NVM(例如:EasyFlash) 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;
    (
  • )自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦) Flash 或分区,方便开发者进行调试、测试;
项目地址:https://github.com/RT-Thread-packages/fal移植FAL:
首先把FAL添加到工程1589880442(1).png接口移植相关的示例代码及文档在软件包的samples文件夹下:作者已经为我们提供了fal_flash_sfud_port.c,fal_flash_stm32f2_port.c,fal_cfg.h三个文件,我们只需要对这三个文件进行修改就可以了。修改fal_flash_sfud_port.c:使用上一步已经移植好的sfud的接口sfud_get_device()进行对SPIFLASH的控制1589880652(1).jpg修改fal_flash_stm32f2_port.c内部flash的操作接口可以直接使用RTT的官方库,添加STM32L431的flash驱动即可,所以不使用fal_flash_stm32f2_port.c1589880851(1).jpg把drv_flash_l4.c中#include "drv_config.h"替换为#include 1589881021(1).jpg修改fal_cfg.h:1589881202(1).jpg
使用FAL软件包初始化FAL的API如下:int fal_init(void);串口打印信息如下:1589881381(1).jpg
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
到此,如果不使用加密和压缩功能的话,rt-fota所需要的依赖环境已经添加完毕,这是rt-fota所能达到的最小尺寸,如果不使用系统和HAL库的话,还可以继续优化代码尺寸
会飞的胖子
会飞的胖子 2020-05-19
我基于nano重新修改了rt-fota的部分实现,AC6使用OZ编译,26KB大小占用
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-19
会飞的胖子 发表于 2020-5-19 21:33
我基于nano重新修改了rt-fota的部分实现,AC6使用OZ编译,26KB大小占用


你移植那个26K包含加密和压缩部分吗,我移植的项目地址https://gitee.com/Aladdin-Wang/RT-FOTA-STM32L431
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-20
会飞的胖子 发表于 2020-5-19 21:33
我基于nano重新修改了rt-fota的部分实现,AC6使用OZ编译,26KB大小占用


方便发一下你重新修改后的rt-fota的代码吗,我移植完不带加密和压缩的话30多K,全功能40多K
pjdu
pjdu 2020-05-21
有RT-Thread studio的版本吗?
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-26
    本帖最后由 Aladdin-Wang 于 2020-5-26 12:14 编辑


1.固件升级简述
固件升级,通常称为 OTA(Over the Air)升级或者 FOTA(Firmware Over-The-Air)升级,即固件通过空中下载进行升级的技术。1.1 bootloader 升级模式
bootloader 的升级模式常见有以下两种:* bootloader 分区 + app1 分区 + app2 分区模式该模式下, bootloader 启动后,检查 app1 和 app2 分区,哪个固件版本最新就运行哪个分区的固件。当有新版本的升级固件时,固件下载程序会将新的固件下载到另外的一个没有运行的 app 分区,下次启动的时候重新选择执行新版本的固件。优点:无需固件搬运,启动速度快。缺点: app1 分区和 app2 分区通常要大小相等,占用 Flash 资源;且 app1 和 app2 分区都只能存放app 固件,不能存放其他固件(如 WiFi 固件)。

* bootloader 分区 + app 分区 + download 分区模式该模式下, bootloader 启动后,检查 download 分区是否有新版本的固件,如果 download 分区内有新版本固件,则将新版本固件从 download 分区搬运到 app 分区,完成后执行 app 分区内的固件;如果 download 分区内没有新版本的固件,则直接执行 app 分区内的固件。当有新版本的升级固件时,固件下载程序会将新的固件下载到 download 分区内,重启后进行升级。优点: download 分区可以比 app 分区小很多(使用压缩固件),节省 Flash 资源,节省下载流量;download 分区也可以下载其他固件,从而升级其他的固件,如 WiFi 固件、 RomFs。缺点:需要搬运固件,首次升级启动速度略慢。

RT-Thread OTA 使用的是 bootloader 升级模式 2, bootloader 分区 + app 分区 + download 分区的组合。2.RT-OTA简介
为了能让开发者快速掌握 OTA 升级这把利器,RT-Thread 开发团队提供了通用的Bootloader。开发者通过该 Bootloader 即可直接使用 RT-Thread OTA 功能,轻松实现对设备端固件的管理、升级与维护。下图展示了 RT-Thread 通用 Bootloader 的软件框架:[img][/img]RT-Thread 开发团队的官方Bootloader以bin文件形式提供, 在线获取地址: http://iot.rt-thread.com3.Flash 分区简述
通常嵌入式系统程序是没有文件系统的,而是将 Flash 分成不同的功能区块,从而形成不同的功能分区。要具备 OTA 固件升级能力,通常需要至少有两个程序运行在设备上。其中负责固件校验升级的程序称之为 bootloader,另一个负责业务逻辑的程序称之为 app。它们负责不同的功能,存储在 Flash 的不同地址范围,从而形成了 bootloader 分区和 app 分区。但多数情况下嵌入式系统程序是运行在 Flash 中的,下载升级固件的时候不会直接向 app 分区写入新的固件,而是先下载到另外的一个分区暂存,这个分区就是 download 分区,也有称之为 app2 分区,这取决于 bootloader 的升级模式。bootloader 分区、 app 分区、 download 分区及其他分区一起构成了分区表。分区表标识了该分区的特有属性,通常包含分区名、分区大小、分区的起止地址等。通用 Bootloader 中的分区表包含如下三个分区:
4.Ymodem文件传输协议
Ymodem 是一种文本传输协议,在 OTA 应用中为空中下载技术提供文件传输的支持。基于 Ymodem协议的固件升级即为 OTA 固件升级的一个具体应用实例。5.RT-OTA功能说明
5.1 升级固件功能
当系统需要升级固件时,Bootloader 将从 download 分区将固件搬运到 app 分区,主要功能流程如下所示:* Bootloader 启动时检查 download 分区和 app 分区中的固件版本。

* 如果两个固件版本相同,则跳转到 app 分区,Bootloader 运行结束。

* 固件版本不同则将 download 分区中的固件搬运到 app 分区。

* 在搬运的过程中 Bootloader 可以对固件进行校验、解密、解压缩等操作。

* 搬运完毕后,删除 download 分区中存储的固件。

* 重启系统跳转到 app 分区中的固件运行,Bootloader 运行结束。

Bootloader 工作过程如下图所示:[img]https://img-blog.csdnimg.cn/20200526085217568.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzMxMDM5MDYx,size_16,color_FFFFFF,t_70[/img]5.2 恢复固件功能
当系统中的固件损坏,Bootloader 将从 factory 分区将固件搬运到 app 分区,主要功能流程如下所示:* Bootloader 启动时检查触发固件恢复的引脚是否为有效电平。

* 如果有效电平持续超过 10S 则将 factory 分区中的固件搬运到 app 分区中。

* 如果有效电平没有持续超过 10S 则继续进行 2.2 小节中介绍的启动步骤。

* 在搬运的过程中 Bootloader 可以对固件进行校验、解密、解压缩等操作。

* 搬运完毕后,保持 factory 分区中的固件不变。

* 重启系统跳转到 app 分区中的固件运行,Bootloader 运行结束。

6.RT-FOTA简介
RT-Thread官方推出了STM32系列单片机的通用bootloader,在其网站可以通过网页配置就可以生成bootloader的烧录文件,使广大嵌入式工程师不用编写一行代码,就能够轻松完成自己产品的bootloader功能。但是由于RTT官方的bootloader软件RT-OTA是商用性质,不公开源码,不仅仅限制了在其他平台的移植,而且也不方便加入产品的特定功能。所以就有了RT-FOTA的由来。RT-FOTA兼容RTThread官方OTA的所有功能,为了与官方的RT-OTA作于区分,所以取名为RT-FOTA。RT-FOTA的项目地址:https://gitee.com/spunky_973/rt-fotaRT-FOTA可以直接使用在RT-Thread的完整版搭载,只需要将rt _ fota.c、rt _ fota.h和rt _ fota_crc.c放入工程中即可实现,然后用env配置相关组件即可。7.RT-FOTA功能说明* 支持RTT官方的RBL打包软件,使用方式也一致。目前支持包括CRC32、AES256、quicklz和fastlz功能;

* 支持命令行模式(FINSH组件)和出厂固件恢复;

* 支持FLASH分区(FAL组件);

* 增加fota和ymdown命令;

* 其他功能可自行方便扩展;


Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-26
完整文章:
403463275
403463275 2020-05-28
同为工程师,为何你这么NB
Aladdin-Wang
Aladdin-Wang 认证专家 2020-05-28
403463275 发表于 2020-5-28 16:35
同为工程师,为何你这么NB


我只是代码的搬运工,RT-FOTA的著作是王希开发的,他的项目地址:https://gitee.com/spunky_973/rt-fota
jerry4cool
jerry4cool 2020-06-19
感谢分享。。。。
armboy
armboy 2020-07-27

移植到stm32f1成功 就是ymodem_ota下不进去 其他都是好的 不知道怎么分析解决

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友