在上文ch5xx 非固定库 OTA 备份升级中我们讲解了如何移植非固定库OTA,下面我们在此基础上讲解下CH584X如何使用DataFlash做备份区进行OTA。
下文在BackupUpgrade_OTA例程进行移植,若使用Peripheral等例程需先参考ch5xx 非固定库 OTA 备份升级移植后再进行修改。
一、Flash 地址规划
为实现 OTA ,需将 Flash 划分为四个独立区域,各区域起始地址与大小如下表所示:
| 区域 | 起始地址 | 大小 | 说明 |
|---|---|---|---|
| Jump IAP | 0x00000000 | 4 KB | 启动跳转程序进入IAP |
| Peripheral APP | 0x00001000 | 464 KB | 应用程序主体(从4KB开始) |
| IAP | 0x00075000 | 12 KB | 升级管理程序(468KB~480KB) |
| imageB(备份区) | 0x0000C000 | 464 KB | 备份区DataFlash(48KB~512KB) |
下面分别介绍 Jump IAP、APP、IAP 三个工程的修改步骤。
二、Jump IAP 工程修改(1 处)
步骤1:修改启动文件(Startup.s)
Jump IAP 的功能仅为跳转至 IAP 起始地址。
j 0x75000 /* 跳转至 IAP 新起始地址 0x00075000 */
三、APP 工程修改(3 处)
步骤1:Link.ld 文件修改
Link.ld 文件定义了各个存储区域的起始地址与占用空间。根据需要相应调整应用程序(APP)区域的起始地址与占用空间。
以起始地址为 0x00010000 的 CH59xBLE_ROM.hex 为例,修改后的 MEMORY 配置如下:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 464K /* APP起始地址与可用长度,与ROM起始地址对应 */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K /* CH584X的RAM为96K */
}
说明:APP 从 Flash 起始地址 0x00001000 开始,最大可用空间为 464KB。
步骤2:ota.h文件修改
/* 整个用户code区分成三块,4K,464K,12K,后两块分别叫做imageA(APP)和imageIAP */
/* data区用做imageB(OTA) */
/* FLASH定义 */
#define FLASH_BLOCK_SIZE EEPROM_BLOCK_SIZE
#define IMAGE_SIZE 464 * 1024
/* imageA定义 */
#define IMAGE_A_FLAG 0x01
#define IMAGE_A_START_ADD 4 * 1024
#define IMAGE_A_SIZE IMAGE_SIZE
/* imageIAP定义 */
#define IMAGE_IAP_FLAG 0x03
#define IMAGE_IAP_START_ADD (IMAGE_A_START_ADD + IMAGE_SIZE)
#define IMAGE_IAP_SIZE 12 * 1024
/* imageB定义 */
#define IMAGE_B_FLAG 0x02
#define IMAGE_DATAFLASH_START_ADD 48 * 1024
#define IMAGE_DATAFLASH_START_ADD_PROM 44 * 1024
#define IMAGE_DATAFLASH_SIZE 464 * 1024
#define IMAGE_DATAFLASH_END_ADD (IMAGE_DATAFLASH_START_ADD + IMAGE_DATAFLASH_SIZE)
步骤3:peripheral.c文件擦除,编程,校验修改
//OTA_FLASH_ERASE_EVT
if(events & OTA_FLASH_ERASE_EVT)
{
uint8_t status;
PRINT("ERASE:%08x num:%d\r\n", (int)(EraseAdd + EraseBlockCnt * FLASH_BLOCK_SIZE), (int)EraseBlockCnt);
status = EEPROM_ERASE(EraseAdd + EraseBlockCnt * FLASH_BLOCK_SIZE, FLASH_BLOCK_SIZE);
/* 擦除失败 */
if(status != SUCCESS)
{
OTA_IAP_SendCMDDealSta(status);
return (events ^ OTA_FLASH_ERASE_EVT);
}
EraseBlockCnt++;
/* 擦除结束 */
if(EraseBlockCnt >= EraseBlockNum)
{
PRINT("ERASE Complete\r\n");
OTA_IAP_SendCMDDealSta(status);
return (events ^ OTA_FLASH_ERASE_EVT);
}
return (events);
}
/*********************************************************************
* @fn Rec_OTA_IAP_DataDeal
*
* @brief 接收到OTA数据包处理
*
* @return none
*/
void Rec_OTA_IAP_DataDeal(void)
{
switch(iap_rec_data.other.buf[0])
{
/* 编程 */
case CMD_IAP_PROM:
{
uint32_t i;
uint8_t status;
OpParaDataLen = iap_rec_data.program.len;
OpAdd = (uint32_t)(iap_rec_data.program.addr[0]);
OpAdd |= ((uint32_t)(iap_rec_data.program.addr[1]) << 8);
OpAdd = OpAdd * 16;
OpAdd += IMAGE_DATAFLASH_START_ADD_PROM;
PRINT("IAP_PROM: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);
/* 当前是ImageA,直接编程 */
status = EEPROM_WRITE(OpAdd, iap_rec_data.program.buf, (uint16_t)OpParaDataLen);
if(status) PRINT("IAP_PROM err \r\n");
OTA_IAP_SendCMDDealSta(status);
break;
}
/* 擦除 -- 蓝牙擦除由主机控制 */
case CMD_IAP_ERASE:
{
OpAdd = (uint32_t)(iap_rec_data.erase.addr[0]);
OpAdd |= ((uint32_t)(iap_rec_data.erase.addr[1]) << 8);
OpAdd = OpAdd * 16;
OpAdd += IMAGE_DATAFLASH_START_ADD_PROM;
EraseBlockNum = (uint32_t)(iap_rec_data.erase.block_num[0]);
EraseBlockNum |= ((uint32_t)(iap_rec_data.erase.block_num[1]) << 8);
EraseAdd = IMAGE_DATAFLASH_START_ADD;
EraseBlockCnt = 0;
/* 检验就放在擦除里清0 */
VerifyStatus = 0;
PRINT("IAP_ERASE start:%08x num:%d\r\n", (int)EraseAdd, (int)EraseBlockNum);
if(EraseAdd < IMAGE_DATAFLASH_START_ADD || (EraseAdd + (EraseBlockNum - 1) * FLASH_BLOCK_SIZE) > IMAGE_DATAFLASH_END_ADD)
{
OTA_IAP_SendCMDDealSta(0xFF);
}
else
{
/* 启动擦除 */
tmos_set_event(Peripheral_TaskID, OTA_FLASH_ERASE_EVT);
}
break;
}
/* 校验 */
case CMD_IAP_VERIFY:
{
uint32_t i;
uint8_t status = 0;
OpParaDataLen = iap_rec_data.verify.len;
OpAdd = (uint32_t)(iap_rec_data.verify.addr[0]);
OpAdd |= ((uint32_t)(iap_rec_data.verify.addr[1]) << 8);
OpAdd = OpAdd * 16;
OpAdd += IMAGE_DATAFLASH_START_ADD_PROM;
PRINT("IAP_VERIFY: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);
/* 当前是ImageA,直接读取ImageB校验 */
status = EEPROM_VERIFY(OpAdd, iap_rec_data.verify.buf, OpParaDataLen);
// status = FLASH_ROM_VERIFY(OpAdd, iap_rec_data.verify.buf, OpParaDataLen);
if(status)
{
PRINT("IAP_VERIFY err \r\n");
}
VerifyStatus |= status;
OTA_IAP_SendCMDDealSta(VerifyStatus);
break;
}
/* 编程结束 */
case CMD_IAP_END:
{
PRINT("IAP_END \r\n");
/* 当前的是ImageA */
/* 关闭当前所有使用中断,或者方便一点直接全部关闭 */
DisableAllIRQ();
/* 修改DataFlash,切换至ImageIAP */
SwitchImageFlag(IMAGE_IAP_FLAG);
/* 等待打印完成 ,复位*/
mDelaymS(10);
SYS_ResetExecute();
break;
}
case CMD_IAP_INFO:
{
uint8_t send_buf[20];
PRINT("IAP_INFO \r\n");
/* IMAGE FLAG */
send_buf[0] = IMAGE_B_FLAG;
/* IMAGE_SIZE */
send_buf[1] = (uint8_t)(IMAGE_SIZE & 0xff);
send_buf[2] = (uint8_t)((IMAGE_SIZE >> 8) & 0xff);
send_buf[3] = (uint8_t)((IMAGE_SIZE >> 16) & 0xff);
send_buf[4] = (uint8_t)((IMAGE_SIZE >> 24) & 0xff);
/* BLOCK SIZE */
send_buf[5] = (uint8_t)(FLASH_BLOCK_SIZE & 0xff);
send_buf[6] = (uint8_t)((FLASH_BLOCK_SIZE >> 8) & 0xff);
send_buf[7] = CHIP_ID&0xFF;
send_buf[8] = (CHIP_ID>>8)&0xFF;
/* 有需要再增加 */
/* 发送信息 */
OTA_IAP_SendData(send_buf, 20);
break;
}
default:
{
OTA_IAP_CMDErrDeal();
break;
}
}
}
uint32_t EEPROM_VERIFY(uint32_t StartAddr, void *Buffer, uint32_t Length)
{
uint32_t dataflash_buf[256];
EEPROM_READ(StartAddr, dataflash_buf, Length);
return tmos_memcmp(dataflash_buf, Buffer, Length) == 0U;
}
四、IAP 工程修改(4 处)
步骤1:修改链接脚本(Link.ld)
链接脚本(.ld)定义了各存储区域的基址与长度。根据地址规划,将APP区域起始地址设为 0x00075000,长度设为 12KB。
MEMORY
{
FLASH (rx) : ORIGIN = 0x00075000, LENGTH = 12K /* IAP从468KB开始,可用12KB */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K /* CH584X的RAM为96K */
}
注意:此处 LENGTH = 12K 即对应地址范围 0x00075000 ~ 0x00040000(468KB ~ 480KB),与APPP区域(4KB-468KB)无缝衔接。
步骤2:修改ota.h文件
/* 整个用户code区分成三块,4K,464K,12K,后两块分别叫做imageA(APP)和imageIAP */
/* data区用做imageB(OTA) */
/* FLASH定义 */
#define FLASH_BLOCK_SIZE EEPROM_BLOCK_SIZE
#define IMAGE_SIZE 464 * 1024
/* imageA定义 */
#define IMAGE_A_FLAG 0x01
#define IMAGE_A_START_ADD 0x1000
#define IMAGE_A_SIZE IMAGE_SIZE
/* imageIAP定义 */
#define IMAGE_IAP_FLAG 0x03
#define IMAGE_IAP_START_ADD (IMAGE_A_START_ADD + IMAGE_SIZE)
#define IMAGE_IAP_SIZE 12 * 1024
/* imageB定义 */
#define IMAGE_B_FLAG 0x02
#define IMAGE_DATAFLASH_START_ADD 32 * 1024
#define IMAGE_DATAFLASH_SIZE 432 * 1024
#define IMAGE_DATAFLASH_END_ADD (IMAGE_DATAFLASH_START_ADD + IMAGE_DATAFLASH_SIZE)
步骤3:peripheral_main.c程序修改
/*********************************************************************
* @fn jump_APP
*
* @brief 切换APP程序
*
* @return none
*/
void jump_APP(void)
{
if(CurrImageFlag == IMAGE_IAP_FLAG)
{
__attribute__((aligned(8))) uint8_t flash_Data[1024];
uint16_t i;
FLASH_ROM_ERASE(IMAGE_A_START_ADD, IMAGE_A_SIZE);
for(i = 0; i < IMAGE_A_SIZE / 1024; i++)
{
EEPROM_READ(IMAGE_DATAFLASH_START_ADD + (i * 1024), flash_Data, 1024);
FLASH_ROM_WRITE(IMAGE_A_START_ADD + (i * 1024), flash_Data, 1024);
}
SwitchImageFlag(IMAGE_A_FLAG);
// 销毁备份代码
EEPROM_ERASE(IMAGE_DATAFLASH_START_ADD, IMAGE_DATAFLASH_SIZE);
}
jumpApp();
}
五、固件烧录与 OTA 验证
1.固定烧录
使用最新版 WCHISPTOOL 直接选择三个 HEX 文件(JUMP IAP/APP/IAP/)进行烧录。
2.OTA验证
验证 OTA 流程:烧录完成后使用 OTA APP 选择新固件进行升级。
六.注意点
使用CH584X时,dataflash只支持4k的倍数擦,app和iap中EEPROM_ERASE接口擦的长度至少为4K(EEPROM_BLOCK_SIZE),否则程序会卡住无法继续运行:
void SwitchImageFlag(uint8_t new_flag)
{
uint16_t i;
uint32_t ver_flag;
/* 读取第一块 */
EEPROM_READ(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
/* 擦除第一块 */
EEPROM_ERASE(OTA_DATAFLASH_ADD, EEPROM_BLOCK_SIZE);
/* 更新Image信息 */
block_buf[0] = new_flag;
/* 编程DataFlash */
EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
}
浙公网安备 33010602011771号