OSAL数据传输(读写操作)

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 );
  }
这两个函数有顺序要求,会被OSAL自动调用。
// 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专门处理系统消息。

bubuko.com,布布扣
 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专门处理按键信息。

bubuko.com,布布扣
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   {
        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.







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值