一、背景
链路层(LL)控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。
广播 为广播数据包,而 扫描 则是监听广播。
GAP通信中角色,中心设备(Central - 主机)用来扫描和连接 外围设备(Peripheral - 从机)。
大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。
也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。
在使用SDK2.4 multi_role工程时,广播和扫描同时开启一段时间后,扫描会停止。
TI回复:
二、流程
- 初始化广播和扫描参数
- 开启广播0.1秒
- 关闭广播
- 开启扫描1秒
- 关闭扫描
- 再次开启广播
三、配置参数
3.1 配置广播参数
3.1.1 广播参数相关宏
// Advertising interval when device is discoverable (units of 625us, 160=100ms)
#define DEFAULT_ADVERTISING_INTERVAL 160
// Limited discoverable mode advertises for 30.72s, and then stops
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
3.1.2 广播相关变量
// 扫描响应包
static uint8_t scanRspData[] =
{
// complete name
0x05, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'B', 'A', 'N', 'D',
// Tx power level
0x02, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
// 广播数据包
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8_t advertData[] =
{
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// service UUID, to notify central devices what services are included
// in this peripheral
0x03, // length of this data
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
LO_UINT16(SIMPLEPROFILE_SERV_UUID),
HI_UINT16(SIMPLEPROFILE_SERV_UUID)
};
3.1.3 配置GAP参数值
以SDK2.4 multi_role工程为例,在 multi_role_init() 初始化多角色应用程序函数中,
/*===================================== 从机 =====================================*/
/*------------------- 广播参数 -------------------*/
uint16_t 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);
GAP_SetParamValue(TGAP_CONN_ADV_INT_MIN, advInt);
GAP_SetParamValue(TGAP_CONN_ADV_INT_MAX, advInt);
3.1.4 配置GAP角色规范(Role Profile)
以SDK2.4 multi_role工程为例,在 multi_role_init() 初始化多角色应用程序函数中,
/*===================================== 从机 =====================================*/
/*------------------- 广播参数 -------------------*/
uint8_t initialAdvertEnable = TRUE; // 是否开机广播
uint16_t advertOffTime = 0;
// 设置开机广播
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initialAdvertEnable, NULL);
// 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
GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t), &advertOffTime, NULL);
// 设置扫描响应包内容
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData, NULL);
// 设置广播包内容
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData, NULL);
3.2 配置扫描参数
3.2.1 扫描参数相关宏
// Enable/Disable Unlimited Scanning Feature
#define ENABLE_UNLIMITED_SCAN_RES FALSE
// Maximum number of scan responses
// this can only be set to 15 because that is the maximum
// amount of item actions the menu module supports
#define DEFAULT_MAX_SCAN_RES 15
#define DEFAULT_SCAN_DURATION 4000 // 值越小,则发现的同一设备广播包越多
#define DEFAULT_SCAN_WIND 80
#define DEFAULT_SCAN_INT 80
3.2.2 配置GAP参数值
以SDK2.4 multi_role工程为例,在 multi_role_init() 初始化多角色应用程序函数中,
/*===================================== 主机 =====================================*/
/*------------------- 扫描参数 -------------------*/
// 扫描处理周期,周期越短,处理次数越多
GAP_SetParamValue(TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION);
// 扫描间隔
GAP_SetParamValue(TGAP_CONN_SCAN_INT, DEFAULT_SCAN_INT);
GAP_SetParamValue(TGAP_CONN_SCAN_WIND, DEFAULT_SCAN_WIND);
GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_INT, DEFAULT_SCAN_INT);
GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_WIND, DEFAULT_SCAN_WIND);
GAP_SetParamValue(TGAP_GEN_DISC_SCAN_INT, DEFAULT_SCAN_INT);
GAP_SetParamValue(TGAP_GEN_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
GAP_SetParamValue(TGAP_LIM_DISC_SCAN_INT, DEFAULT_SCAN_INT);
GAP_SetParamValue(TGAP_LIM_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
GAP_SetParamValue(TGAP_CONN_EST_SCAN_INT, DEFAULT_SCAN_INT);
GAP_SetParamValue(TGAP_CONN_EST_SCAN_WIND, DEFAULT_SCAN_WIND);
3.2.3 配置GAP角色规范(Role Profile)
以SDK2.4 multi_role工程为例,在 multi_role_init() 初始化多角色应用程序函数中,
/*===================================== 主机 =====================================*/
/*------------------- 扫描参数 -------------------*/
// 设置扫描回应设备数
uint8_t scanRes = 0;
// In case that the Unlimited Scanning feature is disabled
// send the number of scan results to the GAP
if(ENABLE_UNLIMITED_SCAN_RES == FALSE)
{
scanRes = DEFAULT_MAX_SCAN_RES; // 最大扫描回应设备数,如果广播的从机超过了15个,只能扫描到先回应的从机
}
GAPRole_SetParameter(GAPROLE_MAX_SCAN_RES, sizeof(uint8_t), &scanRes, NULL);
四、执行函数
4.1 执行广播函数
原mr_doAdvertise函数修改后
/**
@brief 执行广播函数
@param index 1 - 开启广播;0 - 关闭广播
@return TRUE - 成功;FALSE - 失败
*/
bool mr_doAdvertise(uint8_t index)
{
uint8_t adv;
if(!index) // 关闭广播
{
adv = FALSE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &adv, NULL);
}
else // 开启广播
{
adv = TRUE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &adv, NULL);
}
return TRUE;
}
4.2 执行扫描函数
正在扫描标志:
static bool scanningStarted = FALSE;
禁止扫描标志:
bool g_disableScanFlag;
原mr_doScan函数修改后
/**
@brief 执行扫描函数
@param index 1 - 开启扫描;0 - 取消扫描
@return TRUE - 成功;FALSE - 失败
*/
bool mr_doScan(uint8_t index)
{
if(index) // 执行扫描
{
if(linkDB_NumActive() < maxNumBleConns) // 如果连接设备数未饱和
{
if((!scanningStarted) && (g_disableScanFlag == FALSE)) // 不在扫描中或者未禁止扫描
{
scanningStarted = TRUE; // 开始扫描标志置一
GAPRole_StartDiscovery(DEFAULT_DISCOVERY_MODE, // 开始扫描
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
return TRUE;
}
else // 正在扫描中
{
return FALSE;
}
}
else // 连接设备数饱和
{
return FALSE;
}
}
else // 取消扫描
{
GAPRole_CancelDiscovery();
return TRUE;
}
}
五、周期事件
5.1 定义周期事件
以multi_role工程为例,在multi_role.c的CONSTANTS常量定义中,加入CUSTOM_TIMER_EVT
,id号递增。
// Internal Events for RTOS application
#define MR_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31
#define MR_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30
#define MR_STATE_CHANGE_EVT Event_Id_00
#define MR_CHAR_CHANGE_EVT Event_Id_01
#define MR_CONN_EVT_END_EVT Event_Id_02
#define MR_KEY_CHANGE_EVT Event_Id_03
#define MR_PAIRING_STATE_EVT Event_Id_04
#define MR_PASSCODE_NEEDED_EVT Event_Id_05
#define MR_PERIODIC_EVT Event_Id_06
#define TIMER_ENABLE_ADV_EVT Event_Id_07 // 开启广播关闭扫描定时器事件
#define TIMER_ENABLE_SCAN_EVT Event_Id_08 // 开启扫描关闭广播定时器事件
在MR_ALL_EVENTS事件集合定义中,加入刚刚的自定义周期事件。
#define MR_ALL_EVENTS (MR_ICALL_EVT | \
MR_QUEUE_EVT | \
MR_STATE_CHANGE_EVT | \
MR_CHAR_CHANGE_EVT | \
MR_CONN_EVT_END_EVT | \
MR_KEY_CHANGE_EVT | \
MR_PAIRING_STATE_EVT | \
MR_PERIODIC_EVT | \
MR_PASSCODE_NEEDED_EVT | \
TIMER_ENABLE_ADV_EVT | \
TIMER_ENABLE_SCAN_EVT)
5.2 添加周期事件的处理
在multi_role.c的multi_role_taskFxn函数中尾部加入。
/*----------------- 开启广播定时器事件 ------------------*/
if(events & TIMER_ENABLE_ADV_EVT)
{
Timer_EnableAdvCB(); // 开启广播定时器处理函数
}
/*----------------- 开启扫描定时器事件 ------------------*/
if(events & TIMER_ENABLE_SCAN_EVT)
{
Timer_EnableScanCB(); // 开启扫描定时器处理函数
}
5.3 周期事件处理函数
5.3.1 定义开启广播定时器处理函数
以multi_role工程为例,在multi_role.c尾部添加
static void Timer_EnableAdvCB(void)
{
g_disableScanFlag = TRUE; // 禁止扫描标志置一
mr_doAdvertise(1); // 开启广播
Util_startClock(&g_enableScanClock); // 重启开启扫描定时器
}
5.3.2 定义开启扫描定时器处理函数
以multi_role工程为例,在multi_role.c尾部添加
static void Timer_EnableScanCB(void)
{
g_disableScanFlag = FALSE; // 禁止扫描标志清空
mr_doAdvertise(0); // 关闭广播
mr_doScan(1); // 开启扫描
Util_startClock(&g_enableAdvClock); // 重启开启广播定时器
}
5.3.3 声明周期事件处理函数
在multi_role.c的LOCAL FUNCTIONS局部函数中加入
static void Timer_EnableAdvCB(void);
static void Timer_EnableScanCB(void);
5.4 定时器
5.4.1 定义定时器
Clock_Struct g_enableAdvClock;
Clock_Struct g_enableScanClock;
5.4.2 配置定时器时间
#define TIMER_ENABLE_ADV_EVT_PERIOD 1000 // 1000ms
#define TIMER_ENABLE_SCAN_EVT_PERIOD 100 // 100ms
5.4.3 初始化定时器
以multi_role工程为例,在multi_role.c的multi_role_init函数中尾部加入
// 开启广播定时器初始化
Util_constructClock(&g_enableAdvClock, multi_role_clockHandler,
TIMER_ENABLE_ADV_EVT_PERIOD, 0, false, TIMER_ENABLE_ADV_EVT);
Util_constructClock(&g_enableScanClock, multi_role_clockHandler,
TIMER_ENABLE_SCAN_EVT_PERIOD, 0, false, TIMER_ENABLE_SCAN_EVT);
5.5 触发周期事件函数
以multi_role工程为例,在multi_role.c中已经有了multi_role_clockHandler,当定时器到达时间时,会产生一个事件,进入上文的周期事件处理函数。其他工程也有类似的名字。
/*********************************************************************
* @fn multi_role_clockHandler
*
* @brief Handler function for clock timeouts.
*
* @param arg - event type
*/
static void multi_role_clockHandler(UArg arg)
{
// Wake up the application.
Event_post(syncEvent, arg);
}
六、初始化
在multi_role.c的multi_role_processRoleEvent函数中,GAP_DEVICE_INIT_DONE_EVENT事件
// GAPRole started
case GAP_DEVICE_INIT_DONE_EVENT:
{
// Store max pdu size
maxPduSize = pEvent->initDone.dataPktLen;
mr_doScan(1); // 开启扫描
Util_startClock(&g_enableScanClock); // 开启扫描定时器
}
break;
• 由 Leung 写于 2019 年 5 月 30 日
网友评论