1.1、蓝牙的广播在蓝牙开发中占有重要地位,广播频率直接影响到功耗问题和连接快慢,传输数据的快慢问题。现在来看一下广播的初始化。
下面那是源码 :
static void advertising_init(void)
{
uint32_t err_code;
ble_advdata_t advdata;
ble_adv_modes_config_t options;
// Build advertising data struct to pass into @ref ble_advertising_init.
memset(&advdata, 0, sizeof(advdata));
advdata.name_type = BLE_ADVDATA_FULL_NAME;//广播名
advdata.include_appearance = true;
advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;//蓝牙设备模式
advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
advdata.uuids_complete.p_uuids = m_adv_uuids;
memset(&options, 0, sizeof(options));
options.ble_adv_fast_enabled = true;//广播 类型 :快速广播使能
options.ble_adv_fast_interval = APP_ADV_INTERVAL;//广播 间隔
options.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS;//广播超时时间
/************************************自己增加的*************************************************/
options.ble_adv_slow_enabled = true;//广播 类型 :慢速广播使能
options.ble_adv_slow_interval = 400;//广播 间隔
options.ble_adv_slow_timeout = APP_ADV_TIMEOUT_IN_SECONDS;//广播 超时时间
/***************************************************************************************************/
err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);//广播参数初始化
APP_ERROR_CHECK(err_code);
}
广播初始化实际上就是给两个参数 :advdata和options,赋值并初始化。可以下这两个类型:
(1)、 ble_advdata_t advdata
typedef struct
{
ble_advdata_name_type_t name_type; /**< Type of device name. */
uint8_t short_name_len; /**< Length of short device name (if short type is specified). */
bool include_appearance; /**< Determines if Appearance shall be included. */
uint8_t flags; /**< Advertising data Flags field. */
int8_t * p_tx_power_level; /**< TX Power Level field. */
ble_advdata_uuid_list_t uuids_more_available; /**< List of UUIDs in the 'More Available' list. */
ble_advdata_uuid_list_t uuids_complete; /**< List of UUIDs in the 'Complete' list. */
ble_advdata_uuid_list_t uuids_solicited; /**< List of solicited UUIDs. */
ble_advdata_conn_int_t * p_slave_conn_int; /**< Slave Connection Interval Range. */
ble_advdata_manuf_data_t * p_manuf_specific_data; /**< Manufacturer specific data. */
ble_advdata_service_data_t * p_service_data_array; /**< Array of Service data structures. */
uint8_t service_data_count; /**< Number of Service data structures. */
bool include_ble_device_addr; /**< Determines if LE Bluetooth Device Address shall be included. */
ble_advdata_le_role_t le_role; /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */
ble_advdata_tk_value_t * p_tk_value; /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
ble_gap_lesc_oob_data_t * p_lesc_data; /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
} ble_advdata_t;
我们可以看出,这个结构体定义了和广播相关的类型,比如:名称,名称长度,设备模式,发射功率(参数:p_tx_power_level)等,可以根据不同的需求,进行更改。先开一下模式参数: uint8_t flags赋值:
/**@defgroup BLE_GAP_ADV_FLAGS GAP Advertisement Flags
* @{ */
#define BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE (0x01) /**< LE Limited Discoverable Mode. */
#define BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE (0x02) /**< LE General Discoverable Mode. */
#define BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED (0x04) /**< BR/EDR not supported. */
#define BLE_GAP_ADV_FLAG_LE_BR_EDR_CONTROLLER (0x08) /**< Simultaneous LE and BR/EDR, Controller. */
#define BLE_GAP_ADV_FLAG_LE_BR_EDR_HOST (0x10) /**< Simultaneous LE and BR/EDR, Host. */
#define BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE (BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE Limited Discoverable Mode, BR/EDR not supported. */
#define BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE (BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE General Discoverable Mode, BR/EDR not supported. */
/**@} */
蓝牙模式有两种:LE模式和BR/EDR模式,简单解释一下:
LE:Bluetooth smart模式,就是低功耗模式
BR/EDR:蓝牙基本速率和增强数据率,就是可以传输大数据的模式,一般在传输音频,视频等大数据才会有的,NRF51822是没有这个功能。
1.2、再看一下参数: ble_adv_modes_config_t options;
typedef struct
{
bool ble_adv_whitelist_enabled; /**< Enable or disable use of the whitelist. */
bool ble_adv_directed_enabled; /**< Enable or disable direct advertising mode. */
bool ble_adv_directed_slow_enabled; /**< Enable or disable direct advertising mode. */
bool ble_adv_fast_enabled; /**< Enable or disable fast advertising mode. */
bool ble_adv_slow_enabled; /**< Enable or disable slow advertising mode. */
uint32_t ble_adv_directed_slow_interval; /**< Advertising interval for directed advertising. */
uint32_t ble_adv_directed_slow_timeout; /**< Time-out (number of tries) for direct advertising. */
uint32_t ble_adv_fast_interval; /**< Advertising interval for fast advertising. */
uint32_t ble_adv_fast_timeout; /**< Time-out (in seconds) for fast advertising. */
uint32_t ble_adv_slow_interval; /**< Advertising interval for slow advertising. */
uint32_t ble_adv_slow_timeout; /**< Time-out (in seconds) for slow advertising. */
} ble_adv_modes_config_t;
这个参数 主要是设置广播模式,广播的模式 主要有:Direct模式(直连模式)、Fast模式,Slow模式,IDLE模式(停止模式 )以及广播模式的使能,广播时间,超时时间。
超时时间要说明一下,这个时间定义是:以我们设置的广播速率开始广播,到达超时时间的时候,还没有蓝牙连接 ,就会发生超时事件。这个 超时事件处理就是切换模式,一直切换到 最后的IDLE模式里面 ,进入休眠 。如果超时时间设置成 0,就会 不发生超时时间,以当前的模式广播。
2.1、初始化完成之后,就是在协议栈派发函数里面调用广播回调函数 :
void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("-----BLE_GAP_EVT_CONNECTED--\r\n");
on_connected(p_ble_evt);
break;
// Upon disconnection, whitelist will be activated and direct advertising is started.
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("----BLE_GAP_EVT_DISCONNECTEDt--\r\n");
on_disconnected(p_ble_evt);
break;
// Upon time-out, the next advertising mode is started.
case BLE_GAP_EVT_TIMEOUT:
NRF_LOG_INFO("-----advertising Timeout---\r\n");
on_timeout(p_ble_evt);
break;
default:
break;
}
}
这里重点看一下超时函数: on_timeout(p_ble_evt);
static void on_timeout(ble_evt_t const * p_ble_evt)
{
ret_code_t ret;
if (p_ble_evt->evt.gap_evt.params.timeout.src != BLE_GAP_TIMEOUT_SRC_ADVERTISING)
{
// Nothing to do.
return;
}
// Start advertising in the next mode.
ret = ble_advertising_start(adv_mode_next_get(m_adv_mode_current));
if ((ret != NRF_SUCCESS) && (m_error_handler != NULL))
{
m_error_handler(ret);
}
}
这里可以看出 ,当我们设置的广播超时时间溢出的时候,会调用on_timeout()函数,在这个函数里面,主要调用adv_mode_next_get()函数,这个 函数就是寻找下一个使能的广播模式,也就是说,当我们开启了 快速广播,快速广播超时以后,会寻找慢速广播,如果慢速广播使能,就进入慢速广播模式,如果没有开启,就进入IDLE里面,停止广播。这里就和前面我们设置的参数 ble_adv_modes_config_t options有关,在我的程序里面,自己添加了慢速广播模式使能,也设置了慢速广播速率和超时时间,本人感觉,这个慢速广播模式,其实就是重新配置一下广播速率和超时时间而已,如果我们初始化的时候,把慢速广播速率设置成20ms----40ms,其实也是快速广播。 不明白的话,可以上翻查看一下,我的广播初始化函数,里面注释很详细。
3.1、初始化 成功后就是开始广播了:
uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode)
{
上面代码 省略。。。。。。。。。。。。
memset(&m_peer_address, 0, sizeof(m_peer_address));
if ( ((m_adv_modes_config.ble_adv_directed_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW))
)
{
if (m_evt_handler != NULL)
{
m_peer_addr_reply_expected = true;
m_evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST);
}
else
{
m_peer_addr_reply_expected = false;
}
}
m_adv_mode_current = adv_mode_next_avail_get(advertising_mode);
// Fetch the whitelist.
if ((m_evt_handler != NULL) &&
(m_adv_mode_current == BLE_ADV_MODE_FAST || m_adv_mode_current == BLE_ADV_MODE_SLOW) &&
(m_adv_modes_config.ble_adv_whitelist_enabled) &&
(!m_whitelist_temporarily_disabled))
{
#if (NRF_SD_BLE_API_VERSION == 3)
m_whitelist_in_use = false;
#endif
m_whitelist_reply_expected = true;
m_evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST);
}
else
{
m_whitelist_reply_expected = false;
}
// Initialize advertising parameters with default values.
memset(&adv_params, 0, sizeof(adv_params));
adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
adv_params.fp = BLE_GAP_ADV_FP_ANY;
// Set advertising parameters and events according to selected advertising mode.
switch (m_adv_mode_current)
{
case BLE_ADV_MODE_DIRECTED:
NRF_LOG_INFO("---BLE_ADV_MODE_DIRECTED --\r\n");
ret = set_adv_mode_directed(&adv_params);
break;
case BLE_ADV_MODE_DIRECTED_SLOW:
NRF_LOG_INFO("---BLE_ADV_MODE_DIRECTED_SLOW --\r\n");
ret = set_adv_mode_directed_slow(&adv_params);
break;
case BLE_ADV_MODE_FAST:
NRF_LOG_INFO("-----Bluetooth runing Fast -----\r\n");
ret = set_adv_mode_fast(&adv_params);
break;
case BLE_ADV_MODE_SLOW:
NRF_LOG_INFO("-----Bluetooth runing Slow -----\r\n");
ret = set_adv_mode_slow(&adv_params);
break;
case BLE_ADV_MODE_IDLE:
NRF_LOG_INFO("--------Bluetooth runing IDLE ------------\r\n");
m_adv_evt = BLE_ADV_EVT_IDLE;
break;
default:
break;
}
if (m_adv_mode_current != BLE_ADV_MODE_IDLE)
{
ret = sd_ble_gap_adv_start(&adv_params);
if (ret != NRF_SUCCESS)
{
return ret;
}
}
if (m_evt_handler != NULL)
{
m_evt_handler(m_adv_evt);
}
return NRF_SUCCESS;
}
开始 广播函数的功能很简单,就是给广播模式赋值,然后开启广播模式而已。
3.2、事件处理函数:
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
uint32_t err_code;
switch (ble_adv_evt)
{
case BLE_ADV_EVT_FAST:
NRF_LOG_INFO("Fast advertising\r\n");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
APP_ERROR_CHECK(err_code);
break;
case BLE_ADV_EVT_IDLE:
NRF_LOG_INFO("---BLE_ADV_EVT_IDLE---\r\n");
sleep_mode_enter();
break;
default:
break;
}
}
事件处理函数只处理快速广播模式和IDLE模式,在IDLE里面,我们可以看到,就是调用sd_power_system_off();进入睡眠,这个低功耗函数,在我们研发蓝牙低功耗睡眠的时候,也会用到。