固件升级方案

概述

ET6000系列产品均内置了BOOTROM程序,支持固件升级功能。同时部分型号FLASH支持双Bank切换功能。

固件升级方案主要有两种常见的方案:

  1. 使用BOOTROM自带的固件升级功能

  2. 使用用户自定义二级Bootloader进行固件升级

以上两种方案的优缺点如下:

方案

优点

缺点

BOOTROM自带的固件升级功能

无需二次开发,直接使用,支持多种升级方式(UART、CAN)

需要BOOTMODE管脚切换,不灵活,不支持不掉电升级

用户自定义二级Bootloader

灵活性高,支持不掉电升级

需要二次开发,占用一定的FLASH空间

方案一:使用BOOTROM自带的固件升级功能

以ET6001为例,使用BOOTROM自带的固件升级功能,主要步骤如下:

  1. 硬件BOOTMODE管脚或选项字设置为ROM自举启动模式

  2. 复位芯片,使其进入BOOTROM自举模式

  3. 使用ET-TOOL工具连接芯片,选择升级方式(UART或CAN),并加载升级文件

  4. 还原BOOTMODE管脚或选项字设置为正常运行模式

  5. 复位芯片,使其进入正常运行模式

选项字nCRCEN

选项字nCRCEN用于控制BOOTROM正常启动模式下引导APP是否对APP进行CRC校验功能。默认情况下,nCRCEN为1,表示不启用CRC校验。 如果配置了选项字nCRCEN为0,BOOTROM引导APP启动时会进行CRC校验。校验失败则会进入BOOTROM自举模式,等待升级。

小技巧

生成带有CRC校验的APP文件,可以使用ET-TOOL工具。

选项字nOTAMODE

选项字nOTAMODE用于控制BOOTROM正常启动模式下引导APP是否支持双Bank切换OTA模式。默认情况下,nOTAMODE为1,表示不启用OTA模式。 如果配置了选项字nOTAMODE为0,BOOTROM引导APP启动时会支持双Bank切换OTA模式。

小技巧

该功能仅在具有2块PFLASH的芯片上有效,如ET6001、ET6002、ET6039等。

未开启双Bank切换OTA模式时,2块PFLASH可以看作一整块PFLASH用来存储APP程序,开启后,则APP程序的大小只能限制在单块PFLASH的大小范围内

AN0015001

2块PFLASH的虚拟分区映射关系如下:

虚拟分区

映射地址

Bank (nBANKSWAP=1)

物理 Bank (nBANKSWAP=0)

A 分区

0x08000000

Bank0

Bank1

B 分区

0x08040000

Bank1

Bank0

如上表所示,APP的起始地址始终为0x08000000,且在nBANKSWAP=1时,A分区映射到Bank0,B分区映射到Bank1;在nBANKSWAP=0时,A分区映射到Bank1,B分区映射到Bank0。

小技巧

更详细的内容请阅读ET6001的用户手册第7章节以及 ET-TOOL使用说明

与上位机的连接

ET-TOOL工具可以通过UART或CAN与ET6000系列芯片进行连接。具体连接方式如下:

连接方式

UART

CAN

硬件连接

使用USB转串口模块连接芯片的UART0引脚GPIO3(UART0_RX)和GPIO2(UART0_TX)

使用USB转CAN模块连接芯片的CAN0引脚GPIO4(CANFD0_RX)和GPIO8(CANFD0_TX)

软件配置

在ET-TOOL中选择对应的串口号和波特率(115200)

在ET-TOOL中选择对应的CAN接口和波特率(125Kbps)

小技巧

用户可以通过修改选项字 BOOTDFF 来修改默认的UART和CAN的引脚映射。

重要

如需要支持BOOTROM自举功能,外部晶振必须为25MHz。

方案二:使用用户自定义二级Bootloader进行固件升级

用户可以根据自己的需求,编写自定义的二级Bootloader程序,实现固件升级功能。二级Bootloader可以通过UART、CAN、SPI等多种方式与上位机或主控MCU通信,并支持不掉电升级。

二级Bootloader需要实现的功能包括:

  • 系统时钟的配置(可选)

  • 通信外设接口的初始化

  • 解析升级协议接收升级数据

  • PFLASH的擦除和编程

  • 校验升级数据的完整性

  • 切换到新APP的入口地址

二级Bootloader的设计需要注意以下几点:

  1. 二级Bootloader的起始地址必须为0x08000000,这是因为BOOTROM在启动时会将PC指向这个地址。

  2. 二级Bootloader的大小不能超过PFLASH的大小,否则会导致无法正常擦除和编程。

PLASH空间的划分

AN0015003 AN0015002

双Bank切换OTA模式开启后,二级Bootloader需要在A/B分区都有一份。 二级Bootloader的大小可以根据实际需求进行调整,但需要保证在每个分区中都有足够的空间来存放二级Bootloader和新APP。

PFLASH的擦除和编程

PFLASH的擦除和编程需要使用芯片提供的Flash驱动接口。ET6000系列芯片提供了以下HAL层Flash驱动接口:

  • driver_flash_init() - 初始化Flash驱动

  • driver_flash_write(flash_id, offset, len, buf) - 写数据

  • driver_flash_erase(flash_id, offset, len) - 擦除扇区

  • driver_flash_read(flash_id, offset, len, buf) - 读数据

