1.1、蓝牙的广播在蓝牙开发中占有重要地位,广播频率直接影响到功耗问题和连接快慢,传输数据的快慢问题。现在来看一下广播的初始化。
下面那是源码 :

static void advertising_init(void)
{
    uint32_t               err_code;
    ble_advdata_t          advdata;
    ble_adv_modes_config_t options;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    
    advdata.name_type               = BLE_ADVDATA_FULL_NAME;//广播名
    advdata.include_appearance      = true;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;//蓝牙设备模式
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = m_adv_uuids;   
    
    memset(&options, 0, sizeof(options));
    options.ble_adv_fast_enabled  = true;//广播 类型 :快速广播使能
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;//广播 间隔
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;//广播超时时间
    /************************************自己增加的*************************************************/
		options.ble_adv_slow_enabled  = true;//广播 类型 :慢速广播使能
		options.ble_adv_slow_interval  = 400;//广播 间隔 
		options.ble_adv_slow_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;//广播  超时时间 
		/***************************************************************************************************/
    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);//广播参数初始化
    APP_ERROR_CHECK(err_code);
}

广播初始化实际上就是给两个参数 :advdata和options,赋值并初始化。可以下这两个类型:
(1)、 ble_advdata_t advdata

typedef struct
{
    ble_advdata_name_type_t      name_type;                           /**< Type of device name. */
    uint8_t                      short_name_len;                      /**< Length of short device name (if short type is specified). */
    bool                         include_appearance;                  /**< Determines if Appearance shall be included. */
    uint8_t                      flags;                               /**< Advertising data Flags field. */
    int8_t *                     p_tx_power_level;                    /**< TX Power Level field. */
    ble_advdata_uuid_list_t      uuids_more_available;                /**< List of UUIDs in the 'More Available' list. */
    ble_advdata_uuid_list_t      uuids_complete;                      /**< List of UUIDs in the 'Complete' list. */
    ble_advdata_uuid_list_t      uuids_solicited;                     /**< List of solicited UUIDs. */
    ble_advdata_conn_int_t *     p_slave_conn_int;                    /**< Slave Connection Interval Range. */
    ble_advdata_manuf_data_t *   p_manuf_specific_data;               /**< Manufacturer specific data. */
    ble_advdata_service_data_t * p_service_data_array;                /**< Array of Service data structures. */
    uint8_t                      service_data_count;                  /**< Number of Service data structures. */
    bool                         include_ble_device_addr;             /**< Determines if LE Bluetooth Device Address shall be included. */
    ble_advdata_le_role_t        le_role;                             /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */
    ble_advdata_tk_value_t *     p_tk_value;                          /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
    uint8_t *                    p_sec_mgr_oob_flags;                 /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
    ble_gap_lesc_oob_data_t *    p_lesc_data;                         /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
} ble_advdata_t;

我们可以看出,这个结构体定义了和广播相关的类型,比如:名称,名称长度,设备模式,发射功率(参数:p_tx_power_level)等,可以根据不同的需求,进行更改。先开一下模式参数: uint8_t flags赋值:

/**@defgroup BLE_GAP_ADV_FLAGS GAP Advertisement Flags
 * @{ */
#define BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE         (0x01)   /**< LE Limited Discoverable Mode. */
#define BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE         (0x02)   /**< LE General Discoverable Mode. */
#define BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED         (0x04)   /**< BR/EDR not supported. */
#define BLE_GAP_ADV_FLAG_LE_BR_EDR_CONTROLLER         (0x08)   /**< Simultaneous LE and BR/EDR, Controller. */
#define BLE_GAP_ADV_FLAG_LE_BR_EDR_HOST               (0x10)   /**< Simultaneous LE and BR/EDR, Host. */
#define BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE   (BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED)   /**< LE Limited Discoverable Mode, BR/EDR not supported. */
#define BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE   (BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED)   /**< LE General Discoverable Mode, BR/EDR not supported. */
/**@} */

