510
个编辑
更改
→一从多主实验
}
}
</syntaxhighlight>然后我们再看一下断开然后我们再看一下断开连接的处理函数,先判断下是否有设备连接(periph_link_cnt == 0),如果没有设备连接,则返回err。然后我们再判断一下,当前连接数是否是最大连接数量减1(periph_link_cnt == (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - 1)),也就是说刚刚从满数量连接少了一个,这个时候我要需要重新开启广播,等待连接。<syntaxhighlight lang="c" line="1" start="444">
//******************************************************************
// fn : on_disconnected
// Advertising is not running when all connections are taken, and must therefore be started.
advertising_start();
}
}
</syntaxhighlight>讲完了一从多主连接相关的改变,我们接下来来说明一下多主连接的情况下,服务的处理。
首先看下服务初始化函数,这里我们主要是Queue Write初始化时候,需要根据最大连接数量初始化多个m_qwr实例。NUS的初始化部分是不需要改变的。<syntaxhighlight lang="c" line="1" start="314">
//******************************************************************
// fn : services_init
//
// brief : 初始化复位(本例程展示NUS:Nordic Uart Service)
//
// param : none
//
// return : none
static void services_init(void)
{
ret_code_t err_code;
ble_nus_init_t nus_init;
nrf_ble_qwr_init_t qwr_init = {0};
// Initialize Queued Write Module instances.
qwr_init.error_handler = nrf_qwr_error_handler;
for (uint32_t i = 0; i < LINK_TOTAL; i++)
{
err_code = nrf_ble_qwr_init(&m_qwr[i], &qwr_init);
APP_ERROR_CHECK(err_code);
}
// Initialize NUS.
memset(&nus_init, 0, sizeof(nus_init));
nus_init.data_handler = nus_data_handler;
err_code = ble_nus_init(&m_nus, &nus_init);
APP_ERROR_CHECK(err_code);
ble_conn_state_init();
}
</syntaxhighlight>其他就只剩下数据收发部分,我们得考虑有多个连接的设备。从上面一路看下来,我们不难看出,所谓的多主连接,实际上只是对conn_handle的处理(我们宏定义需要修改最大连接数量)。<syntaxhighlight lang="c" line="1" start="281">
//******************************************************************
// fn : nus_data_handler
//
// brief : 用于处理来自Nordic UART服务的数据的功能
// details : 该功能将处理从Nordic UART BLE服务接收的数据并将其发送到UART模块
//
// param : ble_nus_evt_t -> nus事件
//
// return : none
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
if (p_evt->type == BLE_NUS_EVT_RX_DATA)
{
UART_Printf("Recive on connection handle 0x%04x.\n", p_evt->conn_handle);
UART_Write((uint8_t*)p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
}
else if (p_evt->type == BLE_NUS_EVT_TX_RDY)
{
NRF_LOG_DEBUG(" Service is ready to accept new data to be transmitted..");
}
else if(p_evt->type == BLE_NUS_EVT_COMM_STARTED)
{
NRF_LOG_DEBUG("NUS Notification Enable.");
}
else if (p_evt->type == BLE_NUS_EVT_COMM_STOPPED)
{
NRF_LOG_DEBUG("NUS Notification Disable.");
}
}
</syntaxhighlight><syntaxhighlight lang="c" line="1" start="580">
// 串口回调函数
void APP_UartEvtHandle(uart_evt_t* pEvt)
{
uint16_t len = 0;
ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_periph_handles();
switch(pEvt->evt_type)
{
case UART_EVT_RX_TIMEOUT:
case UART_EVT_RX_OVERFLOW:
len = UART_Read(Buf,pEvt->status);
if(len > BLE_NUS_MAX_DATA_LEN) break;
for (uint8_t i = 0; i < conn_handles.len; i++)
{
UART_Printf("Sent on connection handle 0x%04x.\n", conn_handles.conn_handles[i]);
ble_nus_data_send(&m_nus, Buf, &len, conn_handles.conn_handles[i]);
}
break;
}
}
</syntaxhighlight>
=== 一从多主实验 一主多从实验 ===
==== 实验简介 ====
介绍完一从多主的实验,我们来看下一主多从的,从上一个实验的源码讲解中我们不难看出,所谓的多路连接,主要处理的就是连接的句柄。
而在我们的一主都从实验中,大家需要多注意下服务句柄的分配部分,因为涉及到多个从机的服务,所以我们对于句柄的分配,则相关内容要多一些。
==== 硬件说明 ====
串口一向是所有开发板必需的功能之一,nRF52DK开发板采用CH340C芯片,将芯片的UART信号转换为USB接口信号,方便电脑使用USB来虚拟一个串口调试。
CH340C外围电路简单,且不需要外部晶振,nRF52DK开发板采用与Nordic官方开发板相同的串口收发引脚,如下表格所示。
{| class="wikitable"
!网络标号
!芯片引脚号
!连接方式
|-
|CH340_TX
|P0.08(nRF_RX信号)
|通过SW1拨动开关,选择连接
|-
|CH340_RX
|P0.06(nRF_TX信号)
|通过SW1拨动开关,选择连接
|}[[文件:NRF52832 CH340C虚拟串口.png|居中|无框|609x609像素|link=]]图中P5引出了CH340C的流控制引脚,但是没有连接任何线路,所需要使用流控制,可自行连接测试。
==== 实验现象 ====
==== 源码讲解 ====
===== mian.c =====
首先还是一样的,我们先看下ble协议栈初始化的部分,我们定义了可以连接的从机最大数量为8。<syntaxhighlight lang="c" line="1" start="12065">
// <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
</syntaxhighlight><syntaxhighlight lang="c" line="1" start="314">
//******************************************************************
// fn : ble_evt_handler
//
// brief : BLE事件回调
// details : 包含以下几种事件类型:COMMON、GAP、GATT Client、GATT Server、L2CAP
//
// param : ble_evt_t 事件类型
// p_context 未使用
//
// return : none
static void ble_stack_init(void)
{
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// Configure the BLE stack using the default settings.
// Fetch the start address of the application RAM.
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
// Enable BLE stack.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
</syntaxhighlight>接下来我们通用查看一下ble协议栈的回调函数,看下其中对于连接和断开等GAP状态的处理。
BLE_GAP_EVT_CONNECTED连接状态,我们首先调用ble_nus_c_handles_assign函数去分配句柄,但实际上由于第三个参数为NULL,这里我们等于是给nus服务句柄置0。接下来我们调用ble_db_discovery_start函数去发现服务(由于有多个连接,所以我们需要根据当前连接的设备句柄参数去发现服务),成功发现NUS服务之后,会给我们在ble_nus_c_evt_handler回调函数中返回BLE_NUS_C_EVT_DISCOVERY_COMPLETE事件。
BLE_GAP_EVT_DISCONNECTED断开状态,先判断了是否有设备连接,如果没有就直接返回err,并且在最后重新开启扫描。<syntaxhighlight lang="c" line="1" start="207">
//******************************************************************
// fn : db_disc_handler
//
// brief : 用于处理数据库发现事件的函数
// details : 此函数是一个回调函数,用于处理来自数据库发现模块的事件。
// 根据发现的UUID,此功能将事件转发到各自的服务。
//
// param : p_event -> 指向数据库发现事件的指针
//
// return : none
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 is connected, initiate DB
// discovery, update LEDs status, and resume scanning, if necessary.
case BLE_GAP_EVT_CONNECTED:
{
UART_Printf("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_nus_c_handles_assign(&m_ble_nus_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 whether it is needed to look for more
// peripherals to connect to.
if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
{
}
else
{
// Resume scanning.
scan_start();
}
} break; // BLE_GAP_EVT_CONNECTED
// Upon disconnection, reset the connection handle of the peer that disconnected, update
// the LEDs status and start scanning again.
case BLE_GAP_EVT_DISCONNECTED:
{
UART_Printf("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)
{
APP_ERROR_CHECK(err_code);
}
// Start scanning.
scan_start();
} break;
case BLE_GAP_EVT_TIMEOUT:
{
// Timeout for scanning is not specified, so only the connection requests can time out.
if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
{
NRF_LOG_DEBUG("Connection request timed out.");
}
} break;
default:
// No implementation needed.
break;
}
}
</syntaxhighlight>然后我们看下nus_c的初始化,不同于从机设备的多路连接(因为从机自己提供服务,服务是唯一的),主机连接的多个从机有可能存在服务的不同,所以我们需要初始化多个m_ble_nus_c的实例。
同样的在nus_c_init的初始化函数中,我们需要对这些m_ble_nus_c的实例都进行初始化操作。<syntaxhighlight lang="c" line="1" start="51">
BLE_NUS_C_ARRAY_DEF(m_ble_nus_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);
</syntaxhighlight><syntaxhighlight lang="c" line="1" start="286">
//******************************************************************
// fn : nus_c_init
//
// brief : 初始化NUS客户端(Nordic UART Service client)
//
// param : none
//
// return : none
static void nus_c_init(void)
{
ret_code_t err_code;
ble_nus_c_init_t init;
init.evt_handler = ble_nus_c_evt_handler;
for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
{
err_code = ble_nus_c_init(&m_ble_nus_c[i], &init);
APP_ERROR_CHECK(err_code);
}
}
</syntaxhighlight>看完nus_c的初始化函数,我们来看下他的回调函数的事件处理。
BLE_NUS_C_EVT_DISCOVERY_COMPLETE事件是服务发现完成后返回,我们可以在这个事件中去调用ble_nus_c_handles_assign给对应的连接的从设备的服务分配句柄,并且去使能他的notify。
BLE_NUS_C_EVT_NUS_TX_EVT事件是接收到从机的notify数据时返回,我们可以从这里获取到从设备的数据,并且打印出来显示。<syntaxhighlight lang="c" line="1" start="168">
//******************************************************************
// fn : ble_nus_c_evt_handler
//
// brief : NUS事件
//
// param : none
//
// return : none
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
{
ret_code_t err_code;
switch (p_ble_nus_evt->evt_type)
{
case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_INFO("Discovery complete.");
err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
APP_ERROR_CHECK(err_code);
err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("Connected to device with Nordic UART Service.");
break;
case BLE_NUS_C_EVT_NUS_TX_EVT:
UART_Write((uint8_t*)p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
NRF_LOG_DEBUG("Receiving data.");
NRF_LOG_HEXDUMP_DEBUG(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
break;
case BLE_NUS_C_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected.");
break;
default:
break;
}
}
</syntaxhighlight>最后是串口回调函数,我们需要在这边处理接收到的串口数据,利用ble_nus_c_string_send函数发送给与我们主机连接的从机设备,由于是多路连接,所以我们需要一个循环来向多个m_ble_nus_c发送数据(就是像多个从机设备发送)。<syntaxhighlight lang="c" line="1" start="447">
// 串口回调函数
void APP_UartEvtHandle(uart_evt_t* pEvt)
{
uint16_t len = 0;
switch(pEvt->evt_type)
{
case UART_EVT_RX_TIMEOUT:
case UART_EVT_RX_OVERFLOW:
len = UART_Read(Buf,pEvt->status);
for (uint8_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
{
//UART_Printf("Sent on connection handle 0x%04x.\n",0);
ble_nus_c_string_send(&m_ble_nus_c[i], Buf, len);
}
break;
}
}
</syntaxhighlight>