nrf52840蓝牙协议栈样例分析

  蓝牙SDK的example 文件夹提供了开发BLE的模板工程,它具有通用性,可以为自己开发工程提供参考。打开examples\ble_peripheral\ble_app_template文件夹下的main.c文件,主函数main的内容为:

/**@brief Function for application main entry.
 */
int main(void)
{
    bool erase_bonds;

    // Initialize.
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    advertising_init();
    services_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("Template example started.");
    application_timers_start();

    advertising_start(erase_bonds);

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}

  main函数的初始化部分主要分为三部分:
  一是外设的初始化如定时器,按键,LED灯,串口等
  二是协议层的初始化,如协议栈初始化,GAP和GATT初始化,广播初始化,连接参数初始化等。
  三是应用层初始化,如服务初始化,蓝牙服务初始化。

一、外设

1.1、log打印初始化

  log_init()函数就是打印初始化函数,内容如下:

/**@brief Function for initializing the nrf log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}

1.2、定时器

  蓝牙协议栈下的定时器是软件定时器,并采用软件中断来进行触发操作。Nordic官方库关于定时器的驱动库定义在app_timer.c和app_timer.h文件中

1.2.1、定时器初始化

  main函数中的 timers_init()是定时器初始化函数

/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static void timers_init(void)
{
    // Initialize timer module.
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create timers.

    /* YOUR_JOB: Create any timers to be used by the application.
                 Below is an example of how to create a timer.
                 For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
                 one.
       ret_code_t err_code;
       err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
       APP_ERROR_CHECK(err_code); */
}

  调用的API函数有app_timer_init()和app_timer_create()。app_timer_init()函数就是初始化计数器的相关工作。app_timer_create()函数为

ret_code_t app_timer_create(app_timer_id_t const *      p_timer_id,
                            app_timer_mode_t            mode,
                            app_timer_timeout_handler_t timeout_handler);

  p_timer_id是定时器标识符指针
  mode是定时器模式
  timeout_handler是定时器超时溢出的中断 处理函数

1.2.2、定时器开始

  main函数中的定时器开始函数为application_timers_start()

/**@brief Function for starting timers.
 */
static void application_timers_start(void)
{
    /* YOUR_JOB: Start your timers. below is an example of how to start a timer.
       ret_code_t err_code;
       err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
       APP_ERROR_CHECK(err_code); */

}

  app_timer_start函数是定时器开始函数,其参数具体为:

ret_code_t app_timer_start(app_timer_t * p_timer, uint32_t timeout_ticks, void * p_context)

  第一个参数p_timer为前面的定时器ID
  第二个参数timeout_ticks是超时的时间间隔
  第三个参数为p_context,一般可以设置为NULL。

1.3、LED和按键

  Nordic的蓝牙协议栈中的库函数提供了一个BSP来支持按键和LED灯。

1.3.1、按键与LED的初始化

  按键与LED的初始化函数为

/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    ret_code_t err_code;
    bsp_event_t startup_event;

    err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}

  bsp_init函数实现按键和LED的初始化,同时触发对应的设备任务。

二、能源管理

  能源管理初始化函数为power_management_init(),一般不变动。

三、协议栈

  ble_stack_init()是协议初始化函数,协议栈初始化主要做以下工作:
  1、协议栈回复使能应答,主要工作是协议栈时钟初始化配置
  2、初始化协议栈,设置协议栈相关处理函数,
  3、使能协议栈
  4、注册蓝牙处理调度事件
  具体代码如下:

/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

3.1、协议栈使能应答

  协议栈使能应答代码为


