510
个编辑
更改
→ble_nus.c\.h
}
}
</syntaxhighlight>接下来我们查看一下接下来我们查看一下接收到的主机数据的处理,对于接收到的主机数据,我们分两个部分判断,一个是CCCD写的数据(p_nus->tx_handles.cccd_handle),一个是特征值RX的数据(p_nus->rx_handles.value_handle)。 CCCD的数据,我们接收到之后,去判断一下是使能notify还是禁能notify(也就是数据是0x0000还是0x0001)。如果是使能,则向main文件返回BLE_NUS_EVT_COMM_STARTED事件,如果是禁能,则向mian文件返回BLE_NUS_EVT_COMM_STOPPED事件。 RX特征值,是用于接收主机wirte的数据(也就是我们用户通信的数据),我们将接收的数据赋值给回调任务(evt.params.rx_data.p_data = p_evt_write->data; evt.params.rx_data.length = p_evt_write->len;),然后向mian文件返回BLE_NUS_EVT_RX_DATA事件。<syntaxhighlight lang="c" line="1" start="124">
static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
{
{
// Do Nothing. This event is not relevant for this service.
}
}
</syntaxhighlight>最后一个,也就是我们的BLE_GATTS_EVT_HVN_TX_COMPLETE事件的任务处理,这个事件代表的是我们notify数据向主机通知完成。所以这里我们也没有什么需要处理的,只需要向mian文件返回一个BLE_NUS_EVT_TX_RDY事件,代表我们发送完成了(notify方式发送)。<syntaxhighlight lang="c" line="1" start="189">
static void on_hvx_tx_complete(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
ble_nus_evt_t evt;
ble_nus_client_context_t * p_client;
err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
p_ble_evt->evt.gatts_evt.conn_handle,
(void *) &p_client);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
p_ble_evt->evt.gatts_evt.conn_handle);
return;
}
if (p_client->is_notification_enabled)
{
memset(&evt, 0, sizeof(ble_nus_evt_t));
evt.type = BLE_NUS_EVT_TX_RDY;
evt.p_nus = p_nus;
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
evt.p_link_ctx = p_client;
p_nus->data_handler(&evt);
}
}
</syntaxhighlight>上面的部分介绍完之后,整个ble_nus.c文件就只剩下一个函数还没有介绍,也就是ble_nus_data_send函数,这个函数是预留给大家在mian文件中调用,去notify数据的。
在发送数据之前,我们首先先分别判断了一下设备是否连接了,notify属性是否被使能,发送的数据长度是否符合要求。以上都通过之后,我们将会给ble_gatts_hvx_params_t的参数赋值,最后调用sd_ble_gatts_hvx函数发送。<syntaxhighlight lang="c" line="1" start="314">
uint32_t ble_nus_data_send(ble_nus_t * p_nus,
uint8_t * p_data,
uint16_t * p_length,
uint16_t conn_handle)
{
ret_code_t err_code;
ble_gatts_hvx_params_t hvx_params;
ble_nus_client_context_t * p_client;
VERIFY_PARAM_NOT_NULL(p_nus);
err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, conn_handle, (void *) &p_client);
VERIFY_SUCCESS(err_code);
if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL))
{
return NRF_ERROR_NOT_FOUND;
}
if (!p_client->is_notification_enabled)
{
return NRF_ERROR_INVALID_STATE;
}
if (*p_length > BLE_NUS_MAX_DATA_LEN)
{
return NRF_ERROR_INVALID_PARAM;
}
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_nus->tx_handles.value_handle;
hvx_params.p_data = p_data;
hvx_params.p_len = p_length;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
return sd_ble_gatts_hvx(conn_handle, &hvx_params);
}
</syntaxhighlight>
===== mian.c =====
mian文件中的内容,很多都是和之前重复的,这边我们只说明两个部分。一个是nus服务的回调函数,也就是刚刚ble_nus.c中向main文件回调的一些事件的处理;另一部分就是接收到串口的数据,调用刚刚最后说明的notify发送函数去向主机传输数据的部分。
首先看下nus服务的回调函数,其中处理了4个事件,正是刚刚我们从ble_nus.c文件中回调上来的。
BLE_NUS_EVT_RX_DATA事件中,我们将接收的主机write数据,通过串口打印显示。
其他的三个事件中,分别提示了各自代表的意义。<syntaxhighlight lang="c" line="1" start="212">
//******************************************************************
// 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_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.");
//app_timer_start(m_timer_nus, APP_TIMER_TICKS(10), NULL);
}
else if (p_evt->type == BLE_NUS_EVT_COMM_STOPPED)
{
NRF_LOG_DEBUG("NUS Notification Disable.");
}
}
</syntaxhighlight>接下来我们看下串口回调函数,与前面的nus服务的回调类似,不过这个回调是处理串口上传的数据。
我们接收到串口的数据,并且调用ble_nus_data_send函数去将接收的数据,传输给主机。<syntaxhighlight lang="c" line="1" start="434">
// 串口回调函数
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);
ble_nus_data_send(&m_nus, Buf, &len, m_conn_handle); //从串口发出
break;
}
}
==== 实验简介 ====
NUS(Nordic Uart Service)实验,他本质上是和开始的LED与按键的实验是一样的,都是新建一个服务文件,注册好我们需要使用的特征值功能,然后传输数据。不过是要比他们稍微复杂一点,因为我们串口的数据是涉及到收发两个部分(也就是既有write也有notify),所以我们在注册特征值的时候,是要注册两个,一个负责write,一个负责notify。
剩下的就是串口外设的处理,我们这边对于串口驱动不做介绍,大家可以去看基础实验部分的说明。我们在这个实验中仅给大家讲解串口RX收到的数据如何通知(notify)给手机,手机下发(write)的数据如何通过串口TX打印显示出来。
==== 硬件说明 ====
串口一向是所有开发板必需的功能之一,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的流控制引脚,但是没有连接任何线路,所需要使用流控制,可自行连接测试。
==== 实验现象 ====