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

nrf51822---主從通訊(1)

1.目的

   學習nrf51822主機和從機通訊

2.分析

    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.步驟

   1.先把從機的程式下載到一個板子裡面

   注意:a.32.769khz晶體的選型。b.SDK 以及softwaredevice的版本號

    2.來分析下主機的程式

協議棧初始化,串列埠服務初始化,開始掃描引數初始化以及開始掃描

int main(void)
{
    uint32_t err_code;
    
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, NULL);
    
    uart_init();
    buttons_leds_init();
    
    ble_stack_init();
    
    err_code = ble_db_discovery_init();
    APP_ERROR_CHECK(err_code);
    
    nus_c_init();

    // Start scanning for peripherals and initiate connection
    // with devices that advertise NUS UUID.
    scan_start();
    APPL_LOG("Scan started\r\n");
    for (;;)
    {
        power_manage();
    }
}

這裡初始化回撥函式,一個串列埠服務初始化

/**@brief Function for initializing the NUS Client.
 */
static void nus_c_init(void)
{
    uint32_t         err_code;
    ble_nus_c_init_t nus_c_init_t;
    
    nus_c_init_t.evt_handler = ble_nus_c_evt_handler;
    
    err_code = ble_nus_c_init(&m_ble_nus_c, &nus_c_init_t);
    APP_ERROR_CHECK(err_code);
}

這裡是最終註冊函式,看如下函式

sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type); 獲得uuid的型別

 uuid的型別有 

/** @defgroup BLE_UUID_TYPES Types of UUID
 * @{ */
#define BLE_UUID_TYPE_UNKNOWN       0x00 /**< Invalid UUID type. */
#define BLE_UUID_TYPE_BLE           0x01 /**< Bluetooth SIG UUID (16-bit). */
#define BLE_UUID_TYPE_VENDOR_BEGIN  0x02 /**< Vendor UUID types start at this index (128-bit). */
/** @} */

  uart_uuid.type = p_ble_nus_c->uuid_type;  //串列埠服務的型別
  uart_uuid.uuid = BLE_UUID_NUS_SERVICE; //串列埠主服務的UUID
uint32_t ble_nus_c_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init)
{
    uint32_t      err_code;
    ble_uuid_t    uart_uuid;
    ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
        
    if ((p_ble_nus_c == NULL) || (p_ble_nus_c_init == NULL))
    {
        return NRF_ERROR_NULL;
    }
    
    err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    uart_uuid.type = p_ble_nus_c->uuid_type;
    uart_uuid.uuid = BLE_UUID_NUS_SERVICE;
    
    // save the pointer to the ble_uart_c_t struct locally
    mp_ble_nus_c = p_ble_nus_c;
    
    p_ble_nus_c->conn_handle   = BLE_CONN_HANDLE_INVALID;
    p_ble_nus_c->evt_handler   = p_ble_nus_c_init->evt_handler;
    p_ble_nus_c->nus_rx_handle = BLE_GATT_HANDLE_INVALID;
    p_ble_nus_c->nus_tx_handle = BLE_GATT_HANDLE_INVALID;
    
    return ble_db_discovery_evt_register(&uart_uuid, db_discover_evt_handler);
}

串列埠服務回撥函式可以看到對應的事件型別的處理,因為串列埠服務只有2個特徵值,一個notify(通知) 和一個write and write no response(寫並無返回)的屬性

 這裡在描述對應事件的時候就會產生對應的事件,比如掃描的時候發現了write and write no response(BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC)的特徵值就是跳到BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC事件,

 掃描到notify(BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC)的事件,主機要使能從機,這樣才能接到從機主動發來的資訊,

BLE_NUS_C_EVT_NUS_RX_EVT 事件是,當使能了從機,從機發送過來資訊的時候產生的事件,

BLE_NUS_C_EVT_DISCONNECTED ,藍芽斷開事件,斷開從新開始掃描

