5. 周期广播(periodic_advertise)介绍

5.1. 简介

本章节涉及的专有名词较多,用户在阅读本章节之前,可从名词介绍章节了解一些专有名词含义:蓝牙相关名词介绍(非连接周期广播)

  1. 本章主要介绍基于周期广播(periodic_advertise)txrx收发接口,该应用有以下特点:
    • 支持一对多广播数据,一个发送端广播,多个扫描端接收,同一网络下的蓝牙名、间隔和包长需要保持一致才可正常通信

    • 单独一条链路发送数据,对于种类较多的用户数据,用户需要自行做好管理

    • 用户将数据传给蓝牙之后,蓝牙会按照用户配置的广播间隔不断进行广播数据,直到用户更新数据之前,广播包内容不会变化

    • 扫描端需要和广播端同步成功才能接收来自广播端的数据,同步丢失意味着扫描端直到下一次同步成功之前都不能再接收广播端的数据

    • 不支持更新广播间隔

    • 在同一台样机上,不支持TX和RX在同一时间工作

  2. 该主从收发接口受上层应用 vble_adv 管理,应用示例可参考:
    • 广播式对讲机应用;


5.2. 周期广播版本更新与差异说明

周期广播从V1.4.0 SDK版本开始使用1M phy和code S2 phy(注意广播的phy配置在本章参数配置有讲解,与带连接方案的phy配置不在同一个位置),不再使用2M phy。V1.4.0之前的版本(即V1.3.0,V1.3.1,V1.3.2,V1.3.3 SDK版本)使用的都是2M phy

在不同phy配置下,重发间隔有以下差别:
  • 1m phy重发间隔为2.5ms,

  • 2m phy重发间隔为1.25ms,

  • coded S2 phy重发间隔为5ms.

由于广播间隔是由用户自行配置(见下文frame_interval),根据重发间隔信息,我们可以算出传输过程中的重发次数=广播间隔/重发间隔。 重发次数增加,会对通信距离带来一定的改善,但是也会带来一定的延时。

5.3. 周期广播相关参数配置

周期广播运行需要在app_modules.h将TESTE_BLE_EN宏至1,以及将apps/app/bsp/common/bt_common/ble_test/test_app_config.h文件中使能宏定义PADVB_WL_MODE,才可使用。涉及的文件如下:
  • apps/app/bsp/common/bt_common/ble_test/test_app_config.h

  • apps/app/bsp/common/bt_user/vble_adv/vble_adv.c

  • apps/app/bsp/common/bt_user/vble_adv/vble_adv_trans.c

用户需要配置的广播和扫描参数在vble_adv_trans.c中的结构体,收发两端的蓝牙名、广播间隔、广播包长度需要保持一致才能正常通信。

其中广播端(发送端)参数:
  • u_pa_tx.adv_name:广播端蓝牙名,广播端和扫描端的蓝牙名需要一致才能通信;

  • u_pa_tx.frame_len:广播包长度,最大可配置长度不超过160byte;
    • u_pa_tx.frame_interval:周期广播间隔,单位0.625ms。如果配置为32,则广播间隔为32*0.625=20ms。广播间隔最小不低于5ms。从V1.4.0 SDK版本开始,广播间隔仅支持调整为重发间隔的整数倍;

  • u_pa_tx.retry_num:重发次数;(具体参考V1.4.0之前的版本文档。从V1.4.0 SDK版本开始,该配置无效)

  • u_pa_tx.flow_mode:默认配置为0,蓝牙按照配置的间隔定时取数进行广播;

  • u_pa_tx.phy_mode:0表示1M phy,1表示coded S2 phy;

  • u_pa_tx.user_cb.event_cb:蓝牙广播端(发送端)事件回调,注册该事件回调之后,蓝牙会根据相应状态推出对应的事件到回调,用户可根据事件信息做回调操作;

