美文网首页物联网loT从业者物联网相关技术研究
NRF52832学习笔记(29)——主从一体

NRF52832学习笔记(29)——主从一体

作者: Leung_ManWah | 来源:发表于2020-09-30 11:40 被阅读0次

    一、简介

    一个蓝牙设备同时作为主设备和从设备进行使用,这种能力的节点设备称为主从一体设备。

    主从一体提供了扩展 BLE 蓝牙模块的能力,自从一个被称为“链路层拓扑”的功能被添加到蓝牙规范中后,就已经允许蓝牙设备同时作为主设备和从设备,在任何角色组合中操作。

    二、ble_app_hrs_rscs_relay

    打开工程 不同SDK\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay

    2.1 main

    主从一体设备中,如何实现该设备即作为主机又作为从机,关键的问题就是主机和从机事件的切换。首先必须把主机服务和从机服务搭建起来,初始化两种服务类型,然后解决主机的扫描然后发起连接部分、从机的广播然后被连接部分,当两个类型的服务被搭建后,就开始处理蓝牙事件,决定数据流向。


    2.1.1 scan_init

    设置一些扫描参数,如名字过滤、UUID过滤。

    /**@brief Function for initialization the scanning and setting the filters.
     */
    static void scan_init(void)
    {
        ret_code_t          err_code;
        nrf_ble_scan_init_t init_scan;
    
        memset(&init_scan, 0, sizeof(init_scan));
    
        init_scan.p_scan_param = &m_scan_param;
    
        err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
        APP_ERROR_CHECK(err_code);
    
        if (strlen(m_target_periph_name) != 0)
        {
            err_code = nrf_ble_scan_filter_set(&m_scan, 
                                               SCAN_NAME_FILTER, 
                                               m_target_periph_name);
            APP_ERROR_CHECK(err_code);
        }
    
        err_code = nrf_ble_scan_filter_set(&m_scan, 
                                           SCAN_UUID_FILTER, 
                                           &m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_scan_filter_set(&m_scan, 
                                           SCAN_UUID_FILTER, 
                                           &m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_scan_filters_enable(&m_scan, 
                                               NRF_BLE_SCAN_ALL_FILTER, 
                                               false);
        APP_ERROR_CHECK(err_code);
    
    }
    

    2.1.2 gap_params_init

    设置广播中蓝牙名称,连接参数。

    /**@brief Function for initializing the GAP.
     *
     * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
     *          device, including the device name, appearance, and the preferred connection parameters.
     */
    static void gap_params_init(void)
    {
        ret_code_t              err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *)DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = m_scan.conn_params.min_conn_interval;
        gap_conn_params.max_conn_interval = m_scan.conn_params.max_conn_interval;
        gap_conn_params.slave_latency     = m_scan.conn_params.slave_latency;
        gap_conn_params.conn_sup_timeout  = m_scan.conn_params.conn_sup_timeout;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        APP_ERROR_CHECK(err_code);
    }
    

    2.1.3 conn_params_init

    连接参数初始化主要作为从机更新连接参数使用。

    /**@brief Function for initializing the Connection Parameters module.
     */
    static void conn_params_init(void)
    {
        ret_code_t             err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        cp_init.p_conn_params                  = NULL;
        cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
        cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
        cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
        cp_init.start_on_notify_cccd_handle    = BLE_CONN_HANDLE_INVALID; // Start upon connection.
        cp_init.disconnect_on_fail             = true;
        cp_init.evt_handler                    = NULL;  // Ignore events.
        cp_init.error_handler                  = conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    

    2.1.4 advertising_init

    从机服务建立后,就需要对广播进行设置,广播是从机被主机发现的前提条件。

    /**@brief Function for initializing the advertising functionality.
     */
    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance      = true;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }
    

    2.1.5 adv_scan_start

    广播和扫描同时运行

    /**@brief Function for initializing the advertising and the scanning.
     */
    static void adv_scan_start(void)
    {
        ret_code_t err_code;
    
        //check if there are no flash operations in progress
        if (!nrf_fstorage_is_busy(NULL))    // 检测如果没有flash操作
        {
            // Start scanning for peripherals and initiate connection to devices which
            // advertise Heart Rate or Running speed and cadence UUIDs.
            scan_start();                   // 开始扫描
    
            // Turn on the LED to signal scanning.
            bsp_board_led_on(CENTRAL_SCANNING_LED);
    
            // Start advertising.           // 开始广播
            err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(err_code);
        }
    }
    

    三、蓝牙协议栈主机事件

    当主机发起连接成功,或断开连接时,产生主机事件

    /**@brief   Function for handling BLE events from the central application.
     *
     * @details This function parses scanning reports and initiates a connection to peripherals when a
     *          target UUID is found. It updates the status of LEDs used to report the central application
     *          activity.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     */
    static void on_ble_central_evt(ble_evt_t const * p_ble_evt)
    {
        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)
        {
            // Upon connection, check which peripheral is connected (HR or RSC), initiate DB
            // discovery, update LEDs status, and resume scanning, if necessary.
            case BLE_GAP_EVT_CONNECTED:
            {
                NRF_LOG_INFO("Central connected");
                // If no Heart Rate sensor or RSC sensor is currently connected, try to find them on this peripheral.
                if (   (m_conn_handle_hrs_c  == BLE_CONN_HANDLE_INVALID)
                    || (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID))
                {
                    NRF_LOG_INFO("Attempt to find HRS or RSC on conn_handle 0x%x", p_gap_evt->conn_handle);
    
                    err_code = ble_db_discovery_start(&m_db_discovery[0], p_gap_evt->conn_handle);
                    if (err_code == NRF_ERROR_BUSY)
                    {
                        err_code = ble_db_discovery_start(&m_db_discovery[1], p_gap_evt->conn_handle);
                        APP_ERROR_CHECK(err_code);
                    }
                    else
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
    
                // Assign connection handle to the QWR module.
                multi_qwr_conn_handle_assign(p_gap_evt->conn_handle);
    
                // Update LEDs status, and check whether to look for more peripherals to connect to.
                bsp_board_led_on(CENTRAL_CONNECTED_LED);
                if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
                {
                    bsp_board_led_off(CENTRAL_SCANNING_LED);
                }
                else
                {
                    // Resume scanning.
                    bsp_board_led_on(CENTRAL_SCANNING_LED);
                    scan_start();
                }
            } break; // BLE_GAP_EVT_CONNECTED
    
            // Upon disconnection, reset the connection handle of the peer that disconnected,
            // update the LEDs status and start scanning again.
            case BLE_GAP_EVT_DISCONNECTED:
            {
                if (p_gap_evt->conn_handle == m_conn_handle_hrs_c)
                {
                    NRF_LOG_INFO("HRS central disconnected (reason: %d)",
                                 p_gap_evt->params.disconnected.reason);
    
                    m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID;
                    
                    err_code = nrf_ble_scan_filter_set(&m_scan, 
                                                       SCAN_UUID_FILTER, 
                                                       &m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
                    APP_ERROR_CHECK(err_code);
                }
                if (p_gap_evt->conn_handle == m_conn_handle_rscs_c)
                {
                    NRF_LOG_INFO("RSC central disconnected (reason: %d)",
                                 p_gap_evt->params.disconnected.reason);
    
                    m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID;
    
                    err_code = nrf_ble_scan_filter_set(&m_scan, 
                                                       SCAN_UUID_FILTER, 
                                                       &m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
                    APP_ERROR_CHECK(err_code);
                }
    
                if (   (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)
                    || (m_conn_handle_hrs_c  == BLE_CONN_HANDLE_INVALID))
                {
                    // Start scanning.
                    scan_start();
    
                    // Update LEDs status.
                    bsp_board_led_on(CENTRAL_SCANNING_LED);
                }
    
                if (ble_conn_state_central_conn_count() == 0)
                {
                    bsp_board_led_off(CENTRAL_CONNECTED_LED);
                }
            } break; // BLE_GAP_EVT_DISCONNECTED
    
            case BLE_GAP_EVT_TIMEOUT:
            {
                // No timeout for scanning is specified, so only connection attemps can timeout.
                if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
                {
                    NRF_LOG_INFO("Connection Request timed out.");
                }
            } break;
    
            case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
            {
                // Accept 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:
                // No implementation needed.
                break;
        }
    }
    

    四、蓝牙协议栈从机事件

    当从机被连接,或断开连接时,产生从机事件

    /**@brief   Function for handling BLE events from peripheral applications.
     * @details Updates the status LEDs used to report the activity of the peripheral applications.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     */
    static void on_ble_peripheral_evt(ble_evt_t const * p_ble_evt)
    {
        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_CONNECTED:
                NRF_LOG_INFO("Peripheral connected");
                bsp_board_led_off(PERIPHERAL_ADVERTISING_LED);
                bsp_board_led_on(PERIPHERAL_CONNECTED_LED);
    
                // Assign connection handle to the QWR module.
                multi_qwr_conn_handle_assign(p_ble_evt->evt.gap_evt.conn_handle);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Peripheral disconnected. conn_handle: 0x%x, reason: 0x%x",
                             p_gap_evt->conn_handle,
                             p_gap_evt->params.disconnected.reason);
    
                bsp_board_led_off(PERIPHERAL_CONNECTED_LED);
                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:
                // No implementation needed.
                break;
        }
    }
    

    • 由 Leung 写于 2020 年 9 月 30 日

    • 参考:青风电子社区

    相关文章

      网友评论

        本文标题:NRF52832学习笔记(29)——主从一体

        本文链接:https://www.haomeiwen.com/subject/hqjuuktx.html