<pre name="code" class="cpp">/**@snippet [Handling events from the ble_nus_c module] */ 
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt)
{
    uint32_t err_code;
    switch (p_ble_nus_evt->evt_type)
    {
        case BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC:
            APPL_LOG("The device has the device TX characteristic\r\n");
            break;
        
        case BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC:
            err_code = ble_nus_c_rx_notif_enable(p_ble_nus_c);
            APP_ERROR_CHECK(err_code);
            APPL_LOG("The device has the device RX characteristic\r\n");
            break;
                case BLE_NUS_C_EVT_NUS_RX_EVT:
            for (uint32_t i = 0; i < p_ble_nus_evt->data_len; i++)
            {
                while(app_uart_put( p_ble_nus_evt->p_data[i]) != NRF_SUCCESS);
            }
            break;
        
        case BLE_NUS_C_EVT_DISCONNECTED:
            APPL_LOG("NUS device disconnected\r\n");
            scan_start();
            break;
    }
}

開始掃描主要是掃描引數的設定。
<pre name="code" class="cpp">#define SCAN_INTERVAL           0x00A0                          /**< Determines scan interval in units of 0.625 millisecond. */
#define SCAN_WINDOW             0x0050                          /**< Determines scan window in units of 0.625 millisecond. */
#define SCAN_ACTIVE             1                               /**< If 1, performe active scanning (scan requests). */
#define SCAN_SELECTIVE          0                               /**< If 1, ignore unknown devices (non whitelisted). */
#define SCAN_TIMEOUT            0x0000    

/**
 * @brief Parameters used when scanning.
 */
static const ble_gap_scan_params_t m_scan_params = 
  {
    .active      = SCAN_ACTIVE,
    .selective   = SCAN_SELECTIVE,
    .p_whitelist = NULL,
    .interval    = SCAN_INTERVAL,
    .window      = SCAN_WINDOW,
    .timeout     = SCAN_TIMEOUT
  };

/**@brief Function to start scanning.
 */
static void scan_start(void)
{
    uint32_t err_code;
    
    err_code = sd_ble_gap_scan_start(&m_scan_params);
    APP_ERROR_CHECK(err_code);
    
    err_code = bsp_indication_set(BSP_INDICATE_SCANNING);
    APP_ERROR_CHECK(err_code);
}

開始掃描後如果發現了廣播資料,就會產生廣播報告事件。即BLE_GAP_EVT_ADV_REPORT事件,然後在通過is_uuid_present() 函式來找對應的是UUID以及型別,通過這2個特徵來識別是不是要連線的從機,然後通過sd_ble_gap_connect來連線從機,連線是通過mac地址來連線的如下

err_code = sd_ble_gap_connect(&p_adv_report->peer_addr,
                                              &m_scan_params,
                                              &m_connection_param);

 當連線上了藍芽產生,BLE_GAP_EVT_CONNECTED事件,在這裡,

    開始發現服務和特徵值:ble_db_discovery_start(&m_ble_db_discovery, p_ble_evt->evt.gap_evt.conn_handle);具體程式碼如下:

/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 *
 * @details This function is called from the scheduler in the main loop after a BLE stack event has
 *  been received.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 */
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    on_ble_evt(p_ble_evt);  
    bsp_btn_ble_on_ble_evt(p_ble_evt);
    ble_db_discovery_on_ble_evt(&m_ble_db_discovery, p_ble_evt);
    ble_nus_c_on_ble_evt(&m_ble_nus_c,p_ble_evt);
}


