FOTA 固件升级在基本所有的蓝牙芯片基本都会有支持,不像LoRaWAN Zigbee等低速率的无线网络FOTA不是都有实现,ZIGBEE LORA FOTA一个固件传输就需要半个小时多,固件大一些的可能还不止。蓝牙相比ZIGBEE LORA通信速率快多了,因此FOTA基本是每个蓝牙芯片必备功能。
OTA的实现方式有多种,特别是flash规划。常见的有双区备份升级,单区覆盖升级。双区备份和单区覆盖各有优缺点。这里描述的是单区覆盖升级方式。
单区覆盖升级方式CH58X的Flash规划如下:
蓝牙协议栈分布占用固定的flash区域,Bootloader和UserApp两个固件共享同一份蓝牙协议栈库,也即是在Bootloader运行时也可以有BLE功能,这样的话只升级Userapp的时候只需要很小的空间,如果userapp应用复杂,smalluserapp不够用的时候可以把userapp放到largeuserapp位置。按这样分配的话不同型号的产品固件只要侧重userapp的编写维护,不需要每个人都去接触处理FOTA部分的详细逻辑。要进行FOTA升级的时候通过对userapp约定一个GATT CHAR写入特定值让其跳转到bootloader层运行。实际的ble ota文件传输都在boolloader层来实现。
userapp调整到bootloader部分的相关代码:
//设置好相关标志,调用复位让程序能跳进flash首地址的bootloader
R8_GLOB_RESET_KEEP |= 0x01;
SYS_ResetExecute();
跳转到bootloader后去userappheader flash区域判断校验是否有可用的升级固件,如果没有就停留在bootloader里执行,如果有就跳转到新app运行
int main(void) {
SetSysClock(CLK_SOURCE_PLL_60MHz);
#ifdef DEBUG
GPIOA_SetBits(bTXD1);
GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
#endif
//判断去执行新app还是继续停留在bootloader
if(false == ota_enter_check()) {
PRINT("OTA_ENTER NOT VAILED jump to app\r\n");
jump_to_app_use_header();
}
CH58X_BLEInit();
HAL_Init();
GAPRole_PeripheralInit();
Peripheral_Init();
Main_Circulation();
}
如果userapp合法就通过软中断跳转到userapp运行
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void SW_Handler(void) {
app_t jump2app;
if (switch_to_machine_mode == 0x01) {
//asm("li t0, 0x1888");
PFIC->IRER[0] = 0xffffffff;
PFIC->IRER[1] = 0xffffffff;
jump2app = (app_t) (__IO uint32_t*) (jump_app_addr);
jump2app();
}
}
fota固件有特别把userapp长度,userapp flash地址,userapp固件crc做成meta写进升级固件的文件头。这样固件传输的时候bootloader就可以知道userapp的固件存储地址,大小,crc等来做启动校验的相关工作。
typedef struct{
uint32_t header_vaild_flag;
uint16_t version_hw;
uint16_t version_sw;
uint32_t app_start_addr;
uint32_t app_size;
uint32_t app_crc32;
uint32_t header_crc32;
} app_header_t ;
要能让siliconlabs 的efr connect app可以直接对CH58X进行固件升级,需要在设备上实现siliconlabs的FOTA service和char(该链接https://github.com/iot-lorawan/esp32-ota-ble有介绍)
GATT表相关定义
CONST uint8_t siliconlabs_ota_service_uuid[ATT_UUID_SIZE] =
{0xf0, 0x19, 0x21, 0xb4, 0x47, 0x8f, 0xa4, 0xbf, 0xa1, 0x4f, 0x63, 0xfd, 0xee, 0xd6, 0x14, 0x1d};
CONST uint8_t siliconlabs_ota_data_uuid[ATT_UUID_SIZE] =
{0x53, 0xa1, 0x81, 0x1f, 0x58, 0x2c, 0xd0, 0xa5, 0x45, 0x40, 0xfc, 0x34, 0xf3, 0x27, 0x42, 0x98};
CONST uint8_t siliconlabs_ota_cmd_uuid[ATT_UUID_SIZE] =
{0x63, 0x60, 0x32, 0xe0, 0x37, 0x5e, 0xa4, 0x88, 0x53, 0x4e, 0x6d, 0xfb, 0x64, 0x35, 0xbf, 0xf7};
static gattAttribute_t siliconlabs_ota_profile_att_table[] =
{
// siliconlabs ota service
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
GATT_PERMIT_READ, /* permissions */
0, /* handle */
(uint8_t *)&siliconlabs_ota_service /* pValue */
},
// Characteristic for cmd channel Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&siliconlabs_ota_cmd_char_props
},
// Characteristic Value cmd channel
{
{ ATT_UUID_SIZE, siliconlabs_ota_cmd_uuid },
GATT_PERMIT_READ|GATT_PERMIT_WRITE,
0,
(uint8_t *)NULL
},
// Characteristic for data channel Description
{
{ ATT_BT_UUID_SIZE, clientCharCfgUUID },
GATT_PERMIT_READ|GATT_PERMIT_WRITE,
0,
(uint8_t*)siliconlabs_ota_cmd_cccd
},
// Characteristic data channel Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&siliconlabs_ota_data_char_props
},
// Characteristic Value data channel
{
{ ATT_UUID_SIZE, siliconlabs_ota_data_uuid },
GATT_PERMIT_WRITE,
0,
NULL
},
};
userapp编译的时候需要依据是small还是large app去修改对应的ld链接文件
largeapp ld:
MEMORY
{
FLASH (rx) : ORIGIN = 256K, LENGTH = 192K
RAM (xrw) : ORIGIN = 0x20002000, LENGTH = 24K
}
smallapp ld:
MEMORY
{
FLASH (rx) : ORIGIN = 32K, LENGTH = 28K
RAM (xrw) : ORIGIN = 0x20002000, LENGTH = 24K
}
如果是使用largeapp的时候可以不开CH58xBLE_ROM宏,来让新largeusrapp不用blelibgregion的蓝牙库,而直接使用链接进去largeusrapp的当前blelib。