蓝牙模式有两种:LE模式和BR/EDR模式,简单解释一下:
LE:Bluetooth smart模式,就是低功耗模式
BR/EDR:蓝牙基本速率和增强数据率,就是可以传输大数据的模式,一般在传输音频,视频等大数据才会有的,NRF51822是没有这个功能。

1.2、再看一下参数: ble_adv_modes_config_t options;

typedef struct
{
    bool     ble_adv_whitelist_enabled;       /**< Enable or disable use of the whitelist. */
    bool     ble_adv_directed_enabled;        /**< Enable or disable direct advertising mode. */
    bool     ble_adv_directed_slow_enabled;   /**< Enable or disable direct advertising mode. */
    bool     ble_adv_fast_enabled;            /**< Enable or disable fast advertising mode. */
    bool     ble_adv_slow_enabled;            /**< Enable or disable slow advertising mode. */
    uint32_t ble_adv_directed_slow_interval;  /**< Advertising interval for directed advertising. */
    uint32_t ble_adv_directed_slow_timeout;   /**< Time-out (number of tries) for direct advertising. */
    uint32_t ble_adv_fast_interval;           /**< Advertising interval for fast advertising. */
    uint32_t ble_adv_fast_timeout;            /**< Time-out (in seconds) for fast advertising. */
    uint32_t ble_adv_slow_interval;           /**< Advertising interval for slow advertising. */
    uint32_t ble_adv_slow_timeout;            /**< Time-out (in seconds) for slow advertising. */
} ble_adv_modes_config_t;

这个参数 主要是设置广播模式,广播的模式 主要有:Direct模式(直连模式)、Fast模式,Slow模式,IDLE模式(停止模式 )以及广播模式的使能,广播时间,超时时间。
超时时间要说明一下,这个时间定义是:以我们设置的广播速率开始广播,到达超时时间的时候,还没有蓝牙连接 ,就会发生超时事件。这个 超时事件处理就是切换模式,一直切换到 最后的IDLE模式里面 ,进入休眠 。如果超时时间设置成 0,就会 不发生超时时间,以当前的模式广播。

2.1、初始化完成之后,就是在协议栈派发函数里面调用广播回调函数 :

void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
					NRF_LOG_INFO("-----BLE_GAP_EVT_CONNECTED--\r\n");
            on_connected(p_ble_evt);
            break;

        // Upon disconnection, whitelist will be activated and direct advertising is started.
        case BLE_GAP_EVT_DISCONNECTED:
					 NRF_LOG_INFO("----BLE_GAP_EVT_DISCONNECTEDt--\r\n");
            on_disconnected(p_ble_evt);
            break;
            
        // Upon time-out, the next advertising mode is started.
        case BLE_GAP_EVT_TIMEOUT:
					 NRF_LOG_INFO("-----advertising Timeout---\r\n");
            on_timeout(p_ble_evt);
            break;

        default:
            break;
    }
}

这里重点看一下超时函数: on_timeout(p_ble_evt);

static void on_timeout(ble_evt_t const * p_ble_evt)
{
    ret_code_t ret;

    if (p_ble_evt->evt.gap_evt.params.timeout.src != BLE_GAP_TIMEOUT_SRC_ADVERTISING)
    {
        // Nothing to do.
        return;
    }

    // Start advertising in the next mode.
    ret = ble_advertising_start(adv_mode_next_get(m_adv_mode_current));

    if ((ret != NRF_SUCCESS) && (m_error_handler != NULL))
    {
        m_error_handler(ret);
    }
}

这里可以看出 ,当我们设置的广播超时时间溢出的时候,会调用on_timeout()函数,在这个函数里面,主要调用adv_mode_next_get()函数,这个 函数就是寻找下一个使能的广播模式,也就是说,当我们开启了 快速广播,快速广播超时以后,会寻找慢速广播,如果慢速广播使能,就进入慢速广播模式,如果没有开启,就进入IDLE里面,停止广播。这里就和前面我们设置的参数 ble_adv_modes_config_t options有关,在我的程序里面,自己添加了慢速广播模式使能,也设置了慢速广播速率和超时时间,本人感觉,这个慢速广播模式,其实就是重新配置一下广播速率和超时时间而已,如果我们初始化的时候,把慢速广播速率设置成20ms----40ms,其实也是快速广播。 不明白的话,可以上翻查看一下,我的广播初始化函数,里面注释很详细。