/**@brief Function for handling the Application's BLE Stack events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 */
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    uint32_t              err_code;
    const ble_gap_evt_t * p_gap_evt = &p_ble_evt->evt.gap_evt;	
    
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_ADV_REPORT:
        {
            const ble_gap_evt_adv_report_t *p_adv_report = &p_gap_evt->params.adv_report;
                        
            if (is_uuid_present(&m_nus_uuid, p_adv_report))
            {

                err_code = sd_ble_gap_connect(&p_adv_report->peer_addr,
                                              &m_scan_params,
                                              &m_connection_param);
                
                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);
                    APPL_LOG("Connecting to target %02x%02x%02x%02x%02x%02x\r\n",
                             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]
                             );
                }
            }
            break;
        }       
        case BLE_GAP_EVT_CONNECTED:
            APPL_LOG("Connected to target\r\n");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            
            m_ble_nus_c.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            // start discovery of services. The NUS Client waits for a discovery result
            err_code = ble_db_discovery_start(&m_ble_db_discovery, 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)
            {
                APPL_LOG("[APPL]: Scan timed out.\r\n");
                scan_start();
            }
            else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
            {
                APPL_LOG("[APPL]: Connection Request timed out.\r\n");
            }
            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;
           default:
            break;
    }
}

開始發現服務,也會產生對應的事件,程式碼如下:第一次掃描發現的是主服務:產生的是BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP事件。表示找到了主服務

void ble_db_discovery_on_ble_evt(ble_db_discovery_t * const p_db_discovery,
                                 const ble_evt_t * const    p_ble_evt)
{
    if ((p_db_discovery == NULL) || (p_ble_evt == NULL))
    {
        // Null pointer provided. Return.
        return;
    }

    if (!m_initialized)
    {
        return;
    }
      switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
					SEGGER_RTT_printf(0,"BLE_GAP_EVT_CONNECTED\r\n");
            p_db_discovery->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;
        
        case BLE_GAP_EVT_DISCONNECTED:
            memset(p_db_discovery, 0, sizeof(ble_db_discovery_t));
            p_db_discovery->conn_handle = BLE_CONN_HANDLE_INVALID;
            break;

        case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:  <strong>//找到了主服務</strong>
					SEGGER_RTT_printf(0,"BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP\r\n");
            on_primary_srv_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
            break;
        case BLE_GATTC_EVT_CHAR_DISC_RSP:
					 SEGGER_RTT_printf(0,"BLE_GATTC_EVT_CHAR_DISC_RSP\r\n");
            on_characteristic_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
            break;

        case BLE_GATTC_EVT_DESC_DISC_RSP:
					SEGGER_RTT_printf(0,"BLE_GATTC_EVT_DESC_DISC_RSP\r\n");
					  //SEGGER_RTT_printf(0,"\r\np_ble_evt->header.evt_id=%d\r\n,p_ble_evt->header.evt_id");
            on_descriptor_discovery_rsp(p_db_discovery, &(p_ble_evt->evt.gattc_evt));
            break;

        default:
            break;
    }
}

這裡是發現主服務之後的處理,找特徵值:characteristics_discover(p_db_discovery);

static void on_primary_srv_discovery_rsp(ble_db_discovery_t * const    p_db_discovery,
                                         const ble_gattc_evt_t * const p_ble_gattc_evt)
{
    if (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS)
    {
        uint32_t err_code;
        const ble_gattc_evt_prim_srvc_disc_rsp_t * p_prim_srvc_disc_rsp_evt;
        ble_gatt_db_srv_t                        * p_srv_being_discovered;

        p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);

        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);       
        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);
        }
    }
    else
    {
        // Trigger Service Not Found event to the application.
        discovery_complete_evt_trigger(p_db_discovery, false);
   
        on_srv_disc_completion(p_db_discovery);
    }
}
找到特徵值後會產生BLE_GATTC_EVT_CHAR_DISC_RSP事件,然後會處理,on_characteristic_discovery_rsp()函式,該函式注意2點,是否全部找到了特徵值,如果沒有找到繼續找,如果找到了,這找描述符。

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

   判斷是否找全了特徵值,或者是否超出了設定的特徵值的數量,BLE_GATT_DB_MAX_CHARS是預定義的一個特徵值的個數,意思就是,假如一個服務裡面有超過BLE_GATT_DB_MAX_CHARS特徵值,則認為是全部找到了。可以在專案中適當的改變這個值的大小。

  當全部找到了特徵值,則descriptors_discover(p_db_discovery, &raise_discov_complete); 找描述符

