欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > CH585的不升级蓝牙协议栈的备份OTA升级的几个文件解析

CH585的不升级蓝牙协议栈的备份OTA升级的几个文件解析

2025/4/23 5:22:05 来源:https://blog.csdn.net/NEWEVA__zzera22/article/details/146909745  浏览:    关键词:CH585的不升级蓝牙协议栈的备份OTA升级的几个文件解析

1、首先必包含的两个文件一个是jumpIAP 一个是IAP

OTA 即 Over-the-Air Technology,指的是空中下载技术,蓝牙OTA就是通过蓝牙控制升级程序的下载到备份区,然后通过触发IAP来进行程序的转移,最后跳转到新程序。这里是吧IAP和APP分离开了,一般我认为OTA是属于APP的一部分。然后蓝牙协议栈单独放一个地方。jumpIAP和IAP是可以合并的。因为jumpIAP的作用就是跳转到IAP程序。那我直接运行IAP程序不就行了,估计是有自己的考量吧。jumpIAP是放在COD-FLASH的头地址,IAP放在了尾部的地址。

IAP的全称是“In-Application Programming”,即应用编程。 它是一种可以在设备运行应用程序的过程中,对设备的闪存或其他非易失性存储器进行编程或更新的技术。通过IAP,设备可以在不依赖外部编程器的情况下,实现自身程序的更新、升级等功能,常用于实现设备的固件升级、远程更新等场景。在上述代码中,就体现了通过IAP技术来实现程序在不同存储区域之间的搬运和更新等相关功能。

整个OTA流程是什么,蓝牙控制单片机擦除,数据写入B区或外部FLASH,数据备份完成。触发复位,复位起来进入JumpIAP,IAP内检查是否有升级IAP标志,有的话从B区把APP搬移到A区实现空中升级。

2、解读JumpIAP工程做了什么

空的main函数,也就是具体操作在启动文件内

int main(void)
{
}

顺带看一下链接文件

ENTRY( _start )MEMORY
{FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}SECTIONS
{.init :{_sinit = .;. = ALIGN(4);KEEP(*(SORT_NONE(.init)))KEEP(*(.ImageFlag))KEEP(*(.ImageFlag.*)). = ALIGN(4);_einit = .;} >FLASH AT>FLASH}

定义了代码占用的FLASH空间为4K,RAM为128K,当然这是585的配置,584M好像只有96K。

这段链接脚本定义了一个名为 .init 的段,该段包含了程序初始化相关的代码和数据,以及 ImageFlag 相关的段内容。段的起始地址和结束地址分别由 _sinit 和 _einit 符号记录,并且段内地址进行了 4 字节对齐,最终该段将被加载和存储在 FLASH 区域。虽然我不懂什么意思,暂时也不用懂

最关键的启动文件

	.section	.init,"ax",@progbits.global	_start.align	1
_start:j	0x6D000

跳转到436K的地址处 跟最高地址差12K,这就要查OTA里的ota.h内存划分部分了

/* 整个用户code区分成四块,4K,216K,216K,12K,后三块下面分别叫做imageA(APP),imageB(OTA)和imageIAP */

也就是他专门留了12K的内存给IAP程序使用,IAP被放置到436K起始的位置上。

JumpIAP很简单易懂,就是一个跳转指令,根据你设计的IAP大小来跳转到不同的地址上启动IAP。4K的空间是给JumpIAP使用的,它就是0x00000000的地址。

3、解读IAP工程做了什么

链接文件,起始地址436K,CODE-FLASH最后的12K内存

MEMORY
{FLASH (rx) : ORIGIN = 0x0006D000, LENGTH = 12KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}

这里分配了12K内存,整好对应上了。别的链接文件内容就大致不看了,看也看不懂。

启动文件

让程序从 main 函数开始执行,