ret_code_t nrf_sdh_enable_request(void)
{
    ret_code_t ret_code;

    if (m_nrf_sdh_enabled)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    m_nrf_sdh_continue = true;

    // Notify observers about SoftDevice enable request.
    if (sdh_request_observer_notify(NRF_SDH_EVT_ENABLE_REQUEST) == NRF_ERROR_BUSY)
    {
        // Enable process was stopped.
        return NRF_SUCCESS;
    }

    // Notify observers about starting SoftDevice enable process.
    sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLE_PREPARE);

    nrf_clock_lf_cfg_t const clock_lf_cfg =
    {
        .source       = NRF_SDH_CLOCK_LF_SRC,
        .rc_ctiv      = NRF_SDH_CLOCK_LF_RC_CTIV,
        .rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
        .accuracy     = NRF_SDH_CLOCK_LF_ACCURACY
    };

    CRITICAL_REGION_ENTER();
#ifdef ANT_LICENSE_KEY
    ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler, ANT_LICENSE_KEY);
#else
    ret_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler);
#endif
    m_nrf_sdh_enabled = (ret_code == NRF_SUCCESS);
    CRITICAL_REGION_EXIT();

    if (ret_code != NRF_SUCCESS)
    {
        return ret_code;
    }

    m_nrf_sdh_continue  = false;
    m_nrf_sdh_suspended = false;

    // Enable event interrupt.
    // Interrupt priority has already been set by the stack.
    softdevices_evt_irq_enable();

    // Notify observers about a finished SoftDevice enable process.
    sdh_state_observer_notify(NRF_SDH_EVT_STATE_ENABLED);

    return NRF_SUCCESS;
}

  这个函数是用于观察者初始化协议栈是否开始使能。观察者就是开发者,代码中设置了交互方式,如串口等对设备状态进行观察,并且给观察者分配了回调函数

3.2、协议栈默认配置设置

  协议栈默认配置设置,也是协议栈的初始化。函数代码如下:

ret_code_t nrf_sdh_ble_default_cfg_set(uint8_t conn_cfg_tag, uint32_t * p_ram_start)
{
    uint32_t ret_code;

    ret_code = nrf_sdh_ble_app_ram_start_get(p_ram_start);
    if (ret_code != NRF_SUCCESS)
    {
        return ret_code;
    }

#if defined (S112) || defined(S312)
    STATIC_ASSERT(NRF_SDH_BLE_CENTRAL_LINK_COUNT == 0, "When using s112, NRF_SDH_BLE_CENTRAL_LINK_COUNT must be 0.");
#endif

    // Overwrite some of the default settings of the BLE stack.
    // If any of the calls to sd_ble_cfg_set() fail, log the error but carry on so that
    // wrong RAM settings can be caught by nrf_sdh_ble_enable() and a meaningful error
    // message will be printed to the user suggesting the correct value.
    ble_cfg_t ble_cfg;
/*********************************设置连接数目和角色****************************************************************/
#if (NRF_SDH_BLE_TOTAL_LINK_COUNT != 0)
    // Configure the connection count.设置连接数目
    memset(&ble_cfg, 0, sizeof(ble_cfg));
    ble_cfg.conn_cfg.conn_cfg_tag                     = conn_cfg_tag;//设置标号
    ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count   = NRF_SDH_BLE_TOTAL_LINK_COUNT;//总的连接数量
    ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = NRF_SDH_BLE_GAP_EVENT_LENGTH;//GAP事件长度

    ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, *p_ram_start);//添加协议栈配置
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GAP.",
                      nrf_strerror_get(ret_code));
    }

    // Configure the connection roles.配置连接角色
    memset(&ble_cfg, 0, sizeof(ble_cfg));
#if !defined (S122)
    ble_cfg.gap_cfg.role_count_cfg.periph_role_count  = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT;//从机角色数目
#endif // !defined (S122)
#if !defined (S112) && !defined(S312) && !defined(S113)
    ble_cfg.gap_cfg.role_count_cfg.central_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT;//主机角色数目
    ble_cfg.gap_cfg.role_count_cfg.central_sec_count  = MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT,
                                                            BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT);
#endif // !defined (S112) && !defined(S312) && !defined(S113)

    ret_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, *p_ram_start);
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GAP_CFG_ROLE_COUNT.",
                      nrf_strerror_get(ret_code));
    }
	/*********************************设置MTU协商值****************************************************************/

    // Configure the maximum ATT MTU.
