概述
1、profile
Profile 可以理解为一种规范,一个标准的通信协议,profile 存在于从机之中,
蓝牙组织(SIG)规定了一系列的profile,例如 HID OVER GATT、防丢器,心率计等。每个profile会包含多个Service,每个Service代表从机的一种能力。
2、Service
Service 可以理解为一种服务,在BLE从机中,通过有多个服务,例如电量信息服务,系统信息服务等,每个Service里又包含多个Characteristic特征值。每个具体的Characteristic特征值,才是BLe通信的主体。比如当前的电量是80%,所以会通过电量的Characteristic特征值存在从机中的profile里,这样主机就可以通过这个Characteristic来读取80%这个数据。
3、Characteristic特征值
Characteristic特征值,BLE主从机的通信均是通过Characteristic来实现的,可以理解为一个标签,通过这个标签可以获取或者写入相应的内容。
4、UUID
UUID,统一识别码,上面提到的Service和Characteristic都需要一个唯一的UUID来标识。
SimpleBLECentral的读和写
用IAR打开SimpleBLECentral工程,在APP组下可以看到四个文件:
OSAL_xxxx.c 定义tasksArr[],tasksCnt,tasksEvents,以及osalInitTasks( void );
xxxx_Main.c 任务函数的初始化,启动OSAL。
xxx.c 用户任务函数的初始化和函数主体。
xxx.h 与上面是模块化编程的一对。
OSAL_xxxx.c 和 xxxx_Main.c 是OSAL系统抽象层的一部分代码留给了用户定义。
再一个问题就是要分清楚两个概念:
主机(master) | 从机(slave) |
客户端(client) | 服务器(server) |
下面来一步步分析主机SimpleBLECentral是怎么发送和接受数据的:
首先在SimpleBLECentral_Init中:
1.设置最大扫描回应:
// Setup Central Profile
{
uint8 scanRes = DEFAULT_MAX_SCAN_RES;
GAPCentralRole_SetParameter ( GAPCENTRALROLE_MAX_SCAN_RES, sizeof( uint8 ), &scanRes );
}
2,初始化 GATT Client 这个函数只提供接口,函数定义被封装好了。(看不到哦)
server端的GATT_ReadCharValue和GATT_WriteCharValue 定义于此。
// Initialize GATT Client
VOID GATT_InitClient();
3. 注册当前任务的GATT 的notify 和 indication的接收端。
当从机的notify发送过来数据后,由simpleBLETaskId标示的任务函数会收到。
// Register to receive incoming ATT Indications/Notifications
GATT_RegisterForInd( simpleBLETaskId );
4. 注册按键服务:
只有注册了按键服务的 由simpleBLETaskId标示的 任务函数才能收到按键消息。
// Register for all key events - This app will handle all key events
RegisterForKeys( simpleBLETaskId );
5. 启动事件:
告诉系统所有的初始化都已经结束了,可以开始了。通过调用osal_set_event()完成对tasksEvents[simpleBLETaskId]的赋值。
// Setup a delayed profile startup
osal_set_event( simpleBLETaskId, START_DEVICE_EVT );
其次在 SimpleBLECentral_ProcessEvent中:
6. 启动事件之后:
开启主机,并且传递了两个回调函数的地址。
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 );
return ( events ^ START_DEVICE_EVT );
}
// GAP Role Callbacks
static const gapCentralRoleCB_t simpleBLERoleCB =
{
simpleBLECentralRssiCB, // RSSI callback
simpleBLECentralEventCB // Event callback
};
还有两个配对和绑定的回调函数。
/ Bond Manager Callbacks
static const gapBondCBs_t simpleBLEBondCB =
{
simpleBLECentralPasscodeCB,
simpleBLECentralPairStateCB
};
7,系统消息处理函数:
在SYS_EVENT_MSG 下的simpleBLECentral_ProcessOSALMsg专门处理系统消息。
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 );
}
8,按键处理函数:
在KEY_CHANGE下的simpleBLECentral_HandleKeys专门处理按键信息。
static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg ){
switch ( pMsg->event ) {
case KEY_CHANGE:
simpleBLECentral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
break;
case GATT_MSG_EVENT:
simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );
break;
}
}
在simpleBLECentral_HandleKeys中:
9,按键处理中的up 按键:
(void)shift; // Intentionally unreferenced parameter
if ( keys & HAL_KEY_UP ) {
// Start or stop discovery
if ( simpleBLEState != BLE_STATE_CONNECTED ) {
if ( !simpleBLEScanning ) {
simpleBLEScanning = TRUE;
simpleBLEScanRes = 0;
LCD_WRITE_STRING( "Discovering...", HAL_LCD_LINE_1 );
LCD_WRITE_STRING( "", HAL_LCD_LINE_2 );
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST );
}
else {
GAPCentralRole_CancelDiscovery();
}
}
else if ( simpleBLEState == BLE_STATE_CONNECTED &&
simpleBLECharHdl != 0 &&
simpleBLEProcedureInProgress == FALSE )
{
uint8 status;
// Do a read or write as long as no other read or write is in progress
if ( simpleBLEDoWrite ) {
attWriteReq_t req;
req.handle = simpleBLECharHdl;
req.len = 1;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
else {
attReadReq_t req;
req.handle = simpleBLECharHdl;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
if ( status == SUCCESS ) {
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
}
}
HAL_KEY_UP ,判断是否有up按键,如果有:
如果主从机还未连接:GAPCentralRole_StartDiscovery开始扫描。
如果正在扫描:GAPCentralRole_CancelDiscovery取消扫描。
如果已经连接:先写,GATT_WriteCharValue,后读,GATT_ReadCharValue。交替进行。调用这两个函数,OSAL自动调用低层天线发送出去(这部分代码看不到)。实现如下:
<span style="font-size:10px;"> 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 );
}
在simpleBLECentral_ProcessOSALMsg中:
8,按键处理函数:
在KEY_CHANGE下的simpleBLECentral_HandleKeys专门处理按键信息。
static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg ){
switch ( pMsg->event ) {
case KEY_CHANGE:
simpleBLECentral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
break;
case GATT_MSG_EVENT:
simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );
break;
}
}
在simpleBLECentral_HandleKeys中:
9,按键处理中的up 按键:
static void simpleBLECentral_HandleKeys( uint8 shift, uint8 keys ){
(void)shift; // Intentionally unreferenced parameter
if ( keys & HAL_KEY_UP ) {
// Start or stop discovery
if ( simpleBLEState != BLE_STATE_CONNECTED ) {
if ( !simpleBLEScanning ) {
simpleBLEScanning = TRUE;
simpleBLEScanRes = 0;
LCD_WRITE_STRING( "Discovering...", HAL_LCD_LINE_1 );
LCD_WRITE_STRING( "", HAL_LCD_LINE_2 );
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST );
}
else{
GAPCentralRole_CancelDiscovery();
}
}
else if ( simpleBLEState == BLE_STATE_CONNECTED &&
simpleBLECharHdl != 0 &&
simpleBLEProcedureInProgress == FALSE )
{
uint8 status;
// Do a read or write as long as no other read or write is in progress
if ( simpleBLEDoWrite )
{
attWriteReq_t req;
req.handle = simpleBLECharHdl;
req.len = 1;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
else{
// Do a read
attReadReq_t req;
req.handle = simpleBLECharHdl;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
if ( status == SUCCESS ){
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
}
}
</span>
HAL_KEY_UP ,判断是否有up按键,如果有:
如果主从机还未连接:GAPCentralRole_StartDiscovery开始扫描。
如果正在扫描:GAPCentralRole_CancelDiscovery取消扫描。
如果已经连接:先写,GATT_WriteCharValue,后读,GATT_ReadCharValue。交替进行。调用这两个函数,OSAL自动调用低层天线发送出去(这部分代码看不到)。实现如下:
// Do a read or write as long as no other read or write is in progress
if ( simpleBLEDoWrite ) {
attWriteReq_t req;
req.handle = simpleBLECharHdl;
req.len = 1;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
else {
// Do a read
attReadReq_t req;
req.handle = simpleBLECharHdl;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
if ( status == SUCCESS ) {
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
这里注意: 读写的调用方式,要首先定义一个attWriteReq_t req;
10,读写之后的处理:
读写函数完成后,怎么才能知道是否读写成功?当向从机写一个数据(characteristic的uuid)后,从机会发给主机(RSP)一个系统消息(GATT_EVENT_MSG)具体从机调用的函数ATT_ReadByTypeRsp(),调用过程在OSAL中实现 , 所以这个成功与否的响应是在系统消息处理函数中:
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 );
}
其中的simpleBLECentral_ProcessOSALMsg
static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg ){
switch ( pMsg->event ) {
case KEY_CHANGE:
simpleBLECentral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
break;
case GATT_MSG_EVENT:
simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );
break;
}
}
其中的simpleBLECentralProcessGATTMsg
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 );
}
simpleBLEProcedureInProgress = FALSE;
}
else if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE ) {
simpleBLEGATTDiscoveryEvent( pMsg );
}
}
这样就把是否读写成功的信息实时显示在LCD上,其中char值累加的语句在上面的第44行。
SimpleBLEPeripheral的接收(读)(没有写的功能哦)
从机和主机的程序虽然总体类似,但是还是有着很大的区别的:从机包含一个profile的相关代码,这个profile决定了从机的功能。例如防丢器,血压计,血糖计,都是蓝牙组织规定的profile。
1,从机的初始化:
void SimpleBLEPeripheral_Init( uint8 task_id ) {
。。。
// Register callback with SimpleGATTprofile
VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );
。。。
// Setup a delayed profile startup
osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );
}
第6行,注册GATT消息的处理函数
第11行,设置tasksEvent[simpleBLEPeripheral_TaskID],启动BLE从机,进入任务循环函数。
2,任务循环函数:
uint16 SimpleBLEPeripheral_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( simpleBLEPeripheral_TaskID )) != NULL ) { simpleBLEPeripheral_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 & SBP_START_DEVICE_EVT ) { // Start the Device VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs ); // Start Bond Manager VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs ); // Set timer for first periodic event osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD ); return ( events ^ SBP_START_DEVICE_EVT ); } if ( events & SBP_PERIODIC_EVT ) { // Restart timer if ( SBP_PERIODIC_EVT_PERIOD ) { osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD ); } // Perform periodic application task performPeriodicTask(); return (events ^ SBP_PERIODIC_EVT); } #if defined ( PLUS_BROADCASTER ) if ( events & SBP_ADV_IN_CONNECTION_EVT ) { uint8 turnOnAdv = TRUE; // Turn on advertising while in a connection GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &turnOnAdv ); return (events ^ SBP_ADV_IN_CONNECTION_EVT); } #endif // PLUS_BROADCASTER // Discard unknown events return 0; } static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg ) { switch ( pMsg->event ) { #if defined( CC2540_MINIDK ) case KEY_CHANGE: simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys ); break; #endif // #if defined( CC2540_MINIDK ) default:
// do nothing break; } }
其中第22行:由init 函数启动的任务函数入口,启动从机设备,处理启动设备的动作。并且开启周期性的任务处理,这个周期性的任务并不是必须的,这里作为一个简单的notify示例。
3,接收任务的回调函数:
在从机中,数据接收时通过一个GATT CallBack回调函数,系统在接收到数据时会调用这个callBack向我们发出通知。
这个GATT callback函数已经在init的时候注册过了(见上面本节1步骤)。
// Simple GATT Profile Callbacks static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs = { simpleProfileChangeCB // Charactersitic value change callback };
上面的simpleProfileChangeCB 中:
static void simpleProfileChangeCB( uint8 paramID ) {
uint8 newValue;
switch( paramID ) {
case SIMPLEPROFILE_CHAR1:
SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, &newValue );
#if (defined HAL_LCD) && (HAL_LCD == TRUE)
HalLcdWriteStringValue( "Char 1:", (uint16)(newValue), 10, HAL_LCD_LINE_3 );
#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)
break;
case SIMPLEPROFILE_CHAR3:
SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR3, &newValue );
#if (defined HAL_LCD) && (HAL_LCD == TRUE)
HalLcdWriteStringValue( "Char 3:", (uint16)(newValue), 10, HAL_LCD_LINE_3 );
#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)
break;
default:
// should not reach here!
break;
}
}
如果有系统消息就调用回调函数,上面7-11行:输出char1到lcd.
最后
以上就是自信荔枝为你收集整理的OSAL数据传输(读写操作)的全部内容,希望文章能够帮你解决OSAL数据传输(读写操作)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复