static void on_characteristic_discovery_rsp(ble_db_discovery_t * const    p_db_discovery,
                             const 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;    

    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)
    {
        const ble_gattc_evt_char_disc_rsp_t * 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;
        }
              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); <strong> //找特徵值</strong>

            if (err_code != NRF_SUCCESS)
            {
                p_db_discovery->discovery_in_progress = false;

                discovery_error_evt_trigger(p_db_discovery, err_code);
                 
                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); <strong>//找描述符</strong>

        if (err_code != NRF_SUCCESS)
        {
            p_db_discovery->discovery_in_progress = false;

            discovery_error_evt_trigger(p_db_discovery, err_code);
          
            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.
            DB_LOG("[DB]: Discovery of service with UUID 0x%x completed with success for Connection"
                   " handle %d\r\n", p_srv_being_discovered->srv_uuid.uuid,
                   p_db_discovery->conn_handle);

            discovery_complete_evt_trigger(p_db_discovery, true);
             
            on_srv_disc_completion(p_db_discovery);
        }
    }
}

找到了描述符則會產生BLE_GATTC_EVT_DESC_DISC_RSP事件,如果沒有找全 則descriptors_discover(p_db_discovery, &raise_discov_complete);,再次發現描述符,並再次產生BLE_GATTC_EVT_DESC_DISC_RSP事件,如果全部找到了則 discovery_complete_evt_trigger(p_db_discovery, true);
/**@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;

    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.
        // If the descriptor was a CCCD, then the cccd_handle needs to be populated.

        uint32_t i;

        // Loop through all the descriptors to find the CCCD.
        for (i = 0; i < p_desc_disc_rsp_evt->count; i++)
        {
            if (
                p_desc_disc_rsp_evt->descs[i].uuid.uuid ==
                BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG
               )
            {                p_char_being_discovered->cccd_handle = p_desc_disc_rsp_evt->descs[i].handle;

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

        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);
    
            return;
        }
    }
    if (raise_discov_complete)
    {
        DB_LOG("[DB]: Discovery of service with UUID 0x%x completed with success for Connection"
               "handle %d\r\n", p_srv_being_discovered->srv_uuid.uuid,
               p_db_discovery->conn_handle);
        SEGGER_RTT_printf(0,"Discovery\r\n");
        discovery_complete_evt_trigger(p_db_discovery, true);

        on_srv_disc_completion(p_db_discovery);
    }
}

現在來具體分析下這個函式:

首先找到對應的回撥函式p_evt_handler = registered_handler_get(&(p_srv_being_discovered->srv_uuid));

 nus_c_init(); --->  ble_nus_c_init(&m_ble_nus_c, &nus_c_init_t);-->ble_db_discovery_evt_register(&uart_uuid, db_discover_evt_handler);--->registered_handler_set(p_uuid, evt_handler);

static uint32_t registered_handler_set(const ble_uuid_t * const       p_srv_uuid,
                                       ble_db_discovery_evt_handler_t p_evt_handler)
{
    if (m_num_of_handlers_reg < DB_DISCOVERY_MAX_USERS)
    {
        m_registered_handlers[m_num_of_handlers_reg].srv_uuid    = *p_srv_uuid;
        m_registered_handlers[m_num_of_handlers_reg].evt_handler = p_evt_handler;


        m_num_of_handlers_reg++;


        return NRF_SUCCESS;
    }
    else
    {
        return NRF_ERROR_NO_MEM;
    }
}

通過一層一層,把串列埠的服務的型別 和回撥函式複製給了m_registered_handlers結構體儲存期來了,如果還有其他的服務也可以按照同樣步驟新增。

最後把static void db_discover_evt_handler(ble_db_discovery_evt_t * p_evt)函式地址儲存在m_registered_handlers裡面了

再看

static ble_db_discovery_evt_handler_t registered_handler_get(ble_uuid_t * p_srv_uuid)
{
    uint32_t i;


    for (i = 0; i < m_num_of_handlers_reg; i++)
    {
        if (BLE_UUID_EQ(&(m_registered_handlers[i].srv_uuid), p_srv_uuid))
        {
            return (m_registered_handlers[i].evt_handler);
        }
    }


    return NULL;
}

這裡就是把輸入的UUID的型別和m_registered_handlers儲存的UUID的型別對比,如果一樣則返回它對應的回撥函式。

然後在m_pending_user_evts結構體賦值。因為is_srv_found == true

     if (is_srv_found)
            {
                m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =
                    BLE_DB_DISCOVERY_COMPLETE;


                m_pending_user_evts[m_pending_usr_evt_index].evt.params.discovered_db =
                    *p_srv_being_discovered;
            }

最後執行, pending_user_evts_send();

/**@brief Function for sending all pending discovery events to the corresponding user modules.
 */