#if (NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23)//如果MTU协商值不是23
    memset(&ble_cfg, 0x00, sizeof(ble_cfg));
    ble_cfg.conn_cfg.conn_cfg_tag                 = conn_cfg_tag;//设置连接编号
    ble_cfg.conn_cfg.params.gatt_conn_cfg.att_mtu = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;//设置MTU字节长度

    ret_code = sd_ble_cfg_set(BLE_CONN_CFG_GATT, &ble_cfg, *p_ram_start);//协议栈配置的添加
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_CONN_CFG_GATT.",
                      nrf_strerror_get(ret_code));
    }
#endif  // NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23
#endif  // NRF_SDH_BLE_TOTAL_LINK_COUNT != 0
	/*********************************设置UUID数量****************************************************************/

    // Configure number of custom UUIDS.
    memset(&ble_cfg, 0, sizeof(ble_cfg));
    ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = NRF_SDH_BLE_VS_UUID_COUNT;//配置UUID主服务数量

    ret_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, *p_ram_start);
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_COMMON_CFG_VS_UUID.",
                      nrf_strerror_get(ret_code));
    }
	/*********************************设置GATTS属性表的大小****************************************************************/

    // Configure the GATTS attribute table.
    memset(&ble_cfg, 0x00, sizeof(ble_cfg));
    ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE;

    ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, *p_ram_start);
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_ATTR_TAB_SIZE.",
                      nrf_strerror_get(ret_code));
    }
	/*********************************使能服务变化特征值****************************************************************/

    // Configure Service Changed characteristic.
    memset(&ble_cfg, 0x00, sizeof(ble_cfg));
    ble_cfg.gatts_cfg.service_changed.service_changed = NRF_SDH_BLE_SERVICE_CHANGED;

    ret_code = sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &ble_cfg, *p_ram_start);
    if (ret_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("sd_ble_cfg_set() returned %s when attempting to set BLE_GATTS_CFG_SERVICE_CHANGED.",
                      nrf_strerror_get(ret_code));
    }

    return NRF_SUCCESS;
}

  该函数的主要功能有:
  一、配置连接数目和角色
  二、配置MTU协商值,最大传输单元(Maximum Transmission Unit , MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。因为协议数据单元的包头和包尾的长度是固定的,MTU越大,则一个协议数据单元的承载的有效数据就越长,通信效率也越高。传送相同数据所需的数据包个数也越少。但是,传送一个数据包的延迟也越大,包中bit位发生错误的概率也越大。因此在蓝牙5.0协议下限定了MTU的最大值。
  在蓝牙4.0里面定义了一个最大23字节的传输包,ATT默认MTU为23个bytes,除去ATT的opcode一个字节以及ATT的handle两个字节之后,剩下的 20个字节便是留给GATT的了。
  三、配置定制的UUID数目,这个数目指的是私有任务的UUID数目,也就是自定义的128bit的UUID数目。SIG定义的公有任务不计入其中。
  四、GATTS属性表大小.
  五、使能服务变化特征值

3.3、使能协议栈

  配置完协议栈的参数后,使用nrf_sdh_ble_enable函数使能协议栈。程序在运行时,芯片内部的RAM被分为两个区域:协议栈设备RAM区域位于0X20000000和APP_RAM_BASE-1之间,应用程序的RAM位于APP_RAM_BASE和调用堆栈的开始之间。

3.4、注册蓝牙处理事件

  注册蓝牙处理事件是以一种观察者的模式来注册蓝牙处理的回调函数,简单来说就是:注册蓝牙处理事件的回调函数ble_evt_handler。注册蓝牙处理事件就是处理GAP层的事件。

/**@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 = NRF_SUCCESS;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("Disconnected.");
            // LED indication will be changed when advertising starts.
            break;

        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("Connected.");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);//将连接句柄分配给队列写模块
            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;
    }
}

  蓝牙处理事件函数主要是根据协议栈触发的蓝牙事件进行对应的动作,主要有一下几个蓝牙GAP层的事件:
  BLE_GAP_EVT_CONNECTED:连接事件
  BLE_GAP_EVT_DISCONNECTED:断开连接事件
  BLE_GAP_EVT_PHY_UPDATE_REQUEST:PHY更新应答
  BLE_GATTC_EVT_TIMEOUT:客户端超时
  BLE_GATTS_EVT_TIMEOUT:服务端超时
  BLE_GATTS_EVT_TIMEOUT服务端超时和BLE_GATTC_EVT_TIMEOUT客户端超时这两个操作表示主机和从机发生GATT事件超时,主机和从机链路没有按时交流数据包从而引发通信超时。这个时候,从机会调用sd_ble_gap_disconnect函数断开蓝牙连接。
  GAP事件的ID定义为:

/**@brief GAP Event IDs.
 * IDs that uniquely identify an event coming from the stack to the application.
 */
enum BLE_GAP_EVTS
{
  BLE_GAP_EVT_CONNECTED                   = BLE_GAP_EVT_BASE,       /**< Connected to peer.                              \n See @ref ble_gap_evt_connected_t             */
  BLE_GAP_EVT_DISCONNECTED                = BLE_GAP_EVT_BASE + 1,   /**< Disconnected from peer.                         \n See @ref ble_gap_evt_disconnected_t.         */
  BLE_GAP_EVT_CONN_PARAM_UPDATE           = BLE_GAP_EVT_BASE + 2,   /**< Connection Parameters updated.                  \n See @ref ble_gap_evt_conn_param_update_t.    */
  BLE_GAP_EVT_SEC_PARAMS_REQUEST          = BLE_GAP_EVT_BASE + 3,   /**< Request to provide security parameters.         \n Reply with @ref sd_ble_gap_sec_params_reply.  \n See @ref ble_gap_evt_sec_params_request_t. */
  BLE_GAP_EVT_SEC_INFO_REQUEST            = BLE_GAP_EVT_BASE + 4,   /**< Request to provide security information.        \n Reply with @ref sd_ble_gap_sec_info_reply.    \n See @ref ble_gap_evt_sec_info_request_t.   */
  BLE_GAP_EVT_PASSKEY_DISPLAY             = BLE_GAP_EVT_BASE + 5,   /**< Request to display a passkey to the user.       \n In LESC Numeric Comparison, reply with @ref sd_ble_gap_auth_key_reply. \n See @ref ble_gap_evt_passkey_display_t. */
  BLE_GAP_EVT_KEY_PRESSED                 = BLE_GAP_EVT_BASE + 6,   /**< Notification of a keypress on the remote device.\n See @ref ble_gap_evt_key_pressed_t           */
  BLE_GAP_EVT_AUTH_KEY_REQUEST            = BLE_GAP_EVT_BASE + 7,   /**< Request to provide an authentication key.       \n Reply with @ref sd_ble_gap_auth_key_reply.    \n See @ref ble_gap_evt_auth_key_request_t.   */
  BLE_GAP_EVT_LESC_DHKEY_REQUEST          = BLE_GAP_EVT_BASE + 8,   /**< Request to calculate an LE Secure Connections DHKey. \n Reply with @ref sd_ble_gap_lesc_dhkey_reply.  \n See @ref ble_gap_evt_lesc_dhkey_request_t */
  BLE_GAP_EVT_AUTH_STATUS                 = BLE_GAP_EVT_BASE + 9,   /**< Authentication procedure completed with status. \n See @ref ble_gap_evt_auth_status_t.          */
  BLE_GAP_EVT_CONN_SEC_UPDATE             = BLE_GAP_EVT_BASE + 10,  /**< Connection security updated.                    \n See @ref ble_gap_evt_conn_sec_update_t.      */
  BLE_GAP_EVT_TIMEOUT                     = BLE_GAP_EVT_BASE + 11,  /**< Timeout expired.                                \n See @ref ble_gap_evt_timeout_t.              */
  BLE_GAP_EVT_RSSI_CHANGED                = BLE_GAP_EVT_BASE + 12,  /**< RSSI report.                                    \n See @ref ble_gap_evt_rssi_changed_t.         */
  BLE_GAP_EVT_SEC_REQUEST                 = BLE_GAP_EVT_BASE + 14,  /**< Security Request.                               \n Reply with @ref sd_ble_gap_authenticate
. \n See @ref ble_gap_evt_sec_request_t.          */
  BLE_GAP_EVT_SCAN_REQ_REPORT             = BLE_GAP_EVT_BASE + 16,  /**< Scan request report.                            \n See @ref ble_gap_evt_scan_req_report_t. */
  BLE_GAP_EVT_PHY_UPDATE_REQUEST          = BLE_GAP_EVT_BASE + 17,  /**< PHY Update Request.                             \n Reply with @ref sd_ble_gap_phy_update. \n See @ref ble_gap_evt_phy_update_request_t. */
  BLE_GAP_EVT_PHY_UPDATE                  = BLE_GAP_EVT_BASE + 18,  /**< PHY Update Procedure is complete.               \n See @ref ble_gap_evt_phy_update_t.           */
  BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST = BLE_GAP_EVT_BASE + 19,   /**< Data Length Update Request.                     \n Reply with @ref sd_ble_gap_data_length_update. \n See @ref ble_gap_evt_data_length_update_request_t. */
  BLE_GAP_EVT_DATA_LENGTH_UPDATE         = BLE_GAP_EVT_BASE + 20,   /**< LL Data Channel PDU payload length updated.     \n See @ref ble_gap_evt_data_length_update_t. */
  BLE_GAP_EVT_ADV_SET_TERMINATED         = BLE_GAP_EVT_BASE + 22,   /**< Advertising set terminated.                     \n See @ref ble_gap_evt_adv_set_terminated_t. */
};

四、GAP和GATT

4.1、GAP初始化

  通用访问配置文件(Generic Access Profile,GAP),该Profile保证不同的蓝牙产品可以互相发现对方并建立连接。
  GAP初始化函数为gap_params_init,代码如下 :

/**@brief Function for the GAP initialization.
 *
 * @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);

    /* YOUR_JOB: Use an appearance value matching the application's use case.
       err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
       APP_ERROR_CHECK(err_code); */
	/*********************************连接参数设置****************************************************************/

    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;//最小连接间隔
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;//最大连接间隔
    gap_conn_params.slave_latency     = SLAVE_LATENCY;//连接事件中从机设备潜伏周期
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;//连接超时

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}

  主要完成三个工作:
  一、GAP初始化安全模式配置,安全模式指连接时是否需要身份验证。
  二、蓝牙设备名称设置,即设置蓝牙广播的名字,注意名称长度有限制,最好不要超过十八个字节
  三、连接参数设置,连接参数设置,主要是设置:最小连接间隔、最大连接间隔、从机设备潜伏周期、连接超时时间。这些值代表了外围设备针对连接的期望参数。

