CC254x 从机Peripheral工作模式

本文深入讲解了BLE蓝牙技术的核心概念,如主从机连接过程、GAP与GATT的作用及交互方式,并详细分析了SimpleBLEPeripheral_Init函数的功能,介绍了GAP和GATTServer的设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


阅读的源代码:核心主要是位于BLE/project/SimpleBLEPeripheral部分
阅读的参考文档:TI_BLE_Software_Developer's_Guide.pdf,BLE_CC2540_DeepDive_Training_2011.pdf,TI_BLE_Sample_Applications_Guide.pdf,SIG的Core_V4.0.pdf
 
在BLE的源码架构中,感觉是好复杂,还好TI对协议栈不开源,不然就得累死。能力有限只能把整个架构从最简单的主从工作模式入手。
1.BLE中主从机建立连接,到配对和绑定的过程如下图。
 


正如上图所示,最简单一次蓝牙通信需要以上相关步骤,包括discovery device,connect,pairing,bond等4个主要部分。
 
2.BLE中的GAP和GATT
初始接触,感觉十分的抽象,到现在为止,GAP个人认为就是监控上图中的交互状态,比如从广播变成连接,到配对等。
GATT通俗理解为用于主从机之间的客户端和服务器端的数据交互,以Attribute Table来体现。
GAP Role Profile:在GAP剧本里所处的4个角色:广播Advertise,主机central,从机Peripheral,观察者Observer。
GATT Attribute:通用属性配置文件。
 
3.SimpleBLEPeripheral_Init函数解析。
无论是在主机还是从机,任何的蓝牙都将这部分内容作为自己的APPlication,在init中都会完成Bluetooth的GAP,GATT,SM等相关初始化。

void SimpleBLEPeripheral_Init( uint8 task_id )  
{  
  simpleBLEPeripheral_TaskID = task_id;  
  
  // Setup the GAP Peripheral Role Profile  //GAP外设剧本  
  {  
  
    #if defined( CC2540_MINIDK )  
      // For the CC2540DK-MINI keyfob, device doesn't start advertising until button is pressed  
      uint8 initial_advertising_enable = FALSE;  
    #else  
      // For other hardware platforms, device starts advertising upon initialization  
      uint8 initial_advertising_enable = TRUE;//广播使能  
    #endif  
  
    // By setting this to zero, the device will go into the waiting state after  
    // being discoverable for 30.72 second, and will not being advertising again  
    // until the enabler is set back to TRUE  
    uint16 gapRole_AdvertOffTime = 0;  
  
    uint8 enable_update_request = DEFAULT_ENABLE_UPDATE_REQUEST;//使能请求更新  
    uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL; //最小连接间隔0.1ms  
    uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;  //最大连接间隔 1ms  
    uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;//0  
    uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;//10s,定义该参数表示在该时间段内建立的连接不成功  
  
    // Set the GAP Role Parameters  
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );  
    GAPRole_SetParameter( GAPROLE_ADVERT_OFF_TIME, sizeof( uint16 ), &gapRole_AdvertOffTime );  
  
    GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), scanRspData );//当主机扫描到广播后会发出扫描请求,从机就发回该数据到主机  
    GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );//广播参数  
  
    GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, sizeof( uint8 ), &enable_update_request );  
    GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );  
    GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );  
    GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), &desired_slave_latency );  
    GAPRole_SetParameter( GAPROLE_TIMEOUT_MULTIPLIER, sizeof( uint16 ), &desired_conn_timeout );  
  }  
  
  // Set the GAP Characteristics, Set GAP GATT Server parameter  
  GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName );//GAP GATT服务器参数设置  
  
  // Set advertising interval  
  {  
    uint16 advInt = DEFAULT_ADVERTISING_INTERVAL;  
  
    GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, advInt );  
    GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, advInt );  
    GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MIN, advInt );  
    GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MAX, advInt );  
  }  
  
  // Setup the GAP Bond Manager   //GAP 绑定管理器设置  
  {  
    uint32 passkey = 0; // passkey "000000",密钥  
    uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;//配对模式,配置成等待主机的配对请求  
    uint8 mitm = TRUE;  
    uint8 ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;  
    uint8 bonding = TRUE;  
    GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );  
    GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof ( uint8 ), &pairMode );  
    GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof ( uint8 ), &mitm );  
    GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );  
    GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof ( uint8 ), &bonding );  
  }  
  
  // Initialize GATT attributes  
  GGS_AddService( GATT_ALL_SERVICES );            // GAP  
  GATTServApp_AddService( GATT_ALL_SERVICES );    // GATT attributes  
  DevInfo_AddService();                           // Device Information Service  
  SimpleProfile_AddService( GATT_ALL_SERVICES );  // Simple GATT Profile  