扫描端(接收端)参数如下:
  • u_pa_rx.adv_name:扫描端蓝牙名,广播端和扫描端的蓝牙名需要一致才能通信;

  • u_pa_rx.frame_len:广播包长度,最大可配置长度不超过160byte;
    • u_pa_rx.frame_interval:周期广播间隔,单位0.625ms。如果配置为32,则广播间隔为32*0.625=20ms。广播间隔最小不低于5ms。从V1.4.0 SDK版本开始,广播间隔仅支持调整为重发间隔的整数倍;

  • u_pa_rx.retry_num:重发次数;(具体参考V1.4.0之前的版本文档。从V1.4.0 SDK版本开始,该配置无效)

  • u_pa_rx.flow_mode:默认配置为0,蓝牙按照配置的间隔定时接收数据传给应用层;

  • u_pa_rx.phy_mode:0表示1M phy,1表示coded S2 phy;

  • u_pa_rx.user_cb.event_cb:蓝牙扫描端(接收端)事件回调,注册该事件回调之后,蓝牙会根据相应状态推出对应的事件到回调,用户可根据事件信息做回调操作;


5.4. 公共接口

5.4.1. 函数void ble_clock_init(void)

备注

该函数实现初始化蓝牙时钟,使用蓝牙广播功能前必须要调用!

  1. 返回值:无;

5.4.2. 函数void btstack_init(void)

备注

该函数实现初始化蓝牙协议栈。

  1. 返回值:无;

5.4.3. 函数void btstack_exit(void)

备注

该函数实现关闭蓝牙协议栈。

  1. 返回值:无;


5.5. 收发接口

该部分接口可以直接参考 vble_adv无线收发接口介绍

周期广播的所有有效接口均已封装到vble_adv.c,具体的使用可参考SDK广播式对讲机应用。

5.6. 周期广播收发接口使用说明(不包含应用框架)

如果用户不使用SDK的框架,自行设计框架开发,可参考以下流程使用周期广播接口:

5.6.1. 发送端

  • 调用函数ble_clock_init()进行蓝牙时钟初始化;

  • 调用函数btstack_init()进行蓝牙协议栈初始化;

  • 调用函数vble_padv_param_init()进行周期广播参数初始化和注册tx中断回调,tx中断回调内部包含各个事件分支;

  • 当客户需要发送数据时,调用函数vble_adv_tx_init()初始化周期广播tx;

  • 初始化周期广播tx后,调用函数vble_adv_tx_open()启动周期广播,蓝牙根据广播参数初始化时传入的周期广播间隔,定时往回调中推出PUSH_DATA事件。

  • PUSH_DATA事件推出时,用户此时可在该事件分支调用vble_adv_tx_push_data并且将数据传给该函数,进行广播(发送)数据;

5.6.2. 接收端

  • 调用函数ble_clock_init()进行蓝牙时钟初始化;

  • 调用函数btstack_init()进行蓝牙协议栈初始化;

  • 调用函数vble_padv_param_init()进行周期广播参数初始化和注册tx中断回调,tx中断回调内部包含各个事件分支;

  • 当客户需要发送数据时,调用函数vble_adv_rx_init()初始化周期广播扫描;

  • 调用函数vble_adv_rx_open()启动扫描,当蓝牙扫描到对应的广播包时会进行同步,同步成功时,回调函数推出PADVB_EVENT_SYNC_SUCCESS事件

  • 蓝牙收到广播包时PADVB_EVENT_POP_DATA传入事件回调,用户此时将一个空指针的指针传入pop_data函数,如果指针被更新为非空,即为蓝牙收到用户数据,如果指针依然为空,则蓝牙出现丢包;

代码示例如下::

