目录

Host 控制接口( HCI )

主控制器接口( HCI )层是在蓝牙协议栈的主机( Host )和控制器( controller )之间传送命令和事件的薄层。在纯网络处理器应用程序(即 host_test 项目)中,HCI 层通过 SPI 或 UART 等传输协议实现数据处理。

类似 simple_peripheral 的嵌入式无线MCU项目中,HCI 层通过无线 MCU 内的函数调用和回调来实现通信。所有与 Controller 进行通信的命令和事件(如 ATT 、GAP 等)将最终称为 HCI API,从协议栈的 Host 通过 HCI 层传递到 Controller。同样,控制器通过 HCI 将接收到的数据和事件发送到 Host 上层。

除了标准的蓝牙 LE HCI 命令之外,还有许多 HCI 扩展供应商特定的命令可以扩展控制器的一些功能。有关在嵌入式应用程序中调用的可用 HCI 和 HCI 扩展命令的描述,请参阅 BLE Stack API。

BLE5-Stack 支持纯网络处理器配置( host_test ),即允许应用程序在外部 MCU 上运行以连接到 BLE5-Stack 。网络处理器可以通过外部传输协议( UART、 SPI 等)接受所有 LE HCI 命令。由于 BLE Controller 和 Host 都驻留在无线 MCU 上,一些 HCI 命令会使对应的事件被 TI BLE 主机消耗。因此,不可能使用标准 HCI LE 命令将外部的蓝牙主机与 CC2640 无线 MCU 进行接口连接。网络处理器配置应使用 HCI 和 TI 供应商特定的 HCI 命令来实现外部蓝牙应用。

类似于网络处理器配置( host_test ),BLE5-Stack 可以被配置为使用子集 HCI 命令,可用的 HCI 命令的子集是执行蓝牙 RF 认证的子集。HCI 命令通过 UASRT、SPI传递给控制器​​,从而能够切换到完整的嵌入式应用,此配置称为生产测试模式( PTM )。有关如何在嵌入式应用程序上启用 PTM 的信息,请参阅使用生产测试模式( PTM )。

当需要满足以下条件时,应考虑将 PTM 用于直接测试模式( DTM ),以代替完整的网络处理器配置( host_test ):

在应用程序中使用 HCI 和 HCI 供应商特定的命令

按照以下步骤使用这些命令并在应用程序中接收各自的事件:

  1. 包括 HCI 传输层头文件。
    `#include  “hci_tl.h”`
  1. 注册用于 HCI/Host 消息的 GAP,以便从控制器接收事件。这应该在应用程序初始化函数中完成。
    //注册 GAP 用于 HCI /主机消息
    GAP_RegisterForMsgs (selfEntity );
  1. 应用程序能调用 HCI 或 HCI 特定于供应商的命令。请参阅 BLE Stack API Reference,HCI 部分具体介绍可以发送哪些命令。

  2. 作为 HCI_GAP_EVENT_EVENT 的任务间消息返回 HCI 事件 。请参阅 simple_peripheral 项目。

以下部分讨论接收 HCI 事件和 HCI 供应商特定的事件。

标准 LE HCI 命令和事件

这些命令在“ 蓝牙核心规范版本 5.0 ”的“ HCI 命令和事件”一章(第 2 卷,第 E 部分,第 7 节)中有记录。使用这些命令的机制与蓝牙核心规范版本 5.0 中的任何命令相同,包括 HCI LE 命令。

下面的示例演示了如何使用蓝牙核心规范版本 5.0 在应用程序中实现 HCI 命令,使用的命令是读 RSSI 命令。

发送 HCI 命令

  1. 如图 1 在蓝牙核心规范版本 5.0 中查找命令:

图1. 蓝牙核心规范中查找命令

  1. 查找映射到 BLE 堆栈命令。使用 BLE Stack API 参考( HCI 部分 -> HCI 功能映射),显示此命令映射到 HCI_ReadRssiCmd()。

  2. 使用步骤 1 中的 API ,填写参数并从应用程序的某处调用命令。在形成连接后,应该调用此特定命令。这里只有一个参数:一个 2 字节的连接句柄。本例连接句柄是 0x0000:
    `HCI_ReadRssiCmd (0x0000);`