#if defined FEATURE_OAD  
  VOID OADTarget_AddService();                    // OAD Profile  
#endif  
  
  // Setup the SimpleProfile Characteristic Values,对相关数值做初始化  
  {  
    uint8 charValue1 = 1;  
    uint8 charValue2 = 2;  
    uint8 charValue3 = 3;  
    uint8 charValue4 = 4;  
    uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };  
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );  
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );  
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );  
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );  
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );  
  }  
  
  
#if defined( CC2540_MINIDK )  
  
  SK_AddService( GATT_ALL_SERVICES ); // Simple Keys Profile  
  
  // Register for all key events - This app will handle all key events  
  RegisterForKeys( simpleBLEPeripheral_TaskID );//注册按键,在这里当按键按下时,该任务会收到一个事件发来的消息  
  
  // makes sure LEDs are off  
  HalLedSet( (HAL_LED_1 | HAL_LED_2), HAL_LED_MODE_OFF );  
  
  // For keyfob board set GPIO pins into a power-optimized state  
  // Note that there is still some leakage current from the buzzer,  
  // accelerometer, LEDs, and buttons on the PCB.  
  
  P0SEL = 0; // Configure Port 0 as GPIO  
  P1SEL = 0; // Configure Port 1 as GPIO  
  P2SEL = 0; // Configure Port 2 as GPIO  
  
  P0DIR = 0xFC; // Port 0 pins P0.0 and P0.1 as input (buttons),  
                // all others (P0.2-P0.7) as output  
  P1DIR = 0xFF; // All port 1 pins (P1.0-P1.7) as output  
  P2DIR = 0x1F; // All port 1 pins (P2.0-P2.4) as output  
  
  P0 = 0x03; // All pins on port 0 to low except for P0.0 and P0.1 (buttons)  
  P1 = 0;   // All pins on port 1 to low  
  P2 = 0;   // All pins on port 2 to low  
  
#endif // #if defined( CC2540_MINIDK )  
  
#if (defined HAL_LCD) && (HAL_LCD == TRUE)  
  
#if defined FEATURE_OAD  
  #if defined (HAL_IMAGE_A)  
    HalLcdWriteStringValue( "BLE Peri-A", OAD_VER_NUM( _imgHdr.ver ), 16, HAL_LCD_LINE_1 );  
  #else  
    HalLcdWriteStringValue( "BLE Peri-B", OAD_VER_NUM( _imgHdr.ver ), 16, HAL_LCD_LINE_1 );  
  #endif // HAL_IMAGE_A  
#else  
  HalLcdWriteString( "BLE Peripheral", HAL_LCD_LINE_1 );  
#endif // FEATURE_OAD  
  
#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
  
  // Register callback with SimpleGATTprofile  
  VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );//给应用注册回调函数  
  
  // Enable clock divide on halt  
  // This reduces active current while radio is active and CC254x MCU  
  // is halted  
  HCI_EXT_ClkDivOnHaltCmd( HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT );  
  
#if defined ( DC_DC_P0_7 )  
  
  // Enable stack to toggle bypass control on TPS62730 (DC/DC converter)  
  HCI_EXT_MapPmIoPortCmd( HCI_EXT_PM_IO_PORT_P0, HCI_EXT_PM_IO_PORT_PIN7 );  
  
#endif // defined ( DC_DC_P0_7 )  
  
  // Setup a delayed profile startup  
  osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );//标志SBP_START_DEVICE_EVT启动该event  


 