4.2、GATT初始化

  GATT称为通用属性规范(Generic Attribute profile,GATT),GATT层是传输真正数据所在的 层。包括一个数据传输和存储框架以及其基本操作。其大部分设置是在服务中进行的,在主函数中只需要初始化数据长度这个参数。

/**@brief Function for initializing the GATT module.
 */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
    APP_ERROR_CHECK(err_code);
}

  gatt_init函数中调用了nrf_ble_gatt_init函数

ret_code_t nrf_ble_gatt_init(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_handler_t evt_handler)
{
    VERIFY_PARAM_NOT_NULL(p_gatt);

    p_gatt->evt_handler             = evt_handler;
    p_gatt->att_mtu_desired_periph  = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;
    p_gatt->att_mtu_desired_central = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;
    p_gatt->data_length             = NRF_SDH_BLE_GAP_DATA_LENGTH;

    for (uint32_t i = 0; i < NRF_BLE_GATT_LINK_COUNT; i++)
    {
        link_init(&p_gatt->links[i]);
    }

    return NRF_SUCCESS;
}

五、广播

  一个广播数据最多可以携带31字节的数据,它通常包含用户可读的名字、关于设备发送数据包的有关信息、用于表示此设备是否可被发现的标志等。当主机接收到广播包后,它可能发送请求更多数据包的请求,称为扫描回应,如果他被设置成主动扫描,从机设备将会发送一个扫描回应作为对主机请求的回应,扫描回应最多也可以携带31字节的数据。广播扫描回应包的数据结构类型可以和广播包一致。

