CC2640学习笔记——自定义广播

CC2640学习笔记——自定义广播

上文将SDK广播例程导入到了工作区,并修改工程配置使其正常编译下载到CC2640R2F芯片中。接下来笔者通过修改例程实现广播自定义数据。

一、广播信息定义

广播名称ATCBroadcastTest
蓝牙类型LE only(低功耗蓝牙BLE)
广播类型Scannable Undirected event
广播数据ATCTest

二、GAP概述

Generic Access Profile (GAP):低功耗蓝牙协议栈的 GAP 层负责连接功能。该层处理设备的访问模式和过程,包括设备发现、链路建立、链路终止、安全功能的启动和设备配置。
在这里插入图片描述

  1. GAP状态
    基于设备配置的角色,上图 GAP 状态图显示了设备的各种状态。下面描述这些状态。
  • 待机:复位后设备处于初始空闲状态。
  • 广播:设备使用特定数据进行广告,让任何发起设备知道它是可连接设备(此广告包含设备地址,并且可以包含一些附加数据,例如设备名称)。
  • 扫描:扫描设备收到广告后,向广告商发送扫描请求。广告商以扫描响应进行响应。此过程称为设备发现。扫描设备知道广告设备并且可以发起与其的连接。
  • 发起:发起时,发起者必须指定要连接的对端设备地址。如果接收到与对等设备的该地址匹配的通告,则发起设备会发出请求,以使用 GAP 连接状态中描述的连接参数与通告设备建立连接(链接)。
  • Peripheral/Central:当连接形成时,如果设备是广告者,则充当 Peripheral;如果是发起者,则充当 Central。
    蓝牙设备根据应用用例以一种或多种 GAP 角色运行。 GAP 角色利用一种或多种 GAP 状态。基于此配置,许多蓝牙低功耗协议栈事件直接由主应用程序任务处理,有时从 GAP Bond Manager 路由。应用程序可以向堆栈注册以获得某些事件的通知。
  1. GAP角色
    根据设备的配置,GAP 层始终以规范定义的四 个角色之一进行操作:
  • 广播者- 设备是不可连接的广告者。
  • 观察者 - 设备扫描广告但无法发起连接。
  • 外设 - 该设备是一个广告商,可连接并在单个链路层连接中作为外设运行。
  • 中央设备 - 设备扫描广告并启动连接,并在单个或多个链路层连接中作为中央设备运行。

三、广播相关协议概述

1、广播事件

BLE广播事件有七种类型(见《Bluetooth Core Specification》V5.2 Vol6-PartB-4.4.2)。
在这里插入图片描述
关于各种类型的广播事件详情读者可自行研究《蓝牙核心规范》,笔者简述下接下来要使用的不定向可扫描的广播事件(Scannable Undirected event)

2、不定向可扫描广播事件(见《Bluetooth Core Specification》V5.2 Vol6-PartB-4.4.2.5)

可扫描无向广告事件使用 ADV_SCAN_ID 作为指示标志 (ADV_EXT_IND PDU 暂不讨论)。读者有兴趣可以在实验过程中使用抓包工具查看PDU格式及内容,是否跟协议规范一致。
可扫描无向事件类型允许扫描者使用扫描请求(SCAN_REQ PDU)来响应,以请求广播者返回扫描附加信息。
如果广播者从广播过滤策略允许的扫描者接收到包含其设备地址的SCAN_REQ PDU,则其应在同一广播频道索引上使用SCAN_RSP PDU进行回复。发送SCAN_RSP PDU后,或者如果广播过滤策略不允许处理SCAN_REQ PDU,则广播者应移动到下一个使用的主要广播频道索引以发送另一个ADV_SCAN_IND PDU,或者关闭广播事件。
广播事件中两个连续ADV_SCAN_IND PDU开始之间的时间应小于或等于10ms。广播事件应在广告间隔内关闭。
未接收到SCAN_REQ PDU的广播事件的结构如下图所示。
在这里插入图片描述

接收到SCAN_REQ PDU并返回SCAN_RSP PDU的广播事件的结构如下图所示。
在这里插入图片描述
对于不定向可扫描广播事件不会相应来自广播通道上的CONNECT_IND PDU。
广播时间间隔在编写程序时讨论。