int main(void)
{
#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE)PWR_DCDCCfg(ENABLE);
#endifSetSysClock(CLK_SOURCE_PLL_60MHz);
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
#ifdef DEBUGGPIOA_SetBits(bTXD1);GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);UART1_DefInit();
#endifReadImageFlag();jump_APP();
}

串口初始化是用来使能串口打印调试信息的,主要看两点ReadImageFlah和jump_APP函数。其实这个问价内的描述已经很具体了。

即,判断标志以及搬运代码到APP区,这里的APP区固定式A区,B区是备份区。当然你要是有外部FLASH的话,可以从外部搬运进A区,B区失效。

/********************************** (C) COPYRIGHT ******************************** File Name          : main.c* Author             : WCH* Version            : V1.1* Date               : 2019/11/05* Description        : 判断标志以及搬运代码到APP代码区********************************************************************************** Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.* Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics.*******************************************************************************//******************************************************************************/

这个有一个写标志的函数,就是擦对应的地址,写进去,那个读操作,不知道是什么用处,难道是为了稳定对FLASH的擦写。

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

去读了EEPROM的OTA标志存储地址,从前面我们也可以看出,这个就是DATAFLASH的内容,并且这是用FLASH去模拟了EEPROM的操作。

#define OTA_DATAFLASH_ADD      0x00077000 - FLASH_ROM_MAX_SIZE
#define FLASH_ROM_MAX_SIZE  0x070000                  // Flash-ROM maximum program size, 448KB

带进去可以得到OTA_DATAFLASH_ADD地址实际是0x00007000,也就是28K,暂时不知道这个地址是如何确定的。

好像找到了一篇博客讲这个

【沁恒蓝牙mesh】CH58x flash分区与数据存储管理_蓝牙存储flash-CSDN博客

去找操作这个地址原来的函数定义,明确是操作Data-Flash,但是不知道为什么会跟Code-Flash的大小有关联。


/*** @brief   read Data-Flash data block** @param   StartAddr   - Address of the data to be read.* @param   Buffer      - Pointer to the buffer where data should be stored, Must in RAM and be aligned to 4 bytes.* @param   Length      - Size of data to be read, in bytes.** @return  0-SUCCESS  (!0)-FAILURE*/
#define EEPROM_READ(StartAddr,Buffer,Length)        FLASH_EEPROM_CMD( CMD_EEPROM_READ, StartAddr, Buffer, Length )

后面看了一下,虽然EEPROM和FLASH调的都是同一个函数,但是输入的命令是不同的,是分两种,估计就是DataFLASH做了一个偏移,那就解释的通了

#define CMD_FLASH_ROM_START_IO	0x00		// start FlashROM I/O, without parameter
#define CMD_FLASH_ROM_SW_RESET	0x04		// software reset FlashROM, without parameter
#define CMD_GET_ROM_INFO		0x06		// get information from FlashROM, parameter @Address,Buffer
#define CMD_GET_UNIQUE_ID		0x07		// get 64 bit unique ID, parameter @Buffer
#define CMD_FLASH_ROM_PWR_DOWN	0x0D		// power-down FlashROM, without parameter
#define CMD_FLASH_ROM_PWR_UP	0x0C		// power-up FlashROM, without parameter
#define CMD_FLASH_ROM_LOCK		0x08		// lock(protect)/unlock FlashROM data block, return 0 if success, parameter @StartAddr
// StartAddr: 0=unlock all, 1=lock boot code, 3=lock all code and data#define CMD_EEPROM_ERASE		0x09		// erase Data-Flash block, return 0 if success, parameter @StartAddr,Length
#define CMD_EEPROM_WRITE		0x0A		// write Data-Flash data block, return 0 if success, parameter @StartAddr,Buffer,Length
#define CMD_EEPROM_READ			0x0B		// read Data-Flash data block, parameter @StartAddr,Buffer,Length
#define CMD_FLASH_ROM_ERASE		0x01		// erase FlashROM block, return 0 if success, parameter @StartAddr,Length
#define CMD_FLASH_ROM_WRITE		0x02		// write FlashROM data block, minimal block is dword, return 0 if success, parameter @StartAddr,Buffer,Length
#define CMD_FLASH_ROM_VERIFY	0x03		// read FlashROM data block, minimal block is dword, return 0 if success, parameter @StartAddr,Buffer,Length