5.1、广播初始化

  初始化函数如下:

/**@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]);//广播UUID
    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);//设置广播识别号
}

  广播初始化实际上就是初始化两个结构体。一个是advdata广播数据,一个是config选择项

5.2、开始广播

  开始广播内容如下

/**@brief Function for starting advertising.
 */
static void advertising_start(bool erase_bonds)
{
    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
    }
    else
    {
        ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);
    }
}

5.3、广播响应包

  广播包有两种:广播包(Advertising Data)和响应包(Scan Response),其中广播包是每个广播必须的,而响应包是可选的。每个包都是31个字节,数据包中分为有效数据和无效数据两部分。
  有效数据部分:包含若干个广播数据单元,这些数据单元的组成是:第一个字节是长度值Len,表示接下来的Len个字节是数据部分。数据部分的第一个字节表示数据的类型,剩下的字节是真正的数据。其中数据类型非常关键,决定了数据代表的是什么和怎样解析。
  无效数据部分:因为广播包的长度必须是31个字节,如果有效部分不到31字节,剩下的就用0补全。

六、服务初始化

  服务初始化就是建立一个服务声明,分配一个RAM空间。

/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    ret_code_t         err_code;
    nrf_ble_qwr_init_t qwr_init = {0};

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

    /* YOUR_JOB: Add code to initialize the services used by the application.
       ble_xxs_init_t                     xxs_init;
       ble_yys_init_t                     yys_init;

       // Initialize XXX Service.
       memset(&xxs_init, 0, sizeof(xxs_init));

       xxs_init.evt_handler                = NULL;
       xxs_init.is_xxx_notify_supported    = true;
       xxs_init.ble_xx_initial_value.level = 100;

       err_code = ble_bas_init(&m_xxs, &xxs_init);
       APP_ERROR_CHECK(err_code);

       // Initialize YYY Service.
       memset(&yys_init, 0, sizeof(yys_init));
       yys_init.evt_handler                  = on_yys_evt;
       yys_init.ble_yy_initial_value.counter = 0;

       err_code = ble_yy_service_init(&yys_init, &yy_init);
       APP_ERROR_CHECK(err_code);
     */
}