3、消息序列图

(1)低功耗蓝牙协议栈

低功耗蓝牙协议栈模型如图所示。
在这里插入图片描述
关于蓝牙的OSI模型和Host/Controller模型笔者理解有限,不在此献丑,有兴趣可自行研究相关文档。
实现蓝牙的不定向可扫描广播只需要跟 GAP这个对象打交道就行了。GAP上面已经进行了简要叙述。
要理解应用蓝牙广播的配置,需要了解通过应用层代码实现对协议栈 HCI和LL 的控制。
HCI描述参考TI官方协议栈对HCI的介绍
LL描述参考TI官方协议栈对LL的介绍

(2)不定向广播消息序列

下图描述了设备B向设备A发送广播,设备B的Host层和LL层之间的信息交互过程。
在这里插入图片描述

四、代码实现

接下来基于上一篇 广播例程工程 实现不定向可扫描广播,周期广播自定义数据。

1、修改工程名称

将 ble5_simple_broadcaster_cc2640r2lp_app 重命名为 AtcBroadcaster_app。
将 ble5_simple_broadcaster_cc2640r2lp_stack_library 重命名为为 AtcBroadcaster_stack_library。
修改 AtcBroadcaster_app项目配置,选定 AtcBroadcaster_app 打开路径“Project->Properties->Build->Dependencies”,将ble5_simple_broadcaster_cc2640r2lp_stack_library 条目删除,添加AtcBroadcaster_stack_library。点击“Apply and Close”按钮生效更改。
在这里插入图片描述
重新编译两个文件夹,AtcBroadcaster_app 编译完会提示警告“Referenced project ‘ble5_simple_broadcaster_cc2640r2lp_stack_library’ does not exist in the workspace. ”,在文件“AtcBroadcaster_app/.project”中找到该警告位置删除该条目。
在这里插入图片描述
重新编译不再发出警告。

Ps:这个问题是笔者强迫症使然,不修改工程名称,或者忽略掉最后这个警告都不影响后续操作。

2、main 函数

打开路径“AtcBroadcaster_app ->Startup->main.c”,可以看到例程main函数如下所示

/*******************************************************************************
 * @fn          Main
 *
 * @brief       Application Main
 *
 * input parameters
 *
 * @param       None.
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      None.
 */
int main()
{
  /* Register Application callback to trap asserts raised in the Stack */
  RegisterAssertCback(AssertHandler);

  Board_initGeneral();

#ifndef POWER_SAVING
  /* Set constraints for Standby, powerdown and idle mode */
  Power_setConstraint(PowerCC26XX_SB_DISALLOW);
  Power_setConstraint(PowerCC26XX_IDLE_PD_DISALLOW);
#endif // POWER_SAVING

#ifdef ICALL_JT
  user0Cfg.appServiceInfo->timerTickPeriod = Clock_tickPeriod;
  user0Cfg.appServiceInfo->timerMaxMillisecond  = ICall_getMaxMSecs();
#endif  /* ICALL_JT */
  /* Initialize ICall module */
  ICall_init();

  /* Start tasks of external images - Priority 5 */
  ICall_createRemoteTasks();

  /* Kick off application - Priority 1 */
  SimpleBroadcaster_createTask();

  BIOS_start();     /* enable interrupts and start SYS/BIOS */

  return 0;
}

现在简要分析一下主函数。
前两句分别是注册断言回调函数和开发板必要的初始化,可以不必理会。
接下来两个条件条件编译嵌套,查看"AtcBroadcaster_app ->TOOLS->defines->ble5_simple_broadcaster_cc2640r2lp_app_FlashROM_StackLibrary.opt”文件可以看到,声明了"POWER_SAVING"和“ICALL_JT”。
在这里插入图片描述
因此前一个嵌套可以忽略掉,后一个前套内设置了用户参数 user0Cfg,这是初始化蓝牙协议栈的必要参数。
ICall_init(),初始化了ICall模块, ICall 允许应用程序和协议栈在统一的 TI-RTOS 环境中高效运行、通信和共享资源。在这里插入图片描述
ICall_createRemoteTasks(), 创建蓝牙协议栈任务但并未启动, 该任务优先级为 5,即任务最高优先级。前面的用户参数 user0Cfg,在这个函数里作为配置参数。
SimpleBroadcaster_createTask(), 创建用户自定义蓝牙任务,蓝牙行为和事件都在这个任务里。
BIOS_start(),开始 TI-RTOS 任务调度。

