一.概述
1.工作逻辑
蓝牙从机例程(Peripheral)的大致工作逻辑是:
1.初始化开启广播进入广播态等待蓝牙主机的发起连接;;
2.被蓝牙主机连接后,开启了三个tmos任务,第一个任务是周期性的给主机上报数据,第二个任务是交互连接间隔,第三个任务是周期性的打印输出信号强度;
3.被主机断开连接后,关闭两个周期性的任务,开启广播等待再次被连接。
二.详细讲解
1.蓝牙初始化讲解
void Peripheral_Init() { Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent); { uint8_t initial_advertising_enable = TRUE; uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL; uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL; GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable); //开启广播 GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); //设置扫描应答包内容 GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); //设置广播包内容 GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval); //设置连接间隔最小值 GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval); //设置连接间隔最大值 } { uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL; GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt); //设置广播间隔最小值 GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt); //设置广播间隔最小值 GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE); //开启扫描请求通知 } { uint32_t passkey = 0; uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; uint8_t mitm = TRUE; uint8_t bonding = TRUE; uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); //设置初始配对密码,只可以是6个纯数字,若前面有0则设置为1234对应密码001234,默认000000 GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode); //设置配对模式为等待对方请求 GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm); //开启mitm GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); //设置I/O能力为仅显示设备 GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding); //开启绑定 } GGS_AddService(GATT_ALL_SERVICES); //先添加 GAP 和 GATT 服务 GATTServApp_AddService(GATT_ALL_SERVICES); //添加 GATT 服务器应用服务 DevInfo_AddService(); //添加设备信息服务 SimpleProfile_AddService(GATT_ALL_SERVICES); //添加用户自定义服务 GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName); //设置设备名称(非广播名称),将以与广播名称保持一直,ios设备连接一次之后下次搜索会使用该名称 { uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1}; uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2}; uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3}; uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4}; uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5}; SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1); //设置char1的值,使用READ功能时读取此函数设置的值 SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2); //设置char2的值,使用READ功能时读取此函数设置的值 SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3); //设置char3的值,使用READ功能时读取此函数设置的值 SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4); //设置char4的值,使用READ功能时读取此函数设置的值 SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5); //设置char5的值,使用READ功能时读取此函数设置的值 } peripheralInitConnItem(&peripheralConnList); //初始化连接参数 SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs); //注册GATT回调函数用于接收write数据 GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs); //注册广播回调-用于接收扫描请求的回调 tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); //开启设备 }
2.蓝牙状态回调讲解
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len) { switch(paramID) { case SIMPLEPROFILE_CHAR1: //收到char1发送的数据 { uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN]; tmos_memcpy(newValue, pValue, len); PRINT("profile ChangeCB CHAR1.. \n"); break; } case SIMPLEPROFILE_CHAR3: //收到char3发送的数据 { uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN]; tmos_memcpy(newValue, pValue, len); PRINT("profile ChangeCB CHAR3..\n"); break; } /*可以在此处添加notify使能和关闭的case分支 1.首先需添加宏: #define CHAR4_NOTIFY_ENABLE 5 //CHAR4通知开启 #define CHAR4_NOTIFY_DISENABLE 6 //CHAR4通知关闭 2.其次写回调中case GATT_CLIENT_CHAR_CFG_UUID做如下改动: case GATT_CLIENT_CHAR_CFG_UUID:{ uint16_t charCfg = BUILD_UINT16(pValue[0], pValue[1]); status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY); notifyApp = (charCfg == GATT_CFG_NO_OPERATION) ? CHAR4_NOTIFY_DISENABLE : CHAR4_NOTIFY_ENABLE; break; }*/ case CHAR4_NOTIFY_ENABLE: //CHAR4通知开启 { PRINT("CHAR4_NOTIFY_ENABLE.. \n"); break; } case CHAR4_NOTIFY_DISENABLE: //CHAR4通知关闭 { PRINT("CHAR4_NOTIFY_DISENABLE..\n"); break; } default: // should not reach here! break; } }
3.蓝牙连接处理讲解
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent) { gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent; if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT) //通过连接句柄查看当前是否已被连接 { GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle); //若已连接断开此连接 PRINT("Connection max...\n"); } else { peripheralConnList.connHandle = event->connectionHandle; //保存连接句柄值 peripheralConnList.connInterval = event->connInterval; //保存连接间隔值 peripheralConnList.connSlaveLatency = event->connLatency; //保存连接接收延迟值 peripheralConnList.connTimeout = event->connTimeout; //保存连接超时时间值 /*可以新增一些例程中未展示的参数 ,如主机设备的mac地址和地址类型 typedef struct { tmos_event_hdr_t hdr; //AP_MSG_EVENT and status uint8_t opcode; //操作码,固定为 GAP_LINK_ESTABLISHED_EVENT(值为 0x05),标识连接已建立。 uint8_t devAddrType; //设备地址类型: @ref GAP_ADDR_TYPE_DEFINES uint8_t devAddr[B_ADDR_LEN]; //设备地址 uint16_t connectionHandle; //连接句柄 uint8_t connRole; //本地设备在此连接中的角色:- GAP_CENTRAL_ROLE(0x00):中央设备(主动发起连接)- GAP_PERIPHERAL_ROLE(0x01):外围设备(被动接受连接) uint16_t connInterval; //连接间隔 uint16_t connLatency; //连接接收延迟 uint16_t connTimeout; //连接超时时间 uint8_t clockAccuracy; //时钟精度,反映设备时钟的准确性,单位为百万分之一(ppm)。用于同步连接双方的通信时机。 } gapEstLinkReqEvent_t; */ peripheralMTU = ATT_MTU_SIZE; //保存mtu初始值 // Set timer for periodic event tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD); //开启任务给主机发送数据 // Set timer for param update event tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY); //开启期任务和主机交互连接参数 // Start read rssi tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD); //开启任务获取信号强度 PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval); //打印输出连接句柄和连接间隔 } }
4.蓝牙断开连接处理讲解
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent) { gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent; if(event->connectionHandle == peripheralConnList.connHandle) //查询是否是当前连接句柄 { peripheralConnList.connHandle = GAP_CONNHANDLE_INIT; //将连接句柄设置为默认值 peripheralConnList.connInterval = 0; //将连接句柄设置为0 peripheralConnList.connSlaveLatency = 0; //将连接句柄设置为0 peripheralConnList.connTimeout = 0; //将连接句柄设置为0 tmos_stop_task(Peripheral_TaskID, SBP_PERIODIC_EVT); //停止周期发送事件 tmos_stop_task(Peripheral_TaskID, SBP_READ_RSSI_EVT); //停止周期获取信号强度事件 { uint8_t advertising_enable = TRUE; GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable); //重新开启广播 } } else { PRINT("ERR..\n"); //句柄不正确 } }
5.蓝牙发送数据介绍讲解
1. if(events & SBP_PERIODIC_EVT) { if(SBP_PERIODIC_EVT_PERIOD) { tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD); //在事件执行中再次调用该事件形成周期循环 } performPeriodicTask(); //执行周期通知 return (events ^ SBP_PERIODIC_EVT); } 2. static void performPeriodicTask(void) { uint8_t notiData[SIMPLEPROFILE_CHAR4_LEN] = {0x88}; //设置发送数据 peripheralChar4Notify(notiData, SIMPLEPROFILE_CHAR4_LEN); //参入数据和长度,调用发送函数 } 3. static void peripheralChar4Notify(uint8_t *pValue, uint16_t len) { attHandleValueNoti_t noti; if(len > (peripheralMTU - 3)) //判断长度是否大于mtu { PRINT("Too large noti\n"); return; } noti.len = len; noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0); //申请内存 if(noti.pValue) { tmos_memcpy(noti.pValue, pValue, noti.len); //拷贝数据到申请的内存 if(simpleProfile_Notify(peripheralConnList.connHandle, ¬i) != SUCCESS) //判断是否发送成功,成功后底层自动释放申请的内存 { GATT_bm_free((gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI); //发送失败收到释放内存 } } } 4. bStatus_t simpleProfile_Notify(uint16_t connHandle, attHandleValueNoti_t *pNoti) { uint16_t value = GATTServApp_ReadCharCfg(connHandle, simpleProfileChar4Config); if(value & GATT_CLIENT_CFG_NOTIFY) //判断notifications是都被使能 { pNoti->handle = simpleProfileAttrTbl[SIMPLEPROFILE_CHAR4_VALUE_POS].handle; //传入notify属性表value对应的handle值 return GATT_Notification(connHandle, pNoti, FALSE); //返回发送函数的值 } return bleIncorrectMode; //notifications未使能返回失败 }
6.蓝牙接收数据介绍讲解
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len) { switch(paramID) { case SIMPLEPROFILE_CHAR1: //收到char1发送的数据 { uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN]; tmos_memcpy(newValue, pValue, len); PRINT("profile ChangeCB CHAR1.. \n"); break; } case SIMPLEPROFILE_CHAR3: //收到char3发送的数据 { uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN]; tmos_memcpy(newValue, pValue, len); PRINT("profile ChangeCB CHAR3..\n"); break; } /*可以在此处添加notify使能和关闭的case分支 1.首先需添加宏: #define CHAR4_NOTIFY_ENABLE 5 //CHAR4通知开启 #define CHAR4_NOTIFY_DISENABLE 6 //CHAR4通知关闭 2.其次写回调中case GATT_CLIENT_CHAR_CFG_UUID做如下改动: case GATT_CLIENT_CHAR_CFG_UUID:{ uint16_t charCfg = BUILD_UINT16(pValue[0], pValue[1]); status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY); notifyApp = (charCfg == GATT_CFG_NO_OPERATION) ? CHAR4_NOTIFY_DISENABLE : CHAR4_NOTIFY_ENABLE; break; }*/ case CHAR4_NOTIFY_ENABLE: //CHAR4通知开启 { PRINT("CHAR4_NOTIFY_ENABLE.. \n"); break; } case CHAR4_NOTIFY_DISENABLE: //CHAR4通知关闭 { PRINT("CHAR4_NOTIFY_DISENABLE..\n"); break; } default: // should not reach here! break; } }