EVT例程中提供了两种OTA方式,在EVT中BLE目录下有一个PDF说明:WCH蓝牙空中升级(BLE OTA)
方式一是带库升级,整个codeflash分成四个区域,Jump IAP,APP1,APP2,IAP。
Jump IAP为4K,用来跳入到IAP中,此工程main中并无函数,会直接跳转到IAP,IAP的起始地址修改后,此工程启动文件中的地址也需对应修改为IAP的起始地址,
APP1为216K,此app部分需要带有蓝牙从机功能,且需要codeflash为448k的芯片才支持用此方式,
APP2为216K,用于存放待升级固件,
IAP为12K,它的作用就是将APP2区域的固件写到APP1区域。
那么此篇博客就是教大家如果在蓝牙从机例程(Peripheral)上添加OTA功能。
step1:修改LD文件,直接BackupUpgrate_OTA例程中的LD文件替换过来来即可,
step2:修改启动文件,直接BackupUpgrate_OTA例程中的startup.s文件替换过来来即可,
step3:添加缺少的文件
从上图左来看,Peripheral例程中比BackupUpgrate_OTA例程少了三个有关OTA的文件,在对应的位置添加这三个文件即可,如上图右所示。
step4:修改BLE_BUFF_MAX_LEN为251,这样可以提高OTA速度,
step5:添加缺少的ota代码
对比Peripheral例程和BackupUpgrate_OTA例程的peripheral.c文件,peripheral.h文件和peripheral_mian.c文件,把缺少的OTA部分的代码copy一下。
peripheral.c文件中添加代码罗列:
#include "OTA.h" #include "OTAprofile.h"
// OTA IAP VARIABLES /* OTA通讯的帧 */ OTA_IAP_CMD_t iap_rec_data; /* OTA解析结果 */ uint32_t OpParaDataLen = 0; uint32_t OpAdd = 0; /* flash的数据临时存储 */ __attribute__((aligned(8))) uint8_t block_buf[16]; /* Image跳转函数地址定义 */ typedef int (*pImageTaskFn)(void); pImageTaskFn user_image_tasks; /* Flash 擦除过程 */ uint32_t EraseAdd = 0; //擦除地址 uint32_t EraseBlockNum = 0; //需要擦除的块数 uint32_t EraseBlockCnt = 0; //擦除的块计数 /* FLASH 校验过程 */ uint8_t VerifyStatus = 0;
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 };
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); status = FLASH_ROM_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 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(); }
peripheral_main.c文件中添加代码罗列:
#include "OTA.h" #include "OTAprofile.h"
/* 记录当前的Image */
unsigned char CurrImageFlag = 0xff;
/* 用于APP判断文件有效性 */ const uint32_t Address = 0xFFFFFFFF; __attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address;
/* 注意:关于程序升级后flash的操作必须先执行,不开启任何中断,防止操作中断和失败 */ /********************************************************************* * @fn ReadImageFlag * * @brief 读取当前的程序的Image标志,DataFlash如果为空,就默认是ImageA * * @return none */ void ReadImageFlag(void) { OTADataFlashInfo_t p_image_flash; EEPROM_READ(OTA_DATAFLASH_ADD, &p_image_flash, 4); CurrImageFlag = p_image_flash.ImageFlag; /* 程序第一次执行,或者没有更新过,以后更新后在擦除DataFlash */ if((CurrImageFlag != IMAGE_A_FLAG) && (CurrImageFlag != IMAGE_B_FLAG)) { CurrImageFlag = IMAGE_A_FLAG; } }
ReadImageFlag();
peripheral.h文件中添加代码罗列:
#define OTA_FLASH_ERASE_EVT 0x0020