打开主菜单

谷雨文档中心 β

更改

NRF52832DK协议栈实验

添加8,431字节2020年1月8日 (三) 16:20
NUS服务获取实验
===Write属性服务实验===
====实验简介====
通过前面实验的学习,我们已经能够完成主机成功连接从机,并且可以配置我们想要的连接参数(连接间隔、MTU等)。所以有了前面的铺垫,从这一章节开始,我们会给大家讲解如何进行主从机的通信。通过前面实验的学习,我们已经能够完成主机成功连接从机,并且可以配置我们想要的连接参数(连接间隔、MTU等)。所以有了前面的铺垫,从这一章节开始,我们会给大家讲解如何进行主从机的通信。本来我们应该给大家继续在串口透传的程序基础上继续添加服务功能,但由于NUS服务相对比较复杂,所以我们会插入两篇章节(本章节及下一章节),分别给大家讲解LED的Write属性服务,以及BTN的Notify属性服务,然后再切回串口透传例程继续讲解,方便大家学习。
谈到通信,就不得不给大家介绍一个名叫“GATT”的好同志,因为就是他帮我们管理蓝牙的通信,那么对于GATT而言,当两个设备成功连接之后,他们分别作为一下两个设备之一:
3、主机如何通过Write属性,向从机发送数据
 
===Notify属性服务实验===
====实验简介====
通过Write属性服务实验的学习,我们已经知道了从机设备如何注册一个自定义服务(自定义的LED服务),然后主机如何去发现这个服务,并且利用这个服务的特征值与从机设备进行通信,控制从机设备的LED点亮。
 
也就是说,我们已经学会了如何通过主机给从机发送数据,所以接下来我们本章节将会给大家讲解从机如何通过notify属性给主机发送数据。
====实验现象====
主机设备流程:
 
1、扫描符合我们连接过滤要求的从机设备(根据LED服务的UUID过滤)
 
2、成功连接我们的从机设备,并且更新连接参数和MTU
 
3、发现服务,成功发现Ghostyu LED Service
 
4、主机分别按下4个按键,控制从机设备的4个LED依次点亮
[[文件:Nrf52832dk-ble-gattnotify2.png|边框|居中|无框|680x680像素]]
从机设备流程:
 
1、开启广播
 
2、被主机成功连接,并交互连接参数
 
3、等待主机获取服务(一般主机成功获取服务的时间在0.5s~1s之间,这个时间仅供大家参考)
 
4、主机按下按键,从机接收到相应的LED状态数据并打印,并根据这个数据控制板子上的LED点亮
[[文件:Nrf52832dk-ble-gattnotify1.png|边框|居中|无框|684x684像素]]
 
====工程及源码讲解====
 
===== 主机部分 =====
 
===== 从机部分 =====
 
======gy_profile_btn.c\.h======
首先我们还是先看一下服务配置文件,首先还是注册一下服务,注册的服务句柄是p_btn->service_handle。服务注册完成之后,我们注册按键的特征值,可以看到我们分别使能了按键的notify通知属性(add_char_params.char_props.notify = 1;),并且同样使能了read属性(add_char_params.char_props.read = 1;)。
 
这里我们需要注意的是下面的cccd_write_access参数被使能,上一实验大家都好理解需要使能write_access和read_access,因为我们需要使用读写,那么为什么这个例程要使能cccd_write_access呢,这边给大家简单说明一下CCCD。{{Note|text=Client Characteristic Configuration Descriptor(CCCD)是客户端特征配置描述符。当主机向CCCD中写入0x0001,此时使能notify;当写入0x0000时,此时禁止notify。
在nordic的协议栈当中,他的这个notify使能是交给用户自己处理的,也是说即便主机没有向cccd中写入0x0001去使能notify,我们同样可以直接利用notify去发送数据,只能这样不符合规范。|type=info}}<syntaxhighlight lang="c" line="1" start="50">
//******************************************************************************
// fn :ble_btn_init
//
// brief : 初始化BTN服务
//
// param : p_btn -> btn服务结构体
//
// return : uint32_t -> 成功返回SUCCESS,其他返回ERR NO.
uint32_t ble_btn_init(ble_btn_t * p_btn)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
 
// 添加服务(128bit UUID)
ble_uuid128_t base_uuid = {BTN_UUID_BASE};
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_btn->uuid_type);
VERIFY_SUCCESS(err_code);
 
ble_uuid.type = p_btn->uuid_type;
ble_uuid.uuid = BTN_UUID_SERVICE;
 
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_btn->service_handle);
VERIFY_SUCCESS(err_code);
 
