赞
踩
硬件:清风nrf52832开发板
软件:编译环境:keil4, SDK版本:nRF5_SDK_15.0.0_a53641a, 协议栈版本:s132_nrf52_6.0.0
简介:在官方串口例程的基础上添加一个特性,用于控制led,通过向此特性写1,控制led4亮,写0控制led灭
过程:
在ble_nuc.c文件中有两个函数,tx_char_add和rx_char_add,他们分别是添加tx和rx的特性,仿照这两个函数,添加led的特性,代码如下:
static uint32_t led_char_add(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
{
ble_gatts_char_md_t char_md;
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_t attr_md;
memset(&char_md, 0, sizeof(char_md));
char_md.char_props.write = 1;
char_md.char_props.write_wo_resp = 1;
char_md.p_char_user_desc = NULL;
char_md.p_char_pf = NULL;
char_md.p_user_desc_md = NULL;
char_md.p_cccd_md = NULL;
char_md.p_sccd_md = NULL;
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_LED_CHARACTERISTIC;//<span style="color:#FF0000;">此处需要修改,定义自己的UUID</span>
memset(&attr_md, 0, sizeof(attr_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
attr_md.vloc = BLE_GATTS_VLOC_STACK;
attr_md.rd_auth = 0;
attr_md.wr_auth = 0;
attr_md.vlen = 1;
memset(&attr_char_value, 0, sizeof(attr_char_value));
attr_char_value.p_uuid = &ble_uuid;
attr_char_value.p_attr_md = &attr_md;
attr_char_value.init_len = 1;//数据长度为1个字节
attr_char_value.init_offs = 0;
attr_char_value.max_len = BLE_NUS_MAX_LED_CHAR_LEN;
return sd_ble_gatts_characteristic_add(p_nus->service_handle,//主服务的句柄
&char_md,
&attr_char_value,
&p_nus->led_handles);//<span style="color:#FF0000;">此处需要定义自己的句柄</span>
}
此函数是仿照rx_char_add函数建立的。已上代码中需要定义BLE_UUID_NUS_LED_CHARACTERISTIC,修改ble_nus_s结构体
#define BLE_UUID_NUS_LED_CHARACTERISTIC 0x0004
struct ble_nus_s
{
uint8_t uuid_type; /**< UUID type for Nordic UART Service Base UUID. */
uint16_t service_handle; /**< Handle of Nordic UART Service (as provided by the SoftDevice). */
ble_gatts_char_handles_t tx_handles; /**< Handles related to the TX characteristic (as provided by the SoftDevice). */
ble_gatts_char_handles_t rx_handles; /**< Handles related to the RX characteristic (as provided by the SoftDevice). */
ble_gatts_char_handles_t led_handles;//<span style="color:#FF0000;">定义led的句柄</span>
blcm_link_ctx_storage_t * const p_link_ctx_storage; /**< Pointer to link context storage with handles of all current connections and its context. */
ble_nus_data_handler_t data_handler; /**< Event handler to be called for handling received data. */
};
到此为止,led的特性已经添加完毕,接下来需要修改串口事件处理函数。
在ble_nus.c中,void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)此函数用于处理串口服务事件的处理函数,函数如下:
void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_nus_t * p_nus = (ble_nus_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_nus, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_nus, p_ble_evt);/<span style="color:#FF0000;">/执行写事件</span>
break;
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
on_hvx_tx_complete(p_nus, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
当往蓝牙串口服务里写数据时,会跳到BLE_GATTS_EVT_WRITE:这个分支去执行on_write函数,
static void on_write(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;
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;//<span style="color:#FF0000;">保存发生的事件</span>
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);
}
memset(&evt, 0, sizeof(ble_nus_evt_t));
evt.p_nus = p_nus;
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
evt.p_link_ctx = p_client;
NRF_LOG_INFO("TEST\n");
/*xujidong*/
SEGGER_RTT_printf(0, "p_nus->rx_handles.value_handle = %d\n", p_nus->rx_handles.value_handle);
SEGGER_RTT_printf(0, "%sp_nus->tx_handles.value_handle = %d%s\n", RTT_CTRL_BG_BRIGHT_RED,p_nus->tx_handles.value_handle,RTT_CTRL_RESET);
SEGGER_RTT_printf(0, "%sp_nus->led_handles.value_handle = %d%s\n", RTT_CTRL_BG_BRIGHT_RED,p_nus->led_handles.value_handle,RTT_CTRL_RESET);
if ((p_evt_write->handle == p_nus->tx_handles.cccd_handle) &&
(p_evt_write->len == 2))
{
if (p_client != NULL)
{
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
p_client->is_notification_enabled = true;
evt.type = BLE_NUS_EVT_COMM_STARTED;
}
else
{
p_client->is_notification_enabled = false;
evt.type = BLE_NUS_EVT_COMM_STOPPED;
}
if (p_nus->data_handler != NULL)
{
p_nus->data_handler(&evt);
}
}
}
else if ((p_evt_write->handle == p_nus->rx_handles.value_handle) && //<span style="background-color:rgb(255,255,255);"><span style="color:#FF0000;">根据接收数据的句柄,处理接收数据</span></span>
(p_nus->data_handler != NULL))
{
evt.type = BLE_NUS_EVT_RX_DATA;//<span style="color:#FF0000;">将事件标志置为接收数据事件</span>
evt.params.rx_data.p_data = p_evt_write->data;//<span style="color:#FF0000;">保存接收的数据</span>
evt.params.rx_data.length = p_evt_write->len; //<span style="color:#FF0000;">爆出接收的数据长度</span>
p_nus->data_handler(&evt);
}
else if ((p_evt_write->handle == p_nus->led_handles.value_handle) &&//<span style="color:#FF0000;">根据句柄判断是否是led事件</span>
(p_nus->data_handler != NULL))
{
SEGGER_RTT_printf(0, "%shello world%s\n", RTT_CTRL_BG_BRIGHT_RED,RTT_CTRL_RESET);
evt.type = BLE_NUS_EVT_LED_OPT;//<span style="color:#FF0000;">将事件设置为led事件</span>
evt.params.led_data.p_data = p_evt_write->data;
evt.params.led_data.length = p_evt_write->len;
p_nus->data_handler(&evt);//<span style="color:#FF0000;">执行事件处理函数,此处理函数为nus_data_handler</span>
}
else
{
// Do Nothing. This event is not relevant for this service.
}
}
已上代码中,因为添加了led事件,所以需要修改ble_nus_evt_t结构体
//<span style="color:#FF0000;">事件的类型</span>
typedef enum
{
BLE_NUS_EVT_RX_DATA, /**< Data received. */
BLE_NUS_EVT_TX_RDY, /**< Service is ready to accept new data to be transmitted. */
BLE_NUS_EVT_LED_OPT,//<span style="color:#FF0000;">添加led事件类型</span>
BLE_NUS_EVT_COMM_STARTED, /**< Notification has been enabled. */
BLE_NUS_EVT_COMM_STOPPED, /**< Notification has been disabled. */
} ble_nus_evt_type_t;
typedef struct
{
ble_nus_evt_type_t type; /**< Event type. */
ble_nus_t * p_nus; /**< A pointer to the instance. */
uint16_t conn_handle; /**< Connection handle. */
ble_nus_client_context_t * p_link_ctx; /**< A pointer to the link context. */
union
{
ble_nus_evt_rx_data_t rx_data; /**< @ref BLE_NUS_EVT_RX_DATA event data. */
ble_nus_evt_rx_data_t led_data;//<span style="color:#FF0000;">保存led数据</span>
} params;
} ble_nus_evt_t;
在事件结构体中添加了led事件类型,添加了存储led数据的变量,接下来需要在事件回调函数中添加处理led事件的相关代码
事件回调函数位于main.c中,代码如下:
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
if (p_evt->type == BLE_NUS_EVT_RX_DATA)
{
uint32_t err_code;
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);
if(p_evt->params.rx_data.p_data[0] == '1')
{
printf("recv passkey\n");
static uint8_t passkey[] = "654321";
//err_code=sd_ble_opt_set(BLE_GAP_OPT_PASSKEY,&m_static_pin_option);
m_static_pin_option.gap_opt.passkey.p_passkey = passkey;
err_code=sd_ble_opt_set(BLE_GAP_OPT_PASSKEY,&m_static_pin_option);
APP_ERROR_CHECK(err_code);
err_code = sd_ble_gap_disconnect(mp_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
//sd_ble_gap_connect_cancel();
m_conn_handle = BLE_CONN_HANDLE_INVALID;
}
for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
{
do
{
err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
{
NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
APP_ERROR_CHECK(err_code);
}
} while (err_code == NRF_ERROR_BUSY);
}
if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
{
while (app_uart_put('\n') == NRF_ERROR_BUSY);
}
}
if(p_evt->type == BLE_NUS_EVT_LED_OPT)//<span style="color:#FF0000;">处理led事件</span>
{
SEGGER_RTT_printf(0, "%sBLE_NUS_EVT_LED_OPT%s\n", RTT_CTRL_BG_BRIGHT_RED,RTT_CTRL_RESET);
SEGGER_RTT_printf(0, "%sp_data = %d%s\n", RTT_CTRL_BG_BRIGHT_RED,p_evt->params.led_data.p_data[0],RTT_CTRL_RESET);
SEGGER_RTT_printf(0, "%slength = %d%s\n", RTT_CTRL_BG_BRIGHT_RED,p_evt->params.led_data.length,RTT_CTRL_RESET);
if(p_evt->params.led_data.p_data[0] == 1)
{
SEGGER_RTT_printf(0, "LED ON\n");
bsp_board_led_on(3);
}
else if (p_evt->params.led_data.p_data[0] == 0)
{
bsp_board_led_off(3);
SEGGER_RTT_printf(0, "LED OFF\n");
}
}
}
最后总结一下创建一个服务需要具体做哪些工作:
1、创建服务初始化结构体,主要用来保存处理来自于串口服务的回调函数
创建串口服务结构体,主要用来保存各种characteristic的相关句柄(需要创建此类型的全局变量,并将ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)串口服务事件处理函数添加到协议栈中)
创建串口服务事件结构体,主要用来保存事件的类型和收到的数据
2、需要在services_init(void)函数中初始化串口服务
在初始化服务中主要添加了两个Characteristic,以及初始化UUID
3、创建void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)函数用来处理串口服务事件
4、创建static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)函数用来处理具体的工作,此函数被ble_nus_on_ble_evt调用,此函数中根据事件的句柄来判断事件的类型,然后设置相应的事件,由nus_data_handler(ble_nus_evt_t * p_evt)去执行具体的动作
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。