3.1、初始化 成功后就是开始广播了:

uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode)
{
        上面代码 省略。。。。。。。。。。。。
   memset(&m_peer_address, 0, sizeof(m_peer_address));

    if (  ((m_adv_modes_config.ble_adv_directed_enabled)      && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
        ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
        ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW))
       )
    {
        if (m_evt_handler != NULL)
        {
            m_peer_addr_reply_expected = true;
            m_evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST);
        }
        else
        {
            m_peer_addr_reply_expected = false;
        }
    }

    m_adv_mode_current = adv_mode_next_avail_get(advertising_mode);

    // Fetch the whitelist.
    if ((m_evt_handler != NULL) &&
        (m_adv_mode_current == BLE_ADV_MODE_FAST || m_adv_mode_current == BLE_ADV_MODE_SLOW) &&
        (m_adv_modes_config.ble_adv_whitelist_enabled) &&
        (!m_whitelist_temporarily_disabled))
    {
        #if (NRF_SD_BLE_API_VERSION == 3)
            m_whitelist_in_use = false;
        #endif
        m_whitelist_reply_expected = true;
        m_evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST);
    }
    else
    {
        m_whitelist_reply_expected = false;
    }

    // Initialize advertising parameters with default values.
    memset(&adv_params, 0, sizeof(adv_params));

    adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
    adv_params.fp   = BLE_GAP_ADV_FP_ANY;

    // Set advertising parameters and events according to selected advertising mode.
    switch (m_adv_mode_current)
    {
        case BLE_ADV_MODE_DIRECTED:
					  	NRF_LOG_INFO("---BLE_ADV_MODE_DIRECTED --\r\n");
            ret = set_adv_mode_directed(&adv_params);
            break;

        case BLE_ADV_MODE_DIRECTED_SLOW:
					NRF_LOG_INFO("---BLE_ADV_MODE_DIRECTED_SLOW --\r\n");
            ret = set_adv_mode_directed_slow(&adv_params);
            break;

        case BLE_ADV_MODE_FAST:
						NRF_LOG_INFO("-----Bluetooth runing Fast -----\r\n");
            ret = set_adv_mode_fast(&adv_params);
            break;

        case BLE_ADV_MODE_SLOW:
					NRF_LOG_INFO("-----Bluetooth runing Slow -----\r\n");
            ret = set_adv_mode_slow(&adv_params);
            break;

        case BLE_ADV_MODE_IDLE:
					NRF_LOG_INFO("--------Bluetooth runing IDLE ------------\r\n");
            m_adv_evt = BLE_ADV_EVT_IDLE;
            break;

        default:
            break;
    }

    if (m_adv_mode_current != BLE_ADV_MODE_IDLE)
    {
        ret = sd_ble_gap_adv_start(&adv_params);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    }

    if (m_evt_handler != NULL)
    {
        m_evt_handler(m_adv_evt);
    }

    return NRF_SUCCESS;
}

开始 广播函数的功能很简单,就是给广播模式赋值,然后开启广播模式而已。

3.2、事件处理函数:

static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    uint32_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_INFO("Fast advertising\r\n");
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_ADV_EVT_IDLE:
					 NRF_LOG_INFO("---BLE_ADV_EVT_IDLE---\r\n");
            sleep_mode_enter();
            break;

        default:
            break;
    }
}

事件处理函数只处理快速广播模式和IDLE模式,在IDLE里面,我们可以看到,就是调用sd_power_system_off();进入睡眠,这个低功耗函数,在我们研发蓝牙低功耗睡眠的时候,也会用到。