1 前提
通过Simplicity Studio新建的蓝牙工程默认是带有appLoader的,appLoader的一个缺点就是只能实现单区跟新,而且原厂没有开放appLoader的代码,有的时候可能没办满足用户的需求。比如在更新的时候需要led处于快闪的模式,appLoader就没办法做到。
1.1环境
(1)sdk: gecko_sdk_3.2
(2)IDE: Simplicity Studio 5.0
参考的链接:
(1)参考地址 写的很详细
(2)官方的git路径:参开地址
(3)boot
1.2 flash的分区情况
2 实现步骤
2.1 boot 工程的选择
做空中升级之前是需要先烧录一个BootLoader。做双区更新一般情况下flash需要512KB及以上的大小。本文使用的是这个工程Internal Storage Bootloader (single image on 512kB device)
BootLoader的工程需要设置Slot 0也就是app bank2的起始地址,app bank2也就是新固件的存放地址。这里的起始地址是278528(0x4 4000)。如下图所示:
(2)根据工程的autogen\linkerfile.ld文件可知,用户的应用程序存放的起始地址是0x6000,也就是说留给BootLoader的空间为24KB。
2.2 在工程中新建一个OTA服务
基于soc_empty -新建的工程默认有一个OTA服务,这个服务只有一个**Control characteristic,**而且在gatt_configuration.btconf文件中不可修改。这个服务不是我们所需要的,需要删除并自己新建一个OTA服务。
可以在下图的位置删除这个服务:
然后自己手动添加服务,如下图:
这里的Data characteristic需要选择user如下图:
到此图形配置完成
3 升级流程
(1)通过Control characteristic进入升级模式,通过data characteristic接收新固件
设置标志,并重启
(2)bootloader_setImageToBootload(0);
(3)bootloader_rebootAndInstall();
3.2 代码的添加
1:基于app.c文件添加
#include "btl_interface.h"
#include "btl_interface_storage.h"
/* Flag for indicating DFU Reset must be performed */
static BootloaderInformation_t bldInfo;
static BootloaderStorageSlot_t slotInfo;
/* OTA variables */
static uint32_t ota_image_position = 0;
static uint8_t ota_in_progress = 0;
static uint8_t ota_image_finished = 0;
static uint16_t ota_time_elapsed = 0;
2:添加函数
/***********************************************************************
*get_slot_info
* @return
*/
static int32_t get_slot_info(void)
{
int32_t err;
bootloader_getInfo(&bldInfo);
BLE_RTT("Gecko bootloader version: %u.%u\r\n", (bldInfo.version & 0xFF000000) >> 24,
(bldInfo.version & 0x00FF0000) >> 16);
err = bootloader_getStorageSlotInfo(0, &slotInfo);
if(err == BOOTLOADER_OK)
{
BLE_RTT("Slot 0 starts @ 0x%8.8x, size %u bytes\r\n", slotInfo.address, slotInfo.length);
}
else
{
BLE_RTT("Unable to get storage slot info, error %x\r\n", err);
}
return(err);
}
```c
```c
/***********************************************************************************
*erase_slot_if_needed
*/
static void erase_slot_if_needed(void)
{
uint32_t offset = 0, num_blocks = 0, i = 0;
uint8_t buffer[256];
bool dirty = false;
int32_t err = BOOTLOADER_OK;
/* check the download area content by reading it in 256-byte blocks */
num_blocks = slotInfo.length / 256;
while((dirty == 0) && (offset < 256*num_blocks) && (err == BOOTLOADER_OK))
{
err = bootloader_readStorage(0, offset, buffer, 256);
if(err == BOOTLOADER_OK)
{
i = 0;
while(i < 256)
{
if(buffer[i++] != 0xFF)
{
dirty = true;
break;
}
}
offset += 256;
}
BLE_RTT("erase_slot_if_needed.....");
}
if(err != BOOTLOADER_OK)
{
BLE_RTT("error reading flash! %x\r\n", err);
}
else if(dirty)
{
BLE_RTT("download area is not empty, erasing...\r\n");
bootloader_eraseStorageSlot(0);
BLE_RTT("done\r\n");
}
else
{
BLE_RTT("download area is empty\r\n");
}
return;
}
static void print_progress(void)
{
// estimate transfer speed in kbps
int kbps = ota_image_position*8/(1024*ota_time_elapsed);
BLE_RTT("pos: %u, time: %u, kbps: %u\r\n", ota_image_position, ota_time_elapsed, kbps);
}
(3)初始化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();
}
else
{
BLE_RTT("Check that you have installed correct type of Gecko bootloader!\r\n");
}
4.向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;
5.向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;
6. 升级完成之后断开连接,所以需要添加固件的复制
// This event indicates that a connection was closed.
case sl_bt_evt_connection_closed_id:
BLE_RTT("Connection closed, reason: 0x%2.2x\r\n", evt->data.evt_connection_closed.reason);
if(ota_image_finished)
{
BLE_RTT("Installing new image.......\r\n");
bootloader_setImageToBootload(0);
bootloader_rebootAndInstall();
}
else
{
ble_adv_start(true);
BLE_RTT("Started advertising\n");
}
break;
四 手机端验证
1.手动向Control characteristic写0
2 .选择一个.gbl文件
.gbl文件的制作方法参看
(1) 制作升级文件
当应用程序编译完成之后,只要执行根目录下的create_bl_files.bat批处理文件,平台就会自动帮我们生成可用来升级的.gbl文件。
第一次使用该文件的时候需要添加两个用户变量:
可以百度怎么在电脑上添加用户变量
然后点击这里就可以生成文件
去对应的路径找文件
(2):app连接成功后
手动向Control characteristic写0
(3)在选择需要升级的文件
4. 等待升级完成