static void pending_user_evts_send(void)
{
    uint32_t i;


    for (i = 0; i < m_num_of_handlers_reg; i++)
    {
        // Pass the event to the corresponding event handler.
        m_pending_user_evts[i].evt_handler(&(m_pending_user_evts[i].evt));
    }
    
    m_pending_usr_evt_index = 0;
}

在discovery_complete_evt_trigger裡面

    p_evt_handler = registered_handler_get(&(p_srv_being_discovered->srv_uuid));   ------>  m_pending_user_evts[m_pending_usr_evt_index].evt_handler         = p_evt_handler; // 把回撥函式賦值給m_pending_user_evts對應的地址。

   m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =   BLE_DB_DISCOVERY_COMPLETE;

這樣就執行了db_discover_evt_handler(ble_db_discovery_evt_t * p_evt)函式

static void discovery_complete_evt_trigger(ble_db_discovery_t * const p_db_discovery,
                                           bool                       is_srv_found)
{
    ble_db_discovery_evt_handler_t p_evt_handler;
    ble_gatt_db_srv_t            * p_srv_being_discovered;

    p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);

    p_evt_handler = registered_handler_get(&(p_srv_being_discovered->srv_uuid));

    if (p_evt_handler != NULL)
    {
        if (m_pending_usr_evt_index < DB_DISCOVERY_MAX_USERS)
        {           // Insert an event into the pending event list.
            m_pending_user_evts[m_pending_usr_evt_index].evt.conn_handle =
                p_db_discovery->conn_handle;

            if (is_srv_found)
            {
                m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =
                    BLE_DB_DISCOVERY_COMPLETE;

                m_pending_user_evts[m_pending_usr_evt_index].evt.params.discovered_db =
                    *p_srv_being_discovered;
            }
            else
            {              m_pending_user_evts[m_pending_usr_evt_index].evt.evt_type =
                    BLE_DB_DISCOVERY_SRV_NOT_FOUND;
            }
            m_pending_user_evts[m_pending_usr_evt_index].evt_handler = p_evt_handler;

            m_pending_usr_evt_index++;

            if (m_pending_usr_evt_index == m_num_of_handlers_reg)
            {
                // All registered modules have pending events. Send all pending events to the user
                // modules.
                pending_user_evts_send();
            }
            else
            {
                // Too many events pending. Do nothing. (Ideally this should not happen.)
            }
        }
    }
}

這裡會把發現的特徵值全部過一遍,然後執行對應的處理。執行對應的回撥

經過 nus_c_init()---> ble_nus_c_init(); 執行 ,把 ble_nus_c_evt_handler函式地址賦值給了mp_ble_nus_c.evt_handler

所以mp_ble_nus_c->evt_handler 就是 ble_nus_c_evt_handler。

