IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> nrf52840蓝牙协议栈样例分析 -> 正文阅读

[嵌入式]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和调用堆栈的开始之间。

四、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();
    }
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-27 10:04:07  更:2021-11-27 10:06:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 11:36:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码