FLASH操作接口:

void driver_flash_init(void);
/**
* @brief 写数据
*
* @param[in] flash_id  FLASH 的编号:0: PFLASH0; 1: PFLASH1; 2:DFLASH
* @param[in] offset    offset flash地址偏移,需保证不超出芯片FLASH,且是8字节对齐
* @param[in] len       len buffer深度,需保证不超出芯片FLASH,(offset+len-1) <= (FLASH_END-FLASH_BASE)
* @param[in] buf       buf 写入数据缓冲区
*
* @return 0 on success, or a negative error code on failure.
*/
int32_t driver_flash_write(uint32_t flash_id, uint32_t offset, uint32_t len, uint8_t *buf);
/**
* @brief 擦除扇区
*
* @param[in] flash_id  FLASH 的编号:0: PFLASH0; 1: PFLASH1; 2:DFLASH
* @param[in] offset    offset flash地址偏移,需保证不超出当前bank flash容量,且是8字节对齐
* @param[in] len       len buffer深度,需保证不超出当前bank flash
*
* @attention 如果擦除长度不足1KB,则还是会按1个sector大小擦除
* @return 0 on success, or a negative error code on failure.
*/
int32_t driver_flash_erase(uint32_t flash_id, uint32_t offset, uint32_t len);
/**
* @brief  读数据
*
* @param[in] flash_id  FLASH 的编号:0: PFLASH0; 1: PFLASH1; 2:DFLASH
* @param[in] offset    offset flash地址偏移,需保证不超出当前bank flash容量
* @param[in] len       len buffer深度,需保证不超出当前bank flash
*
* @return 0 on success, or a negative error code on failure.
*/
int32_t driver_flash_read(uint32_t flash_id, uint32_t offset, uint32_t len, uint8_t *buf);

FLASH操作示例:

/* 0 : pflash0, 1: pflash1, 2: dflash */
#define CURRENT_FLASH  1

int main(void)
{
    uint32_t offset = 0x8003;
    uint32_t eaddr = 0;
    UartInit();
    memset(mem_read_buffer, 0xFF, FLASH_SECTOR_SIZE);
    /*初始化 flash, 上电调用一次即可 */
    driver_flash_init();
    printf("Flash driver init successful.\n");
    fill_pattern_mem(0x5555555555555555, 0xaaaaaaaaaaaaaaaa);
    /* 以sector单位进行擦除,擦除地址需要按1024字节对齐 */
    driver_flash_erase(CURRENT_FLASH, offset, FLASH_SECTOR_SIZE);
    printf("Flash erase successful.\n");
    /* 写flash, 偏移地址需要按8字节对齐  */
    driver_flash_write(CURRENT_FLASH, offset, FLASH_SECTOR_SIZE, mem_pattern_buffer);
    printf("Flash write successful.\n");
    /* 读flash  */
    driver_flash_read(CURRENT_FLASH, offset, FLASH_SECTOR_SIZE,mem_read_buffer);
    printf("Flash read successful.\n");
    /* 回读内容比较 */
    if (memcmp_addr(mem_pattern_buffer, mem_read_buffer, FLASH_SECTOR_SIZE, &eaddr) != 0)
    {
        printf("Flash erase-write-read failed at address 0x%x\n", eaddr);
    }
    else
    {
    printf("Flash erase-write-read success!!\n");
    }
    while (1);
}

备注

在使用Flash驱动接口进行擦除和编程时,需要注意不能擦除和编写当前运行的APP程序所在的分区,否则会导致系统异常。

切换到新APP的入口地址

如下为切换到新APP的入口地址的代码示例:

typedef void (*Function_Pointer)(void);
/**
* @brief 切换到新APP的入口地址
*
* @param[in] app_entry  新APP的入口地址
*/
void jump_to_app(uint32_t app_entry)
{
    Function_Pointer jump_to_address;
    // 回收bootloader使用的资源
    bootloader_cleanup();
    // 检查新APP的入口地址是否有效
    jump_to_address = (Function_Pointer)(*(__IO uint32_t *)(app_entry + 4U));
    // 设置栈顶指针
    __set_MSP(*(volatile uint32_t *)app_entry);
    // 跳转到新APP入口
    jump_to_address();
    while (1); // 如果跳转失败,进入死循环
}

时钟配置要求

无论是bootloader切换到app, 还是app切换到bootloader, 如果bootloader和app程序中的时钟及分频参数配置不一直, 需要在system_et6x.c 中 先将时钟切换到低速时钟,再修改时钟频率。

void SystemInit(void)
{
  //
  //
  /* 现将时钟切换到OSC25M, 再进行时钟及总线分频配置 */
  if (CRG_setClockOutSource(CRG_CLKSRC_OSC25M) != DRV_OK)
  {
      while (1);
  }
  DELAY_US(1);
  SystemClock_Config();
  /* Default Flash Clock is 50MHz */
  SystemFlash_TrimLoad(FLASH_FRQ_50M_LVL);
  //
  //
}

示例代码

该示例工程包含:

  • 单Bank OTA升级 Keil工程及源码

  • 双Bank OTA升级 Keil工程及源码

  • 工程说明文档

  • OTA demo上位机烧录工具