p_evt就發現一個服務的全部資訊的結構體,比如主服務的UUID,特徵值的UUID一個許可權,handle,發現特徵值的個數(p_evt->params.discovered_db.char_count)等等,因為串列埠有2個特徵值,write和notify 對應BLE_UUID_NUS_TX_CHARACTERISTIC和BLE_UUID_NUS_RX_CHARACTERISTIC 2個UUID,所以只要在找到對應的uuid,並儲存下handle就好了。(假如提前並不知道對方的屬性呢?)

 注意:為什麼這裡要儲存handle,因為當主機發送要發資料給從機的時候,比如有好幾個特徵值,要往某個特定的UUID傳送資料,就必須知道地址,而handle就是類是地址,每個特徵值對應的地址,當主機連上從機後,知道了handle就可以往特定的許可權的UUID通訊了。

static void db_discover_evt_handler(ble_db_discovery_evt_t * p_evt)
{
    // Check if the NUS was discovered.
    if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
        p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_NUS_SERVICE &&
        p_evt->params.discovered_db.srv_uuid.type == mp_ble_nus_c->uuid_type)
    {

        uint32_t i;
       
        for (i = 0; i < p_evt->params.discovered_db.char_count; i++)
        {
            if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid 
                == BLE_UUID_NUS_TX_CHARACTERISTIC)
            {
                mp_ble_nus_c->nus_tx_handle =
                    p_evt->params.discovered_db.charateristics[i].characteristic.handle_value;
                
                if (mp_ble_nus_c->evt_handler != NULL)                {
                    ble_nus_c_evt_t nus_c_evt;
                    
                    nus_c_evt.evt_type = BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC;
                    mp_ble_nus_c->evt_handler(mp_ble_nus_c, &nus_c_evt);
                }
            }
            else if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid 
                        == BLE_UUID_NUS_RX_CHARACTERISTIC)
            {
                mp_ble_nus_c->nus_rx_handle =
                    p_evt->params.discovered_db.charateristics[i].characteristic.handle_value;
                mp_ble_nus_c->nus_rx_cccd_handle =
                    p_evt->params.discovered_db.charateristics[i].cccd_handle;  

                if (mp_ble_nus_c->evt_handler != NULL)                {
                    ble_nus_c_evt_t nus_c_evt;
                
                    nus_c_evt.evt_type = BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC;
                    mp_ble_nus_c->evt_handler(mp_ble_nus_c, &nus_c_evt);
                }
            }
        }
    }
}


接下看對應的事件會對應對的case 事件。這裡看 BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC 事件。這事件表示特徵值的屬性為notify 所以要使能監聽才能收到資料,使能操作ble_nus_c_rx_notif_enable(p_ble_nus_c);

BLE_NUS_C_EVT_NUS_RX_EVT為接收從機發送資料事件。

/**@snippet [Handling events from the ble_nus_c module] */ 
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt)
{
    uint32_t err_code;
    switch (p_ble_nus_evt->evt_type)
    {
        case BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC:
            APPL_LOG("The device has the device TX characteristic\r\n");
            break;
        
        case BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC:
            err_code = ble_nus_c_rx_notif_enable(p_ble_nus_c);
            APP_ERROR_CHECK(err_code);
            APPL_LOG("The device has the device RX characteristic\r\n");
            break;       
        case BLE_NUS_C_EVT_NUS_RX_EVT:
            for (uint32_t i = 0; i < p_ble_nus_evt->data_len; i++)
            {
                while(app_uart_put( p_ble_nus_evt->p_data[i]) != NRF_SUCCESS);
            }
            break;
        
        case BLE_NUS_C_EVT_DISCONNECTED:
            APPL_LOG("NUS device disconnected\r\n");
            scan_start();
            break;
    }
}


好了 到現在大概分析完了程式執行的流程。。。。。。上一張圖對應ble_db_discovery_on_ble_evt()函式,執行流程