nrf52840蓝牙协议栈主机一拖八,参考蓝牙SDK的example中ble_central里面的ble_app_multilink_central样例。本文主要是参考ble_app_multilink_central样例,但是nordic的SDK的example中ble_central里面的ble_app_multilink_central样例有严重问题,所以进行了修改,从而实现蓝牙主机一拖八的功能。
蓝牙主机一拖八的工程里面的主从连接流程和nrf52840蓝牙协议栈主机BLE串口基本一致,所以不再赘述。本文只是分析两者的差异和做一些总结性的描述。
nrf52840蓝牙协主机最多可以连接20个从机,但是nordic给出的样板工程ble_app_multilink_central只连接了八个从机。所以,代码工程也是分析八个从机。
一、观察者模式协议处理函数
nrf52840的蓝牙协议栈引入了观察者模式来处理协议栈里面的事件。所以处理蓝牙工作过程的任何事件都必须通过观察者模式函数来处理。
蓝牙主机观察者模式函数主要有:nrf_ble_gatt_on_ble_evt、ble_lbs_c_on_ble_evt、ble_db_discovery_on_ble_evt、ble_evt_handler
1.1、nrf_ble_gatt_on_ble_evt函数
工作在GATT层,在GAP层连接成功或者断开后,GAP上抛事件后GATT层的事件处理。
void nrf_ble_gatt_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
nrf_ble_gatt_t * p_gatt = (nrf_ble_gatt_t *)p_context;
uint16_t conn_handle = p_ble_evt->evt.common_evt.conn_handle;
if (conn_handle >= NRF_BLE_GATT_LINK_COUNT)
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connected_evt(p_gatt, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected_evt(p_gatt, p_ble_evt);
break;
case BLE_GATTC_EVT_EXCHANGE_MTU_RSP:
on_exchange_mtu_rsp_evt(p_gatt, p_ble_evt);
break;
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
on_exchange_mtu_request_evt(p_gatt, p_ble_evt);
break;
#if !defined (S112)
case BLE_GAP_EVT_DATA_LENGTH_UPDATE:
on_data_length_update_evt(p_gatt, p_ble_evt);
break;
case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
on_data_length_update_request_evt(p_gatt, p_ble_evt);
break;
#endif // !defined (S112)
default:
break;
}
if (p_gatt->links[conn_handle].att_mtu_exchange_pending)
{
ret_code_t err_code;
err_code = sd_ble_gattc_exchange_mtu_request(conn_handle,
p_gatt->links[conn_handle].att_mtu_desired);
if (err_code == NRF_SUCCESS)
{
p_gatt->links[conn_handle].att_mtu_exchange_pending = false;
p_gatt->links[conn_handle].att_mtu_exchange_requested = true;
NRF_LOG_DEBUG("Requesting to update ATT MTU to %u bytes on connection 0x%x (retry).",
p_gatt->links[conn_handle].att_mtu_desired, conn_handle);
}
else if (err_code != NRF_ERROR_BUSY)
{
NRF_LOG_ERROR("sd_ble_gattc_exchange_mtu_request() returned %s.",
nrf_strerror_get(err_code));
}
}
}
1.2、ble_lbs_c_on_ble_evt函数
工作在GATT层,在GATT层出现通知事件时的处理
void ble_lbs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_lbs_c_t * p_ble_lbs_c = (ble_lbs_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_lbs_c, p_ble_evt);
break;
case BLE_GATTC_EVT_WRITE_RSP:
on_write_rsp(p_ble_lbs_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_lbs_c, p_ble_evt);
break;
default:
break;
}
}
1.3、ble_db_discovery_on_ble_evt函数
工作在GATT层,在GATT层进行连接时的事件处理(寻找主服务、查找UUID、特征值)
void ble_db_discovery_on_ble_evt(ble_evt_t const * p_ble_evt,
void * p_context)
{
VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
VERIFY_PARAM_NOT_NULL_VOID(p_context);
VERIFY_MODULE_INITIALIZED_VOID();
ble_db_discovery_t * p_db_discovery = (ble_db_discovery_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
on_primary_srv_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
break;
case BLE_GATTC_EVT_CHAR_DISC_RSP:
on_characteristic_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
break;
case BLE_GATTC_EVT_DESC_DISC_RSP:
on_descriptor_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_db_discovery, &(p_ble_evt->evt.gap_evt));
break;
default:
break;
}
if ( (p_db_discovery->discovery_pending)
&& (p_ble_evt->header.evt_id >= BLE_GATTC_EVT_BASE)
&& (p_ble_evt->header.evt_id <= BLE_GATTC_EVT_LAST)
&& (p_ble_evt->evt.gattc_evt.conn_handle == p_db_discovery->conn_handle))
{
(void)discovery_start(p_db_discovery, p_db_discovery->conn_handle);
}
}
1.4、ble_evt_handler函数
工作在GAP层,GAP层连接过程的事件处理
/**
* @brief Function for handling BLE events.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Context.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
uint32_t err_code;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
if (m_num_connections == 0)
{
err_code = connection_buttons_configure();
CALL_HANDLER_ON_ERROR(err_code);
}
m_num_connections++;
break;
case BLE_GAP_EVT_DISCONNECTED:
m_num_connections--;
if (m_num_connections == 0)
{
err_code = advertising_buttons_configure();
CALL_HANDLER_ON_ERROR(err_code);
}
break;
default:
break;
}
}
二、一拖八与一主一从的区别
2.1、NRF_SDH_BLE_CENTRAL_LINK_COUNT宏定义的变化
主机从机连接数量的变化,在sdk_config.h文件中
// <o> NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links.
#ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
#define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 0
#endif
// <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links.
#ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
#define NRF_SDH_BLE_CENTRAL_LINK_COUNT 8
#endif
// <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Total link count.
// <i> Maximum number of total concurrent connections using the default configuration.
#ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
#define NRF_SDH_BLE_TOTAL_LINK_COUNT 8
#endif
表示可以连接八个从机。
2.2、观察者模式函数的声明和定义
主机串口的观察者模式函数的定义如下:
BLE_NUS_C_DEF(m_ble_nus_c); /**< BLE NUS service client instance. */
NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */
BLE_DB_DISCOVERY_DEF(m_db_disc); /**< DB discovery module instance. */
一主多从的观察者模式函数的定义如下:
NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */
BLE_LBS_C_ARRAY_DEF(m_lbs_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT); /**< LED Button client instances. */
BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, NRF_SDH_BLE_CENTRAL_LINK_COUNT); /**< Database discovery module instances. */
说明,在一主多从的模式下,观察者模式函数被定义成了函数数组。
2.3 、蓝牙处理函数ble_evt_handler的变化
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code;
// For readability.
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
switch (p_ble_evt->header.evt_id)
{
// Upon connection, check which peripheral has connected, initiate DB
// discovery, update LEDs status and resume scanning if necessary.
case BLE_GAP_EVT_CONNECTED:
{
NRF_LOG_INFO("Connection 0x%x established, starting DB discovery.",
p_gap_evt->conn_handle);
APP_ERROR_CHECK_BOOL(p_gap_evt->conn_handle < NRF_SDH_BLE_CENTRAL_LINK_COUNT);
err_code = ble_lbs_c_handles_assign(&m_lbs_c[p_gap_evt->conn_handle],
p_gap_evt->conn_handle,
NULL);
APP_ERROR_CHECK(err_code);
err_code = ble_db_discovery_start(&m_db_disc[p_gap_evt->conn_handle],
p_gap_evt->conn_handle);
if (err_code != NRF_ERROR_BUSY)
{
APP_ERROR_CHECK(err_code);
}
// Update LEDs status, and check if we should be looking for more
// peripherals to connect to.
bsp_board_led_on(CENTRAL_CONNECTED_LED);
if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
{
bsp_board_led_off(CENTRAL_SCANNING_LED);
}
else
{
// Resume scanning.
bsp_board_led_on(CENTRAL_SCANNING_LED);
scan_start();
}
} break; // BLE_GAP_EVT_CONNECTED
// Upon disconnection, reset the connection handle of the peer which disconnected, update
// the LEDs status and start scanning again.
case BLE_GAP_EVT_DISCONNECTED:
{
NRF_LOG_INFO("LBS central link 0x%x disconnected (reason: 0x%x)",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
if (ble_conn_state_central_conn_count() == 0)
{
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
// Turn off connection indication LED
bsp_board_led_off(CENTRAL_CONNECTED_LED);
}
// Start scanning
scan_start();
// Turn on LED for indicating scanning
bsp_board_led_on(CENTRAL_SCANNING_LED);
} break;
case BLE_GAP_EVT_ADV_REPORT:
on_adv_report(&p_gap_evt->params.adv_report);
break;
case BLE_GAP_EVT_TIMEOUT:
{
// We have not specified a timeout for scanning, so only connection attemps can timeout.
if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
{
NRF_LOG_DEBUG("Connection request timed out.");
}
} break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST.");
// Accept parameters requested by peer.
err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
&p_gap_evt->params.conn_param_update_request.conn_params);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GATTC_EVT_TIMEOUT:
{
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GATTS_EVT_TIMEOUT:
{
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
} break;
default:
// No implementation needed.
break;
}
}
在发生BLE_GAP_EVT_CONNECTED事件时,每个连接的从机都分配一个操作句柄。在发生BLE_GAP_EVT_DISCONNECTED事件时都会判断连接的状态。
2.4、lbs_c_init函数变化
主机初始化函数
/**@brief LED Button collector initialization. */
static void lbs_c_init(void)
{
ret_code_t err_code;
ble_lbs_c_init_t lbs_c_init_obj;
lbs_c_init_obj.evt_handler = lbs_c_evt_handler;
for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
{
err_code = ble_lbs_c_init(&m_lbs_c[i], &lbs_c_init_obj);
APP_ERROR_CHECK(err_code);
}
}
根据定义数组逐个初始化。