一.概述
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; } }
浙公網安備 33010602011771號