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 事件,同时保持对底层硬件资源的高效利用,非常适合低功耗蓝牙设备的开发场景。