例程主函数逻辑清晰,叙述简要。在此框架下适当修改即可。

3、自定义蓝牙任务

(1) 修改文件名称

项目浏览器中打开路径 “AtcBroadcaster_app->Application”,将 “simple_broadcaster.c” 和 “simple_broadcaster.h” 两个文件名称改为 “AtcBroadcaster.c”、“AtcBroadcaster.h”。
更改 “AtcBroadcaster.h” 条件编译语句。

······
#ifndef ATCBROADCASTER_H/* SIMPLEBROADCASTER_H */
#define ATCBROADCASTER_H/* SIMPLEBROADCASTER_H */

#ifdef __cplusplus
extern "C"
{
#endif
······
(2) 修改 AtcBroadcaster.c

首先使用替换工具将“Simple”字符串替换为“Atc”。
在这里插入图片描述

(3) 广播任务

例程广播任务代码如下所示

/*********************************************************************
 * @fn      AtcBroadcaster_processEvent
 *
 * @brief   Application task entry point for the Atc Broadcaster.
 *
 * @param   none
 *
 * @return  none
 */
static void AtcBroadcaster_taskFxn(UArg a0, UArg a1)
{
  // Initialize application
  AtcBroadcaster_init();

  // Application main loop
  for (;;)
  {
    // Get the ticks since startup
    uint32_t tickStart = Clock_getTicks();

    uint32_t events;

    events = Event_pend(syncEvent, Event_Id_NONE, SB_ALL_EVENTS,
                        ICALL_TIMEOUT_FOREVER);

    if (events)
    {
      ICall_EntityID dest;
      ICall_ServiceEnum src;
      ICall_HciExtEvt *pMsg = NULL;

      if (ICall_fetchServiceMsg(&src, &dest,
                                (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
      {
        if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
        {
          // Process inter-task message
          AtcBroadcaster_processStackMsg((ICall_Hdr *)pMsg);
        }

        if (pMsg)
        {
          ICall_freeMsg(pMsg);
        }
      }

      // If RTOS queue is not empty, process app message.
      if (events & SB_QUEUE_EVT)
      {
        while (!Queue_empty(appMsgQueueHandle))
        {
          sbEvt_t *pMsg = (sbEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
          if (pMsg)
          {
            // Process message.
            AtcBroadcaster_processAppMsg(pMsg);

            // Free the space from the message.
            ICall_free(pMsg);
          }
        }
      }
    }
  }
}

AtcBroadcaster_init(), 调用这个函数,对使用蓝牙应用进行必要的配置。
在主循环中调用 Event_pend() 函数等待 ICall 事件和用户自定义事件发生,并在后面的代码处理他们。
AtcBroadcaster_processStackMsg(), 处理协议栈对应用任务发送的事件标志。
AtcBroadcaster_processAppMsg(), 处理用户自定义的时间标志。

状态机是个很好的工具,能帮助编程人员理清编程对象的行为特征,初学者建议可以多了解一下。

(4) 广播任务初始化

初始化代码如下,

/*********************************************************************
 * @fn      AtcBroadcaster_init
 *
 * @brief   Initialization function for the Atc Broadcaster App
 *          Task. This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notification ...).
 *
 * @param   none
 *
 * @return  none
 */
static void AtcBroadcaster_init(void)
{
  // ******************************************************************
  // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
  // ******************************************************************
  // Register the current thread as an ICall dispatcher application
  // so that the application can send and receive messages.
  ICall_registerApp(&selfEntity, &syncEvent);

#ifdef USE_RCOSC
  RCOSC_enableCalibration();
#endif // USE_RCOSC

  // Create an RTOS queue for message from profile to be sent to app.
  appMsgQueueHandle = Util_constructQueue(&appMsgQueue);

  // Open LCD
  dispHandle = Display_open(Display_Type_ANY, NULL);

  // Register with GAP for HCI/Host messages. This is needed to receive HCI
  // events. For more information, see the HCI section in the User's Guide:
  // http://software-dl.ti.com/lprf/ble5stack-latest/
  GAP_RegisterForMsgs(selfEntity);

  //Initialize GAP layer for Peripheral role and register to receive GAP events
  GAP_DeviceInit(GAP_PROFILE_BROADCASTER, selfEntity, addrMode, NULL);

  Display_printf(dispHandle, 0, 0, "BLE Broadcaster");
}

ICall_registerApp(), 这是要使用协议栈必须调用的函数,它注册当前任务为ICall 调度应用程序,使其能和协议栈收发数据通信。
Util_constructQueue(), 创建一个应用消息队列,用于接收协议栈发给应用任务的消息。
GAP_RegisterForMsgs(), HCI 传输协议栈和蓝牙控制单元间的命令和消息,这个函数设置用户任务可接收 HCI 消息。
GAP_DeviceInit(), GAP 设备初始化,将当前设备 GAP 角色设置为广告者。

(5) 协议栈事件处理

协议栈事件处理代码如下

/*********************************************************************
 * @fn      AtcBroadcaster_processStackMsg
 *
 * @brief   Process an incoming stack message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void AtcBroadcaster_processStackMsg(ICall_Hdr *pMsg)
{
  switch (pMsg->event)
  {
    case GAP_MSG_EVENT:
      AtcBroadcaster_processGapMessage((gapEventHdr_t*) pMsg);
      break;

    default:
      // do nothing
      break;
  }
}

可以看到例程中仅实现了一个 GAP_MSG_EVENT 事件,这事件处理在协议栈 GAP 这个对象的相关事务,蓝牙广播就是 GAP 的业务范围。因此需要着重学习 GAP 消息处理函数,在例程的基础上稍加修改就能达到效果了。
协议栈事件有很多,GAP_MSG_EVENT 只是其中很重要中的一个。协议栈定义的事件标志可以找到定义如下。

/*********************************************************************
 * BLE OSAL GAP GLOBAL Events
 */
#define GAP_EVENT_SIGN_COUNTER_CHANGED  0x4000  //!< The device level sign counter changed

// GAP - Messages IDs (0xD0 - 0xDF)
#define GAP_MSG_EVENT                         0xD0 //!< Incoming GAP message

// SM - Messages IDs (0xC1 - 0xCF)
#define SM_NEW_RAND_KEY_EVENT                 0xC1 //!< New Rand Key Event message
#define SM_MSG_EVENT                          0xC2 //!< Incoming SM message

// GATT - Messages IDs (0xB0 - 0xBF)
#define GATT_MSG_EVENT                        0xB0 //!< Incoming GATT message
#define GATT_SERV_MSG_EVENT                   0xB1 //!< Incoming GATT Serv App message

// L2CAP - Messages IDs (0xA0 - 0xAF)
#define L2CAP_DATA_EVENT                      0xA0 //!< Incoming data on a channel
#define L2CAP_SIGNAL_EVENT                    0xA2 //!< Incoming Signaling message

// HCI - Messages IDs (0x90 - 0x9F)
#define HCI_DATA_EVENT                        0x90 //!< HCI Data Event message
#define HCI_GAP_EVENT_EVENT                   0x91 //!< GAP Event message
#define HCI_SMP_EVENT_EVENT                   0x92 //!< SMP Event message
#define HCI_EXT_CMD_EVENT                     0x93 //!< HCI Extended Command Event message
#define HCI_SMP_META_EVENT_EVENT              0x94 //!< SMP Meta Event message
#define HCI_GAP_META_EVENT_EVENT              0x95 //!< GAP Meta Event message

// ICall and Dispatch - Messages IDs (0x80 - 0x8F)
#define ICALL_EVENT_EVENT                     0x80 //!< ICall Event message
#define ICALL_CMD_EVENT                       0x81 //!< ICall Command Event message
#define DISPATCH_CMD_EVENT                    0x82 //!< Dispatch Command Event message
(6) GAP 消息处理

GAP 消息处理函数如下所示,可以看出也仅实现了GAP_DEVICE_INIT_DONE_EVENT 这个事件。这个事件是在函数 AtcBroadcaster_init() 中调用了 GAP_DeviceInit() 函数初始化当前设备为 广播者后,协议栈向应用任务出的事件标志,表示设备初始化完成。

/*********************************************************************
 * @fn      AtcBroadcaster_processGapMessage
 *
 * @brief   Process an incoming GAP event.
 *
 * @param   pMsg - message to process
 */
static void AtcBroadcaster_processGapMessage(gapEventHdr_t *pMsg)
{
  switch(pMsg->opcode)
  {
    case GAP_DEVICE_INIT_DONE_EVENT:
    {
      bStatus_t status = FAILURE;

      gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;

      if(pPkt->hdr.status == SUCCESS)
      {
        // Store the system ID
        uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];

        // use 6 bytes of device address for 8 bytes of system ID value
        systemId[0] = pPkt->devAddr[0];
        systemId[1] = pPkt->devAddr[1];
        systemId[2] = pPkt->devAddr[2];

        // set middle bytes to zero
        systemId[4] = 0x00;
        systemId[3] = 0x00;

        // shift three bytes up
        systemId[7] = pPkt->devAddr[5];
        systemId[6] = pPkt->devAddr[4];
        systemId[5] = pPkt->devAddr[3];

        // Set Device Info Service Parameter
        DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);

        // Display device address
        Display_printf(dispHandle, 1, 0, "%s Addr: %s",
                       (addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
                       Util_convertBdAddr2Str(pPkt->devAddr));
        Display_printf(dispHandle, 2, 0, "Initialized");

        // Setup and start Advertising
        // For more information, see the GAP section in the User's Guide:
        // http://software-dl.ti.com/lprf/ble5stack-latest/

        // Temporary memory for advertising parameters. These will be copied
        // by the GapAdv module
        GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;

        #ifndef BEACON_FEATURE
          advParamLegacy.eventProps = GAP_ADV_PROP_SCANNABLE | GAP_ADV_PROP_LEGACY;
        #else
          advParamLegacy.eventProps = GAP_ADV_PROP_LEGACY;
        #endif // !BEACON_FEATURE

        // Create Advertisement set
        status = GapAdv_create(&AtcBroadcaster_advCallback, &advParamLegacy,
                               &advHandleLegacy);
        AtcBROADCASTER_ASSERT(status == SUCCESS);

        // Load advertising data
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
                                     sizeof(advertData), advertData);
        AtcBROADCASTER_ASSERT(status == SUCCESS);

        // Load scan response data
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
                                     sizeof(scanRspData), scanRspData);
        AtcBROADCASTER_ASSERT(status == SUCCESS);

        // Set event mask
        status = GapAdv_setEventMask(advHandleLegacy,
                                     GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                                     GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                     GAP_ADV_EVT_MASK_SET_TERMINATED);
        AtcBROADCASTER_ASSERT(status == SUCCESS);

        // Enable legacy advertising
        status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        AtcBROADCASTER_ASSERT(status == SUCCESS);
      }

      break;
    }

    default:
      Display_clearLine(dispHandle, 2);
      break;
  }
}

现在修改这个函数完成自定义数据的广播。

4、自定义信息广播实现

(1) 广播 PDU 类型
advParamLegacy.eventProps = GAP_ADV_PROP_SCANNABLE | GAP_ADV_PROP_LEGACY;

当前设置为不定向可扫描的蓝牙广告。

(2) 广播间隔

例程默认参数,

/// Default parameters for legacy, scannable, connectable advertising
#define GAPADV_PARAMS_LEGACY_SCANN_CONN {                                  \
  .eventProps = GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_SCANNABLE |        \
                GAP_ADV_PROP_LEGACY,                                       \
  .primIntMin = 160,                                                       \
  .primIntMax = 160,                                                       \
  .primChanMap = GAP_ADV_CHAN_ALL,                                         \
  .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,                       \
  .peerAddr = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },                      \
  .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,                               \
  .txPower = GAP_ADV_TX_POWER_NO_PREFERENCE,                               \
  .primPhy = GAP_ADV_PRIM_PHY_1_MBPS,                                      \
  .secPhy = GAP_ADV_SEC_PHY_1_MBPS,                                        \
  .sid = 0                                                                 \
}

