【WCH蓝牙系列芯片】-基于CH592开发板—蓝牙不带库方式双备份APP升级

------------------------------------------------------------------------------------------------------------------------------------ 

在CH592的蓝牙OTA升级中,官网例程中是包含有两个方式,一种是备份无线升级方式,另一种是固定库无线升级方式

 

image

  方式一备份升级方式,适用于 flash 资源充裕的应用,优点是 APP+LIB 都可以升级,缺点是APP的flash 需要对半分使用。上电优先运行 IAP 程序,之后跳转 APP 执行用户程序。APP 文件为独立完整的功能文件,运行时可以通过无线方式接收完整的 OTA 备份升级文件,然后软复位进入 IAP 程序,IAP 会将备份区的 OTA 升级文件拷贝到 APP应用程序区,最后跳转回升级后的 APP 应用程序运行新的固件,升级固件带 LIB 编译。

  方式二采用 APP+IAP+LIB 的方式,适用于 flash 资源比较紧凑的应用,优点是节省 flash,缺点是 LIB 固定的,无法升级,如果遇到 LIB 需要升级的情况,只能通过有线下载烧录全升级。方式二上电优先运行 IAP 程序,之后由 IAP 跳转 APP 执行用户程序。升级时由 APP 触发升级跳转到 IAP 程序,IAP 通过无线方式接收升级固件并擦除写入 APP 用户代码区,升级完成后跳转 APP 执行新的用户程序。

  在个别应用中,APP的FLASH占用不大的情况,有的时候需要在方式二升级过程中,也需要将APP的flash的空间,分成A,B两个分区。

  在例程中OnlyUpdateApp_Peripheral程序中,默认的APP的大小分配的是44K空间,这个是为了方便一些192Kflash的芯片也能直接使用OTA。这次使用的CH592芯片FLASH是448K大小。因此可以针对APP的大小进行扩大,可以扩大到236K的大小。

具体的移植步骤可以参考:CH585 592 582 573OTA例程讲解二(方式二:使用固定库+扩大APP程序空间)

在这个基础上,再对APP的236K的空间大小对半分使用,

1、修改OnlyUpdateApp_IAP的程序

  直接将BackupUpgrade_IAP程序中的ota.h和peripheral_main.c直接复制到OnlyUpdateApp_IAP中,其中包含切换dataflash里的ImageFlag程序void SwitchImageFlag(uint8_t new_flag),切换APP程序的void jump_APP(void);还有读取当前的程序的Image标志,DataFlash如果为空,就默认是ImageA,如果为ImageA,则跳转ImageA的程序void ReadImageFlag(void)。

image

  在ota.h程序中,需要定义APP的两个分区大小,这里imageA和imageB的FLASH大小都是设置为116K,设置这个116K的大小是为了能够被4K整数倍擦除,因为在升级过程中,FLASH的擦除是按照4K的大小,如果236K的大小对半分应该是118K的大小空间,但是这个118K的大小空间是不能被4K整数倍擦除,就会导致后面升级之后的程序,不能正常运行。

  针对这个问题,后续还可以减小对IAP和JumpIAP的空间大小,多留出FLASH大小给APP使用,确保对半分出出来的FLASH大小可以被4K整数倍擦除。

image

2、修改OnlyUpdateApp_Peripheral的程序

修改LD文件的FLASGH大小,改为116K,起始地址改从0x00001000开始

image

在peripheral_main.c程序中,添加ReadImageFlag程序,上电前读取当前的程序的Image标志,DataFlash如果为空,就默认是ImageA

image

image

在peripheral.c中,将原本固定库升级方式的程序,全部替换成备份升级方式的程序,

 

image

image

image

image

image

image

/*********************************************************************
 * @fn      OTA_IAP_SendData
 *
 * @brief   OTA IAP发送数据,使用时限制20字节以内
 *
 * @param   p_send_data - 发送数据的指针
 * @param   send_len    - 发送数据的长度
 *
 * @return  none
 */
