1. 程式人生 > >nrf51822---主從一體通訊(3)

nrf51822---主從一體通訊(3)

.目的

   學習nrf51822主機和從機通訊

2.分析

    nrf51822---主從通訊(1)和nrf51822---主從通訊(2)都是在已知服務的基礎上面來找的。但是在實際中並不知道對方的屬性,可能只告訴你要連線藍芽的名字。那怎麼辦呢???

3.平臺:

協議棧版本:SDK10.0.0

編譯軟體:keil 5.12

硬體平臺:nrf51822最小系統

例子:SDK 10.0.0\examples\ble_peripheral\ble_app_uart\pca10028\s110\arm4 從機例子

          SDK10.0\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay\pca10028\s130\arm4 做主機

4步驟.

  首先來大概分析下程式碼。

    首先在主函式裡.2個從機,控制服務初始化的過程,然後掃描開始,廣播開始,即主從一體

int main(void)
{
    ret_code_t err_code;
    bool       erase_bonds;

    err_code = NRF_LOG_INIT();
    APP_ERROR_CHECK(err_code);

    NRF_LOG_PRINTF("Relay Example\r\n");

    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, NULL);
    buttons_leds_init(&erase_bonds);

    if (erase_bonds == true)
    {
        NRF_LOG("Bonds erased!\r\n");
    }
    ble_stack_init();

    peer_manager_init(erase_bonds);

    db_discovery_init();
    hrs_c_init();  //HES初始化
    rscs_c_init(); //rscs初始化

    gap_params_init();
    conn_params_init();
    services_init();
    advertising_init();

    /** 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.
    LEDS_ON(CENTRAL_SCANNING_LED);

    // Start advertising.
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST); //廣播初始化
    APP_ERROR_CHECK(err_code);

    for (;;)
    {
        // Wait for BLE events.
        power_manage();
    }
}

接下來看掃描到了廣播,產生BLE_GAP_EVT_ADV_REPORT事件程式碼:

 case BLE_GAP_EVT_ADV_REPORT:
        {
            uint32_t err_code;
            data_t   adv_data;
            data_t   type_data;

            // For readibility.  找到掃描的地址
            const ble_gap_addr_t  * const peer_addr = &p_gap_evt->params.adv_report.peer_addr;

            // Initialize advertisement report for parsing.
            adv_data.p_data     = (uint8_t *)p_gap_evt->params.adv_report.data;  //掃描廣播的資料地址
            adv_data.data_len   = p_gap_evt->params.adv_report.dlen;  //掃描廣播的長度
            //找識別符號為BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE的資料地址和長度
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE,
                                        &adv_data,
                                        &type_data);
      if (err_code != NRF_SUCCESS)
            {
                // Look for the services in 'complete' if it was not found in 'more available'.
                err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE,
                                            &adv_data,
                                            &type_data);

                if (err_code != NRF_SUCCESS)
                {
                    // If we can't parse the data, then exit.
                    break;
                }
            }
            // Verify if any UUID match the Heart rate or Running speed and cadence services.
            for (uint32_t u_index = 0; u_index < (type_data.data_len / UUID16_SIZE); u_index++)
            {
                bool        do_connect = false;
                uint16_t    extracted_uuid;
                  //複製出UUID到extracted
                UUID16_EXTRACT(&extracted_uuid, &type_data.p_data[u_index * UUID16_SIZE]);
                /** We do not want to connect to two peripherals offering the same service, so when
                 *  a UUID is matched, we check that we are not already connected to a peer which
                 *  offers the same service. We then save the peer address, so that upon connection
                 *  we can tell which peer has connected and update its respective connection
                 *  handle. */ //判斷UUID是不是心率的UUID並且看是否已經連上了
                if ((extracted_uuid      == BLE_UUID_HEART_RATE_SERVICE) &&
                    (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID))
                {               do_connect = true;
                    memcpy(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)); //複製掃描的藍芽地址到periph_addr_hrs
                }    //或者UUID是不是RSCS的UUID並且沒有連上
                else if ((extracted_uuid       == BLE_UUID_RUNNING_SPEED_AND_CADENCE) &&
                         (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID))
                {
                    do_connect = true;
                    memcpy(&periph_addr_rsc, peer_addr, sizeof(ble_gap_addr_t)); //複製藍芽地址
                }

                if (do_connect)
                {
                    // Initiate connection.   //連線這地址的裝置
                    err_code = sd_ble_gap_connect(peer_addr, &m_scan_param, &m_connection_param);
                    if (err_code != NRF_SUCCESS)
                    {
                        APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code);
                    }
                }
            }
        } break; // BLE_GAP_ADV_REPORT

 如果連上了裝置則產生BLE_GAP_EVT_CONNECTED事件

   case BLE_GAP_EVT_CONNECTED:
        {
            uint32_t err_code;

            // For readability.
            const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.connected.peer_addr;  //找到藍芽的地址

            /** Check which peer has connected, save the connection handle and initiate DB discovery.
             *  DB discovery will invoke a callback (hrs_c_evt_handler and rscs_c_evt_handler)
             *  upon completion, which is used to enable notifications from the peer. */                                                                           if(memcmp(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)) == 0)  //連線上的藍芽地址和和被連線的藍芽地址對比 一直則正確
            {
                NRF_LOG_PRINTF("HRS central connected\r\n");
                // Reset the peer address we had saved.
                memset(&periph_addr_hrs, 0, sizeof(ble_gap_addr_t));

                m_conn_handle_hrs_c = p_gap_evt->conn_handle;  //儲存連線的handle  這個很總有可以用來區別哪個裝置

                NRF_LOG_PRINTF("Starting DB discovery for HRS\r\n");
                err_code = ble_db_discovery_start(&m_ble_db_discovery_hrs, p_gap_evt->conn_handle);  //開始發現服務
                APP_ERROR_CHECK(err_code);
            }            else if(memcmp(&periph_addr_rsc, peer_addr, sizeof(ble_gap_addr_t)) == 0)  <span style="font-family: Arial, Helvetica, sans-serif;">//連線上的藍芽地址和和被連線的藍芽地址對比 一直則正確</span>
            {
                NRF_LOG_PRINTF("RSC central connected\r\n");
                // Reset the peer address we had saved.
                memset(&periph_addr_rsc, 0, sizeof(ble_gap_addr_t)); 

                m_conn_handle_rscs_c = p_gap_evt->conn_handle;//儲存連線的handle

                NRF_LOG_PRINTF("Starting DB discovery for RSCS\r\n");
                err_code = ble_db_discovery_start(&m_ble_db_discovery_rsc, p_gap_evt->conn_handle);  //開始掃描服務裝置
                APP_ERROR_CHECK(err_code);
            }

            /** Update LEDs status, and check if we should be looking for more
             *  peripherals to connect to. */
            LEDS_ON(CENTRAL_CONNECTED_LED);
            if (ble_conn_state_n_centrals() == MAX_CONNECTED_CENTRALS)//判斷連線的裝置是否為設定的裝置值
            {
                LEDS_OFF(CENTRAL_SCANNING_LED);//
            }
            else         {
                // Resume scanning.
                LEDS_ON(CENTRAL_SCANNING_LED);
                scan_start();  //繼續掃描
            }
        } break; // BLE_GAP_EVT_CONNECTED