其中 primIntMin , primIntMax , 表示广播间隔 数值 1 代表 0.625ms,160 * 0.625ms = 100ms。这个参数保持默认不做修改。
参数其中还有, 广播通道,过滤,Phy 等相关设置,这里不做讨论。

(3) 修改广播内容

例程广播内容:

// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] =
{
  // Flags; this sets the device to use limited discoverable
  // mode (advertises for 30 seconds at a time) instead of general
  // discoverable mode (advertises indefinitely)
  0x02,   // length of this data
  GAP_ADTYPE_FLAGS,
  GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,

#ifndef BEACON_FEATURE

  // three-byte broadcast of the data "1 2 3"
  0x04,   // length of this data including the data type byte
  GAP_ADTYPE_MANUFACTURER_SPECIFIC, // manufacturer specific adv data type
  1,
  2,
  3

#else

  // 25 byte beacon advertisement data
  // Preamble: Company ID - 0x000D for TI, refer to https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers
  // Data type: Beacon (0x02)
  // Data length: 0x15
  // UUID: 00000000-0000-0000-0000-000000000000 (null beacon)
  // Major: 1 (0x0001)
  // Minor: 1 (0x0001)
  // Measured Power: -59 (0xc5)
  0x1A, // length of this data including the data type byte
  GAP_ADTYPE_MANUFACTURER_SPECIFIC, // manufacturer specific adv data type
  0x0D, // Company ID - Fixed
  0x00, // Company ID - Fixed
  0x02, // Data Type - Fixed
  0x15, // Data Length - Fixed
  0x00, // UUID - Variable based on different use cases/applications
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // UUID
  0x00, // Major
  0x01, // Major
  0x00, // Minor
  0x01, // Minor
  0xc5  // Power - The 2's complement of the calibrated Tx Power

#endif // !BEACON_FEATURE
};