u8 g_data_buf[SEND_FRAME_LEN];
void vble_padv_radio_tx_callback(const PADVB_EVENT event)
{
    u32 evt = (u32)event;
    switch (evt) {
    case PADVB_EVENT_INIT:
        log_info("PADVB_EVENT_INIT\n");
        break;
    case PADVB_EVENT_UNINIT:
        log_info("PADVB_EVENT_UNINIT\n");
        break;
    case PADVB_EVENT_OPEN:
        log_info("PADVB_EVENT_OPEN\n");
        break;
    case PADVB_EVENT_PUSH_DATA_TIMEOUT:
        log_info("PADVB_EVENT_PUSH_DATA_TIMEOUT\n");
        break;
    case PADVB_EVENT_CLOSE:
        log_info("PADVB_EVENT_CLOSE\n");
        break;
    case PADVB_EVENT_PUSH_DATA:
        /* log_info("PADVB_EVENT_PUSH_DATA\n"); */
        /* 用户需要通过蓝牙发送数据的时候 */
        vble_adv_tx_push_data(g_adv_data_buf, u_pa_tx.frame_len);
        /* 注意PUSH_DATA事件分支中只能调用一次vble_adv_tx_push_data函数 */
        /* ...... */
        break;
    default:
        break;
    }
}
void vble_padv_radio_rx_callback(const PADVB_EVENT event)
{
    u32 evt = (u32)event;
    u8 *rxdata = NULL;
    switch (evt) {
    case PADVB_EVENT_INIT:
        log_info("PADVB_EVENT_INIT\n");
        break;
    case PADVB_EVENT_UNINIT:
        log_info("PADVB_EVENT_UNINIT\n");
        break;
    case PADVB_EVENT_OPEN:
        log_info("PADVB_EVENT_OPEN\n");
        break;
    case PADVB_EVENT_CLOSE:
        log_info("PADVB_EVENT_CLOSE\n");
        break;
    case PADVB_EVENT_SYNC_SUCCESS:
        log_info("PADVB_EVENT_SYNC_SUCCESS\n");
        /* 发送端与接收端同步成功,此事件推出后接收端可在PADVB_EVENT_POP_DATA事件接收数据 */
        break;
    case PADVB_EVENT_SYNC_LOST:
        log_info("PADVB_EVENT_SYNC_LOST\n");
        /* 发送端与接收端同步超时,此事件推出后接收端需要等下一次同步成功后才能接收数据 */
        break;
    case PADVB_EVENT_POP_DATA:
        /* log_info("PADVB_EVENT_POP_DATA\n"); */
        vble_adv_rx_pop_data(&rxdata);
        if (rxdata == NULL) {
            /* 蓝牙出现丢包 */
            break;
        }
        /* ------用户流程start------ */
        /* 用户可从rxdata取到用户数据 */
        /* ------用户流程end------ */
        break;
    default:
        break;
    }
}

void vble_padv_param_init(void)
{
    memset(&u_pa_rx, 0, sizeof(padvb_user_param_t));
    memset(&u_pa_tx, 0, sizeof(padvb_user_param_t));

    memcpy(u_pa_rx.adv_name, "jla_padva_aw30", 14);
    u_pa_rx.frame_len = SEND_FRAME_LEN;
    u_pa_rx.frame_interval = 32; //20ms
    u_pa_rx.phy_mode = 0;
    u_pa_rx.retry_num = 8;
    u_pa_rx.flow_mode = 0;
    u_pa_rx.user_cb.event_cb = vble_padv_radio_rx_callback;

    memcpy(u_pa_tx.adv_name, "jla_padva_aw30", 14);
    u_pa_tx.frame_len = SEND_FRAME_LEN;
    u_pa_tx.frame_interval = 32; //20ms
    u_pa_tx.phy_mode = 0;
    u_pa_tx.retry_num = 8;
    u_pa_tx.flow_mode = 0;
    u_pa_tx.user_cb.event_cb = vble_padv_radio_tx_callback;
}
void padv_app(void)
{
    ble_clock_init();           //蓝牙时钟初始化
    btstack_init();             //蓝牙协议栈初始化
    vble_padv_parm_init();      //广播参数初始化

    while(1) {
        /* ...... */
        /* 用户流程 */
        /* ...... */

        /* 需要通过蓝牙发数据的时候 */
        vble_adv_tx_init();
        vble_adv_tx_open();
        /* ...... */

        /* 由于广播是不可靠传输,而且tx需要在PADVB_EVENT_PUSH_DATA事件回调取数才能发送数据, */
        /* rx要和tx建立同步之后才能通信,而且tx数据发出后至少需要一个广播间隔的时间才能让rx收到数据,因此tx_open和tx_close之间需要存在一定延时 */

        /* 需要停止发送数据的时候 */
        vble_adv_tx_close();
        vble_adv_tx_uninit();
        /* ...... */

        /* 需要通过蓝牙接收数据的时候 */
        vble_adv_rx_init();
        vble_adv_rx_open();
        /* ...... */

        /* 由于广播发送端和接收端同步需要时间,而且需要在事件回调才能接收数据, */
        /* 因此rx_open和rx_close之间需要存在一定延时 */

        /* 需要停止接收数据的时候 */
        vble_adv_rx_close();
        vble_adv_rx_uninit();
        /* ...... */
    }
}