一 环境搭建
开发环境使用官方提供的Simplicity Studio,在官网即可下载到。silicon 官网:https://www.silabs.com/
该平台的SDK以及文档全部都可以通过Simplicity Studio下载得到,因此下载安装Simplicity Studio是第一步。
1 安装SDK
-
Simplicity Studio安装完之后,你的界面就如下图所示。这个工具需要进行简单的注册,注册是免费的。点击下载按钮,下载安装sdk。
-
选择Install by Product Group
-
选择Bluetooth,这里只关心Bluetooth,因此只需要勾选Bluetooth即可。点击下一步。
这里会列出来,你有权限下载的内容。点击下一步。
-
勾选需要下载的内容。点击Finish,等待安装完成。我的环境已经安装过了Bluetooth sdk,因此这里没有这个选项。
2 环境设置
外观设置
Windows, Preferences, General, Appearance, Colors and Fonts设置自己喜欢的字体
设置SDK
Windows, Preferences, Simplicity Studio, SDKs
3 参考手册
在一切就绪之后,发现在主界面还是什么都没有。
这里需要先建一个product,选择一个板子和芯片。我这里以Thunderboard EFR32BG22(SLTB010A)为例。
添加完板子之后,就可以在主界面看到大量的文档和demo了。做为刚接触silicon平台的开发者来说从QSG139: Bluetooth® SDK v2.x Quick Start Guide这个文档入手是个不错的选择。
二 新建工程
- 点击 New Project,选择Bluetooth SDK
- 选择一个工程做为模板,官方建议选择SOC - Empty做为模板。
- 设置工程名字,选择工程存放目录。
- 使用GNU ARM 编译链。点击finish等待工程创建完成即可。
这个平台对开发者其实是很友好的,工程创建完之后,平台已经为我们做了大量的工作,用户只需要关心自己的业务逻辑部分即可。
三 目录结构
工程的文件结构如下图所示:
- app
主要定义一些基础的宏定义,比如MAX和MIN - hardware
关于硬件的一些设置和宏定义。 - platform
平台的主要功能。提供一些库、硬件驱动等。 - protocol
这里主要是指BLE协议。 - soc-empty_2.isc
蓝牙配置文件。silicon平台使用该文件来对蓝牙进行可视化的配置,降低了开发的难度。 - gatt.xml/gatt_db.c/gatt_db.h
该文件是根据soc-empty_2.isc自动生成的。 - efr32bg22c224f512im40.ld
整个工程的链接指导文件,设置Flash和RAM的起始地址及大小,设置堆栈大小等。
四 代码架构
1 添加自定义服务
工程创建完成之后,平台默认给我们添加了3个服务
- GeneGeneric Access
- Device Information
- Silicon Labs OTA
下面我们来添加一个串口透传服务。
-
选中 Custom BLE GATT,点击右边新建按钮,new server
这里我们可以修改该服务的Name以及UUID,并将Advertise Service勾选上表示在广播的时候将该服务广播出去。
-
给服务添加TX特征
选中服务uart,点击右边的新建按钮,选择 new characteristic
-
Name 设置该特征的名字,可自定义
-
UUID 设置该特征的UUID,可自定义
-
Value Type 该设置决定了可以向特征写入什么类型的数据,这里选择utf-8
-
Length 可以向TX写入的最长数据
-
设置特征的权限,这里的权限指的是用户可以对这个特征进行的操作。
我们这里给TX特性添加一个Notify的权限,表示当前设备会主动的向用户端发送数据。根据蓝牙核心卷的规定,具有Notify权限必须要添加一个描述(Descriptors): Client Characteristic Configuration, 该描述是方便用户是否决定接收设备主动发来的数据。
Client Characteristic Configuration可以直接从左侧的Descriptors栏里面直接拖拽到TX下面,给这个Descriptors添加Read和Write权限,参数选择默认即可。
- 给服务添加RX特性
添加RX特性的过程和TX一样,这里给RX的权限有 Write 和 Write without Response
一切就绪之后,点击Generate自动生成配置文件。
编译通过之后会在 \GNU ARM v7.2.1 - Default 目录下生成可执行文件。烧录到板子之后,使用EFR Connect连接,可以看到我们刚才设置的效果。
2 代码分析
main.c
static gecko_configuration_t config = {
.config_flags = 0, /* Check flag options from UG136 */
#if defined(FEATURE_LFXO) || defined(PLFRCO_PRESENT)
.sleep.flags = SLEEP_FLAGS_DEEP_SLEEP_ENABLE, /* Sleep is enabled */
#else
.sleep.flags = 0,
#endif
.bluetooth.max_connections = MAX_CONNECTIONS, /* Maximum number of simultaneous connections */
.bluetooth.max_advertisers = MAX_ADVERTISERS, /* Maximum number of advertisement sets */
.bluetooth.heap = bluetooth_stack_heap, /* Bluetooth stack memory for connection management */
.bluetooth.heap_size = sizeof(bluetooth_stack_heap), /* Bluetooth stack memory for connection management */
#if defined(FEATURE_LFXO)
.bluetooth.sleep_clock_accuracy = 100, /* Accuracy of the Low Frequency Crystal Oscillator in ppm. *
* Do not modify if you are using a module */
#elif defined(PLFRCO_PRESENT)
.bluetooth.sleep_clock_accuracy = 500, /* In case of internal RCO the sleep clock accuracy is 500 ppm */
#endif
.gattdb = &bg_gattdb_data, /* Pointer to GATT database */
.ota.flags = 0, /* Check flag options from UG136 */
.ota.device_name_len = 3, /* Length of the device name in OTA DFU mode */
.ota.device_name_ptr = "OTA", /* Device name in OTA DFU mode */
.ota.antenna_defined = APP_RF_CONFIG_ANTENNA,
.ota.antenna = APP_RF_ANTENNA,
.pa.config_enable = 1, /* Set this to be a valid PA config */
#if defined(FEATURE_PA_INPUT_FROM_VBAT)
.pa.input = GECKO_RADIO_PA_INPUT_VBAT, /* Configure PA input to VBAT */
#else
.pa.input = GECKO_RADIO_PA_INPUT_DCDC, /* Configure PA input to DCDC */
#endif // defined(FEATURE_PA_INPUT_FROM_VBAT)
.rf.flags = APP_RF_CONFIG_ANTENNA, /* Enable antenna configuration. */
.rf.antenna = APP_RF_ANTENNA, /* Select antenna path! */
};
config 是整个系统的主要配置文件,生成的代码只是部分配置,如果需要其它设置可以参考相关的文档说明。
比如我们在软件里面需要开启软件定时器,只需要添加max_timers 字段。
int main(void)
{
/* 初始化MCU,设置MCU时钟频率,设置用到的其它模块的时钟频率 */
initMcu();
/* 初始化跟板子相关的一些外设 */
initBoard();
/* Initialize application */
initApp();
initVcomEnable();
/* 可以在这里添加用户自定义的初始化代码 */
//......
/* 主要逻辑在这里*/
appMain(&config);
}
app.h
//RX和TX的值,需要根据gatt_db.c来进行修改
#define RX_ATTRIBUTE (0x000b)
#define TX_ATTRIBUTE (0x000d)
app.c
#define MAX_BLE_DATA_LEN 21
#define REPLY_LEN MAX_BLE_DATA_LEN
uint8_t reply[21];
char* test_replay = "abcd123";
/* 主循环 */
void appMain(gecko_configuration_t *pconfig)
{
#if DISABLE_SLEEP > 0
pconfig->sleep.flags = 0;
#endif
/* Initialize debug prints. Note: debug prints are off by default. See DEBUG_LEVEL in app.h */
initLog();
/* 根据 main.c 中的config 文件来初始化协议栈,当初始化完成之后会生成一个gecko_evt_system_boot_id事件在该事件产生之前不能对蓝牙做其他操作 */
gecko_init(pconfig);
while (1) {
/* Event pointer for handling events */
struct gecko_cmd_packet* evt;
/* if there are no events pending then the next call to gecko_wait_event() may cause
* device go to deep sleep. Make sure that debug prints are flushed before going to sleep */
if (!gecko_event_pending()) {
flushLog();
}
/* 等待一个事件,这个函数会阻塞调用。 详细说明参考文档 UG136. */
evt = gecko_wait_event();
/* Handle events */
switch (BGLIB_MSG_ID(evt->header)) {
/* This boot event is generated when the system boots up after reset.
* Do not call any stack commands before receiving the boot event.
* Here the system is set to start advertising immediately after boot procedure. */
case gecko_evt_system_boot_id:
bootMessage(&(evt->data.evt_system_boot));
printLog("boot event - starting advertising\r\n");
/* Set advertising parameters. 100ms advertisement interval.
* The first parameter is advertising set handle
* The next two parameters are minimum and maximum advertising interval, both in
* units of (milliseconds * 1.6).
* The last two parameters are duration and maxevents left as default. */
gecko_cmd_le_gap_set_advertise_timing(0, 160, 160, 0, 0);
/* 开始广播 */
gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);
break;
case gecko_evt_le_connection_opened_id:
printLog("connection opened\r\n");
break;
case gecko_evt_le_connection_closed_id:
printLog("connection closed, reason: 0x%2.2x\r\n", evt->data.evt_le_connection_closed.reason);
/* 如果OTA标志被设置,则重启系统,并标记OTA类型*/
if (boot_to_dfu) {
/* 传入的值为2表示通过蓝牙进行空中升级 */
gecko_cmd_system_reset(2);
} else {
/* Restart advertising after client has disconnected */
gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);
}
break;
/* Events related to OTA upgrading
----------------------------------------------------------------------------- */
/* Check if the user-type OTA Control Characteristic was written.
* If ota_control was written, boot the device into Device Firmware Upgrade (DFU) mode. */
case gecko_evt_gatt_server_user_write_request_id:
//收到OTA命令
if (evt->data.evt_gatt_server_user_write_request.characteristic == gattdb_ota_control) {
/* 设置OTA标志*/
boot_to_dfu = 1;
/* 回复用户 */
gecko_cmd_gatt_server_send_user_write_response(
evt->data.evt_gatt_server_user_write_request.connection,
gattdb_ota_control,
bg_err_success);
/* 断开连接准备进入OTA模式 */
gecko_cmd_le_connection_close(evt->data.evt_gatt_server_user_write_request.connection);
}
break;
/* 根据应用的需要添加其他的事件,该平台所支持的事件都可以在 native_gecko.h 中找到 */
case gecko_evt_gatt_server_attribute_value_id:
{
struct gecko_msg_gatt_server_attribute_value_evt_t* att = &evt->data.evt_gatt_server_attribute_value;
//当Rx收到数据之后,这里默认回复一个abcd123
if(att->attribute == RX_ATTRIBUTE){
uint8 connection = att->connection;
gecko_cmd_gatt_server_send_characteristic_notification(connection, TX_ATTRIBUTE, strlen(test_replay), test_replay);
}
}
break;
default:
break;
}
}
}
3 空中升级
空中升级对于开发者,可以说是非常的友好。
详细的信息可以参看文档:ug266-gecko-bootloader-user-guide.pdf 和 an1086-gecko-bootloader-bluetooth.pdf
这些文档在silicon的官网都可以搜到。
3.1 制作升级文件
当应用程序编译完成之后,只要执行根目录下的create_bl_files.bat批处理文件,平台就会自动帮我们生成可用来升级的.gbl文件。
第一次使用该文件的时候需要添加两个用户变量:
在执行该批处理的时候可能会遇到这样的问题:
应该是当前系统要求命令路径不能带空格,因此修改这个批处理文件中的命令,PATH_SCMD 和 OBJCOPY都加上双引号即可。
比如将
%OBJCOPY% -O srec -j .text_apploader* "%PATH_OUT%" "%PATH_GBL%\%OTA_APPLO_NAME%.srec"
修改成
"%OBJCOPY%" -O srec -j .text_apploader* "%PATH_OUT%" "%PATH_GBL%\%OTA_APPLO_NAME%.srec"
执行成功之后会在根目录下生成一个 output_gbl 文件夹里面有升级需要的gbl文件。
我们这里选择application.gbl,不带crc,只更新应用部分。
3.2 flash分布
在0地址处存放的是Gecko BootLoader,这部分代码需要另外一个工程进行生成。
silicon把appLoader以可执行文件的形式提供给用户,当我们的app编译完成之后appLoader会被自动的添加到我们的bin文件中。
因此我们生成的bin文件包含appLoader、协议栈、应用。
3.3 空中升级过程
- app收到OTA指令之后,断开当前连接并调用gecko_cmd_system_reset(2); 设置OTA标志并重启系统。
- Gecko Boot检测到OTA标志,进入appLoader。进入appLoader之后开始通过蓝牙接收用户发送过来的新固件。
- appLoader将收到的新固件直接覆盖原来的app区域。
- 升级完之后,appLoader清除OTA标志并重启系统。
- Gecko Boot没有检测到OTA标志,则进入app开始执行。
可以使用EFR Connect进行升级。