【WCH蓝牙系列芯片】-基于CH32V208开发板—OTA带库升级
-------------------------------------------------------------------------------------------------------------------------------------
在CH32V208的OTA升级中有两种方式,方式一是带库升级,方式二是不带库升级。
在EVT资源包中BLE目录下有一个PDF针对于WCH CH32V208 蓝牙空中升级(BLE OTA)做出说明。
方式一的带库升级,即新固件下载属于应用程序功能的一部分,在新固件下载过程中,应用可以正常使用,下载完成后,系统完成执行新固件的操作,至此整个升级过程结束(比如 EVT/BLE/BackupUpgrade_XXX 例子)
方式二的不带库升级,在升级的时候整个升级过程中 APP 正常功能都无法使用。系统需要先从应用模式跳入到 BootLoader/IAP 模式,由 BootLoader/IAP 进行新固件下载工作,直接覆盖老固件,至升级结束(比如EVT\EXAM\BLE\OnlyUpdateApp_XXX 例子)。
这里针对带库升级做一个示例,在蓝牙串口透传程序(BLE-UART)中添加OTA功能的程序,进行带库升级操作。
带库升级中(备份升级方式),适用于 flash 资源充裕的应用,优点是 APP+LIB 都可以升级,缺点是 flash 需要对半分使用。
将整个448K的CodeFlash分为四个部分,Jump IAP、APP、OTA、IAP.
IAP为16K, 跳转 APP 执行用户程序,此工程启动文件中的地址也需对应修改为IAP的起始地址,
APP为216K,这个app部分需要带有蓝牙从机功能,且需要codeflash为448k的芯片才支持用此方式,
OTA为216K,用于存放待升级固件,
设备上电优先运行 IAP 程序,之后跳转 APP 执行用户程序。APP 文件为独立完整的功能文件,运行时可以通过无线方式接收完整的 OTA 备份升级文件,然后软复位进入 IAP 程序,IAP 会将备份区的 OTA 升级文件拷贝到 APP应用程序区,最后跳转回升级后的 APP 应用程序运行新的固件。升级固件带 LIB 编译(LIB 约 90K)
第一步:
修改LD文件,直接将BackupUpgrate_OTA例程中的LD文件中的Link.ld中的程序直接替换BLE_UART的Link.ld即可。
第二步:
修改启动文件,将BackupUpgrate_OTA例程中的startup_ch32v20x_D8W.S文件替换BLE_UART的Startup文件下即可。
第三步:
对比BackupUpgrate_OTA例程,将图片中三个文件复制添加到BLE_UART相对应的位置。
第四步:
修改BLE_BUFF_MAX_LEN为251,这样可以提高OTA速度
第五步:
对比BackupUpgrate_OTA例程的peripheral.c文件,peripheral.h文件和peripheral_mian.c文件,
把缺少的OTA部分的代码复制到BLE_UART相对应的位置。
在peripheral.c文件中添加如下:
#include "OTA.h" #include "OTAprofile.h" // OTA IAP VARIABLES /* OTA communication frame */ OTA_IAP_CMD_t iap_rec_data; /* OTA analysis results */ uint32_t OpParaDataLen = 0; uint32_t OpAdd = 0; uint16_t block_buf_len=0; uint32_t prom_addr=0; /* Flash data temporary storage */ __attribute__((aligned(8))) uint8_t block_buf[512]; /* IMAGE jump function address definition */ typedef int (*pImageTaskFn)(void); pImageTaskFn user_image_tasks; /* Flash erase */ uint32_t EraseAdd = 0; //Removal address uint32_t EraseBlockNum = 0; //Number of blocks that need to be erased uint32_t EraseBlockCnt = 0; //Scratching block count /* FLASH verification status */ uint8_t VerifyStatus = 0; //OTA升级 void OTA_IAPReadDataComplete(unsigned char index); void OTA_IAPWriteData(unsigned char index, unsigned char *p_data, unsigned char w_len); void Rec_OTA_IAP_DataDeal(void); void OTA_IAP_SendCMDDealSta(uint8_t deal_status); // Simple GATT Profile Callbacks static OTAProfileCBs_t Peripheral_OTA_IAPProfileCBs = { OTA_IAPReadDataComplete, // Charactersitic value change callback OTA_IAPWriteData };
// Initialize GATT attributes OTAProfile_AddService(GATT_ALL_SERVICES);
// Register callback with OTAGATTprofile OTAProfile_RegisterAppCBs(&Peripheral_OTA_IAPProfileCBs); //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); FLASH_Unlock(); status = FLASH_ErasePage(EraseAdd + EraseBlockCnt * FLASH_BLOCK_SIZE); FLASH_Lock(); /* Erase failed */ if(status != FLASH_COMPLETE) { OTA_IAP_SendCMDDealSta(status); return (events ^ OTA_FLASH_ERASE_EVT); } EraseBlockCnt++; /* End of erase */ if(EraseBlockCnt >= EraseBlockNum) { PRINT("ERASE Complete\r\n"); OTA_IAP_SendCMDDealSta(SUCCESS); return (events ^ OTA_FLASH_ERASE_EVT); } return (events); } //OTA升级 /********************************************************************* * @fn OTA_IAP_SendData * * @brief OTA IAP sends data, which is limited to 20 bytes when used * * @param p_send_data - Pointer to send data * @param send_len - Length of data sent * * @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 Status return of OTA IAP execution * * @param deal_status - Return state * * @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 abnormal command code processing * * @return none */ void OTA_IAP_CMDErrDeal(void) { OTA_IAP_SendCMDDealSta(0xfe); } /********************************************************************* * @fn SwitchImageFlag * * @brief Switch ImageFlag in dataflash * * @param new_flag - Switching ImageFlag * * @return none */ void SwitchImageFlag(uint8_t new_flag) { uint16_t i; uint32_t ver_flag; /* Read the first block */ FLASH_read(OTA_DATAFLASH_ADD, &block_buf[0], 4); FLASH_Unlock_Fast(); /* Erase the first block */ FLASH_ErasePage_Fast( OTA_DATAFLASH_ADD ); /* Update Image information */ block_buf[0] = new_flag; block_buf[1] = 0x5A; block_buf[2] = 0x5A; block_buf[3] = 0x5A; /* Program DataFlash */ FLASH_ProgramPage_Fast( OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0]); FLASH_Lock_Fast(); } /********************************************************************* * @fn DisableAllIRQ * * @brief Disable all interrupts * * @return none */ void DisableAllIRQ(void) { __disable_irq(); } /********************************************************************* * @fn Rec_OTA_IAP_DataDeal * * @brief Received OTA packet processing * * @return none */ void Rec_OTA_IAP_DataDeal(void) { switch(iap_rec_data.other.buf[0]) { /* Programming */ case CMD_IAP_PROM: { uint32_t i; FLASH_Status status = FLASH_COMPLETE; 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+0x08000000; PRINT("IAP_PROM: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen); /* Current is ImageA, programming directly */ tmos_memcpy(&block_buf[block_buf_len], iap_rec_data.program.buf, OpParaDataLen); block_buf_len+=OpParaDataLen; if( block_buf_len>=FLASH_PAGE_SIZE ) { FLASH_Unlock_Fast(); FLASH_ProgramPage_Fast(prom_addr, (uint32_t*)block_buf); FLASH_Lock_Fast(); tmos_memcpy(block_buf, &block_buf[FLASH_PAGE_SIZE], block_buf_len-FLASH_PAGE_SIZE); block_buf_len-=FLASH_PAGE_SIZE; prom_addr+=FLASH_PAGE_SIZE; } OTA_IAP_SendCMDDealSta(SUCCESS); break; } /* Erase -- Bluetooth erase is controlled by the host */ 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+0x08000000; 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; /* The inspection is placed in the era of clearing 0 */ VerifyStatus = 0; prom_addr = IMAGE_B_START_ADD; PRINT("IAP_ERASE start:%08x num:%d\r\n", (int)EraseAdd, (int)EraseBlockNum); if((EraseAdd < IMAGE_B_START_ADD) || ((EraseAdd + (EraseBlockNum - 1) * FLASH_BLOCK_SIZE) > (IMAGE_B_START_ADD+IMAGE_B_SIZE))) { OTA_IAP_SendCMDDealSta(0xFF); } else { /* Start erasing */ tmos_set_event(Peripheral_TaskID, OTA_FLASH_ERASE_EVT); } break; } /* Verify */ case CMD_IAP_VERIFY: { uint32_t i; uint8_t status = 0; uint8_t verifyData[iap_rec_data.verify.len]; if( block_buf_len ) { FLASH_Unlock_Fast(); FLASH_ProgramPage_Fast(prom_addr, (uint32_t*)block_buf); FLASH_Lock_Fast(); block_buf_len=0; prom_addr=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+0x08000000; PRINT("IAP_VERIFY: %08x len:%d \r\n", (int)OpAdd, (int)OpParaDataLen); FLASH_read(OpAdd, verifyData, OpParaDataLen); /* Currently ImageA, read ImageB for verification directly*/ status = tmos_memcmp(verifyData, iap_rec_data.verify.buf, OpParaDataLen); if(status == FALSE) { PRINT("IAP_VERIFY err \r\n"); VerifyStatus = 0xFF; } OTA_IAP_SendCMDDealSta(VerifyStatus); break; } /* End of rogramming */ case CMD_IAP_END: { PRINT("IAP_END \r\n"); /* The current one is ImageA */ /* Close all the current use interrupt, or it is convenient to directly close */ DisableAllIRQ(); /* Modify DataFlash and switch to ImageIAP */ SwitchImageFlag(IMAGE_IAP_FLAG); /* Wait for printing to complete, reset*/ Delay_Ms(10); NVIC_SystemReset(); 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; /* Add more if necessary */ /* send message */ OTA_IAP_SendData(send_buf, 20); break; } default: { OTA_IAP_CMDErrDeal(); break; } } } /********************************************************************* * @fn OTA_IAPReadDataComplete * * @brief OTA data reading complete processing * * @param index - OTA channel serial number * * @return none */ void OTA_IAPReadDataComplete(unsigned char index) { PRINT("OTA Send Comp \r\n"); } /********************************************************************* * @fn OTA_IAPWriteData * * @brief OTA channel data receiving complete processing * * @param index - OTA channel serial number * @param p_data - Written data * @param w_len - Length * * @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(); } /********************************************************************* * @fn FLASH_read * * @brief Read flash * * @return none */ void FLASH_read(uint32_t addr, uint8_t *pData, uint32_t len) { uint32_t i; for(i=0;i<len;i++) { *pData++ = *(uint8_t*)addr++; } }
在peripheral.h文件中添加:
#define OTA_FLASH_ERASE_EVT 0x0100 /* * Read flash */ void FLASH_read(uint32_t addr, uint8_t *pData, uint32_t len);
在peripheral_main.c文件中添加如下:
#include "OTA.h" #include "OTAprofile.h" /* Record the current image */ unsigned char CurrImageFlag = 0xff; //记录当前的Image /* Used for app judgment file effectiveness */ const uint32_t Address = 0xFFFFFFFF; __attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address; /* 注意:关于程序升级后flash的操作必须先执行,不开启任何中断,防止操作中断和失败 */ /* Note: The operation of Flash after the program is upgraded must be performed first without * turning on any interruption to prevent operation interruption and failure */ /********************************************************************* * @fn ReadImageFlag * * @brief Read the iMage logo of the current program. If the DataFlash is empty, * it will be ImageA by default 读取当前的程序的Image标志,DataFlash如果为空,就默认是ImageA * * @return none */ void ReadImageFlag(void) { OTADataFlashInfo_t p_image_flash; FLASH_read(OTA_DATAFLASH_ADD, (uint8_t*)&p_image_flash, 4); CurrImageFlag = p_image_flash.ImageFlag; /* The program is executed for the first time, or it has not been updated, * and the DataFlash will be erased after the subsequent update. */ if((CurrImageFlag != IMAGE_A_FLAG) && (CurrImageFlag != IMAGE_B_FLAG)) { CurrImageFlag = IMAGE_A_FLAG; } }
第六步:
修改完成后,通过WCHISP烧录工具,将IAP和BLE_UART的两个HEX文件同时烧录至V208板子中。
第七步:
通过手机打开OTA升级工具,找到蓝牙设备32V208BLEUART
点击CETINFO,点击IMAGEA,选择要升级固件
点击START,进行OTA升级等待完成。
升级完成后,再一次搜索蓝牙设备,此时的蓝牙设备为CH32V208-OTA-Back
OTAProfile_AddService(GATT_ALL_SERVICES);