七、连接参数更新

  连接参数更新初始化,并没有设置连接参数值,连接参数值已经在GAP初始化中设置了。连接参数更新主要是为了功耗和数据传输之间的一个动态妥协的策略。代码如下

/**@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_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.evt_handler                    = on_conn_params_evt;
    cp_init.error_handler                  = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}

八、电源待机

  主函数中,最后一个循环等待,调用了idle_state_handle函数,意思为无效状态操作

/**@brief Function for handling the idle state (main loop).
 *
 * @details If there is no pending log operation, then sleep until next the next event occurs.
 */
static void idle_state_handle(void)
{
    if (NRF_LOG_PROCESS() == false)
    {
        nrf_pwr_mgmt_run();
    }
}
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: nRF52840是一款由Nordic Semiconductor(挪威北方半导体)推出的蓝牙低功耗(Bluetooth Low Energy,BLE)芯片。nRF52840开发指南是为了帮助开发人员更好地了解和使用nRF52840芯片而编写的一份技术文档。 在nRF52840开发指南中,首先介绍了nRF52840芯片的概述,包括其主要特点和应用领域。然后详细介绍了nRF52840芯片的硬件设计,包括引脚功能、电源管理、时钟和无线电等方面的设计要点,帮助开发人员在设计电路板时更好地利用芯片的功能。 接着,nRF52840开发指南向开发人员介绍了nRF5软件开发套件(SDK),该套件提供了在nRF52840芯片上进行应用程序开发所需的软件工具和示例代码。文中详细介绍了如何安装和配置SDK,并给出了一些常见的应用程序开发示例。 在软件开发部分中,nRF52840开发指南还介绍了nRF52840的软件架构和支持的协议栈,如BLE协议栈和ANT协议栈。开发人员可以根据自己的需求选择合适的协议栈,并按照指南中给出的步骤进行相应的配置和开发工作。 最后,nRF52840开发指南还提供了一些在nRF52840开发中常见问题的解答,以及针对特定应用场景的案例分析和优化建议,帮助开发人员更好地理解和应用nRF52840芯片。 综上所述,nRF52840开发指南是一本重要的技术文档,对于想要了解和使用nRF52840芯片进行开发的人员来说,是一份不可或缺的参考资料。 ### 回答2: 要下载nRF52840开发指南,可以按照以下步骤进行操作。 首先,打开Nordic Semiconductor官方网站,找到nRF52840产品页面。在该页面中,可以找到各种与nRF52840相关的文档和指南。 在该页面上,找到“开发指南”的选项,并点击进入。在这个页面上,将提供nRF52840开发指南的下载链接。 点击下载链接,然后选择保存文件的位置。可以选择将文件保存到计算机的硬盘中,或者将其保存到移动存储设备,如USB闪存驱动器。 等待下载完成后,打开下载的文件。一般来说,nRF52840开发指南会以PDF格式提供,因此需要确保计算机上有能够阅读PDF文件的软件,如Adobe Acrobat Reader。 在打开的文件中,可以找到详细的nRF52840开发指南。该指南将包含关于nRF52840芯片的详细说明、硬件设计指南、软件开发指南、编程示例和应用案例等内容。 可以根据自己的需要,阅读和学习开发指南中提供的内容。这个指南将有助于理解和学习如何使用nRF52840开发板进行硬件和软件开发,以便于设计和构建自己的应用程序。 总而言之,要下载nRF52840开发指南,需要访问Nordic Semiconductor官方网站,找到nRF52840产品页面并下载相关的开发指南。这些指南将提供关于nRF52840芯片的详细信息和相关开发技术的指导。 ### 回答3: nrf52840开发指南是一本关于nRF52840蓝牙低功耗多协议系统级芯片开发的书籍。该指南提供了有关硬件和软件开发、调试和优化的详细信息,旨在帮助开发者快速掌握nRF52840的应用。 首先,为了下载nrf52840开发指南,可以通过Nordic Semiconductor官方网站或其开发者社区获得。在网站或社区上,会提供资源下载的链接或指引。 下载nrf52840开发指南后,我们可以逐步了解如何开始使用nRF52840硬件。其中包括了nRF52840的硬件特性和规格,以及连接外部器件的方法。此外,该指南还介绍了如何使用Nordic Semiconductor的开发套件和软件工具,如nRF Connect,以开发和测试nRF52840的应用程序。 在软件开发方面,nrf52840开发指南详细介绍了nRF5 Software Development Kit(SDK)和nRF Command Line Tools的安装和使用。SDK提供了丰富的软件库和示例代码,以支持不同的应用场景。开发者可以学习如何使用SDK创建自己的应用程序,并了解如何将代码编译、烧录和调试到nRF52840芯片上。 此外,该指南还提供了有关nRF52840的低功耗特性和优化技巧的详细指导。开发者可以学习如何使用低功耗模式和功耗管理功能,以延长设备的电池寿命。 总之,nrf52840开发指南是一本全面而详细的指南,为开发者提供了学习和开发nRF52840芯片的必备工具和资源。通过下载并阅读该指南,开发者可以深入了解nRF52840的开发过程和技术细节,从而更好地应用该芯片开发出高性能低功耗的物联网设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值