BLE 中心设备学习笔记
概述
本文记录了基于 CH573 芯片的 BLE(蓝牙低功耗)中心设备如何扫描、连接周边设备,并获取其服务、特征和描述符的完整流程。
BLE GATT 协议基础
GATT 层次结构
Profile(配置文件)
└── Service(服务)
└── Characteristic(特征)
├── Value(值)
└── Descriptor(描述符)
核心概念
●服务(Service):一组相关特征的集合,实现特定功能
●特征(Characteristic):包含数据值和访问属性(读、写、通知等)
●描述符(Descriptor):特征的附加信息,如 CCCD(客户端特征配置描述符)
●句柄(Handle):用于标识属性的唯一数字
完整工作流程
1. 设备初始化
初始化入口函数
voidCentral_Init()
{
// 注册任务,获取任务ID
centralTaskId = TMOS_ProcessEventRegister(Central_ProcessEvent);
// 设置 GAP 参数
GAP_SetParamValue(TGAP_DISC_SCAN, DEFAULT_SCAN_DURATION);
GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_MIN_CONNECTION_INTERVAL);
// 初始化 GAP Bond Manager 参数
uint32_t passkey = DEFAULT_PASSCODE;
uint8_t pairMode = DEFAULT_PAIRING_MODE;
uint8_t mitm = DEFAULT_MITM_MODE;
uint8_t ioCap = DEFAULT_IO_CAPABILITIES;
uint8_t bonding = DEFAULT_BONDING_MODE;
GAPBondMgr_SetParameter(GAPBOND_CENT_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
GAPBondMgr_SetParameter(GAPBOND_CENT_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_CENT_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_CENT_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_CENT_BONDING_ENABLED, sizeof(uint8_t), &bonding);
// 初始化 GATT Client
GATT_InitClient();
// 注册接收 ATT 指示/通知
GATT_RegisterForInd(centralTaskId);
// 设置延迟启动设备
tmos_set_event(centralTaskId, START_DEVICE_EVT);
}
启动设备角色
if(events & START_DEVICE_EVT)
{
// 启动中心设备
GAPRole_CentralStartDevice(centralTaskId, ¢ralBondCB, ¢ralRoleCB);
return (events ^ START_DEVICE_EVT);
}
2. 设备扫描
扫描函数
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
扫描参数
●DEFAULT_DISCOVERY_MODE:发现模式
●DEFAULT_DISCOVERY_ACTIVE_SCAN:是否启用主动扫描
●DEFAULT_DISCOVERY_WHITE_LIST:是否使用白名单
扫描触发时机
1.初始化完成后:GAP_DEVICE_INIT_DONE_EVENT 事件
2.设备未找到时:GAP_DEVICE_DISCOVERY_EVENT 事件中重新扫描
3.连接断开后:GAP_LINK_TERMINATED_EVENT 事件中重新扫描
扫描到设备处理
case GAP_DEVICE_INFO_EVENT:
{
// 添加设备到列表
centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
}
break;
扫描完成处理
case GAP_DEVICE_DISCOVERY_EVENT:
{
// 检查是否找到目标设备
for(i = 0; i < centralScanRes; i++)
{
if(tmos_memcmp(PeerAddrDef, centralDevList[i].addr, B_ADDR_LEN))
break;
}
// 未找到目标设备
if(i == centralScanRes)
{
PRINT("Device not found...\n");
centralScanRes = 0;
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
PRINT("Discovering...\n");
}
// 找到目标设备
else
{
PRINT("Device found...\n");
GAPRole_CentralEstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
DEFAULT_LINK_WHITE_LIST,
centralDevList[i].addrType,
centralDevList[i].addr);
// 启动连接超时事件
tmos_start_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT, ESTABLISH_LINK_TIMEOUT);
PRINT("Connecting...\n");
}
}
break;
3. 连接建立
发起连接
GAPRole_CentralEstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
DEFAULT_LINK_WHITE_LIST,
centralDevList[i].addrType,
centralDevList[i].addr);
连接参数
●DEFAULT_LINK_HIGH_DUTY_CYCLE:连接占空比
●DEFAULT_LINK_WHITE_LIST:白名单设置
●centralDevList[i].addrType:目标设备地址类型
●centralDevList[i].addr:目标设备蓝牙地址
连接建立事件处理
case GAP_LINK_ESTABLISHED_EVENT:
{
// 停止连接超时任务
tmos_stop_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT);
if(pEvent->gap.hdr.status == SUCCESS)
{
centralState = BLE_STATE_CONNECTED;
centralConnHandle = pEvent->linkCmpl.connectionHandle;
centralProcedureInProgress = TRUE;
// 更新 MTU
attExchangeMTUReq_t req = {
.clientRxMTU = BLE_BUFF_MAX_LEN - 4,
};
GATT_ExchangeMTU(centralConnHandle, &req, centralTaskId);
// 启动服务发现
tmos_start_task(centralTaskId, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);
// 启动连接参数更新(如果配置)
if(centralParamUpdate)
{
tmos_start_task(centralTaskId, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);
}
// 启动 RSSI 轮询(如果配置)
if(centralRssi)
{
tmos_start_task(centralTaskId, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
}
PRINT("Connected...\n");
}
else
{
PRINT("Connect Failed...Reason:%X\n", pEvent->gap.hdr.status);
PRINT("Discovering...\n");
centralScanRes = 0;
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
}
}
break;
4. 服务发现
服务发现启动事件
if(events & START_SVC_DISCOVERY_EVT)
{
// 开始服务发现
centralStartDiscovery();
return (events ^ START_SVC_DISCOVERY_EVT);
}
服务发现函数
staticvoidcentralStartDiscovery(void)
{
uint8_t uuid[ATT_BT_UUID_SIZE] = {LO_UINT16(SIMPLEPROFILE_SERV_UUID),
HI_UINT16(SIMPLEPROFILE_SERV_UUID)};
// 初始化缓存句柄
centralSvcStartHdl = centralSvcEndHdl = centralCharHdl = 0;
centralDiscState = BLE_DISC_STATE_SVC;
// 通过 UUID 发现主服务
GATT_DiscPrimaryServiceByUUID(centralConnHandle,
uuid,
ATT_BT_UUID_SIZE,
centralTaskId);
}
服务发现结果处理
if(centralDiscState == BLE_DISC_STATE_SVC)
{
// 服务发现,存储句柄
if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->msg.findByTypeValueRsp.numInfo > 0)
{
centralSvcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
centralSvcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
PRINT("Found Profile Service handle : %x ~ %x \n", centralSvcStartHdl, centralSvcEndHdl);
}
// 过程完成
if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->hdr.status == bleProcedureComplete) ||
(pMsg->method == ATT_ERROR_RSP))
{
if(centralSvcStartHdl != 0)
{
// 发现特征
centralDiscState = BLE_DISC_STATE_CHAR;
req.startHandle = centralSvcStartHdl;
req.endHandle = centralSvcEndHdl;
req.type.len = ATT_BT_UUID_SIZE;
req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
}
}
}
5. 特征发现
特征发现过程
在服务句柄范围内,通过 UUID 查找特征。
elseif(centralDiscState == BLE_DISC_STATE_CHAR)
{
// 特征发现,存储句柄
if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
pMsg->msg.readByTypeRsp.numPairs > 0)
{
centralCharHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
pMsg->msg.readByTypeRsp.pDataList[1]);
// 开始读写操作
tmos_start_task(centralTaskId, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);
PRINT("Found Characteristic 1 handle : %x \n", centralCharHdl);
}
if((pMsg->method == ATT_READ_BY_TYPE_RSP &&
pMsg->hdr.status == bleProcedureComplete) ||
(pMsg->method == ATT_ERROR_RSP))
{
// 发现 CCCD
centralDiscState = BLE_DISC_STATE_CCCD;
req.startHandle = centralSvcStartHdl;
req.endHandle = centralSvcEndHdl;
req.type.len = ATT_BT_UUID_SIZE;
req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
}
}
6. 描述符发现
CCCD 发现过程
CCCD(Client Characteristic Configuration Descriptor)用于启用或禁用特征的通知功能。
elseif(centralDiscState == BLE_DISC_STATE_CCCD)
{
// 特征发现,存储句柄
if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
pMsg->msg.readByTypeRsp.numPairs > 0)
{
centralCCCDHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
pMsg->msg.readByTypeRsp.pDataList[1]);
centralProcedureInProgress = FALSE;
// 开始写 CCCD
tmos_start_task(centralTaskId, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);
PRINT("Found client characteristic configuration handle : %x \n", centralCCCDHdl);
}
centralDiscState = BLE_DISC_STATE_IDLE;
}
7. 数据读写
读写事件处理
if(events & START_READ_OR_WRITE_EVT)
{
if(centralProcedureInProgress == FALSE)
{
if(centralDoWrite)
{
// 写操作
attWriteReq_t req;
req.cmd = FALSE;
req.sig = FALSE;
req.handle = centralCharHdl;
req.len = 1;
req.pValue = GATT_bm_alloc(centralConnHandle, ATT_WRITE_REQ, req.len, NULL, 0);
if(req.pValue != NULL)
{
*req.pValue = centralCharVal;
if(GATT_WriteCharValue(centralConnHandle, &req, centralTaskId) == SUCCESS)
{
centralProcedureInProgress = TRUE;
centralDoWrite = !centralDoWrite;
tmos_start_task(centralTaskId, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);
}
else
{
GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
}
}
}
else
{
// 读操作
attReadReq_t req;
req.handle = centralCharHdl;
if(GATT_ReadCharValue(centralConnHandle, &req, centralTaskId) == SUCCESS)
{
centralProcedureInProgress = TRUE;
centralDoWrite = !centralDoWrite;
}
}
}
return (events ^ START_READ_OR_WRITE_EVT);
}
GATT 消息处理
staticvoidcentralProcessGATTMsg(gattMsgEvent_t *pMsg)
{
if(centralState != BLE_STATE_CONNECTED)
{
GATT_bm_free(&pMsg->msg, pMsg->method);
return;
}
// 处理读响应
if((pMsg->method == ATT_READ_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
{
if(pMsg->method == ATT_ERROR_RSP)
{
uint8_t status = pMsg->msg.errorRsp.errCode;
PRINT("Read Error: %x\n", status);
}
else
{
PRINT("Read rsp: %x\n", *pMsg->msg.readRsp.pValue);
}
centralProcedureInProgress = FALSE;
}
// 处理写响应
elseif((pMsg->method == ATT_WRITE_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
{
if(pMsg->method == ATT_ERROR_RSP)
{
uint8_t status = pMsg->msg.errorRsp.errCode;
PRINT("Write Error: %x\n", status);
}
else
{
PRINT("Write success \n");
}
centralProcedureInProgress = FALSE;
}
// 处理通知
elseif(pMsg->method == ATT_HANDLE_VALUE_NOTI)
{
PRINT("Receive noti: %x\n", *pMsg->msg.handleValueNoti.pValue);
}
// 处理服务发现
elseif(centralDiscState != BLE_DISC_STATE_IDLE)
{
centralGATTDiscoveryEvent(pMsg);
}
GATT_bm_free(&pMsg->msg, pMsg->method);
}
关键函数总结
GAP 层函数
1.GAPRole_CentralStartDevice() - 启动中心设备角色
2.GAPRole_CentralStartDiscovery() - 开始设备扫描
3.GAPRole_CentralEstablishLink() - 发起连接
4.GAPRole_TerminateLink() - 终止连接
GATT 层函数
1.GATT_DiscPrimaryServiceByUUID() - 通过 UUID 发现服务
2.GATT_ReadUsingCharUUID() - 通过 UUID 发现特征
3.GATT_ReadCharValue() - 读取特征值
4.GATT_WriteCharValue() - 写入特征值
5.GATT_ExchangeMTU() - 交换 MTU
GAP Bond Manager 函数
1.GAPBondMgr_SetParameter() - 设置绑定参数
2.GAPBondMgr_PasscodeRsp() - 密码响应
状态机设计
服务发现状态
enum {
BLE_DISC_STATE_IDLE = 0, // 空闲状态
BLE_DISC_STATE_SVC, // 服务发现状态
BLE_DISC_STATE_CHAR, // 特征发现状态
BLE_DISC_STATE_CCCD, // CCCD 发现状态
};
连接状态
enum {
BLE_STATE_IDLE = 0, // 空闲状态
BLE_STATE_CONNECTING, // 连接中状态
BLE_STATE_CONNECTED, // 已连接状态
};
学习要点总结
1.分步发现:BLE GATT 采用分步发现机制,先发现服务,再发现特征,最后发现描述符
2.句柄管理:使用句柄来标识和访问属性,避免重复传输 UUID
3.事件驱动:整个流程基于事件驱动模型,使用 TMOS 任务系统
4.状态机:通过状态机管理发现过程,确保流程有序进行
5.异步操作:所有 GATT 操作都是异步的,通过回调和事件通知结果
6.MTU 协商:连接建立后协商 MTU,优化数据传输效率
7.安全配对:通过 GAP Bond Manager 管理安全配对和绑定
8.通知机制:通过 CCCD 启用特征通知,实现主动数据推送
扩展阅读
●蓝牙核心规范(Bluetooth Core Specification)
●GATT 协议详解
●CH573 BLE 开发文档
●TMOS 操作系统使用指南
学习日期:2026-03-12
学习内容:BLE 中心设备服务、特征、描述符发现流程
开发平台:CH573