当前位置:   article > 正文

nrf52840蓝牙协议栈主机BLE串口_ble串口协议

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();//待机状态
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

  首先是定时器、按键和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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  在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_REPORT

        case 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 result
            err_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 supported
            err_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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

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 connect
            err_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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

  该函数首先通过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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
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);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

  如果发现主服务的信息(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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

  如果找到了从机主服务的全部特征值,就会开始描述符的查找。即触发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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

  如果查找完描述符,则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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

  在此函数内调用了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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

  在这个函数里面有一句

 p_ble_bas_c->evt_handler(p_ble_bas_c, &ble_bas_c_evt);
  • 1

  这里的函数指向的是ble_nus_c_evt_handler函数。因为在初始化函数nus_c_init内将函数指针指向了此处。如下所示。

init.evt_handler = ble_nus_c_evt_handler;
  • 1

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

  该函数里面的BLE_NUS_C_EVT_DISCOVERY_COMPLETE事件类型,正是第2.5章节里面的上抛事件。
  ble_nus_chars_received_uart_print函数内部实际就是调用了app_uart_put函数将数据通过串口发送出去。

四、主机串口接收数据通过蓝牙发送数据流分析

  主机串口接收数据通过蓝牙发送数据流分析和nrf52840蓝牙协议栈从机BLE串口里面的第3.1章的串口转蓝牙基本相同。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/194189
推荐阅读
相关标签
  

闽ICP备14008679号