4.GAP部分的初始化和相关API以及,事件的处理过程。
在上3中的init中可以看到GAP作为Peripheral Role需要设置的核心参数如下
GAPROLE_ADVERT_ENABLED:广播使能。
GAPROLE_ADVERT_DATA:广播时的参数,
GAPROLE_SCAN_RSP_DATA:从机扫描响应,返回的数据包
GAPROLE_MIN_CONN_INTERVAL:处于连接状态后的设备,都会有个hop,一段时间内进行数据交互,以保证两者是连接的。当前后两次交互时,需要等待的最小间隔时间
GAPROLE_MAX_CONN_INTERVAL:...需要等待的最大间隔时间
GAPROLE_SLAVE_LATENCY:处于连接后,从机可以做出不响应连接请求的间隔数目,即跳过n个交互的连接。
GAPROLE_TIMEOUT_MULTIPLIER:从上次成功连接到这次连接成功的最大允许延时。如果规定时间内未成功则认为本次连接失败,丢弃。该值必须比有效连接的间隔大。
GAPROLE_PARAM_UPDATE_ENABLE:请求主机更新参数,主机可以接受也可以拒绝。
 
GAP通过在启动设备事件的任务处理中启动设备,其实主要是向GAP中注册回调函数,让系统在发现自身运行状态变化时,调用该函数,方便应用层进行相关操作。
VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs );//启动设备,注册回调函数,用于监督设备的状态变化:广播、连接、配对、绑定等。
static void peripheralStateNotificationCB( gaprole_States_t newState )//传入参数由GPA自己输入,内部调用回调函数给用户  
  switch ( newState )  
  {  
    case GAPROLE_STARTED://在GAPRole_StartDevice后发生状态变化  
      {  
        uint8 ownAddress[B_ADDR_LEN];  
        uint8 systemId[DEVINFO_SYSTEM_ID_LEN];  
  
        GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);  
  
        // use 6 bytes of device address for 8 bytes of system ID value  
        systemId[0] = ownAddress[0];  
        systemId[1] = ownAddress[1];  
        systemId[2] = ownAddress[2];  
  
        // set middle bytes to zero  
        systemId[4] = 0x00;  
        systemId[3] = 0x00;  
  
        // shift three bytes up  
        systemId[7] = ownAddress[5];  
        systemId[6] = ownAddress[4];  
        systemId[5] = ownAddress[3];  
  
        DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);  
  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          // Display device address  
          HalLcdWriteString( bdAddr2Str( ownAddress ),  HAL_LCD_LINE_2 );  
          HalLcdWriteString( "Initialized",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    case GAPROLE_ADVERTISING:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "Advertising",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    case GAPROLE_CONNECTED:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "Connected",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    case GAPROLE_WAITING:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "Disconnected",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    case GAPROLE_WAITING_AFTER_TIMEOUT:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "Timed Out",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    case GAPROLE_ERROR:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "Error",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
    default:  
      {  
        #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
          HalLcdWriteString( "",  HAL_LCD_LINE_3 );  
        #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
      }  
      break;  
  
  }  



可以看到GAP Role 作为外设从机时,各种状态的变化,而这些状态 变化都由GAP(不开源部分)调用回调函数,将当前状态参数传入,以使得不同的APP做出反应。体现了一种回调函数设计的便捷性。


5.GATT Server的相关设置函数。
  // Initialize GATT attributes
  GGS_AddService( GATT_ALL_SERVICES );            // GAP Service
  GATTServApp_AddService( GATT_ALL_SERVICES );    // GATT attributes
  DevInfo_AddService();                           // Device Information Service
  SimpleProfile_AddService( GATT_ALL_SERVICES );  // Simple GATT Profile
通常一个GATT中GAP server和GATT server是必须强制存在的(Mandatory)以及自己设计的profile server.
作为GATT的server和client,主要通过Attribute来进行交互,当client请求server读取数据时,通过如下注册的回调函数来进行访问。
  // Register callback with SimpleGATTprofile
  VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );//给应用注册回调函数
在回调函数中对时间做出处理。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值