关键的函数读映像

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_IAP_FLAG)){CurrImageFlag = IMAGE_A_FLAG;}PRINT("Image Flag %02x\n", CurrImageFlag);
}

初始去读,如果是这片FLASH没操作过,默认就是0xFF,跟标志位不服,就默认是第一次操作。映像调整为APP的映像。

跳转IAP函数

#define jumpApp    ((void (*)(void))((int *)IMAGE_A_START_ADD))
void jump_APP(void)
{if(CurrImageFlag == IMAGE_IAP_FLAG){__attribute__((aligned(8))) uint8_t flash_Data[1024];uint8_t i;FLASH_ROM_ERASE(IMAGE_A_START_ADD, IMAGE_A_SIZE);for(i = 0; i < IMAGE_A_SIZE / 1024; i++){FLASH_ROM_READ(IMAGE_B_START_ADD + (i * 1024), flash_Data, 1024);FLASH_ROM_WRITE(IMAGE_A_START_ADD + (i * 1024), flash_Data, 1024);}SwitchImageFlag(IMAGE_A_FLAG);// 销毁备份代码FLASH_ROM_ERASE(IMAGE_B_START_ADD, IMAGE_A_SIZE);}jumpApp();
}

流程就类似Bootloader,读标志位,是否等于升级在线标志位,等于就开辟一个1K大小的RAM,从B区搬到A区,然后擦除B区内容。最后跳转到A区的APP部分执行应用程序。

4、BACK-OTA工程的解读

首先还是链接文件和启动文件

链接文件内的内容,起始地址0x1000,也就是从4K开始,跳过了JumpIAP部分,大小是OTA里设置的216K,根据自己的需求调整。RAM还是585的128K,584M为96K,如果使用了蓝牙协议栈的话还要给蓝牙留RAM使用。

MEMORY
{FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 216KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}

启动函数里没有什么特殊的要讲,毕竟我现在也没完全读懂。

然后就是主函数文件内做的操作

void ReadImageFlag(void)与IAP一样,就是读取映像标志。

int main(void)
{
#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE)PWR_DCDCCfg(ENABLE);
#endifHSECFG_Capacitance(HSECap_18p);SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz);
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
#ifdef DEBUGGPIOA_SetBits(GPIO_Pin_14);GPIOPinRemap(ENABLE, RB_PIN_UART0);GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA);UART0_DefInit();
#endifPRINT("%s\n", VER_LIB);ReadImageFlag();CH58x_BLEInit();HAL_Init();GAPRole_PeripheralInit();Peripheral_Init();Main_Circulation();
}

这里面的先读映像,然后 BLE 库初始化,初始化硬件,初始化蓝牙从机,初始化从机的参数,进入主循环。

由于使用蓝牙功能会自动调用一个非抢占式的OS,TMOS。

TMOS就是对蓝牙任务和其他任务进行分别调度。其他任务一般就创建一个,然后在任务里再搞一个不同事件的调度。可以弄31事件好像。这块还没完全了解。

void HAL_Init()
{halTaskID = TMOS_ProcessEventRegister(HAL_ProcessEvent);HAL_TimeInit();
#if(defined HAL_SLEEP) && (HAL_SLEEP == TRUE)HAL_SleepInit();
#endif
#if(defined HAL_LED) && (HAL_LED == TRUE)HAL_LedInit();
#endif
#if(defined HAL_KEY) && (HAL_KEY == TRUE)HAL_KeyInit();
#endif
#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, 800); // 添加校准任务,500ms启动,单次校准耗时小于10ms
#endif
//    tmos_start_task( halTaskID, HAL_TEST_EVENT, 1600 );    // 添加一个测试任务
}

初始化硬件,注册了一个任务,HAL_ProcessEvent,然后后面后自动回调这个任务函数,根据是否发生了对应的事件,来执行相应操作后清事件位。这里就涉及TMOS系统,不过多讲解。

/******************************************************************************** @fn      HAL_ProcessEvent** @brief   硬件层事务处理** @param   task_id - The TMOS assigned task ID.* @param   events  - events to process.  This is a bit map and can*                      contain more than one event.** @return  events.*/
tmosEvents HAL_ProcessEvent(tmosTaskID task_id, tmosEvents events)
{uint8_t *msgPtr;if(events & SYS_EVENT_MSG){ // 处理HAL层消息,调用tmos_msg_receive读取消息,处理完成后删除消息。msgPtr = tmos_msg_receive(task_id);if(msgPtr){/* De-allocate */tmos_msg_deallocate(msgPtr);}return events ^ SYS_EVENT_MSG;}if(events & LED_BLINK_EVENT){
#if(defined HAL_LED) && (HAL_LED == TRUE)HalLedUpdate();
#endif // HAL_LEDreturn events ^ LED_BLINK_EVENT;}if(events & HAL_KEY_EVENT){
#if(defined HAL_KEY) && (HAL_KEY == TRUE)HAL_KeyPoll(); /* Check for keys */tmos_start_task(halTaskID, HAL_KEY_EVENT, MS1_TO_SYSTEM_TIME(100));return events ^ HAL_KEY_EVENT;
#endif}if(events & HAL_REG_INIT_EVENT){uint8_t x32Kpw;
#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE) // 校准任务,单次校准耗时小于10ms
#ifndef RF_8KBLE_RegInit();                                                  // 校准RF,会关闭RF并改变RF相关寄存器,如果使用了RF收发函数需注意校准后再重新启用
#endif
#if(CLK_OSC32K)Lib_Calibration_LSI(); // 校准内部RC
#elif(HAL_SLEEP)x32Kpw = (R8_XT32K_TUNE & 0xfc) | 0x01;sys_safe_access_enable();R8_XT32K_TUNE = x32Kpw; // LSE驱动电流降低到额定电流sys_safe_access_disable();
#endiftmos_start_task(halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME(BLE_CALIBRATION_PERIOD));return events ^ HAL_REG_INIT_EVENT;
#endif}if(events & HAL_TEST_EVENT){PRINT("* \n");tmos_start_task(halTaskID, HAL_TEST_EVENT, MS1_TO_SYSTEM_TIME(1000));return events ^ HAL_TEST_EVENT;}return 0;
}

Peripheral_Init,外设应用任务的初始化函数。其实特指的是蓝牙相关的,最关键的是它所带的一些事件

/********************************************************************** @fn      Peripheral_ProcessEvent** @brief   Peripheral Application Task event processor.  This function*          is called to process all events for the task.  Events*          include timers, messages and any other user defined events.** @param   task_id  - The TMOS assigned task ID.* @param   events - events to process.  This is a bit map and can*                   contain more than one event.** @return  events not processed*/
uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{//  VOID task_id; // TMOS required parameter that isn't used in this functionif(events & SYS_EVENT_MSG){uint8_t *pMsg;if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL){Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);// Release the TMOS messagetmos_msg_deallocate(pMsg);}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}if(events & SBP_START_DEVICE_EVT){// Start the DeviceGAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);// Set timer for first periodic eventtmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);return (events ^ SBP_START_DEVICE_EVT);}if(events & SBP_PERIODIC_EVT){// Restart timerif(SBP_PERIODIC_EVT_PERIOD){tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);}// Perform periodic application taskperformPeriodicTask();return (events ^ SBP_PERIODIC_EVT);}//OTA_FLASH_ERASE_EVTif(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);}// Discard unknown eventsreturn 0;
}

暂时先看到这,后面有一大串,暂时没理清,理清了再继续写吧

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词