接收 HCI event

< 注意:确保注册消息,否则事件不会发送到应用程序。使用 GAP_RegisterForMsgs() 注册 GAP 消息。

1.如图 2 ,查看蓝牙核心规范版本 5.0 以查看返回事件的格式:

图2. 返回事件的格式

  1. 此命令返回一个 Command complete 事件( hciEvt_CmdComplete_t ),在处理 HCI_GAP_EVENT_EVENT 时需要将其添加。这在下面进一步详细说明。
        static  uint8_t  SimpleBLEPeripheral_processStackMsg (ICall_Hdr *  pMsg )
        { 
           uint8_t  safeToDealloc  =  TRUE ; 
           switch  (pMsg - > event )
           { 
              case  HCI_GAP_EVENT_EVENT :
              { 
                 //处理 HCI 消息切换(pMsg-> status)
              {

              //处理 HCI 命令完成事件情况
              HCI_COMMAND_COMPLETE_EVENT_CODE :
              { 
                 //解析命令完成事件的操作码和状态
                 hciEvt_CmdComplete_t *  command_complete  =  (hciEvt_CmdComplete_t * ) pMsg ; 
                 uint8_t  status  =  command_complete - > pReturnParam [ 0 ];

                 // find which command complete is for 
                 switch  (command_complete - > cmdOpcode )
                 { 
                    case  HCI_READ_RSSI :
                    { 
                       if  (status  ==  SUCCESS )
                       { 
                          uint16_t  handle  =  BUILD_UINT16 ( command_complete - > pReturnParam [ 2 ], command_complete - > pReturnParam [ 1 ] );

                          //检查句柄
                          if  (handle  ==  0x00 )
                          { 
                             //存储 RSSI 
                             uint8_t  rssi  =  command_complete - > pReturnParam [ 3 ];

首先,检查堆栈消息的状态以查看它是什么类型的 HCI 事件。本例是一个 HCI_COMMAND_COMPLETE_EVENT_CODE。然后,将作为消息( pMsg )从堆栈返回的事件转换为 hciEvt_CmdComplete_t,其定义为:

    // Command Complete Event typedef struct
    { 
      osal_event_hdr_t  hdr ; 
      uint8  numHciCmdPkt ; 
      uint16  cmdOpcode ; 
      uint8 *  pReturnParam ; 
    }  hciEvt_CmdComplete_t ;

首先检查 cmdOpcode,发现它与 HCI_READ_RSSI 匹配 。然后检查事件的状态,现在事件已知,可以使用蓝牙核心规范版本 5.0 中的信息来解析 pReturnParmam 。蓝牙核心规范版本 5.0 API 声明 pReturnParam 的第一个字节为状态,第二和第三个字节是句柄。之后检查 RSSI 以查看它是否对应于所需的连接手柄,RSSI 值可以通过读取 pReturnParam 的第四个字节来找到。最后存储 RSSI 值。

HCI 供应商特定的命令

这些命令在“ TI 供应商特定 HCI 指南”中有说明。对于所有特定于供应商的命令,使用这些命令的机制是相同的。下面的示例演示了如何使用“ TI 供应商特定 HCI 指南” 在应用程序中实现 HCI 命令。使用的命令是 HCI 扩展包错误率。

发送 HCI 供应商特定的命令

  1. 如图 4 ,在 TI BLE 供应商特定指南中查找命令:

图4. 供应商特定指南中查找指令

  1. 使用 BLE Stack API Reference 中的 HCI 部分找到实现此命令的 BLE Stack 函数:HCI_EXT_PacketErrorRateCmd()。

  2. 使用步骤 1 中的 API,填写参数并从应用程序调用命令。在这种特定情况下,连接形成后应该调用此命令。第一个参数是一个 2 字节的 connHandle,本例是等于 0x0000。第二个参数是读取计数器的 1 字节命令( HCI_EXT_PER_READ )。因此,使用:

    HCI_EXT_PacketErrorRateCmd ( 0 , HCI_EXT_PER_READ );

接收 HCI 供应商特定事件

  1. 如图 5 ,在“ TI 供应商特定 HCI 指南”中查找相应的事件


图5. 供应商特定指南中查找事件

  1. 如命令 API 的“对应事件”部分所述,命令返回的是“供应商特定命令完成事件”( hciEvt_VSCmdComplete_t ),在处理 HCI_GAP_EVENT_EVENT 处理时需要添加该事件。这在下面进一步详细说明。
        static uint8_t SimpleBLEPeripheral_processStackMsg(ICall_Hdr* pMsg)
        {
           uint8_t safeToDealloc = TRUE;
           switch (pMsg->event)
           {
              case HCI_GAP_EVENT_EVENT:
              {
                 // Process HCI message
                 switch(pMsg->status)
                 {
                    // Process HCI Vendor Specific Command Complete Event
                    case HCI_VE_EVENT_CODE:
                       {
                          // Parse Command Complete Event for opcode and status
                          hciEvt_VSCmdComplete_t* command_complete = (hciEvt_VSCmdComplete_t*)pMsg;

                          // Find which command this command complete is for
                          switch(command_complete->cmdOpcode)
                          {
                             case HCI_EXT_PER:
                             {
                                uint8_t status = command_complete->pEventParam[2];
                                if (status == SUCCESS)
                                {
                                   uint8_t cmdVal = command_complete->pEventParam[3];

                                   if (cmdVal == 1) //if we were reading packet error rate
                                   {
                                      uint16_t numPkts = BUILD_UINT16(command_complete->pEventParam[5], command_complete->pEventParam[4]);
                                      uint16_t numCrcErr = BUILD_UINT16(command_complete->pEventParam[7], command_complete->pEventParam[6]);
                                      uint16_t numEvents = BUILD_UINT16(command_complete->pEventParam[9], command_complete->pEventParam[8]);
                                      uint16_t numMissedEvents = BUILD_UINT16(command_complete->pEventParam[11], command_complete->pEventParam[10]);

首先,检查堆栈消息的状态以查看它是什么类型的 HCI 事件,本例是一个 HCI_VE_EVENT_CODE。接下来,把它作为消息( pMsg)从堆栈返回的事件转换为 hciEvt_VSCmdComplete_t ,其定义为:

    typedef struct
    {
       osal_event_hdr_t hdr; uint8 length;
       uint16 cmdOpcode; uint8* pEventParam;
    } hciEvt_VSCmdComplete_t;

通过读取 command_complete -> cmdOpcode 来检查操作码,并发现它与 HCI_EXT_PER 匹配。

接下来,解析* pEventParam 并提取事件 API 中定义的参数。前两个字节(图 6 中以红色显示)是事件操作码( 0x1404 ),第三个字节是状态。所有供应商特定事件都是这种情况。

从 pEventParam 的第四个字节,第三个参数开始,使用“ TI BLE 供应商指南”中的事件 API 进行解析。所有供应商特定事件都是这种情况。对于此示例, pEventParam 的第四个字节对应于 cmdVal 参数。pEventParam 在存储器中的显示如下图 6 。

图6. 存储器中的字节参数

通过读取事件参数的第三个字节( command_complete -> pEventParam [2])来检查状态。图 6 中以黄色显示。

从事件参数( command_complete -> pEventParam [3])的第四个字节开始,事件 API 指出下一个参数是一个字节的 cmdVal。cmdVal 用来验证该事件是否对应于 PER 计数器的读取,显示为粉色。

使用事件 API 继续解析,下一个参数是两个字节的 numPkts 。这是通过在事件参数的第五和第六个字节中构建一个 uint16_t 来找到的,显示为蓝色。以类似的方式,从第七和八个字节的事件参数(绿色显示)中找到 numCrcErr。

接下来,从事件参数的第九和第十个字节找到 numEvents(以橙色显示)。最后,从事件参数的第十一和第十二个字节找到 numMissedEvents(以紫色显示)。

加入我们

文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。

CC2640R2F&BLE5.0-乐控畅联 © Copyright 2017, 成都乐控畅联科技有限公司.