打开主菜单

谷雨文档中心 β

更改

NRF52832DK协议栈实验

添加9,931字节2020年1月7日 (二) 14:19
实验总结
4、当主从机配置的MTU大小不同时,我们以小的数值作准。
===LED控制实验===
====实验简介====
我们的蓝牙实战实验将从蓝牙控制IO输出高低电平,以此来控制开发板上LED点亮和熄灭开始。
在这个实验中,我们主要会展示一下,有关特征值的write以及read属性,其中由于read属性相对而言使用的较少。我们主要会给大家介绍write属性,也就是主机给从机发送数据的属性。
====硬件说明====
nRF52DK开发板采用与Nordic官方开发板相同的指示灯电路,原则协议栈例程可以直接控制指示灯,无需修改代码。
 
指示灯与芯片引脚对应关系如下表格。
{| class="wikitable"
!网络标号
!芯片引脚号
!连接方式
|-
|LED1
|P0.17
|直连,低电平亮灯
|-
|LED2
|P0.18
|直连,低电平亮灯
|-
|LED3
|P0.19
|直连,低电平亮灯
|-
|LED4
|P0.20
|直连,低电平亮灯
|}[[文件:NRF52832DK LED灯.png|居中|无框|473x473像素|link=]]
====实验现象====
手机端通过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 : none
void 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 : none
static 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->handle == p_led->led_char_handles.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 : none
static 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 : none
static 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><span> </span>
=== NUS服务获取实验 ===
510
个编辑