void OTA_IAP_SendData(uint8_t *p_send_data, uint8_t send_len)
{
    OTAProfile_SendData(OTAPROFILE_CHAR, p_send_data, send_len);
}
/*********************************************************************
 * @fn      OTA_IAP_SendCMDDealSta
 *
 * @brief   OTA IAP执行的状态返回
 *
 * @param   deal_status - 返回的状态
 *
 * @return  none
 */
void OTA_IAP_SendCMDDealSta(uint8_t deal_status)
{
    uint8_t send_buf[2];

    send_buf[0] = deal_status;
    send_buf[1] = 0;
    OTA_IAP_SendData(send_buf, 2);
}
/*********************************************************************
 * @fn      OTA_IAP_CMDErrDeal
 *
 * @brief   OTA IAP异常命令码处理
 *
 * @return  none
 */
void OTA_IAP_CMDErrDeal(void)
{
    OTA_IAP_SendCMDDealSta(0xfe);
}
/*********************************************************************
 * @fn      SwitchImageFlag
 *
 * @brief   切换dataflash里的ImageFlag
 *
 * @param   new_flag    - 切换的ImageFlag
 *
 * @return  none
 */
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_PAGE_SIZE);

    /* 更新Image信息 */
    block_buf[0] = new_flag;

    /* 编程DataFlash */
    EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);
}
/*********************************************************************
 * @fn      DisableAllIRQ
 *
 * @brief   关闭所有的中断
 *
 * @return  none
 */
void DisableAllIRQ(void)
{
    SYS_DisableAllIrq(NULL);
}

/*********************************************************************
 * @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_A_SIZE;

            PRINT("IAP_PROM: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);

            /* 当前是ImageA,直接编程 */
            status = FLASH_ROM_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_A_SIZE;

            EraseBlockNum = (uint32_t)(iap_rec_data.erase.block_num[0]);
            EraseBlockNum |= ((uint32_t)(iap_rec_data.erase.block_num[1]) << 8);
            EraseAdd = OpAdd;
            EraseBlockCnt = 0;

            /* 检验就放在擦除里清0 */
            VerifyStatus = 0;

            PRINT("IAP_ERASE start:%08x num:%d\r\n", (int)OpAdd, (int)EraseBlockNum);

            if(EraseAdd < IMAGE_B_START_ADD || (EraseAdd + (EraseBlockNum - 1) * FLASH_BLOCK_SIZE) > IMAGE_IAP_START_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_A_SIZE;
            PRINT("IAP_VERIFY: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen);

            /* 当前是ImageA,直接读取ImageB校验 */
            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;
        }
    }
}
/*********************************************************************
 * @fn      OTA_IAPReadDataComplete
 *
 * @brief   OTA 数据读取完成处理
 *
 * @param   index   - OTA 通道序号
 *
 * @return  none
 */
void OTA_IAPReadDataComplete(unsigned char index)
{
    PRINT("OTA Send Comp \r\n");
}
/*********************************************************************
 * @fn      OTA_IAPWriteData
 *
 * @brief   OTA 通道数据接收完成处理
 *
 * @param   index   - OTA 通道序号
 * @param   p_data  - 写入的数据
 * @param   w_len   - 写入的长度
 *
 * @return  none
 */
void OTA_IAPWriteData(unsigned char index, unsigned char *p_data, unsigned char w_len)
{
    unsigned char  rec_len;
    unsigned char *rec_data;

    rec_len = w_len;
    rec_data = p_data;
    tmos_memcpy((unsigned char *)&iap_rec_data, rec_data, rec_len);
    Rec_OTA_IAP_DataDeal();

添加OTA.h程序,存放FLASH的大小和起始位置

image

image

  添加OTA的服务,可以安卓OTA上位机连接上APP从机设备之后,可以通过OTA的服务属性来进行传输升级的固件数据

image

3、验证测试

升级前

image

image

升级成功后

image

 

posted on 2026-06-02 21:08  凡仕  阅读(146)  评论(0)    收藏  举报