BLE 開發¶
FreeRTOS 應用的 BLE 由兩部分組成:Zephyr v4.4.0 的 BLE Host(原始碼)與 IPRO BLE 控制器(預編譯函式庫)。應用以 Zephyr 的 bt_* 回呼 API 撰寫,.config 的三個設定決定從 16 個控制器封存檔中連結哪一個。本頁範例取自 apps/bluetooth/ble/ipro_ble_remote,以 defconfig_reef 建置給 IPRO7AI Reef EVK。
重點
- Host:Zephyr v4.4.0,含 GAP、GATT、L2CAP、SMP、ISO 與 LE Audio;Host 與控制器之間是晶片內的 HCI,沒有 UART。
- 控制器:16 個 IPRO7 封存檔,
link1_periph/link1/link2/link4×bt52/bt54_iso2× 除錯版/release 版,由CONFIG_IPRO_BLE_CTLR_LIB、CONFIG_IPRO_BLE_CTLR_FEATURE、CONFIG_IPRO_BLE_CTLR_CFG_DBG選擇。 - 初始化順序:
ipro_ble_ctlr_controller_init()→hci_driver_ipro_ble_ctlr_init()→bt_enable()。 - Reef 的 BLE 組態:
ipro_ble_remote、ipro_throughput_test、ipro_le_audio_headset加DEFCONFIG=defconfig_reef,以及ipro_agora_sentino(BLE 配網)。 - 未包含於 V2.2.1:控制器原始碼、PAwR/ESL 應用、IPRO6S 的 BLE 控制器。
協定堆疊架構¶
ipro_bt_host/
├── upstream/zephyr/ Zephyr v4.4.0 Host 原始碼(include/、subsys/bluetooth/)
├── port/ FreeRTOS 移植層:HCI 驅動、kernel 介面、設定儲存、加密
└── tinycrypt/
ipro_ble_controller/
├── include/ 公開標頭 ipro_ble_controller.h
├── hcitl/ 晶片內 HCI 傳輸
├── plf/ipro7/ 控制器 API 標頭(初始化、低功耗、NVDS)
├── lib/ 16 個 IPRO7 預編譯封存檔
└── cmake/ 封存檔選擇規則(ipro7_build.cmake)
每個 BLE 應用的 .config 都需要:
缺少 CONFIG_BT_CTLR_IPRO 或 CONFIG_BT_HOST_IPRO_V44 時,建置在 CMake 設定階段就會停止。Host 與控制器元件會匯出自己的 include 路徑,應用的 CMakeLists.txt 不需要另外加入 zephyr/bluetooth/*.h 的路徑。
Zephyr RTOS 映像使用 Zephyr 本身的 Host,搭配同一組控制器封存檔;V2.2.1 的 Zephyr 只有 IPRO7 EVB 與 IPRO7AI EVK 的板子定義,見 Zephyr RTOS。
控制器選擇¶
| 設定 | 值 | 未設定時 | 意義 |
|---|---|---|---|
CONFIG_IPRO_BLE_CTLR_LIB |
link1_periph、link1、link2、link4 |
link1_periph |
link1_periph 只有 peripheral 與 broadcaster 角色;其餘的數字是連線數 |
CONFIG_IPRO_BLE_CTLR_FEATURE |
bt52、bt54_iso2 |
bt52 |
bt54_iso2 加入 ISO 通道(LE Audio) |
CONFIG_IPRO_BLE_CTLR_CFG_DBG |
y 或不設定 |
不設定 | 不設定時連結 _release 封存檔;設 y 連結除錯版 |
檔名規則為 libipro_ble_controller_ipro7_<LIB>[_bt54_iso2][_release].a,bt52 沒有功能後綴。lib/ 下的 16 個封存檔:
| LIB | bt52 |
bt52 release |
bt54_iso2 |
bt54_iso2 release |
|---|---|---|---|---|
link1_periph |
…_link1_periph.a |
…_link1_periph_release.a |
…_link1_periph_bt54_iso2.a |
…_link1_periph_bt54_iso2_release.a |
link1 |
…_link1.a |
…_link1_release.a |
…_link1_bt54_iso2.a |
…_link1_bt54_iso2_release.a |
link2 |
…_link2.a |
…_link2_release.a |
…_link2_bt54_iso2.a |
…_link2_bt54_iso2_release.a |
link4 |
…_link4.a |
…_link4_release.a |
…_link4_bt54_iso2.a |
…_link4_bt54_iso2_release.a |
設定階段會檢查:
CONFIG_EM_SIZE(BLE Exchange Memory,單位 KB)必須設定。bt54_iso2需要CONFIG_EM_SIZE至少 32。LIB或FEATURE的值不在上表時,建置停止。
CONFIG_BT_CONN(Host 的連線數)不可超過封存檔的連線數。Exchange Memory 從 OCRAM 頂端保留,見記憶體配置。
V2.2.1 隨附的 Reef 組態可作為起點:
| 應用與組態 | LIB | FEATURE | 除錯版封存檔 | CONFIG_BT_CONN |
CONFIG_EM_SIZE |
|---|---|---|---|---|---|
ipro_ble_remote defconfig_reef |
link1_periph |
bt52 |
否 | 1 | 16 |
ipro_throughput_test defconfig_reef |
link2 |
bt52 |
否 | 2 | 32 |
ipro_le_audio_headset defconfig_reef |
link1_periph |
bt54_iso2 |
是 | 1 | 32 |
ipro_agora_sentino .config |
link1_periph |
bt52 |
否 | 1 | 16 |
ipro_ble_transparent、ipro_ble_hid_keyboard 與 ipro_pdm_mic_demo 是 IPRO7 EVB 與 IPRO7AI EVK 的 BLE 範例,不能建置給 Reef。
CONFIG_BLUETOOTH_LE_ENABLE=y
CONFIG_BT_CTLR_IPRO=y
CONFIG_IPRO_BLE_CTLR_LIB="link1_periph"
CONFIG_IPRO_BLE_CTLR_FEATURE="bt52"
CONFIG_EM_SIZE=16
CONFIG_BT_CONN=1
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_BROADCASTER=y
CONFIG_PHY_UPDATE=y
CONFIG_BT_DATA_LEN_UPDATE=y
CONFIG_BT_HOST_IPRO_V44=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_ENABLE_NVDS=y
CONFIG_CFG_NVDS=y
建置不解析 Kconfig 的相依關係,請從最接近的隨附應用複製整份設定檔再修改,見建置系統與設定。
初始化順序¶
flowchart LR
A["ipro_ble_ctlr_controller_init()"] --> B["hci_driver_ipro_ble_ctlr_init()"] --> C["bt_enable(bt_ready_cb)"] --> D["bt_ready_cb:<br/>host_ready、載入設定、<br/>註冊回呼、開始廣播"]
ipro_ble_ctlr_controller_init(configMAX_PRIORITIES - 1);
vTaskDelay(pdMS_TO_TICKS(50));
hci_driver_ipro_ble_ctlr_init();
vTaskDelay(pdMS_TO_TICKS(50));
bt_enable(bt_ready_cb);
- 在排程器啟動後的 FreeRTOS 任務中執行;中間的
vTaskDelay()需要排程器。 ipro_ble_ctlr_controller_init()建立控制器任務。控制器有無線電時序的硬性期限,優先權要高於應用任務;隨附應用使用configMAX_PRIORITIES - 1。成功回傳 0,控制器已啟動過回傳 -1,任務建立失敗回傳 -2。bt_ready_cb中先呼叫ipro_ble_ctlr_hcitl_host_ready(),告知 HCI 初始化完成,控制器才能在事件之間睡眠。- 啟用
CONFIG_BT_SETTINGS時,在bt_ready_cb中先settings_load_subtree("bt")載入配對資料,再開始廣播。 - Reef 沒有 32.768 kHz 晶振。依
ipro_throughput_test的 README,Reef 在重置後約 7 秒才印出 BLE 就緒的訊息,因為控制器會先檢驗 32 kHz 時脈,再退回 RC32K(記錄中出現XTAL32K rejected)。
API 用法¶
以下三段依序放在同一個 .c 檔即可編譯,內容取自 ipro_ble_remote 的 src/services/remote_services.c 與 src/app/remote_app.c。應用使用 Zephyr 的回呼式 API;控制器內部的訊息機制不對應用開放。
GATT 服務¶
以 BT_GATT_SERVICE_DEFINE 在編譯期定義服務。下例是一個可讀取並通知溫度的服務(UUID 沿用 ipro_ble_remote 的環境服務 0xFF30/0xFF31;在 Reef 上回報晶片內建溫度感測器的讀值):
#include <FreeRTOS.h>
#include <task.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/settings/settings.h>
#include <hci_driver_ipro_ble_ctlr.h>
#include <ipro_ble_controller.h>
#define BT_UUID_ENV_SVC BT_UUID_DECLARE_16(0xFF30)
#define BT_UUID_ENV_TEMP BT_UUID_DECLARE_16(0xFF31)
static int16_t temperature = 2500; /* 0.01 °C */
static bool temp_notify_enabled;
static ssize_t temp_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&temperature, sizeof(temperature));
}
static void temp_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
temp_notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}
BT_GATT_SERVICE_DEFINE(env_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_ENV_SVC),
BT_GATT_CHARACTERISTIC(BT_UUID_ENV_TEMP,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
temp_read, NULL, NULL),
BT_GATT_CCC(temp_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);
/* attr_env_svc[1] 是溫度特徵值的宣告;
* bt_gatt_notify() 使用緊接其後的數值屬性 */
static int notify_temperature(struct bt_conn *conn)
{
if (!temp_notify_enabled) {
return 0;
}
return bt_gatt_notify(conn, &attr_env_svc[1], &temperature, sizeof(temperature));
}
廣播與連線¶
#define DEVICE_NAME "IPRO-Remote"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
static struct bt_conn *current_conn;
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static int start_advertising(void)
{
struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.options = BT_LE_ADV_OPT_CONN, /* 可連線 */
.interval_min = 160, /* 100 ms,單位 0.625 ms */
.interval_max = 320, /* 200 ms */
};
return bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
return;
}
current_conn = bt_conn_ref(conn);
(void)notify_temperature(current_conn);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
(void)conn;
(void)reason;
if (current_conn) {
bt_conn_unref(current_conn);
current_conn = NULL;
}
/* 此回呼在 Host 執行緒中執行,不可阻塞。ipro_ble_remote 不在這裡
* 重新廣播,而是以 FreeRTOS 計時器在 500 ms 後重新開始。 */
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
本版 Host 沒有 BT_LE_ADV_CONN_NAME 巨集。請如上例把裝置名稱放進廣播資料,或使用 Host 預先定義的參數巨集,例如 BT_LE_ADV_CONN_FAST_1。
啟動¶
static void bt_ready_cb(int err)
{
if (err) {
return;
}
ipro_ble_ctlr_hcitl_host_ready();
#if defined(CONFIG_BT_SETTINGS)
(void)settings_load_subtree("bt");
#endif
bt_conn_cb_register(&conn_callbacks);
(void)start_advertising();
}
/* 在應用任務中呼叫 */
int ble_init(void)
{
int err;
err = ipro_ble_ctlr_controller_init(configMAX_PRIORITIES - 1);
if (err) {
return err;
}
vTaskDelay(pdMS_TO_TICKS(50));
err = hci_driver_ipro_ble_ctlr_init();
if (err) {
return err;
}
vTaskDelay(pdMS_TO_TICKS(50));
return bt_enable(bt_ready_cb);
}
- 要把配對資料存進 Flash,設定
CONFIG_BT_SETTINGS=y與CONFIG_EASYFLASH4=y(Host 的設定儲存使用 EasyFlash,資料寫在分割表的PSM分割區)。ipro_le_audio_headset的defconfig_reef這樣設定;ipro_ble_remote的defconfig_reef不保存配對資料。 - 連線後提高傳輸量:
ipro_ble_remote在connected中以bt_gatt_exchange_mtu()交換 MTU,並在CONFIG_BT_USER_DATA_LEN_UPDATE時以bt_conn_le_data_len_update()要求 251 位元組的 DLE。
在 Reef 上動手做¶
把 ipro_ble_remote 建置給 Reef,從手機或電腦連線,透過 GATT 改變 Reef 上 RGB LED 的顏色。需要:U6 接好主控台線的 Reef、IntelProCube 命令列工具(下文以 <ICUBE> 表示),以及裝有通用 BLE App(nRF Connect、LightBlue)的手機,或有 Python 與藍牙的 macOS/Linux 電腦。
-
確認設定
defconfig_reef隨附即完整。下面是其中與本例相關的設定:2 MB Flash 與 boot2 分割表的 Reef 板子設定、應用本身的 OTA 開關,以及 BLE 區塊。新的 Reef BLE 應用保留板子與 BLE 的設定即可;CONFIG_IPRO_BLE_REMOTE_OTA只屬於ipro_ble_remote。apps/bluetooth/ble/ipro_ble_remote/defconfig_reef(節錄)CONFIG_IPRO7AI_REEF=y CONFIG_IPRO7_FLASH_2M=y CONFIG_PARTITION=y CONFIG_IPRO_BLE_REMOTE_OTA=y CONFIG_BLUETOOTH_LE_ENABLE=y CONFIG_BT_CTLR_IPRO=y CONFIG_IPRO_BLE_CTLR_LIB="link1_periph" CONFIG_IPRO_BLE_CTLR_FEATURE="bt52" CONFIG_EM_SIZE=16 CONFIG_BT_CONN=1 CONFIG_BT_PERIPHERAL=y CONFIG_BT_BROADCASTER=y CONFIG_BT_HOST_IPRO_V44=y CONFIG_BT_SMP=y -
看懂要寫入的特徵值
Reef 專用的 0xFF80 服務有一個 LED Colour 特徵值 0xFF81:3 個位元組 R、G、B。寫入處理函式檢查長度、記錄顏色,再交給 LED 驅動:
apps/bluetooth/ble/ipro_ble_remote/src/services/reef_service.c(節錄)static ssize_t color_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { const uint8_t *d = buf; (void)conn; (void)attr; (void)flags; if (offset != 0u) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } if (len != sizeof(s_ctx.rgb)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } memcpy(s_ctx.rgb, d, sizeof(s_ctx.rgb)); IPRO_LOGI(LOG_TAG, "[LED] Colour %u,%u,%u", d[0], d[1], d[2]); if (s_ctx.h.led_color) { s_ctx.h.led_color(d[0], d[1], d[2]); } return len; } BT_GATT_SERVICE_DEFINE(reef_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_REEF_SVC), BT_GATT_CHARACTERISTIC(BT_UUID_REEF_LED_COLOR, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, color_read, color_write, NULL), /* ... 按鍵事件、音量、提示音與板卡資訊特徵值 ... */ );這個檔案的
LOG_TAG是"REEF_SVC"。要加入自己的特徵值,照同樣方式多寫一個BT_GATT_CHARACTERISTIC項目與讀寫處理函式。 -
建置
輸出:
apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_IPRO7.bin。 -
連同 boot2 與 A/B 分割表燒錄
讓 Reef 進入下載模式(按住 SW1 BOOT、按一下 SW2 RESET、放開 SW1;或在執行中的主控台輸入
reboot_dl),再在 IntelProCube 目錄下燒錄:cd <ICUBE> ./ipro_iot_tool_macos --chipname ipro7 --chip-variant IPRO7AI \ --port /dev/cu.usbserial-XXXX --baudrate 921600 \ --boot2 chips/ipro7/builtin_imgs/boot2_v0.6.0/ipro_boot2_IPRO7_v0.6.0.bin \ --pt <SDK>/apps/bluetooth/ble/ipro_ble_remote/partition_cfg_2M_remote_ab.toml \ --firmware <SDK>/apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_IPRO7.bin燒錄失敗時工具也回傳 0,請確認輸出有
[All Success],再按 SW2(RESET)。這份分割表就是 OTA 更新使用的配置;燒錄細節見燒錄與序列埠。 -
看主控台並連線
以 115200 baud 開啟主控台。boot2 的
[BOOT2] IPRO7 Boot2 v0.6.0之後,應用會印出下列訊息(省略記錄前綴,中間還有其他行):[BLE] Initialized [BLE] Services ready [OTA] Service ready [BLE] All services initialized [BLE] Advertising: IPRO-Remote連線到
IPRO-Remote,對特徵值 0xFF81 寫入FF 00 00。主控台印出Connected! Peer: …,接著[LED] Colour 255,0,0。再對 0xFF11(LED State)寫入01,LED 就以這個顏色點亮。沒有手機時,可在電腦上執行隨附的 bleak 測試,它會寫入顏色並讀回板卡資訊:python3 -m venv .venv && .venv/bin/pip install bleak .venv/bin/python apps/bluetooth/ble/ipro_ble_remote/tools/reef_ble_test.py info led畫面印出
board info:與LED colour [255, 0, 0] …,LED 以紅色閃爍。在主控台輸入reef status可從板子端看到同樣的狀態。
下一步:以 Reef 組態為起點建立自己的應用,見建立第一個應用;用主控台與主機端腳本檢查應用,見測試應用。搭配 Android App 的完整 GATT 操作見第一個 BLE 應用。
效能與吞吐量¶
ipro_throughput_test 的 README 實測:兩片執行 defconfig_reef 的 Reef EVK(link2 控制器),ATT MTU 247,DLE 251 octets,每封包 244 位元組,連線間隔 50 ms,啟用 CONFIG_IPRO7_BLE_HOTCODE=y:
| PHY | 單向吞吐量 |
|---|---|
| 2M | 1.34 Mbps |
| 1M | 747 kbps |
CONFIG_IPRO7_BLE_HOTCODE=y把控制器、Host 與 FreeRTOS kernel 的每封包路徑放到 OCRAM 執行,約占 88 KB OCRAM。同樣條件下不啟用時(程式在 Flash),2M PHY 約 0.6 Mbps。- 此選項不能與 AI+影像的 OCRAM 配置,或低功耗設定(
CONFIG_HAL_LP、CONFIG_IPRO_PM_*、CONFIG_OCRAM_NORET_SPLIT)同時使用,連結腳本產生器會拒絕。 - 大量資料建議 2M PHY、連線間隔 30–50 ms、244 位元組的封包。
測試在主控台以 tp slave、tp master、tp conn、tp phy、tp start 操作,見 BLE 吞吐量測試。
LE Audio¶
LE Audio 需要 CONFIG_IPRO_BLE_CTLR_FEATURE="bt54_iso2" 與 CONFIG_EM_SIZE=32,再加上 CONFIG_BT_AUDIO=y 與 BAP、PACS、ASCS 等角色設定,LC3 以 CONFIG_COMPONENTS_LC3_ENABLE=y 加入。Host 的 LE Audio 標頭在 ipro_bt_host/upstream/zephyr/include/zephyr/bluetooth/audio/。隨附的 FreeRTOS 範例是單播耳機 ipro_le_audio_headset,以 DEFCONFIG=defconfig_reef 建置給 Reef;設定、協定、音訊路徑與目前狀態見音訊與 LE Audio。
低功耗¶
ipro_ble_remote的defconfig_reef不使用 PDS:Reef 由 USB 供電,相機與音訊也需要時脈。- 控制器的 PDS 睡眠只接受 16 KB EM 的配置;
bt54_iso2(32 KB EM)組態下控制器保留原生睡眠,但不進入 PDS。 CONFIG_IPRO_PM_BLE與CONFIG_IPRO_PM_PDS_CONNECTED會強制從原始碼建置控制器,V2.2.1 無法建置,見本版限制。- BLE 搭配 PDS31 睡眠與配對後重新連線的範例是
ipro_pdm_mic_demo,屬於 IPRO7 EVB/IPRO7AI EVK。
睡眠模式、喚醒來源與量測方法見低功耗。
本版限制¶
- 控制器只以預編譯函式庫提供,V2.2.1 不含其原始碼。以下設定需要從原始碼建置控制器,會讓建置停止:
CONFIG_IPRO_BLE_CTLR_FORCE_SOURCE_BUILD、CONFIG_IPRO_BLE_CTLR_SUBRATING、CONFIG_IPRO_BLE_CTLR_CSSA、CONFIG_IPRO_PM_BLE、CONFIG_IPRO_PM_PDS_CONNECTED。 - PAwR 與 ESL:Host 標頭含週期性廣播相關 API,但沒有 PAwR/ESL 應用。未包含於 V2.2.1,請洽 INTELPRO。
ipro_ble_remote的 OTA 服務只在defconfig_reef開啟;預設.config建置的是不含 OTA 的 IPRO7 EVB 映像。- V2.2.1 不含 IPRO6S 的 BLE 控制器;本套件中的 IPRO6S 應用只有 Wi-Fi。
參考應用¶
| 應用(Reef 建置) | 內容 |
|---|---|
ipro_ble_remote(DEFCONFIG=defconfig_reef) |
Reef 的 GATT 服務:WS2812 LED、按鍵、晶片溫度、UART、OV5640 JPEG 拍照與動態偵測、圖片上傳、PDM 麥克風音量、提示音,以及 BLE OTA。附 Android App ipro_remote_demo.apk 與主機端腳本 tools/reef_ble_test.py、tools/reef_ota.py |
ipro_throughput_test(DEFCONFIG=defconfig_reef) |
雙板吞吐量測試:1M/2M/Coded PHY、DLE、MTU 247 |
ipro_le_audio_headset(DEFCONFIG=defconfig_reef) |
LE Audio 單播耳機 |
ipro_agora_sentino(.config) |
雲端語音應用的 BLE 配網,Wi-Fi 經 IPRO6S FIN 卡 |
ipro_ble_transparent(AT 介面的資料通道)、ipro_ble_hid_keyboard(HID over GATT)與 ipro_pdm_mic_demo 只能建置給 IPRO7 EVB 或 IPRO7AI EVK。