當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > BLE 數(shù)據(jù)接發(fā)流程
BLE概述
BLE分為兩部分:控制器和主機。對于4.0以前的藍(lán)牙,這兩部分是分開的。所有profile(用來定義設(shè)備或組件的角色)和應(yīng)用都建構(gòu)在GAP或GATT之上。下面由結(jié)構(gòu)圖的底層組件開始介紹。
協(xié)議梭的實現(xiàn)方式采用分層的思想,控制器部分包括物理層、鏈路層、主機控制接口層主機部分包括邏輯鏈路控制及自適應(yīng)協(xié)議層、安全管理層、屬性協(xié)議層、通用訪問配置文件層、通用屬性配置文件層;上層可以調(diào)用下層提供的函數(shù)來實現(xiàn)需要的功能。
PHY層:1Mbps自適應(yīng)跳頻GFSK(高斯頻移鍵控),運行在免證的2.4GHz。
LL層為RF控制器(射頻),控制設(shè)備處于準(zhǔn)備(standby)、廣播、監(jiān)聽/掃描(scan)、初始化、連接,這五種狀態(tài)中任一種。
HCI層:為接口層,向上為主機提供軟件應(yīng)用程序接口(API),對外為外部硬件控制接口,可以通過串口、SPI、USB來實現(xiàn)設(shè)備控制。
L2CAP層:提供數(shù)據(jù)封裝服務(wù)
SM層(加密);提供配對和密匙分發(fā),實現(xiàn)安全連接和數(shù)據(jù)交換
ATT層:負(fù)責(zé)數(shù)據(jù)檢索
GATT層:出納負(fù)責(zé)處理向上與應(yīng)用打交道,而GATT負(fù)責(zé)向下把檢索任務(wù)子進程交給ATT層去做,其關(guān)鍵工作是把為檢索工作提供合適的profile結(jié)構(gòu),而profile由檢索關(guān)鍵詞(characteristics)組成。
GAP層:向上提供應(yīng)用程序接口,向下管理各層的相應(yīng)的功能,尤其是指示LL層的五種狀態(tài)切換,指導(dǎo)SM層做好加密工作。
BLE 啟動流程
在IAR 工程的左側(cè)有很多文件夾,如APP 、HAL、OSAL、 PROFILES等,如圖下圖所示, 這些文件夾下面包含了很多源代碼,這種實現(xiàn)方式與藍(lán)牙4.0 BLE 協(xié)議的分層思想是相對應(yīng)的,盡量將實現(xiàn)某些功能的函數(shù)放在同一個文件夾下。
圖 BLE 4.0 工程列表
下面從main()函數(shù)入手,看看main函數(shù)都做啦哪些工作。下找到simpleBLECentral_Main.c文件中的main函數(shù)入口。代碼如下:
int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT();
// Initialize board I/O
InitBoard( OB_COLD );
/* Initialze the HAL driver */
HalDriverInit();
/* Initialize NV system */
osal_snv_init();
/* Initialize LL */
/* Initialize the operating system */
osal_init_system();
/* Enable interrupts */
HAL_ENABLE_INTERRUPTS();
// Final board initialization
InitBoard( OB_READY );
#if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY );
#endif
/* Start OSAL */
osal_start_system(); // No Return from here
return 0;
}
在main函數(shù)中大部分是對CC2540的初始化和對無線網(wǎng)絡(luò)協(xié)議的配置。主要的就是osal_start_system()函數(shù)的處理,這是重點,只有執(zhí)行這個函數(shù),藍(lán)牙BLE的協(xié)議棧才是真正的運行。
為下函數(shù)為任務(wù)啟動過程。實現(xiàn)不停的查看事件表,如果有事件發(fā)生就調(diào)用相應(yīng)的事件處理函數(shù)。
void osal_run_system( void )
{
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif
Hal_ProcessPoll();
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
事件處理函數(shù)
如果有事件調(diào)用SimpleBLEPeripheral_ProcessEvent()函數(shù)處理相應(yīng)的消息事件。在simpleBLECentral.c文件中。
uint16 SimpleBLECentral_ProcessEvent( uint8 task_id, uint16 events )
{
VOID task_id; // OSAL required parameter that isn't used in this function
if ( events & SYS_EVENT_MSG )
{
uint8 *pMsg;
if ( (pMsg = osal_msg_receive( simpleBLETaskId )) != NULL )
{
simpleBLECentral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );
// Release the OSAL message
VOID osal_msg_deallocate( pMsg );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if ( events & START_DEVICE_EVT )
{
// Start the Device
VOID GAPCentralRole_StartDevice( (gapCentralRoleCB_t *) &simpleBLERoleCB );
// Register with bond manager after starting device
GAPBondMgr_Register( (gapBondCBs_t *) &simpleBLEBondCB );
//自動開始搜索
if ( !simpleBLEScanning & simpleBLEScanRes == 0 )
{
simpleBLEScanning = TRUE;
simpleBLEScanRes = 0;
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST );
LCD_WRITE_STRING( "Scanning...", HAL_LCD_LINE_1 );
}
else
{
LCD_WRITE_STRING( "No Scan", HAL_LCD_LINE_1 );
}
return ( events ^ START_DEVICE_EVT );
}
if ( events & START_DISCOVERY_EVT )
{
simpleBLECentralStartDiscovery( );
return ( events ^ START_DISCOVERY_EVT );
}
// Discard unknown events
return 0;
}
接收數(shù)據(jù)
本函數(shù)主要是處理無線的數(shù)據(jù),如把接收到的數(shù)據(jù)打印到串口終端。打印函數(shù)如以下代碼中的紅色字體部分。注意紅色部分。
static void simpleBLECentralProcessGATTMsg( gattMsgEvent_t *pMsg )
{
if ( simpleBLEState != BLE_STATE_CONNECTED )
{
// In case a GATT message came after a connection has dropped,
// ignore the message
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 status = pMsg->msg.errorRsp.errCode;
LCD_WRITE_STRING_VALUE( "Read Error", status, 10, HAL_LCD_LINE_1 );
}
else
{
// After a successful read, display the read value
uint8 valueRead = pMsg->msg.readRsp.value[0];
LCD_WRITE_STRING_VALUE( "Read rsp:", valueRead, 10, HAL_LCD_LINE_1 );
}
simpleBLEProcedureInProgress = FALSE;
}
else if ( ( pMsg->method == ATT_WRITE_RSP ) ||
( ( pMsg->method == ATT_ERROR_RSP ) &&
( pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ ) ) )
{
if ( pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP )
{
uint8 status = pMsg->msg.errorRsp.errCode;
LCD_WRITE_STRING_VALUE( "Write Error", status, 10, HAL_LCD_LINE_1 );
}
else
{
// After a succesful write, display the value that was written and increment value
//LCD_WRITE_STRING_VALUE( "Write sent:", simpleBLECharVal++, 10, HAL_LCD_LINE_1 );
simpleBLEChar6DoWrite = TRUE;
}
simpleBLEProcedureInProgress = FALSE;
}
else if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE )
{
simpleBLEGATTDiscoveryEvent( pMsg );
}
else if ( ( pMsg->method == ATT_HANDLE_VALUE_NOTI ) ) //通知
{
if( pMsg->msg.handleValueNoti.handle == 0x0038) //CHAR7的通知 串口打印
{
if(pMsg->msg.handleValueNoti.value[0]>=15)
{
NPI_WriteTransport(&pMsg->msg.handleValueNoti.value[1],14 );
NPI_WriteTransport("...\n",4 );
}
else
{
NPI_WriteTransport(&pMsg->msg.handleValueNoti.value[1],pMsg->msg.handleValueNoti.value[0] );
}
}
}
}
發(fā)送數(shù)據(jù)
從串口讀取數(shù)據(jù)存入buf[]緩沖區(qū),然后調(diào)用協(xié)議棧函數(shù)GATT_WriteCharValue()發(fā)送出去。
NpiSerialCallback()函數(shù)為串口回調(diào)函數(shù),當(dāng)串口有數(shù)據(jù)時就會調(diào)用此函數(shù),并且同時會產(chǎn)生串口事件HAL_UART_RX_TIMEOUT。
static void NpiSerialCallback( uint8 port, uint8 events )
{
(void)port;
uint8 numBytes = 0;
uint8 buf[128];
if (events & HAL_UART_RX_TIMEOUT) //串口有數(shù)據(jù)
{
numBytes = NPI_RxBufLen(); //讀出串口緩沖區(qū)有多少字節(jié)
if(numBytes)
{
if ( ( simpleBLEState == BLE_STATE_CONNECTED ) && ( simpleBLECharHd6 != 0 ) ) //已連接并獲取完CHAR6的Handle就寫CHAR6
{
if(simpleBLEChar6DoWrite) //寫入成功后再寫入
{
attWriteReq_t AttReq;
if ( numBytes >= SIMPLEPROFILE_CHAR6_LEN ) buf[0] = SIMPLEPROFILE_CHAR6_LEN-1;
else buf[0] = numBytes;
NPI_ReadTransport(&buf[1],buf[0]); //從串口讀出數(shù)據(jù)
AttReq.handle = simpleBLECharHd6;
ttReq.len = SIMPLEPROFILE_CHAR6_LEN;
AttReq.sig = 0;
AttReq.cmd = 0;
osal_memcpy(AttReq.value,buf,SIMPLEPROFILE_CHAR6_LEN);
GATT_WriteCharValue( 0, &AttReq, simpleBLETaskId );
simpleBLEChar6DoWrite = FALSE;
}
}
else
{
NPI_WriteTransport("Not Ready\n", 10 );
NPI_ReadTransport(buf,numBytes); //釋放串口數(shù)據(jù)
}
}
}
}
BLE Central串口通信協(xié)議
在上位機上顯示終端的實時信息,必須有相應(yīng)的傳輸協(xié)議,協(xié)議里含有終端的相應(yīng)信息。
制定協(xié)議如下:
( 1 ) 串口打印數(shù)據(jù)協(xié)議信息:
21 B 01 00 54 00 00 01 3E 14 EB
21 協(xié)議頭’!’ B:BLE 01 00:模塊源節(jié)點地址 54:類型 00 00 01:數(shù)據(jù)/設(shè)備狀態(tài) 3E 14:父節(jié)點地址 EB:校驗
如果第2數(shù)據(jù)類型為’B’,則代表所連接的設(shè)備是BLE。
( 2 ) 協(xié)調(diào)節(jié)點串口發(fā)送的信息:
# C B f 00 01 01
“# C”控制終端的協(xié)議頭 B:BLE 00 01:終端節(jié)點地址 01 控制命令(1:開/0:關(guān))