通过Simplicity Studio新建的蓝牙工程默认是带有appLoader的,appLoader的一个缺点就是只能实现单区跟新,而且原厂没有开放appLoader的代码,有的时候可能没办满足用户的需求。比如在更新的时候需要led处于快闪的模式,appLoader就没办法做到。
appLoader的更新流程参考: https://blog.csdn.net/chengbaojin/article/details/113369125
下面说一下如何实现在用户的程序中实现OTA。
本文使用的环境
- sdk: gecko_sdk_3.1
- IDE: Simplicity Studio 5.0
一 flash存储情况
做空中升级之前是需要先烧录一个BootLoader。做双区更新一般情况下flash需要512KB及以上的大小。本文使用的是这个工程bootloader-storage-internal-single-512k
BootLoader的工程需要设置Slot 0也就是app bank2的起始地址,app bank2也就是新固件的存放地址。这里的起始地址是278528(0x4 4000)。如下图所示:
根据工程的autogen\linkerfile.ld文件可知,用户的应用程序存放的起始地址是0x6000,也就是说留给BootLoader的空间为24KB。
二 在工程中新建一个OTA服务
新建的工程默认有一个OTA服务,这个服务只有一个Control characteristic,而且在gatt_configuration.btconf文件中不可修改。这个服务不是我们所需要的,需要删除并自己新建一个OTA服务。
可以在下图的位置删除这个服务:
然后自己手动添加服务,如下图:
这里的Data characteristic需要选择user如下图:
三 升级流程
- 通过Control characteristic进入升级模式,通过data characteristic接收新固件
- 设置标志,并重启
bootloader_setImageToBootload(0);
bootloader_rebootAndInstall();
- 初始化BootLoader
当接收到sl_bt_evt_system_boot_id事件之后调用bootloader_init进行初始化
/* bootloader init must be called before calling other bootloader_xxx API calls */
bootloader_init();
/* read slot information from bootloader */
if(get_slot_info() == BOOTLOADER_OK)
{
/* the download area is erased here (if needed), prior to any connections are opened */
erase_slot_if_needed();
}
- 向Control characteristic写0,进入升级模式
//实测发现向Control characteristic写0生成的是这个事件
case sl_bt_evt_gatt_server_attribute_value_id:{
uint32_t connection = evt->data.evt_gatt_server_attribute_value.connection;
uint32_t characteristic = evt->data.evt_gatt_server_attribute_value.attribute;
LOGD("characteristic == %d\r\n",characteristic);
if(characteristic == gattdb_ota_control)
{
switch(evt->data.evt_gatt_server_attribute_value.value.data[0])
{
case 0://Erase and use slot 0
// NOTE: download are is NOT erased here, because the long blocking delay would result in supervision timeout
//bootloader_eraseStorageSlot(0);
LOGD("control 0\r\n");
ota_image_position=0;
ota_in_progress=1;
sl_bt_gatt_server_send_user_write_response(connection,characteristic,0);
break;
case 3://END OTA process
//wait for connection close and then reboot
ota_in_progress=0;
ota_image_finished=1;
LOGD("upload finished. received file size %u bytes\r\n", ota_image_position);
sl_bt_gatt_server_send_user_write_response(connection,characteristic,0);
break;
default:
break;
}
}
}
break;
- 向Data characteristic写新的固件
bootloader_writeStorage的起始地址就是BootLoader中设置的slot 0的地址。bootloader_writeStorage其实是在BootLoader中实现的,这里只是一个地址的引用。
case sl_bt_evt_gatt_server_user_write_request_id:
{
uint32_t connection = evt->data.evt_gatt_server_user_write_request.connection;
uint32_t characteristic = evt->data.evt_gatt_server_user_write_request.characteristic;
LOGD("characteristic == %d\r\n",characteristic);
if(characteristic == gattdb_ota_control)
{
switch(evt->data.evt_gatt_server_user_write_request.value.data[0])
{
case 0://Erase and use slot 0
// NOTE: download are is NOT erased here, because the long blocking delay would result in supervision timeout
//bootloader_eraseStorageSlot(0);
LOGD("control 0\r\n");
ota_image_position=0;
ota_in_progress=1;
break;
case 3://END OTA process
//wait for connection close and then reboot
ota_in_progress=0;
ota_image_finished=1;
LOGD("upload finished. received file size %u bytes\r\n", ota_image_position);
break;
default:
break;
}
} else if(characteristic == gattdb_ota_data)
{
if(ota_in_progress)
{
LOGD("wirite data\r\n");
bootloader_writeStorage(0,//use slot 0
ota_image_position,
evt->data.evt_gatt_server_user_write_request.value.data,
evt->data.evt_gatt_server_user_write_request.value.len);
ota_image_position+=evt->data.evt_gatt_server_user_write_request.value.len;
}
}
//gecko_cmd_gatt_server_send_user_write_response(connection,characteristic,0);
sl_bt_gatt_server_send_user_write_response(connection,characteristic,0);
}
break;
- 升级完成之后断开连接
case sl_bt_evt_connection_closed_id:{
// Restart advertising after client has disconnected.
pFunction Jump_To_Application;
uint32_t JumpAddress;
if (ota_image_finished) {
LOGD("Installing new image\r\n");
bootloader_setImageToBootload(0);
//设置安装新固件的标志位,复位之后会进入BootLoader,BootLoader负责将bank2中的内容搬运到bank1中
bootloader_rebootAndInstall();
} else{
sc = sl_bt_advertiser_start(
advertising_set_handle,
advertiser_general_discoverable,
advertiser_connectable_scannable);
sl_app_assert(sc == SL_STATUS_OK,
"[E: 0x%04x] Failed to start advertising\n",
(int)sc);
}
}
break;
四 手机端验证
- 手动向Control characteristic写0
- 选择一个.gbl文件
.gbl文件的制作方法参看:https://blog.csdn.net/chengbaojin/article/details/113369125
3. 等待升级完成
参考文档: