on_connect函數
/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.** @param[in] p_epd EPD Service structure.* @param[in] p_ble_evt Pointer to the event received from BLE stack.*/
static void on_connect(ble_epd_t * p_epd, ble_evt_t * p_ble_evt)
{p_epd->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;EPD_GPIO_Init();
}
函數功能概述
on_connect
函數是藍牙低功耗 (BLE) 事件處理的核心部分,主要在設備成功建立藍牙連接時被觸發。其核心功能是管理連接句柄并初始化電子紙顯示屏 (EPD) 的 GPIO 引腳配置。
代碼詳細解析
/*** @brief 處理S110 SoftDevice的BLE_GAP_EVT_CONNECTED事件* @param[in] p_epd EPD服務結構指針,包含連接狀態和配置信息* @param[in] p_ble_evt BLE事件指針,包含連接事件的具體數據*/
static void on_connect(ble_epd_t * p_epd, ble_evt_t * p_ble_evt)
{// 1. 保存連接句柄p_epd->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;// 2. 初始化EPD相關的GPIO引腳EPD_GPIO_Init();
}
關鍵操作說明
-
連接句柄管理
- 通過
p_ble_evt->evt.gap_evt.conn_handle
獲取藍牙連接的句柄(conn_handle
) - 將句柄存儲到
p_epd->conn_handle
中,用于后續通信時標識該連接 - 連接句柄是 BLE 通信的基礎,類似網絡連接中的 Socket 描述符
- 通過
-
GPIO 初始化
- 調用
EPD_GPIO_Init()
函數初始化 EPD 硬件相關的 GPIO 引腳 - 初始化操作可能包括:
- 設置引腳方向(輸入 / 輸出)
- 配置引腳電平(上拉 / 下拉)
- 初始化 SPI/I2C 等通信接口的引腳
- 這一步為后續 EPD 顯示數據傳輸做好硬件準備
- 調用
實際應用場景
當手機或其他 BLE 主機設備掃描并連接到搭載該代碼的設備時:
- 藍牙協議棧會生成
BLE_GAP_EVT_CONNECTED
事件 - 事件處理框架會調用
on_connect
函數 - 函數保存連接句柄并初始化 EPD 硬件,使設備準備好接收主機發送的顯示指令
- 后續主機可通過該連接句柄向 EPD 發送數據(如時間、圖像等)
潛在優化點
-
錯誤處理補充
可添加EPD_GPIO_Init()
的返回值檢查,確保初始化成功:uint32_t err_code = EPD_GPIO_Init(); if (err_code != NRF_SUCCESS) {NRF_LOG_ERROR("EPD GPIO init failed, error: %d", err_code);// 可選:斷開連接或記錄錯誤狀態 }
-
功耗優化
若系統對功耗敏感,可在初始化時配置 GPIO 引腳的低功耗模式:void EPD_GPIO_Init() {// 示例:配置引腳為低功耗輸出模式nrf_gpio_cfg_output(引腳號);nrf_gpio_pin_write(引腳號, 低電平); }
-
連接參數記錄
可擴展代碼以記錄連接參數(如連接間隔、超時時間):p_epd->conn_params.interval = p_ble_evt->evt.gap_evt.params.conn_params.interval; p_epd->conn_params.timeout = p_ble_evt->evt.gap_evt.params.conn_params.timeout;
與其他函數的協作關系
- 與
on_disconnect
函數對應:
連接時調用EPD_GPIO_Init()
,斷開時調用EPD_GPIO_Uninit()
釋放資源 - 與
epd_service_on_write
函數協作:
連接建立后,on_connect
保存的conn_handle
用于ble_epd_string_send
等函數發送數據 - 與硬件驅動的交互:
EPD_GPIO_Init()
依賴底層 GPIO 驅動實現引腳配置
該函數是 BLE 連接建立后硬件初始化的關鍵環節,確保 EPD 設備在連接狀態下能正常接收和處理顯示指令。
ble_evt_t
結構體解析
結構體概述
ble_evt_t
是藍牙低功耗 (BLE) 協議棧中用于表示各類事件的核心數據結構。它采用 "事件頭 + 事件聯合體" 的設計模式,能夠高效處理不同類型的 BLE 事件,同時保持內存占用的最小化。
結構體定義詳解
typedef struct
{ble_evt_hdr_t header; /**< 事件頭,包含事件類型標識等公共信息 */union{ble_common_evt_t common_evt; /**< 通用事件,事件ID屬于BLE_EVT_*系列 */ble_gap_evt_t gap_evt; /**< GAP層事件,事件ID屬于BLE_GAP_EVT_*系列 */ble_gattc_evt_t gattc_evt; /**< GATT客戶端事件,事件ID屬于BLE_GATTC_EVT*系列 */ble_gatts_evt_t gatts_evt; /**< GATT服務器事件,事件ID屬于BLE_GATTS_EVT*系列 */} evt; /**< 事件聯合體,根據事件類型存儲具體事件數據 */
} ble_evt_t;
核心成員解析
1.?header
字段 - 事件頭結構
- 類型:
ble_evt_hdr_t
(通常包含事件 ID 和長度信息) - 作用:
- 標識事件類型(通過
header.evt_id
) - 提供事件數據長度(通過
header.evt_len
) - 作為所有事件的公共前綴,便于統一處理
- 標識事件類型(通過
typedef struct
{uint16_t evt_id; /**< Value from a BLE_<module>_EVT series. */uint16_t evt_len; /**< Length in octets including this header. */
} ble_evt_hdr_t;
?
2.?evt
聯合體 - 事件具體數據
聯合體設計的核心優勢是內存共享,不同事件類型復用同一塊內存空間:
聯合體成員 | 對應事件類型 | 典型應用場景 |
---|---|---|
common_evt | 通用基礎事件 | 協議棧內部狀態變化 |
gap_evt | GAP(通用訪問配置文件)事件 | 連接建立、斷開、設備發現等 |
gattc_evt | GATT 客戶端事件 | 讀取 / 寫入遠程服務數據 |
gatts_evt | GATT 服務器事件 | 接收客戶端寫入、通知請求等 |
設計模式分析
這種 "頭部 + 聯合體" 的設計屬于標記聯合模式 (Tagged Union),在嵌入式系統中非常常見:
-
空間效率:
- 無論處理哪種事件,結構體總大小等于
header
?+ 最大事件結構體的大小 - 避免為每種事件類型單獨分配內存
- 無論處理哪種事件,結構體總大小等于
-
類型安全:
- 通過
header.evt_id
判斷當前事件類型 - 確保訪問聯合體成員時類型匹配
- 通過
-
擴展性:
- 新增事件類型時只需擴展聯合體成員
- 保持結構體接口兼容性
典型使用場景
在事件處理函數中,通常按以下流程處理ble_evt_t
:
void ble_evt_handler(ble_evt_t *p_ble_evt)
{// 1. 通過事件頭獲取事件類型uint32_t evt_id = p_ble_evt->header.evt_id;// 2. 根據事件類型處理不同事件switch (evt_id){case BLE_GAP_EVT_CONNECTED:// 處理連接事件,使用p_ble_evt->evt.gap_evton_connect(p_epd, p_ble_evt);break;case BLE_GATTS_EVT_WRITE:// 處理寫入事件,使用p_ble_evt->evt.gatts_evton_write(p_epd, p_ble_evt);break;// 其他事件處理...default:break;}
}
內存布局與對齊注意事項
-
內存對齊:
- 聯合體成員需遵循最大對齊規則
- 例如:若
ble_gatts_evt_t
包含 64 位成員,則整個聯合體按 8 字節對齊
-
內存占用計算:
sizeof(ble_evt_t) = sizeof(ble_evt_hdr_t) + max(sizeof(ble_common_evt_t),sizeof(ble_gap_evt_t),sizeof(ble_gattc_evt_t),sizeof(ble_gatts_evt_t) )
-
跨平臺注意事項:
- 需通過
#pragma pack
等指令控制結構體對齊 - 避免因不同編譯器對齊規則導致的協議兼容性問題
- 需通過
與其他結構的關聯
-
ble_evt_hdr_t
結構:
通常包含uint16_t evt_id
和uint16_t evt_len
字段,作為所有事件的公共標識 -
各事件具體結構:
ble_gap_evt_t
包含連接句柄、連接參數等 GAP 層數據ble_gatts_evt_t
包含服務句柄、特征值句柄等 GATT 服務器數據- 這些結構根據藍牙核心規范定義,確保協議兼容性
這種設計使得 BLE 協議棧能夠以統一接口處理各類事件,同時保持高效的內存使用,非常適合資源受限的嵌入式系統環境。
ble_gap_evt_t
結構體深度解析
結構體整體架構
ble_gap_evt_t
是藍牙低功耗 (GAP) 層事件的核心數據結構,用于表示設備在連接、斷開、安全認證等過程中產生的各類事件。其設計采用 "連接句柄 + 事件參數聯合體" 的模式,能夠高效處理多種 GAP 事件類型。
typedef struct
{uint16_t conn_handle; /**< 事件發生的連接句柄 */union /**< 事件參數聯合體,由外層結構體的evt_id標識當前類型 */{ble_gap_evt_connected_t connected; /**< 連接事件參數 */ble_gap_evt_disconnected_t disconnected; /**< 斷開連接事件參數 */ble_gap_evt_conn_param_update_t conn_param_update; /**< 連接參數更新事件參數 */// 省略中間事件類型...ble_gap_evt_phy_update_request_t phy_update_request; /**< PHY更新請求事件參數 */ble_gap_evt_phy_update_t phy_update; /**< PHY更新事件參數 */} params; /**< 事件具體參數 */
} ble_gap_evt_t;
核心成員詳解
1.?conn_handle
?- 連接句柄
- 類型:
uint16_t
- 作用:
- 唯一標識一個藍牙連接,類似網絡通信中的 Socket 句柄
- 在多連接場景中用于區分不同的客戶端連接
- 所有 GAP 事件都與特定連接相關聯
2.?params
聯合體 - 事件參數集合
聯合體包含 16 種不同類型的事件參數結構,以下是關鍵事件類型解析:
事件類型 | 事件 ID 前綴 | 核心應用場景 |
---|---|---|
connected | BLE_GAP_EVT_CONNECTED | 藍牙連接建立時,攜帶連接參數和句柄 |
disconnected | BLE_GAP_EVT_DISCONNECTED | 連接斷開時,攜帶斷開原因碼 |
conn_param_update | BLE_GAP_EVT_CONN_PARAM_UPDATE | 連接參數(間隔、超時等)更新時 |
sec_params_request | BLE_GAP_EVT_SEC_PARAMS_REQUEST | 安全參數請求,如加密密鑰協商 |
auth_status | BLE_GAP_EVT_AUTH_STATUS | 認證狀態通知,攜帶認證結果 |
timeout | BLE_GAP_EVT_TIMEOUT | 連接或操作超時,攜帶超時類型 |
rssi_changed | BLE_GAP_EVT_RSSI_CHANGED | 接收信號強度變化,攜帶 RSSI 值 |
設計模式與內存優化
這種 "句柄 + 聯合體" 的設計體現了嵌入式系統中典型的空間效率優先原則:
-
內存共享機制:
- 無論處理哪種事件,聯合體僅占用最大事件結構的內存空間
- 例如:
ble_gap_evt_connected_t
通常比ble_gap_evt_timeout_t
更復雜,聯合體大小由前者決定
-
類型安全控制:
- 通過外層
ble_evt_t
的header.evt_id
字段判斷當前事件類型 - 確保訪問聯合體成員時類型匹配,避免內存越界
- 通過外層
-
協議兼容性設計:
- 結構體定義嚴格遵循藍牙核心規范 v5.3
- 新增事件類型時只需擴展聯合體成員,不破壞現有接口
典型事件處理流程
以下是處理BLE_GAP_EVT_CONNECTED
事件的示例代碼,展示如何解析ble_gap_evt_t
:
c
void ble_evt_handler(ble_evt_t *p_ble_evt)
{if (p_ble_evt->header.evt_id == BLE_GAP_EVT_CONNECTED){// 1. 獲取連接句柄uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;// 2. 解析連接事件參數ble_gap_evt_connected_t *p_connected = &p_ble_evt->evt.gap_evt.params.connected;// 3. 提取連接參數uint16_t conn_interval = p_connected->conn_params.interval; // 連接間隔 (1.25ms單位)uint16_t conn_latency = p_connected->conn_params.latency; // 連接延遲uint16_t supervision_timeout = p_connected->conn_params.sup_timeout; // 超時時間// 4. 業務邏輯處理on_connect(p_epd, p_ble_evt);}
}
內存布局與對齊考量
-
對齊規則:
- 聯合體成員按各自結構的最大對齊要求進行內存對齊
- 例如:若
ble_gap_evt_connected_t
包含 32 位成員,則整個聯合體按 4 字節對齊
-
內存占用計算:
plaintext
sizeof(ble_gap_evt_t) = sizeof(uint16_t) + max(sizeof(ble_gap_evt_connected_t),sizeof(ble_gap_evt_disconnected_t),// ... 其他事件結構大小sizeof(ble_gap_evt_phy_update_t) )
-
編譯器優化建議:
- 使用
#pragma pack(push, 4)
等指令控制對齊,避免字節填充浪費內存 - 在跨平臺開發中,通過
__attribute__((packed))
確保結構體布局一致
- 使用
與其他結構的協作關系
-
與
ble_evt_t
的關系:ble_gap_evt_t
是ble_evt_t
聯合體中的一個成員- 通過
ble_evt_t->evt.gap_evt
訪問 GAP 事件相關數據
-
與事件處理函數的交互:
on_connect
、on_disconnect
等函數接收ble_evt_t*
指針- 通過類型轉換獲取
ble_gap_evt_t
中的具體事件參數
-
與協議棧的集成:
- 結構體定義由 Nordic Semiconductor SDK 提供
- 底層協議棧填充事件數據后,通過回調函數傳遞給應用層
這種設計使得應用層能夠以統一接口處理各類 GAP 事件,同時保持對底層硬件資源的高效利用,非常適合低功耗藍牙設備的開發場景。