蓝牙透传的profile的构建
在nordic提供的空白模板的基础上
1.初始化片上串口模块
2.构建蓝牙串口透传服务
3.串口穿透服务初始化这个函数非常重要
蓝牙服务的初始化
-
services_init(),这里头包含了应用程序的初始化(黑色点击可以跳转)
在以下代码中注册服务的流程如下
1.初始化服务结构体,结构体中包含:一个回调函数的句柄(可以是多个吗?)
-
ble_uarts_init_t结构体
typedef struct { ble_uarts_data_handler_t data_handler; //处理接收数据的事件句柄,这个句柄的类型如下个代码块 } ble_uarts_init_t;
typedef void (* ble_uarts_data_handler_t) (ble_uarts_evt_t * p_evt);
由于括号()的优先级大于*所以(*p)首先是一个指针,指向函数p,这里用了typedef,所以如果定义
ble_uarts_data_handler_t data_handler //那么data_handler本身就是一个以(ble_uarts_evt_t * p_evt);为输入的回调函数的句柄,在句柄调用的地方直接输入 参数data_handler,即可调用这个函数。
定义了一个函数指针,同时这个函数指针的参数是一个结构体指针:形式就是(*p)(type * a) 。
2.用memset清空初始化结构体
3.设置该服务的回调函数
4.初始化该服务两个参数(实例化服务,初始化结构体)
-
ble_uarts_init(ble_uarts_t * p_uarts, ble_uarts_init_t const * p_uarts_init)
//初始化串口透传服务 uint32_t ble_uarts_init(ble_uarts_t * p_uarts, ble_uarts_init_t const * p_uarts_init) { ret_code_t err_code; //定义16位UUID结构体变量 ble_uuid_t ble_uuid; //定义128位UUID结构体变量,并初始化为串口透传服务UUID基数 ble_uuid128_t nus_base_uuid = UARTS_BASE_UUID; //定义特征参数结构体变量 ble_add_char_params_t add_char_params; //检查指针是否为NULL VERIFY_PARAM_NOT_NULL(p_uarts); VERIFY_PARAM_NOT_NULL(p_uarts_init); //拷贝串口透传服务初始化结构体中的事件句柄 p_uarts->data_handler = p_uarts_init->data_handler; //将自定义UUID基数添加到SoftDevice err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_uarts->uuid_type); VERIFY_SUCCESS(err_code); //UUID类型和数值赋值给ble_uuid变量 ble_uuid.type = p_uarts->uuid_type; ble_uuid.uuid = BLE_UUID_UARTS_SERVICE; //添加串口透传服务 err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_uarts->service_handle); VERIFY_SUCCESS(err_code); /*---------------------以下代码添加RX特征--------------------*/ //添加RX特征 //配置参数之前先清零add_char_params memset(&add_char_params, 0, sizeof(add_char_params)); //RX特征的UUID add_char_params.uuid = BLE_UUID_UARTS_RX_CHARACTERISTIC; //RX特征的UUID类型 add_char_params.uuid_type = p_uarts->uuid_type; //设置RX特征值的最大长度 add_char_params.max_len = BLE_UARTS_MAX_RX_CHAR_LEN; //设置RX特征值的初始长度 add_char_params.init_len = sizeof(uint8_t); //设置RX的特征值长度为可变长度 add_char_params.is_var_len = true; //设置RX特征的性质支持写 add_char_params.char_props.write = 1; //设置RX特征的性质支持无响应写 add_char_params.char_props.write_wo_resp = 1; //设置读/写RX特征值的安全需求:无安全性 add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN; //为串口透传服务添加RX特征 err_code = characteristic_add(p_uarts->service_handle, &add_char_params, &p_uarts->rx_handles); //检查返回的错误代码 if (err_code != NRF_SUCCESS) { return err_code; } /*---------------------添加RX特征-END------------------------*/ /*---------------------以下代码添加TX特征--------------------*/ //添加TX特征 //配置参数之前先清零add_char_params memset(&add_char_params, 0, sizeof(add_char_params)); //TX特征的UUID add_char_params.uuid = BLE_UUID_UARTS_TX_CHARACTERISTIC; //TX特征的UUID类型 add_char_params.uuid_type = p_uarts->uuid_type; //设置TX特征值的最大长度 add_char_params.max_len = BLE_UARTS_MAX_TX_CHAR_LEN; //设置TX特征值的初始长度 add_char_params.init_len = sizeof(uint8_t); //设置TX的特征值长度为可变长度 add_char_params.is_var_len = true; //设置TX特征的性质:支持通知 add_char_params.char_props.notify = 1; //设置读/写RX特征值的安全需求:无安全性 add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN; add_char_params.cccd_write_access = SEC_OPEN; //为串口透传服务添加TX特征 return characteristic_add(p_uarts->service_handle, &add_char_params, &p_uarts->tx_handles); /*---------------------添加TX特征-END------------------------*/ }
//初始化串口透传服务 uint32_t ble_uarts_init(ble_uarts_t * p_uarts, ble_uarts_init_t const * p_uarts_init) { ret_code_t err_code; //定义16位UUID结构体变量 ble_uuid_t ble_uuid; //定义128位UUID结构体变量,并初始化为串口透传服务UUID基数 ble_uuid128_t nus_base_uuid = UARTS_BASE_UUID; //定义特征参数结构体变量 ble_add_char_params_t add_char_params; //检查指针是否为NULL VERIFY_PARAM_NOT_NULL(p_uarts); VERIFY_PARAM_NOT_NULL(p_uarts_init); //拷贝串口透传服务初始化结构体中的事件句柄 p_uarts->data_handler = p_uarts_init->data_handler; //将自定义UUID基数添加到SoftDevice err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_uarts->uuid_type); VERIFY_SUCCESS(err_code); //UUID类型和数值赋值给ble_uuid变量 ble_uuid.type = p_uarts->uuid_type; ble_uuid.uuid = BLE_UUID_UARTS_SERVICE; //添加串口透传服务 err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_uarts->service_handle); VERIFY_SUCCESS(err_code); /*---------------------以下代码添加RX特征--------------------*/ //添加RX特征 //配置参数之前先清零add_char_params memset(&add_char_params, 0, sizeof(add_char_params)); //RX特征的UUID add_char_params.uuid = BLE_UUID_UARTS_RX_CHARACTERISTIC; //RX特征的UUID类型 add_char_params.uuid_type = p_uarts->uuid_type; //设置RX特征值的最大长度 add_char_params.max_len = BLE_UARTS_MAX_RX_CHAR_LEN; //设置RX特征值的初始长度 add_char_params.init_len = sizeof(uint8_t); //设置RX的特征值长度为可变长度 add_char_params.is_var_len = true; //设置RX特征的性质支持写 add_char_params.char_props.write = 1; //设置RX特征的性质支持无响应写 add_char_params.char_props.write_wo_resp = 1; //设置读/写RX特征值的安全需求:无安全性 add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN; //为串口透传服务添加RX特征 err_code = characteristic_add(p_uarts->service_handle, &add_char_params, &p_uarts->rx_handles); //检查返回的错误代码 if (err_code != NRF_SUCCESS) { return err_code; } /*---------------------添加RX特征-END------------------------*/ /*---------------------以下代码添加TX特征--------------------*/ //添加TX特征 //配置参数之前先清零add_char_params memset(&add_char_params, 0, sizeof(add_char_params)); //TX特征的UUID add_char_params.uuid = BLE_UUID_UARTS_TX_CHARACTERISTIC; //TX特征的UUID类型 add_char_params.uuid_type = p_uarts->uuid_type; //设置TX特征值的最大长度 add_char_params.max_len = BLE_UARTS_MAX_TX_CHAR_LEN; //设置TX特征值的初始长度 add_char_params.init_len = sizeof(uint8_t); //设置TX的特征值长度为可变长度 add_char_params.is_var_len = true; //设置TX特征的性质:支持通知 add_char_params.char_props.notify = 1; //设置读/写RX特征值的安全需求:无安全性 add_char_params.read_access = SEC_OPEN; add_char_params.write_access = SEC_OPEN; add_char_params.cccd_write_access = SEC_OPEN; //为串口透传服务添加TX特征 return characteristic_add(p_uarts->service_handle, &add_char_params, &p_uarts->tx_handles); /*---------------------添加TX特征-END------------------------*/ }
static void services_init(void) { ret_code_t err_code; //定义串口透传初始化结构体 [ble_uarts_init_t](https://www.notion.so/profile-d789da71b5b4457abf8b7bd76b989633) uarts_init;//这个结构体中只包含了一个事件句柄data_handler //定义排队写入初始化结构体变量 nrf_ble_qwr_init_t qwr_init = {0}; //排队写入事件处理函数 qwr_init.error_handler = nrf_qwr_error_handler; //初始化排队写入模块 err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); //检查函数返回值 APP_ERROR_CHECK(err_code); /*------------------以下代码初始化串口透传服务-------------*/ //清零串口透传服务初始化结构体 memset(&uarts_init, 0, sizeof(uarts_init)); //设置串口透传事件回调函数 uarts_init.data_handler = uarts_data_handler; //初始化串口透传服务 err_code = ble_uarts_init(&m_uarts, &uarts_init); APP_ERROR_CHECK(err_code); /*------------------初始化串口透传服务-END-----------------*/ }
-
串口透传事件的回调
传入的参数ble_uarts_evt_t * p_evt
-
ble_uarts_evt_t结构体:
// 串口透传事件需要的的所有参数 typedef struct { ble_uarts_evt_type_t type; //事件类型 ble_uarts_t * p_uarts; //指向串口透传实例的指针 uint16_t conn_handle; //连接句柄 union { ble_uarts_evt_rx_data_t rx_data; //BLE_NUS_EVT_RX_DATA事件数据 } params; } ble_uarts_evt_t; //串口事件的枚举默认第一个为0,第二个为1-------type成员 typedef enum { BLE_UARTS_EVT_RX_DATA, //接收到新的数据-----0 BLE_UARTS_EVT_TX_RDY, //准备就绪,可以发送新数据-------1 } ble_uarts_evt_type_t; // 这个结构体的成员有数据缓存和数据长度。-------rx_data typedef struct { uint8_t const * p_data; //指向存放接收数据的缓存 uint16_t length; //接收的数据长度 } ble_uarts_evt_rx_data_t; typedef struct ble_uarts_s ble_uarts_t;//把ble_uarts_s 重命名为 ble_uarts_t,分别有以下5个成员 struct ble_uarts_s { uint8_t uuid_type; //UUID类型 uint16_t service_handle; //串口透传服务句柄(由协议栈提供) ble_gatts_char_handles_t tx_handles; //TX特征句柄 ble_gatts_char_handles_t rx_handles; //RX特征句柄 ble_uarts_data_handler_t data_handler; //处理接收数据的事件句柄 };
代码整体结构讲解
-
整体结构
很显然,在串口打印中涉及到两方面————收和发,使用一个for循环在串口打印数据,打印方法为调用app_uart_put()这个API。使用一个for循环嵌套do— -while(err_code == NRF_ERROR_BUSY)这个结束条件是,忙等待+ app_uart_put()。完成串口打印。做一个判断,如果不是正忙(NRF_ERROR_BUSY)和发送成功(NRF_SUCCESS),就要日志打印NRF_LOG_ERROR()错误信息。
static void uarts_data_handler(ble_uarts_evt_t * p_evt) { //========================此下是接收BLE事件,并打印到串口事件======== //判断事件类型:接收到新数据事件 if (p_evt->type == BLE_UARTS_EVT_RX_DATA) { uint32_t err_code; //串口打印出接收的数据 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); } } //========================此下是发送就绪事件======== //判断事件类型:发送就绪事件,该事件在后面的试验会用到,当前我们在该事件中翻转指示灯D4的状态,指示该事件的产生 if (p_evt->type == BLE_UARTS_EVT_TX_RDY) { nrf_gpio_pin_toggle(LED_4); } }
adhjaohof