您的位置:首页 > 其它

CC2541 BLE源码阅读知识积累之外设从机Peripheral工作模式

2013-07-23 02:52 411 查看
阅读的源代码:核心主要是位于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 );//给应用注册回调函数

在回调函数中对时间做出处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: