固件升级方案
概述
ET6000系列产品均内置了BOOTROM程序,支持固件升级功能。同时部分型号FLASH支持双Bank切换功能。
固件升级方案主要有两种常见的方案:
使用BOOTROM自带的固件升级功能
使用用户自定义二级Bootloader进行固件升级
以上两种方案的优缺点如下:
方案 |
优点 |
缺点 |
|---|---|---|
BOOTROM自带的固件升级功能 |
无需二次开发,直接使用,支持多种升级方式(UART、CAN) |
需要BOOTMODE管脚切换,不灵活,不支持不掉电升级 |
用户自定义二级Bootloader |
灵活性高,支持不掉电升级 |
需要二次开发,占用一定的FLASH空间 |
方案一:使用BOOTROM自带的固件升级功能
以ET6001为例,使用BOOTROM自带的固件升级功能,主要步骤如下:
硬件BOOTMODE管脚或选项字设置为ROM自举启动模式
复位芯片,使其进入BOOTROM自举模式
使用ET-TOOL工具连接芯片,选择升级方式(UART或CAN),并加载升级文件
还原BOOTMODE管脚或选项字设置为正常运行模式
复位芯片,使其进入正常运行模式
选项字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的大小范围内。

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的设计需要注意以下几点:
二级Bootloader的起始地址必须为0x08000000,这是因为BOOTROM在启动时会将PC指向这个地址。
二级Bootloader的大小不能超过PFLASH的大小,否则会导致无法正常擦除和编程。
PLASH空间的划分

双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上位机烧录工具