斷開某裝置。會產生BLE_GAP_EVT_DISCONNECTED事件,

  case BLE_GAP_EVT_DISCONNECTED:
        {
            uint8_t n_centrals;

            if (p_gap_evt->conn_handle == m_conn_handle_hrs_c) //判斷斷開的裝置是哪個裝置,是否為HRS裝置。找出對應的handle
            {
                NRF_LOG_PRINTF("HRS central disconnected (reason: %d)\r\n",
                       p_gap_evt->params.disconnected.reason);

                m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID;  //handle初始化
            }
            else if(p_gap_evt->conn_handle == m_conn_handle_rscs_c) //判斷斷開的裝置是不是RSCS裝置
            {
                NRF_LOG_PRINTF("RSC central disconnected (reason: %d)\r\n",
                       p_gap_evt->params.disconnected.reason);

                m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID; //handle初始化
            }
            // Start scanning
            scan_start(); //這裡會不會衝突。

            // Update LEDs status.
            LEDS_ON(CENTRAL_SCANNING_LED);
            n_centrals = ble_conn_state_n_centrals();
            if (n_centrals == 0)
            {
                LEDS_OFF(CENTRAL_CONNECTED_LED);
            }
        } break; // BLE_GAP_EVT_DISCONNECTED

以上程式碼發現,如果連上了2個裝置這個時候,不再scan_start(),在使用中斷開了一個裝置是否需要從新scan_start()再次連線一個呢?或者又如,值連線了一個裝置,並且主機也還在掃描,並且這個連上的裝置也斷開了,假如呼叫了scan_start();會怎麼樣?實驗證明無影響。。