修改为

// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] =
{
  // Flags; this sets the device to use limited discoverable
  // mode (advertises for 30 seconds at a time) instead of general
  // discoverable mode (advertises indefinitely)
  0x02,   // length of this data
  GAP_ADTYPE_FLAGS,
  GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
};

广播数据最长为 31 字节,一组有效广播数据第一个字节为当前数据的长度(长度包括数据类型 1 字节 和 荷载数据 n 字节),第二个字节为数据标志, 后面的数据为荷载数据。一组有效广播数据长度为 1 + 1 + n 字节。
当前广播数据共三字节, 0x02 表示这组数据的长度,GAP_ADTYPE_FLAGS 为广播标志,GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED表示不支持 BR/EDR, 即当前设备为 LE 蓝牙设备。

(4) 修改扫描回复数据

例程扫描恢复数据为:

// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
  // complete name
  0x15,   // length of this data
  GAP_ADTYPE_LOCAL_NAME_COMPLETE,
  'S',
  'i',
  'm',
  'p',
  'l',
  'e',
  'B',
  'L',
  'E',
  'B',
  'r',
  'o',
  'a',
  'd',
  'c',
  'a',
  's',
  't',
  'e',
  'r',

  // Tx power level
  0x02,   // length of this data
  GAP_ADTYPE_POWER_LEVEL,
  0       // 0dBm
};

修改为:

// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
  // complete name
  0x0F,   // length of this data
  GAP_ADTYPE_LOCAL_NAME_COMPLETE,
  'A', 't', 'c', 'B', 'r', 'o', 'a', 'd', 'c', 'a', 's', 't', 'e', 'r',

  // User define String
  0x08,   // length of this data
  GAP_ADTYPE_MANUFACTURER_SPECIFIC,
  'A', 'T', 'C', 'T', 'e', 's', 't'
};

和广播数据一样,扫描回复数据最大也只能到 31 字节。第一组数据定义了蓝牙全名为“AtcBroadcaster”,第二组数据定义了自定义数据为“ATCTest”。

5、结果调试

编译程序并下载到 CC2640R2F平台。
使用手机APP可以看到,收到的广播数据。
在这里插入图片描述
可看到全名为 “AtcBroadcaster”,在Comoany一栏数据为 “41 54 43 54 65 73 74”
对照ASCII码表可译出为"ATCTest"。
在这里插入图片描述

五、总结

当前项目可自行下载。
蓝牙广播者,是蓝牙调试里最简单的角色,可以很快地的调试出可观测结果。下一篇试着使用定时器计数,测量芯片温度等数据来使用更新广播数据。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATC Working

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值