// 添加BTN特征值(属性是Write和Read、长度是4)
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BTN_UUID_CHAR;
add_char_params.uuid_type = p_btn->uuid_type;
add_char_params.init_len = BTN_UUID_CHAR_LEN;
add_char_params.max_len = BTN_UUID_CHAR_LEN;
add_char_params.char_props.read = 1;
add_char_params.char_props.notify = 1;
add_char_params.read_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
 
return characteristic_add(p_btn->service_handle, &add_char_params, &p_btn->btn_char_handles);
}
</syntaxhighlight>服务部分剩下的处理流程和上一实验是类型的,只不过上一个实验是处理的wirte属性,而这个实验是处理notify属性。
 
首先在BLE事件处理的函数中,本来我们应该要处理CCCD_Write的数据的,但是这边我们偷懒了,不去做处理,验证一下主机不发送数据使能notify,我们也可以通过notify发送数据出去。<syntaxhighlight lang="c" line="1" start="31">
//******************************************************************************
// fn :ble_led_on_ble_evt
//
// brief : BLE事件处理函数
//
// param : p_ble_evt -> ble事件
// p_context -> ble事件处理程序的参数(暂时理解应该是不同的功能,注册时所携带的结构体参数)
//
// return : none
void ble_led_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
switch (p_ble_evt->header.evt_id)
{
 
default:
break;
}
}
</syntaxhighlight>下面这个函数,就是我们notify发送数据的函数,他的参数我们只需要配置4个。
 
type配置为BLE_GATT_HVX_NOTIFICATION,代表是notify属性的数据;
 
handle我们需要配置为我们按键特征值的value.handle,代表的是按键特征值的Value这个列表的句柄;
 
剩下的p_data和p_len就是我们需要发送的数据以及数据的长度。<syntaxhighlight lang="c" line="1" start="7">
//******************************************************************************
// fn :ble_lbs_on_button_change
//
// brief : 处理按键按下,状态更新的事件
//
// param : conn_handle -> 连接的句柄
// p_btn -> btn结构体
// button_state -> 按键状态
//
// return : none
uint32_t ble_lbs_on_button_change(uint16_t conn_handle, ble_btn_t * p_btn, uint8_t *button_state)
{
ble_gatts_hvx_params_t params;
uint16_t len = BTN_UUID_CHAR_LEN;
 
memset(&params, 0, sizeof(params));
params.type = BLE_GATT_HVX_NOTIFICATION;
params.handle = p_btn->btn_char_handles.value_handle;
params.p_data = button_state;
params.p_len = &len;
 
return sd_ble_gatts_hvx(conn_handle, &params);
}
</syntaxhighlight>
======gy_serial_btn.c\.h======
按键的外设处理驱动文件,这个详细的说明也是请大家查看基础实验部分的,这里我们只关注一下按键被触发,然后通过notify函数,去向手机发送通知。<syntaxhighlight lang="c" line="1" start="41">
//******************************************************************
// fn : btn_timeout_handler
//
// brief : 按键定时器超时任务
//
// param : p_event -> 指向数据库发现事件的指针
//
// return : none
static void btn_timeout_handler(void * p_context)
{
uint8_t btnStateBuf[BTN_UUID_CHAR_LEN] = {0};
if(nrf_drv_gpiote_in_is_set(BUTTON_1) == 0)
{
NRF_LOG_INFO("BTN1");
btnStateBuf[0] = 0x01;
btnState = 1;
}
 
if(nrf_drv_gpiote_in_is_set(BUTTON_2) == 0)
{
NRF_LOG_INFO("BTN2");
btnStateBuf[1] = 0x01;
btnState = 1;
}
if(nrf_drv_gpiote_in_is_set(BUTTON_3) == 0)
{
NRF_LOG_INFO("BTN3");
btnStateBuf[2] = 0x01;
btnState = 1;
}
if(nrf_drv_gpiote_in_is_set(BUTTON_4) == 0)
{
NRF_LOG_INFO("BTN4");
btnStateBuf[3] = 0x01;
btnState = 1;
}
if(btnState)
{
ble_lbs_on_button_change(0x0000, &m_btn, btnStateBuf);
}
btnState = 0;
}
</syntaxhighlight>
======main.c======
由于这个例程的按键通知我们是放到了按键驱动文件中处理,所以在main函数当中我们只需要去初始化一下这个服务就可以了。<syntaxhighlight lang="c" line="1" start="194">
//******************************************************************
// fn : services_init
//
// brief : 初始化复位(本例程展示NUS:Nordic Uart Service)
//
// param : none
//
// return : none
static void services_init(void)
{
uint32_t err_code;
 
err_code = ble_btn_init(&m_btn);
APP_ERROR_CHECK(err_code);
}
</syntaxhighlight>
 
==== <span> </span>实验总结 ====
=== NUS服务获取实验 ===
510
个编辑