在上文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 IAPAPPIAP 三个工程的修改步骤。

二、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);
}

 

posted on 2026-09-02 14:09  WCH蓝牙应用分享  阅读(16)  评论(0)    收藏  举报