打开主菜单

谷雨文档中心 β

更改

NRF52832DK协议栈高级实验

添加11,987字节2019年8月20日 (二) 17:05
源码讲解
我们的蓝牙实战实验将从蓝牙控制IO输出高低电平,以此来控制开发板上LED点亮和熄灭开始。
在这个实验中,我们主要会展示一下,有关特征值的write以及read属性,其中由于read属性相对而言使用的较少。我们主要会给大家介绍write属性,也就是主机给从机发送数据的属性。
 
==== 硬件说明 ====
nRF52DK开发板采用与Nordic官方开发板相同的指示灯电路,原则协议栈例程可以直接控制指示灯,无需修改代码。
手机端通过nordic的app"nrf master control panel",发起对设备的扫描和连接,连接成功之后,我们通过UUID FFF1给开发板发送数据。
例如发送0x00,0x00,0x00,0x00给开发板,此时开发板的4个LED灯均被点亮;同样的我们给将某位数据改成0x01,对应的LED就会熄灭。[[文件==== 源码讲解 ==== ===== gy_profile_led.c\.h =====我们首先查看一下他的服务文件,也就是gy_profile_led,我们我们就是通过这个服务来接收手机端发送的LED控制数据的。 可以看到这个服务初始化ble_led_init函数中对于服务以及他的特征值属性的初始化过程,首先我们先初始化一个回调(p_led->led_write_handler = p_led_init->led_write_handler;),这个回调是用来将gy_profile_led这一层的数据,上传给mian文件去处理。 接下来是服务的添加,首先是调用sd_ble_uuid_vs_add去添加服务,然后给这个ble_uuid的服务的参数赋值,最后调用sd_ble_gatts_service_add函数去注册这个服务,这边我们注册的服务句柄是p_led->service_handle。 注册完服务之后,我们就要开始添加我们的特征值characteristic,特征值的添加也是一样的,首先配置特征值的参数,这些参数中我们主要关注一下ble_gatt_char_props_t,这个参数是用来定义特征值的属性的,可以看到我们的属性有如下几种:<syntaxhighlight lang="c">/**@brief GATT Characteristic Properties. */typedef struct{ /* Standard properties */ uint8_t broadcast :1; /**< 广播 */ uint8_t read :1; /**< 读 */ uint8_t write_wo_resp :1; /**< 写指令 */ uint8_t write :1; /**< 写*/ uint8_t notify :1; /**< 通知*/ uint8_t indicate :1; /**< 暗示 */ uint8_t auth_signed_wr :1; /**< 签名写指令 */} ble_gatt_char_props_t;</syntaxhighlight>因为我们的led的服务,是手机写数据给开发板控制LED,所以我们要定义特征值写使能(add_char_params.char_props.write = 1;),另外当我们需要知道上次是发送的什么控制数据给开发板时,我们需要读一下数据,所以这边我们同样定义一下读使能(add_char_params.char_props.read = 1;),当我们配置完特征值的属性之后,我们调用characteristic_add函数,去向刚刚注册的p_led->service_handle服务中添加我们的特征值。<syntaxhighlight lang="c" line="1" start="53">//******************************************************************************// fn :ble_led_init//// brief : 初始化LED服务//// param : p_led -> led服务结构体// p_led_init -> led服务初始化结构体//// return : uint32_t -> 成功返回SUCCESS,其他返回ERR NO.uint32_t ble_led_init(ble_led_t * p_led, const ble_led_init_t * p_led_init){ uint32_t err_code; ble_uuid_t ble_uuid; ble_add_char_params_t add_char_params;  // 初始化服务结构体 p_led->led_write_handler = p_led_init->led_write_handler;  // 添加服务(128bit UUID) ble_uuid128_t base_uuid = {LED_UUID_BASE}; err_code = sd_ble_uuid_vs_add(&base_uuid, &p_led->uuid_type); VERIFY_SUCCESS(err_code);  ble_uuid.type = p_led->uuid_type; ble_uuid.uuid = LED_UUID_SERVICE;  err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_led->service_handle); VERIFY_SUCCESS(err_code);  // 添加LED特征值(属性是Write和Read、长度是4) memset(&add_char_params, 0, sizeof(add_char_params)); add_char_params.uuid = LED_UUID_CHAR; add_char_params.uuid_type = p_led->uuid_type; add_char_params.init_len = LED_UUID_CHAR_LEN; add_char_params.max_len = LED_UUID_CHAR_LEN; add_char_params.char_props.read = 1; add_char_params.char_props.write = 1;  add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN;  return characteristic_add(p_led->service_handle, &add_char_params, &p_led->led_char_handles);}</syntaxhighlight>看完服务的初始化,我们来看下我们的服务注册之后是怎么来进行工作的,首先我们看下ble_led_on_ble_evt这个函数,这个函数在我们mian函数中注册BLE_LED_DEF(m_led);实例的时候被引用。<syntaxhighlight lang="c" line="1" start="15">//******************************************************************************// fn :BLE_LED_DEF//// brief : 初始化LED服务实例//// param : _name -> 实例的名称//// return : none#define BLE_LED_DEF(_name) \static ble_led_t _name; \NRF_SDH_BLE_OBSERVER(_name ## _obs, \ BLE_LED_BLE_OBSERVER_PRIO, \ ble_led_on_ble_evt, &_name)</syntaxhighlight>这个m_led实例注册,涉及到NRF_SDH_BLE_OBSERVER的使用,简单的理解就是利用这个注册了实例之后,当底层有GAP或者GATT消息返回的时候,就会触发ble_led_on_ble_evt函数。 因为我们LED服务这里需要接收手机端write的LED控制数据,所以我们在事件判断中,判断是否出现GATT Write事件,一旦出现了,我们调用on_write函数去处理这个事件。<syntaxhighlight lang="c" line="1" start="28">//******************************************************************************// fn :ble_led_on_ble_evt//// brief : BLE事件处理函数//// param : p_ble_evt -> ble事件// p_context -> ble事件处理程序的参数(暂时理解应该是不同的功能,注册时所携带的结构体参数)//// return : nonevoid ble_led_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context){ ble_led_t * p_led = (ble_led_t *)p_context;  switch (p_ble_evt->header.evt_id) { // GATT Client Write事件 case BLE_GATTS_EVT_WRITE: on_write(p_led, p_ble_evt); break;  default: break; }}</syntaxhighlight>on_write函数用于处理接收的write数据,我们判断一下接收的数据是否符合我们的要求,如果符合,那么我们通过初始化函数中的回调函数,将接收到的值上传到main函数中去处理。<syntaxhighlight lang="c" line="1" start="7">//******************************************************************************// fn :on_write//// brief : 处理Write事件的函数。//// param : p_led -> led服务结构体// p_ble_evt -> ble事件//// return :NRF52nonestatic void on_write(ble_led_t * p_led, ble_evt_t const * p_ble_evt){ ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt-实战>evt.gatts_evt.params.write;  if ( (p_evt_write-LED>handle == p_led->led_char_handles.mp4|居中|边框value_handle) && (p_evt_write->len <= LED_UUID_CHAR_LEN) && (p_led->led_write_handler != NULL)) { p_led->led_write_handler((uint8_t*)p_evt_write->data); }}</syntaxhighlight> ===== gy_serial_led.c\.h =====有关外设处理,请大家查看基础实验部分 ===== main.c =====main文件中也不给大家全部介绍了,这个和蓝牙协议实验部分是重合的,我们只关注实验改动的部分。 我们看下服务初始化的部分,可以看到调用了我们gy_profile_led中的ble_led_init函数初始化注册了我们的LED服务,并且通用注册了一个回调函数。 在这个回调函数led_write_handler中,我们可以获取到gy_profile_led中上传上来的接收到的wirte数据,并且利用这个数据进行LED的控制。<syntaxhighlight lang="c" line="1" start="195">//******************************************************************// fn : nus_data_handler//// brief : 用于处理来自Nordic UART服务的数据的功能// details : 该功能将处理从Nordic UART BLE服务接收的数据并将其发送到UART模块//// param : ble_nus_evt_t -> nus事件//// return : nonestatic void led_write_handler(uint8_t * new_state){ NRF_LOG_INFO("Recive State:%02X,%02X,%02X,%02X",new_state[0],new_state[1],new_state[2],new_state[3]); LED_Control(BSP_LED_0, new_state[0]); LED_Control(BSP_LED_1, new_state[1]); LED_Control(BSP_LED_2, new_state[2]); LED_Control(BSP_LED_3, new_state[3]);} //******************************************************************// fn : services_init//// brief : 初始化复位(本例程展示NUS:Nordic Uart Service)//// param : none//// return : nonestatic void services_init(void){ uint32_t err_code; ble_led_init_t led_init;  // Initialize NUS. memset(&led_init, 0, sizeof(led_init));  led_init.led_write_handler = led_write_handler;
err_code ==== 源码讲解 ====ble_led_init(&m_led, &led_init); APP_ERROR_CHECK(err_code);}</syntaxhighlight>
=== 按键控制实验 ===
==== 实验简介 ====
按键控制实验,就是开发板上的按键按下,然后我们将通过蓝牙上报手机是哪一个按键被按下。
在上一个LED控制实验中,我们主要讲了Write属性,也就是主机给从机发送数据。在这个实验中,我们将会给大家展示从机如何给主机发送数据,也就是notify属性的使用。
 
==== 硬件说明 ====
nRF52DK开发板采用与Nordic官方开发板相同的按键电路,原则协议栈例程可以直接使用按键,无需修改代码。
 
按键与芯片引脚对应关系如下表格。
{| class="wikitable"
!网络标号
!芯片引脚号
!连接方式
|-
|BTN1
|P0.13
|直连,下降沿(低电平)触发
|-
|BTN2
|P0.14
|直连,下降沿(低电平)触发
|-
|BTN3
|P0.15
|直连,下降沿(低电平)触发
|-
|BTN4
|P0.16
|直连,下降沿(低电平)触发
|}[[文件:NRF52832DK 按键电路.png|居中|无框|475x475像素|链接=http://doc.iotxx.com/%E6%96%87%E4%BB%B6:NRF52832DK_%E6%8C%89%E9%94%AE%E7%94%B5%E8%B7%AF.png]]
==== 实验现象 ====
首先还是使用nordic的app "nrf master control panel"去连接我们的开发板,连接成功之后,我们点击UUID FFE1特征值的使用notify的按钮(三个向下箭头的图标)。
 
使能了notify之后,我们分别按下开发板上的按键S1-S4,可以看到手机上显示对应的上报状态数据。
==== 源码讲解 ====
 
===== gy_profile_btn.c\.h =====
首先我们还是先看一下服务配置文件,首先还是注册一下服务,注册的服务句柄是p_btn->service_handle。服务注册完成之后,我们注册按键的特征值,可以看到我们分别使能了按键的notify通知属性<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>
=== 基站实验 ===
510
个编辑