nrf52840蓝牙协议栈主机BLE串口
nrf52840蓝牙协议栈主BLE串口,参考蓝牙SDK的example中ble_central里面的ble_app_uart样例。本文主要是参考ble_app_uart样例,但是nordic的SDK的example中ble_central里面的ble_app_uart样例有严重问题,所以进行了修改,从而实现蓝牙主机串口的功能。
蓝牙主机机串口的数据传递模式是:从机通过蓝牙发送数据到主机,主机接收到蓝牙数据后通过串口转发出去;主机从串口接收数据,将数据通过蓝牙发送给从机。
蓝牙串口主要有三部分的工作,第一部分是建立串口,第二部分是建立BLE,第三部分是搭建蓝牙和串口的双向数据通道。本文只重点分析主机端蓝牙的建立及处理,蓝牙通用的配置分析参见文章nrf52840蓝牙协议栈样例分析,建立串口参见文章nrf52840蓝牙协议栈从机BLE串口。
一、程序流程分析
主函数内容如下
int main(void)
{// Initialize.log_init();//打印初始化timer_init();//软件定时器初始化uart_init();//串口初始化buttons_leds_init();//按键和LED初始化db_discovery_init();//蓝牙数据发现初始化power_management_init();//能量管理初始化ble_stack_init();//协议栈初始化gatt_init();//GATT初始化nus_c_init();//蓝牙主机功能初始化// Start execution.printf("BLE UART central example started.\r\n");NRF_LOG_INFO("BLE UART central example started.");scan_start();//开始扫描// Enter main loop.for (;;){idle_state_handle();//待机状态}
}
首先是定时器、按键和LED初始化,和串口初始化,他们和从机串口基本没有变化。
其次,是协议栈初始化,里面有部分变化,第一在ble_stack_init函数内设置的主机和从机角色变化,第二注册的回调函数ble_evt_handler有变化。
二、主机连接从机过程分析
2.1、主机开始扫描广播
首先在main函数内调用scan_start函数进行扫描
/**@brief Function to start scanning. */
static void scan_start(void)
{ret_code_t ret;ret = sd_ble_gap_scan_start(&m_scan_params, &m_scan_buffer);APP_ERROR_CHECK(ret);ret = bsp_indication_set(BSP_INDICATE_SCANNING);APP_ERROR_CHECK(ret);
}
在scan_start函数内调用sd_ble_gap_scan_start函数进行扫描。如果发现了从机广播,则协议栈的GAP层会抛出BLE_GAP_EVT_ADV_REPORT事件,就是广播报告。那么在协议栈初始化函数ble_stack_init内注册的派发函数ble_evt_handler就会被调用。
ble_evt_handler函数内容如下:
/**@brief Function for handling BLE events.** @param[in] p_ble_evt Bluetooth stack event.* @param[in] p_context Unused.*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{ret_code_t err_code;ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;switch (p_ble_evt->header.evt_id){case BLE_GAP_EVT_ADV_REPORT:on_adv_report(&p_gap_evt->params.adv_report);break; // BLE_GAP_EVT_ADV_REPORTcase BLE_GAP_EVT_CONNECTED:NRF_LOG_INFO("Connected to target");err_code = ble_nus_c_handles_assign(&m_ble_nus_c, p_ble_evt->evt.gap_evt.conn_handle, NULL);APP_ERROR_CHECK(err_code);err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);APP_ERROR_CHECK(err_code);// start discovery of services. The NUS Client waits for a discovery resulterr_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);APP_ERROR_CHECK(err_code);break;case BLE_GAP_EVT_TIMEOUT:if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN){NRF_LOG_INFO("Scan timed out.");scan_start();}else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN){NRF_LOG_INFO("Connection Request timed out.");}break;case BLE_GAP_EVT_SEC_PARAMS_REQUEST:// Pairing not supportederr_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);APP_ERROR_CHECK(err_code);break;case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:// Accepting parameters requested by peer.err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,&p_gap_evt->params.conn_param_update_request.conn_params);APP_ERROR_CHECK(err_code);break;case BLE_GAP_EVT_PHY_UPDATE_REQUEST:{NRF_LOG_DEBUG("PHY update request.");ble_gap_phys_t const phys ={.rx_phys = BLE_GAP_PHY_AUTO,.tx_phys = BLE_GAP_PHY_AUTO,};err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);APP_ERROR_CHECK(err_code);} break;case BLE_GATTC_EVT_TIMEOUT:// Disconnect on GATT Client timeout event.NRF_LOG_DEBUG("GATT Client Timeout.");err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);APP_ERROR_CHECK(err_code);break;case BLE_GATTS_EVT_TIMEOUT:// Disconnect on GATT Server timeout event.NRF_LOG_DEBUG("GATT Server Timeout.");err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);APP_ERROR_CHECK(err_code);break;default:break;}
}
2.2、主机分析广播数据并连接
当产生BLE_GAP_EVT_ADV_REPORT事件后调用的on_adv_report函数如下
/**@brief Function for handling the advertising report BLE event.** @param[in] p_adv_report Advertising report from the SoftDevice.*/
static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
{ret_code_t err_code;if (ble_advdata_uuid_find(p_adv_report->data.p_data, p_adv_report->data.len, &m_nus_uuid)){err_code = sd_ble_gap_connect(&p_adv_report->peer_addr,&m_scan_params,&m_connection_param,APP_BLE_CONN_CFG_TAG);if (err_code == NRF_SUCCESS){// scan is automatically stopped by the connecterr_code = bsp_indication_set(BSP_INDICATE_IDLE);APP_ERROR_CHECK(err_code);NRF_LOG_INFO("Connecting to target %02x%02x%02x%02x%02x%02x",p_adv_report->peer_addr.addr[0],p_adv_report->peer_addr.addr[1],p_adv_report->peer_addr.addr[2],p_adv_report->peer_addr.addr[3],p_adv_report->peer_addr.addr[4],p_adv_report->peer_addr.addr[5]);}}else{err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);APP_ERROR_CHECK(err_code);}
}
该函数首先通过ble_advdata_uuid_find函数判断广播数据是否是自己需要的UUID,如果是就调用sd_ble_gap_connect函数进行连接,否则就继续扫描。
2.3、连接成功并寻找服务
如果连接成功,则协议栈会产生BLE_GAP_EVT_CONNECTED事件,在BLE_GAP_EVT_CONNECTED事件里面,主机要调用ble_db_discovery_start函数寻找GATT层的服务。ble_db_discovery_start会调用discovery_start进行具体的操作。
static uint32_t discovery_start(ble_db_discovery_t * const p_db_discovery, uint16_t conn_handle)
{ret_code_t err_code;ble_gatt_db_srv_t * p_srv_being_discovered;nrf_ble_gq_req_t db_srv_disc_req;memset(p_db_discovery, 0x00, sizeof(ble_db_discovery_t));memset(&db_srv_disc_req, 0x00, sizeof(nrf_ble_gq_req_t));err_code = nrf_ble_gq_conn_handle_register(mp_gatt_queue, conn_handle);VERIFY_SUCCESS(err_code);p_db_discovery->conn_handle = conn_handle;p_db_discovery->pending_usr_evt_index = 0;p_db_discovery->discoveries_count = 0;p_db_discovery->curr_srv_ind = 0;p_db_discovery->curr_char_ind = 0;p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);p_srv_being_discovered->srv_uuid = m_registered_handlers[p_db_discovery->curr_srv_ind];NRF_LOG_DEBUG("Starting discovery of service with UUID 0x%x on connection handle 0x%x.",p_srv_being_discovered->srv_uuid.uuid, conn_handle);db_srv_disc_req.type = NRF_BLE_GQ_REQ_SRV_DISCOVERY;db_srv_disc_req.params.gattc_srv_disc.start_handle = SRV_DISC_START_HANDLE;db_srv_disc_req.params.gattc_srv_disc.srvc_uuid = p_srv_being_discovered->srv_uuid;db_srv_disc_req.error_handler.p_ctx = p_db_discovery;db_srv_disc_req.error_handler.cb = discovery_error_handler;err_code = nrf_ble_gq_item_add(mp_gatt_queue, &db_srv_disc_req, conn_handle);if (err_code == NRF_SUCCESS){p_db_discovery->discovery_in_progress = true;}return err_code;
}
2.4、发现主服务
一旦启动寻找主服务,则剩余的动作过程就进入了GATT协议层,此时就需要进入GATT层处理函数ble_db_discovery_on_ble_evt里面了。 ble_db_discovery_on_ble_evt的声明如下:
/**@brief Macro for defining a ble_db_discovery instance.** @param _name Name of the instance.* @hideinitializer*/
#define BLE_DB_DISCOVERY_DEF(_name) \
static ble_db_discovery_t _name = {.discovery_in_progress = 0, \.conn_handle = BLE_CONN_HANDLE_INVALID}; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \BLE_DB_DISC_BLE_OBSERVER_PRIO, \ble_db_discovery_on_ble_evt, &_name)
ble_db_discovery_on_ble_evt函数的定义如下:
void ble_db_discovery_on_ble_evt(ble_evt_t const * p_ble_evt,void * p_context)
{VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);VERIFY_PARAM_NOT_NULL_VOID(p_context);VERIFY_MODULE_INITIALIZED_VOID();ble_db_discovery_t * p_db_discovery = (ble_db_discovery_t *)p_context;switch (p_ble_evt->header.evt_id){case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP://主服务发现报告on_primary_srv_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));break;case BLE_GATTC_EVT_CHAR_DISC_RSP://寻找特征值on_characteristic_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));break;case BLE_GATTC_EVT_DESC_DISC_RSP://寻找描述符on_descriptor_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));break;case BLE_GAP_EVT_DISCONNECTED:on_disconnected(p_db_discovery, &(p_ble_evt->evt.gap_evt));break;default:break;}
}
2.4.1、查找从机主服务的UUID
程序流程进入到GATT层协议后首先要做的是寻找主服务,也就是在GAP层协议触发的BLE_GAP_EVT_CONNECTED事件后启动的ble_db_discovery_start,会在GATT层产生一个BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP事件,也就是主服务发现事件。在这个事件下通过on_primary_srv_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt))函数寻找主服务的信息参数。
/**@brief Function for handling primary service discovery response.** @details This function will handle the primary service discovery response and start the* discovery of characteristics within that service.** @param[in] p_db_discovery Pointer to the DB Discovery structure.* @param[in] p_ble_gattc_evt Pointer to the GATT Client event.*/
static void on_primary_srv_discovery_rsp(ble_db_discovery_t * p_db_discovery,ble_gattc_evt_t const * p_ble_gattc_evt)
{ble_gatt_db_srv_t * p_srv_being_discovered;p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);if (p_ble_gattc_evt->conn_handle != p_db_discovery->conn_handle){return;}if (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS){uint32_t err_code;ble_gattc_evt_prim_srvc_disc_rsp_t const * p_prim_srvc_disc_rsp_evt;NRF_LOG_DEBUG("Found service UUID 0x%x.", p_srv_being_discovered->srv_uuid.uuid);p_prim_srvc_disc_rsp_evt = &(p_ble_gattc_evt->params.prim_srvc_disc_rsp);p_srv_being_discovered->srv_uuid = p_prim_srvc_disc_rsp_evt->services[0].uuid;p_srv_being_discovered->handle_range = p_prim_srvc_disc_rsp_evt->services[0].handle_range;err_code = characteristics_discover(p_db_discovery, p_ble_gattc_evt->conn_handle);if (err_code != NRF_SUCCESS){p_db_discovery->discovery_in_progress = false;// Error with discovering the service.// Indicate the error to the registered user application.discovery_error_evt_trigger(p_db_discovery, err_code, p_ble_gattc_evt->conn_handle);m_pending_user_evts[0].evt.evt_type = BLE_DB_DISCOVERY_AVAILABLE;m_pending_user_evts[0].evt.conn_handle = p_ble_gattc_evt->conn_handle;}}else{NRF_LOG_DEBUG("Service UUID 0x%x not found.", p_srv_being_discovered->srv_uuid.uuid);// Trigger Service Not Found event to the application.discovery_complete_evt_trigger(p_db_discovery, false, p_ble_gattc_evt->conn_handle);on_srv_disc_completion(p_db_discovery, p_ble_gattc_evt->conn_handle);}
}
如果发现主服务的信息(UUID),则会在GATT层触发BLE_GATTC_EVT_CHAR_DISC_RSP,在这个事件下 ,发现从机设备的特征值参数。
2.4.2、查找从机主服务的特征值
在GATT层触发BLE_GATTC_EVT_CHAR_DISC_RSP事件下 ,程序流程通过on_characteristic_discovery_rsp函数发现从机设备的特征值。
从机设备的主服务会定义多个特征值,因此在代码里面设置BLE_GATT_DB_MAX_CHARS宏定义来规定特征值的最大数量。在找到全部的特征值后,会启动查找描述符的工作。
/**@brief Function for handling characteristic discovery response.** @param[in] p_db_discovery Pointer to the DB Discovery structure.* @param[in] p_ble_gattc_evt Pointer to the GATT Client event.*/
static void on_characteristic_discovery_rsp(ble_db_discovery_t * p_db_discovery,ble_gattc_evt_t const * p_ble_gattc_evt)
{uint32_t err_code;ble_gatt_db_srv_t * p_srv_being_discovered;bool perform_desc_discov = false;if (p_ble_gattc_evt->conn_handle != p_db_discovery->conn_handle){return;}p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);if (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS){ble_gattc_evt_char_disc_rsp_t const * p_char_disc_rsp_evt;p_char_disc_rsp_evt = &(p_ble_gattc_evt->params.char_disc_rsp);// Find out the number of characteristics that were previously discovered (in earlier// characteristic discovery responses, if any).uint8_t num_chars_prev_disc = p_srv_being_discovered->char_count;// Find out the number of characteristics that are currently discovered (in the// characteristic discovery response being handled).uint8_t num_chars_curr_disc = p_char_disc_rsp_evt->count;// Check if the total number of discovered characteristics are supported by this module.if ((num_chars_prev_disc + num_chars_curr_disc) <= BLE_GATT_DB_MAX_CHARS){// Update the characteristics count.p_srv_being_discovered->char_count += num_chars_curr_disc;}else{// The number of characteristics discovered at the peer is more than the supported// maximum. This module will store only the characteristics found up to this point.p_srv_being_discovered->char_count = BLE_GATT_DB_MAX_CHARS;}uint32_t i;uint32_t j;for (i = num_chars_prev_disc, j = 0; i < p_srv_being_discovered->char_count; i++, j++){p_srv_being_discovered->charateristics[i].characteristic =p_char_disc_rsp_evt->chars[j];p_srv_being_discovered->charateristics[i].cccd_handle = BLE_GATT_HANDLE_INVALID;p_srv_being_discovered->charateristics[i].ext_prop_handle = BLE_GATT_HANDLE_INVALID;p_srv_being_discovered->charateristics[i].user_desc_handle = BLE_GATT_HANDLE_INVALID;p_srv_being_discovered->charateristics[i].report_ref_handle = BLE_GATT_HANDLE_INVALID;}ble_gattc_char_t * p_last_known_char;p_last_known_char = &(p_srv_being_discovered->charateristics[i - 1].characteristic);// If no more characteristic discovery is required, or if the maximum number of supported// characteristic per service has been reached, descriptor discovery will be performed.if ( !is_char_discovery_reqd(p_db_discovery, p_last_known_char)|| (p_srv_being_discovered->char_count == BLE_GATT_DB_MAX_CHARS)){perform_desc_discov = true;}else{// Update the current characteristic index.p_db_discovery->curr_char_ind = p_srv_being_discovered->char_count;// Perform another round of characteristic discovery.err_code = characteristics_discover(p_db_discovery, p_ble_gattc_evt->conn_handle);if (err_code != NRF_SUCCESS){p_db_discovery->discovery_in_progress = false;discovery_error_evt_trigger(p_db_discovery, err_code, p_ble_gattc_evt->conn_handle);m_pending_user_evts[0].evt.evt_type = BLE_DB_DISCOVERY_AVAILABLE;m_pending_user_evts[0].evt.conn_handle = p_ble_gattc_evt->conn_handle;return;}}}else{// The previous characteristic discovery resulted in no characteristics.// descriptor discovery should be performed.perform_desc_discov = true;}if (perform_desc_discov){bool raise_discov_complete;p_db_discovery->curr_char_ind = 0;err_code = descriptors_discover(p_db_discovery,&raise_discov_complete,p_ble_gattc_evt->conn_handle);if (err_code != NRF_SUCCESS){p_db_discovery->discovery_in_progress = false;discovery_error_evt_trigger(p_db_discovery, err_code, p_ble_gattc_evt->conn_handle);m_pending_user_evts[0].evt.evt_type = BLE_DB_DISCOVERY_AVAILABLE;m_pending_user_evts[0].evt.conn_handle = p_ble_gattc_evt->conn_handle;return;}if (raise_discov_complete){// No more characteristics and descriptors need to be discovered. Discovery is complete.// Send a discovery complete event to the user application.NRF_LOG_DEBUG("Discovery of service with UUID 0x%x completed with success"" on connection handle 0x%x.",p_srv_being_discovered->srv_uuid.uuid,p_ble_gattc_evt->conn_handle);discovery_complete_evt_trigger(p_db_discovery, true, p_ble_gattc_evt->conn_handle);on_srv_disc_completion(p_db_discovery, p_ble_gattc_evt->conn_handle);}}
}
如果找到了从机主服务的全部特征值,就会开始描述符的查找。即触发BLE_GATTC_EVT_DESC_DISC_RSP事件。
2.4.3、查找从机主服务的描述符
如果触发BLE_GATTC_EVT_DESC_DISC_RSP事件,则会调用on_descriptor_discovery_rsp函数:
/**@brief Function for handling descriptor discovery response.** @param[in] p_db_discovery Pointer to the DB Discovery structure.* @param[in] p_ble_gattc_evt Pointer to the GATT Client event.*/
static void on_descriptor_discovery_rsp(ble_db_discovery_t * const p_db_discovery,const ble_gattc_evt_t * const p_ble_gattc_evt)
{const ble_gattc_evt_desc_disc_rsp_t * p_desc_disc_rsp_evt;ble_gatt_db_srv_t * p_srv_being_discovered;if (p_ble_gattc_evt->conn_handle != p_db_discovery->conn_handle){return;}p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);p_desc_disc_rsp_evt = &(p_ble_gattc_evt->params.desc_disc_rsp);ble_gatt_db_char_t * p_char_being_discovered =&(p_srv_being_discovered->charateristics[p_db_discovery->curr_char_ind]);if (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS){// The descriptor was found at the peer.// Iterate through and collect CCCD, Extended Properties,// User Description & Report Reference descriptor handles.for (uint32_t i = 0; i < p_desc_disc_rsp_evt->count; i++){switch (p_desc_disc_rsp_evt->descs[i].uuid.uuid){case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:p_char_being_discovered->cccd_handle =p_desc_disc_rsp_evt->descs[i].handle;break;case BLE_UUID_DESCRIPTOR_CHAR_EXT_PROP:p_char_being_discovered->ext_prop_handle =p_desc_disc_rsp_evt->descs[i].handle;break;case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC:p_char_being_discovered->user_desc_handle =p_desc_disc_rsp_evt->descs[i].handle;break;case BLE_UUID_REPORT_REF_DESCR:p_char_being_discovered->report_ref_handle =p_desc_disc_rsp_evt->descs[i].handle;break;}/* Break if we've found all the descriptors we are looking for. */if (p_char_being_discovered->cccd_handle != BLE_GATT_HANDLE_INVALID &&p_char_being_discovered->ext_prop_handle != BLE_GATT_HANDLE_INVALID &&p_char_being_discovered->user_desc_handle != BLE_GATT_HANDLE_INVALID &&p_char_being_discovered->report_ref_handle != BLE_GATT_HANDLE_INVALID){break;}}}bool raise_discov_complete = false;if ((p_db_discovery->curr_char_ind + 1) == p_srv_being_discovered->char_count){// No more characteristics and descriptors need to be discovered. Discovery is complete.// Send a discovery complete event to the user application.raise_discov_complete = true;}else{// Begin discovery of descriptors for the next characteristic.uint32_t err_code;p_db_discovery->curr_char_ind++;err_code = descriptors_discover(p_db_discovery,&raise_discov_complete,p_ble_gattc_evt->conn_handle);if (err_code != NRF_SUCCESS){p_db_discovery->discovery_in_progress = false;// Error with discovering the service.// Indicate the error to the registered user application.discovery_error_evt_trigger(p_db_discovery, err_code, p_ble_gattc_evt->conn_handle);m_pending_user_evts[0].evt.evt_type = BLE_DB_DISCOVERY_AVAILABLE;m_pending_user_evts[0].evt.conn_handle = p_ble_gattc_evt->conn_handle;return;}}if (raise_discov_complete){NRF_LOG_DEBUG("Discovery of service with UUID 0x%x completed with success"" on connection handle 0x%x.",p_srv_being_discovered->srv_uuid.uuid,p_ble_gattc_evt->conn_handle);discovery_complete_evt_trigger(p_db_discovery, true, p_ble_gattc_evt->conn_handle);on_srv_disc_completion(p_db_discovery, p_ble_gattc_evt->conn_handle);}
}
如果查找完描述符,则raise_discov_complete会被置位,进而会调用discovery_complete_evt_trigger函数。discovery_complete_evt_trigger函数里面会触发协议栈的BLE_DB_DISCOVERY_COMPLETE事件。
2.5、发现中断函数
在main函数的db_discovery_init()中定义了一个发现服务的中断函数db_disc_handler
/** @brief Function for initializing the Database Discovery Module. */
static void db_discovery_init(void)
{ret_code_t err_code = ble_db_discovery_init(db_disc_handler);APP_ERROR_CHECK(err_code);
}
db_disc_handler中断函数定义如下;
/**@brief Function for handling database discovery events.** @details This function is callback function to handle events from the database discovery module.* Depending on the UUIDs that are discovered, this function should forward the events* to their respective services.** @param[in] p_event Pointer to the database discovery event.*/
static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{ble_nus_c_on_db_disc_evt(&m_ble_nus_c, p_evt);
}
ble_nus_c_on_db_disc_evt最终会触发BLE_NUS_C_EVT_DISCOVERY_COMPLETE事件,至此完成了主机和从机的蓝牙连接过程。
三、从机到主机的蓝牙数据流分析
3.1、函数嵌套分析
从机到主机的蓝牙数据处理是靠协议栈回调函数ble_nus_c_on_ble_evt来处理的。ble_nus_c_on_ble_evt的声明如下:
/**@brief Macro for defining a ble_nus_c instance.** @param _name Name of the instance.* @hideinitializer*/
#define BLE_NUS_C_DEF(_name) \
static ble_nus_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \BLE_NUS_C_BLE_OBSERVER_PRIO, \ble_nus_c_on_ble_evt, &_name)
ble_nus_c_on_ble_evt的定义如下:
void ble_nus_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{ble_nus_c_t * p_ble_nus_c = (ble_nus_c_t *)p_context;if ((p_ble_nus_c == NULL) || (p_ble_evt == NULL)){return;}if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)||(p_ble_nus_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle)){return;}switch (p_ble_evt->header.evt_id){case BLE_GATTC_EVT_HVX:on_hvx(p_ble_nus_c, p_ble_evt);break;case BLE_GAP_EVT_DISCONNECTED:if (p_ble_evt->evt.gap_evt.conn_handle == p_ble_nus_c->conn_handle&& p_ble_nus_c->evt_handler != NULL){ble_nus_c_evt_t nus_c_evt;nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCONNECTED;p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);}break;default:// No implementation needed.break;}
}
在此函数内调用了on_hvx函数
/**@brief Function for handling Handle Value Notification received from the SoftDevice.** @details This function handles the Handle Value Notification received from the SoftDevice* and checks whether it is a notification of the Battery Level measurement from the peer. If* it is, this function decodes the battery level measurement and sends it to the* application.** @param[in] p_ble_bas_c Pointer to the Battery Service Client structure.* @param[in] p_ble_evt Pointer to the BLE event received.*/
static void on_hvx(ble_bas_c_t * p_ble_bas_c, ble_evt_t const * p_ble_evt)
{// Check if the event is on the link for this instance.if (p_ble_bas_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle){return;}// Check if this notification is a battery level notification.if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_bas_c->peer_bas_db.bl_handle){if (p_ble_evt->evt.gattc_evt.params.hvx.len == 1){ble_bas_c_evt_t ble_bas_c_evt;ble_bas_c_evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;ble_bas_c_evt.evt_type = BLE_BAS_C_EVT_BATT_NOTIFICATION;ble_bas_c_evt.params.battery_level = p_ble_evt->evt.gattc_evt.params.hvx.data[0];p_ble_bas_c->evt_handler(p_ble_bas_c, &ble_bas_c_evt);}}
}
在这个函数里面有一句
p_ble_bas_c->evt_handler(p_ble_bas_c, &ble_bas_c_evt);
这里的函数指向的是ble_nus_c_evt_handler函数。因为在初始化函数nus_c_init内将函数指针指向了此处。如下所示。
init.evt_handler = ble_nus_c_evt_handler;
3.2、数据处理分析
数据处理分析函数ble_nus_c_evt_handler如下:
/**@brief Callback handling NUS Client events.** @details This function is called to notify the application of NUS client events.** @param[in] p_ble_nus_c NUS Client Handle. This identifies the NUS client* @param[in] p_ble_nus_evt Pointer to the NUS Client event.*//**@snippet [Handling events from the ble_nus_c module] */
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
{ret_code_t err_code;switch (p_ble_nus_evt->evt_type){case BLE_NUS_C_EVT_DISCOVERY_COMPLETE://主机连接从机完成事件NRF_LOG_INFO("Discovery complete.");//分配主机蓝牙操作句柄的空间err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);APP_ERROR_CHECK(err_code);err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);//使能TX通知APP_ERROR_CHECK(err_code);NRF_LOG_INFO("Connected to device with Nordic UART Service.");break;case BLE_NUS_C_EVT_NUS_TX_EVT://从机上传TX事件ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);break;case BLE_NUS_C_EVT_DISCONNECTED:NRF_LOG_INFO("Disconnected.");scan_start();break;}
}
该函数里面的BLE_NUS_C_EVT_DISCOVERY_COMPLETE事件类型,正是第2.5章节里面的上抛事件。
ble_nus_chars_received_uart_print函数内部实际就是调用了app_uart_put函数将数据通过串口发送出去。
四、主机串口接收数据通过蓝牙发送数据流分析
主机串口接收数据通过蓝牙发送数据流分析和nrf52840蓝牙协议栈从机BLE串口里面的第3.1章的串口转蓝牙基本相同。
nrf52840蓝牙协议栈主机BLE串口相关推荐
- nrf52840蓝牙协议栈从机BLE串口
nrf52840蓝牙协议栈从机BLE串口,参考蓝牙SDK的example中的ble_app_uart样例.本文主要是分析ble_app_uart样例. 蓝牙从机串口的工作模式是:主机通过蓝牙发 ...
- nrf52840蓝牙协议栈样例分析
蓝牙SDK的example 文件夹提供了开发BLE的模板工程,它具有通用性,可以为自己开发工程提供参考.打开examples\ble_peripheral\ble_app_template文件夹下 ...
- BLE学习(1):蓝牙协议栈的介绍
蓝牙官方(The Bluetooth Special Interest Group)定义了低功耗蓝牙(Bluetooth low energy,即LE)和基础/增强速率蓝牙(Bluetooth bas ...
- 汇承4.0蓝牙BLE串口助手HC-COM的使用方法及出错的原因和改正方法
本文主要介绍在使用汇承4.0蓝牙BLE串口助手HC-COM,通过手机向蓝牙模块发送数据.或者通过手机向与蓝牙模块连接的单片机发送指令时,串口中断函数的编写方法及出错的原因和改正方法 我们怎 ...
- 【转载】传统蓝牙协议栈 串口协议SPP
零. 概述 主要介绍下蓝牙协议栈(bluetooth stack) 串口协议(bluetooth SPP)Serial Port Profile 协议概念介绍. 一. 声明 本专栏文章我们会以连载的方 ...
- 蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙串口协议SPP演示以及实现原理
零. 概述 主要介绍下蓝牙协议栈开发板跑传统蓝牙串口协议SPP AT指令以及上位机操作步骤,以及原理 一. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:蓝牙综合介绍 ...
- 三.非协议栈实现BLE蓝牙广播(2.4G实现BLE广播)
非协议栈实现BLE广播(2.4G实现BLE广播) 现在的人啊,用协议栈用多了,BLE物理层是怎么广播都不知道了,且听我慢慢道来 这里实现的BLE广播包是基于上一章介绍的,我要通过2.4G来发出这个广播 ...
- Android 蓝牙BLE串口通信之高低位转换、16进制字符串转换float浮点型
蓝牙技术的普及与发展,为传统设备提供了一种低成本无线通信的方式.串口作为一种使用广泛的通信接口,通过串口转蓝牙,进行无线通信传输的需求逐渐展现出来. 蓝牙串口模块是嵌入式设备上的常用模块,它可以方便地 ...
- 【蓝牙学习笔记二】初识BLE蓝牙协议栈
BLE蓝牙协议栈 下面通过图形+文字的方式以方便大家记忆,BLE蓝牙协议栈的各个层次的功能. 下面推荐两篇好博文: 1.这篇博文对蓝牙协议栈的每个层次更详细的讲解:https://blog.csdn. ...
- 如何实现蓝牙空中升级BLE OTA
如何实现BLE OTA?什么叫DFU?如何通过UART实现固件升级?又如何通过USB实现固件升级?怎么保证升级的安全性?什么叫双备份(dual bank)DFU?什么叫单备份(single bank) ...
最新文章
- tomcat跳转报错_微信小程序开发:使用reLaunch跳转时报错的解决步骤
- 酒店wifi代理服务器没有响应,wn10连接酒店wifi的登录界面无法弹出如何处理
- keras模型中的默认初始化权重
- 干货!看云原生时代阿里云的四个“最”
- 基于Linux的集群系统(五) Linux集群系统的实现
- Atitit 扩大个人影响力和宣传目录1. 发文舆论阵地 11.1. 简书 知乎 csdn等 11.2. Ifttt出发同步 11.3. 问答平台 知乎 quaro 11.4. Tik
- PC电脑控制手机iphone(iOS 11、iOS 12、iOS 13),需越狱
- PHP运行的环境安装
- 3分钟打动投资人:商业计划书篇
- 【原创】LabView制作实时读取Excel正态分布图
- 西班牙国家德比次回合时间确定 中国球迷需熬夜
- 联机系统的服务器,远程联机服务器系统
- flume1.9学习笔记
- 《算法笔记》4.3小节——算法初步->递归
- web项目的启动时文件加载顺序
- 十分钟了解K-means聚类
- 洛谷P1914题解——(本地测试AC但是交题爆零全WA)使用getchar();
- 关于产品的一些思考——新浪之爱问资料和爱问知识人
- LINGO 12.0安装步骤
- 2021 百度指数采集工具与研究
热门文章
- 基于JavaWeb的小型超市管理系统的设计与实现
- ubuntu下安装绿联的AC650网卡驱动
- mysql data目录 恢复_利用mysql data文件夹恢复数据
- 自动驾驶中ROS操作系统的重要性
- 2018计算机考研大纲408,2018考研计算机408考试大纲.pdf
- eos java调用命名sql_普元 EOS Platform 7.6 开发命名SQL缓存示例
- php最新猜骰子精美ui源码,ThinkPHP全新UI猜猜乐H5游戏源码
- 浏览器Cookie详解
- grads插值_GrADS学习资料:第2章 数据处理
- 对select标签中的option默认选中后端的数据