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串口相关推荐

  1. nrf52840蓝牙协议栈从机BLE串口

      nrf52840蓝牙协议栈从机BLE串口,参考蓝牙SDK的example中的ble_app_uart样例.本文主要是分析ble_app_uart样例.   蓝牙从机串口的工作模式是:主机通过蓝牙发 ...

  2. nrf52840蓝牙协议栈样例分析

      蓝牙SDK的example 文件夹提供了开发BLE的模板工程,它具有通用性,可以为自己开发工程提供参考.打开examples\ble_peripheral\ble_app_template文件夹下 ...

  3. BLE学习(1):蓝牙协议栈的介绍

    蓝牙官方(The Bluetooth Special Interest Group)定义了低功耗蓝牙(Bluetooth low energy,即LE)和基础/增强速率蓝牙(Bluetooth bas ...

  4. 汇承4.0蓝牙BLE串口助手HC-COM的使用方法及出错的原因和改正方法

       本文主要介绍在使用汇承4.0蓝牙BLE串口助手HC-COM,通过手机向蓝牙模块发送数据.或者通过手机向与蓝牙模块连接的单片机发送指令时,串口中断函数的编写方法及出错的原因和改正方法    我们怎 ...

  5. 【转载】传统蓝牙协议栈 串口协议SPP

    零. 概述 主要介绍下蓝牙协议栈(bluetooth stack) 串口协议(bluetooth SPP)Serial Port Profile 协议概念介绍. 一. 声明 本专栏文章我们会以连载的方 ...

  6. 蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙串口协议SPP演示以及实现原理

    零. 概述 主要介绍下蓝牙协议栈开发板跑传统蓝牙串口协议SPP AT指令以及上位机操作步骤,以及原理 一. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:蓝牙综合介绍 ...

  7. 三.非协议栈实现BLE蓝牙广播(2.4G实现BLE广播)

    非协议栈实现BLE广播(2.4G实现BLE广播) 现在的人啊,用协议栈用多了,BLE物理层是怎么广播都不知道了,且听我慢慢道来 这里实现的BLE广播包是基于上一章介绍的,我要通过2.4G来发出这个广播 ...

  8. Android 蓝牙BLE串口通信之高低位转换、16进制字符串转换float浮点型

    蓝牙技术的普及与发展,为传统设备提供了一种低成本无线通信的方式.串口作为一种使用广泛的通信接口,通过串口转蓝牙,进行无线通信传输的需求逐渐展现出来. 蓝牙串口模块是嵌入式设备上的常用模块,它可以方便地 ...

  9. 【蓝牙学习笔记二】初识BLE蓝牙协议栈

    BLE蓝牙协议栈 下面通过图形+文字的方式以方便大家记忆,BLE蓝牙协议栈的各个层次的功能. 下面推荐两篇好博文: 1.这篇博文对蓝牙协议栈的每个层次更详细的讲解:https://blog.csdn. ...

  10. 如何实现蓝牙空中升级BLE OTA

    如何实现BLE OTA?什么叫DFU?如何通过UART实现固件升级?又如何通过USB实现固件升级?怎么保证升级的安全性?什么叫双备份(dual bank)DFU?什么叫单备份(single bank) ...

最新文章

  1. tomcat跳转报错_微信小程序开发:使用reLaunch跳转时报错的解决步骤
  2. 酒店wifi代理服务器没有响应,wn10连接酒店wifi的登录界面无法弹出如何处理
  3. keras模型中的默认初始化权重
  4. 干货!看云原生时代阿里云的四个“最”
  5. 基于Linux的集群系统(五) Linux集群系统的实现
  6. Atitit 扩大个人影响力和宣传目录1. 发文舆论阵地 11.1. 简书 知乎 csdn等 11.2. Ifttt出发同步 11.3. 问答平台 知乎 quaro 11.4. Tik
  7. PC电脑控制手机iphone(iOS 11、iOS 12、iOS 13),需越狱
  8. PHP运行的环境安装
  9. 3分钟打动投资人:商业计划书篇
  10. 【原创】LabView制作实时读取Excel正态分布图
  11. 西班牙国家德比次回合时间确定 中国球迷需熬夜
  12. 联机系统的服务器,远程联机服务器系统
  13. flume1.9学习笔记
  14. 《算法笔记》4.3小节——算法初步->递归
  15. web项目的启动时文件加载顺序
  16. 十分钟了解K-means聚类
  17. 洛谷P1914题解——(本地测试AC但是交题爆零全WA)使用getchar();
  18. 关于产品的一些思考——新浪之爱问资料和爱问知识人
  19. LINGO 12.0安装步骤
  20. 2021 百度指数采集工具与研究

热门文章

  1. 基于JavaWeb的小型超市管理系统的设计与实现
  2. ubuntu下安装绿联的AC650网卡驱动
  3. mysql data目录 恢复_利用mysql data文件夹恢复数据
  4. 自动驾驶中ROS操作系统的重要性
  5. 2018计算机考研大纲408,2018考研计算机408考试大纲.pdf
  6. eos java调用命名sql_普元 EOS Platform 7.6 开发命名SQL缓存示例
  7. php最新猜骰子精美ui源码,ThinkPHP全新UI猜猜乐H5游戏源码
  8. 浏览器Cookie详解
  9. grads插值_GrADS学习资料:第2章 数据处理
  10. 对select标签中的option默认选中后端的数据