1. 概述

zephyr 主要支持低功耗蓝牙(BLE). 也支持传统蓝牙(BR/EDR)主机的部分协议.

2. BLE 分层

完整的 BLE 协议栈主要分为如下 3 层:
  • Host 层
    该层位于应用下方, 由多个(非实时)网络和传输协议组成, 使应用能够以标准的, 可互操作的方式与对等设备通信.
  • Controller 层
    控制器实现链路层(LE LL), 这是一种低层次的实时协议, 与无线电硬件一起提供空口通信中可互操作的标准协议. LL 调度数据包的接收和发送, 保证数据的传输, 并处理所有 LL 控制流程.
  • Radio Hardware 层
    硬件实现所需的模拟和数字基带功能块, 允许链路层固件在 2.4GHz 频段发送和接收.

3. 主机控制器接口(HCI)

蓝牙规范描述了一个主机和一个控制器通信的格式. 被称为主机控制器接口协议(HCI). HCI 可以在不同的物理传输上实现, 例如 UART, SPI, USB. 该协议规定了主机发送给控制器的命令和从控制器返回的事件, 以及需要在空口传输的用户数据和协议数据的格式. HCI 确保不同的主机和控制器能以标准的方式进行通信, 从而能够组合来自不同供应商的主机和控制器.

4. 配置

协议的 3 个独立层和白哦准话的接口, 使得主机和控制器可以在不同的平台上实现. 通常使用以下两种配置:
  • 单芯片配置
    在该配置下, 单个微控制器实现所有 3 层和应用本身. 这也被称为片上系统(SoC). 这种场景下, BLE 主机和 BLE 控制器可以通过 RAM 中的队列和函数调用直接通信. 规范没有指定 HCI 是如何在这种单片机中配置实现, 因此 HCI 命令, 事件和两者间的数据流是特定实现的. 这种配置非常适用于那些要求小内存占用和尽可能低的功耗的应用程序和设计, 因为协议栈和应用都运行在单个 IC 上.
  • 双芯片配置
    这种配置使用两个独立的芯片, 一个运行应用程序和主机, 另一个搭载控制器和射频硬件. 有时也称为连接芯片配置. 当使用 Zephyr OS 作为控制器时, 该配置允许广泛的主机组合. 由于HCI确保主机 和控制器实现之间的互操作性, 当然也包括 Zephyr 自己的 主机和控制器, Zephyr 控制器的用户可以选择使用他们喜欢的任何平台上运行的任何主机. 例如, 主机可以是运行在支持 Linux 的任何处理器上的 Linux BLE 主机协议栈(BlueZ). 主机处理器也可以运行 Zephyr 和 Zephyr OS BLE 主机协议栈. 相反, 也支持运行 Zephyr 主机协议栈的芯片与不运行 Zephyr 控制器的外部控制器组合.

5. 构建类型

Zephyr 的软件协议栈是个高度可配置的 RTOS. 特别是 BLE 子系统在构建过程中可以多种方式配置, 仅包含特色和层以适用要求少量的 RAM 和 ROM 占用以及低功耗的场景. 下面是从 Zephyr 代码库生成构建不同的 BLE 的简短列表.
  • 仅构建控制器
    当只作为 BLE 控制器, Zephyr 包括链路层和一个特殊应用. 这个应随 HCI 选择的物理传输不同而不同. 为其中之一 (hci_uart / hci_usb / hci_spi).
    这个应用作为 UART, SPI 或 USB外设 与 控制器子系统之间的桥梁, 监听 HCI 命令, 发送应用数据并响应事件和接收数据. 这种类型的构建设置 Kconfig 的以下选项值:
CONFIG_BT =y
CONFIG_BT_HCI =y
CONFIG_BT_HCI_RAW =y
CONFIG_BT_CTLR =y
CONFIG_BT_LL_SW =y (如果使用开源的链路层)
  • 仅构建主机
    Zephyr OS Host 包含应用程序和主机协议栈, 与 HCI 驱动程序(UART/SPI)一起与外部控制器芯片通信. 这种类型的构建设置 Kconfig 的以下选项值:
CONFIG_BT =y
CONFIG_BT_HCI =y
CONFIG_BT_CTLR =n

除了用于仅构建控制器的示例外, 位于 sample/bluetooth 中的所有示例都可以作为仅构建主机的示例.

  • 组合构建
    包括应用程序, 主机和控制器, 专门用于单芯片配置(SoC). 这种类型的构建设置 Kconfig 的以下选项值:
CONFIG_BT =y
CONFIG_BT_HCI =y
CONFIG_BT_CTLR =y
CONFIG_BT_LL_SW =y (如果使用开源的链路层)

除了用于仅构建控制器的示例外, 位于 sample/bluetooth 中的所有示例都可以作为组合构建的示例.

下图表示使用 Zephyr 组合构建(构建包含同一个固件镜像中的 BLE 主机和控制器):

当使用组合或者双芯片配置, 可能有以下几种主机和控制器组合:

当使用 Zephyr Host, Zephyr OS 的两个实例必须使用不同的配置构建, 产生的两个独立的镜像, 必须分别编程到每个芯片. 主机构建镜像包含应用, BLE Host 和 已选择的 HCI 驱动(UART or SPI), 控制器构建运行 hci_uart 或 hci_spi 应用, 为 BLE 控制器提供接口.

这种配置不限于使用 Zephyr OS Host, 如上图右所示. 可以从现有的 GNU/Linux 发行版本选择一个, 其中大多数都包含 Linux 自己的 BLE Host(BlueZ), 通过 UART 或 USB 连接到 Zephyr OS Controller 构建的一个或多个实例. 作为一个主机, BlueZ 支持多个控制器同时为共享主机协议栈的应用同时提供一个以上的 BLE 射频操作.

6. 源码树结构

协议栈按以下方式在源码树中分布:
  • subsys/bluetooth/host
    主机协议栈, 是 HCI 命令和事件处理以及连接跟踪发生的地方. 核心协议中的 L2CAP, ATT 和 SWP 协议在此实现.
  • subsys/bluetooth/controller
    控制器实现, 实现 HCI 的控制器部分, 链路层以及对射频收发器的访问.
  • include/bluetooth/
    开放的 API 接口头文件, 应用需要包含头文件以使用蓝牙功能.
  • drivers/bluetooth/
    HCI 传输驱动. 每个 HCI 传输都需要它自己的驱动. 例如, 两种常用类型(3-Wire and 5-Wire)的UART传输协议都有他们自己的驱动.
  • samples/bluetooth/
    蓝牙的示例代码. 这是开始开发蓝牙应用很好的参考.
  • tests/bluetooth/
    测试应用. 这些应用用于验证蓝牙协议栈的功能, 并不是示例代码的最佳代码.
  • doc/guides/bluetooth/
    额外的文档, 如 PICS 文档.

7. 主机

蓝牙主机实现了所有高层协议和配置文件, 并且为应用程序提供了高层的 API. 下图显示了主机的主要协议和配置文件.

主机协议栈的最底层为 HCI 驱动, 它负责抽象出 HCI 传输的细节. 它提供了一个基本的 API 用于将数据冲控制器传递到主机, 反之亦然.

也许 HCI 处理之上最重要的块是通用访问配置(GAP). GAP通过定义四个不同角色简化了蓝牙的访问:

  • 面向连接的角色

    1. 外围(如: 智能传感器, 通常具有有限的用户界面)
    2. 中心(通常是手机或电脑)
  • 无连接的角色
    1. 广播者(发送 BLE 广播, 如: 智能信标)
    2. 观察者(扫描 BLE 广播)

每一个角色都有自己的构建时的配置选项, CONFIG_BT_PERIPHERAL, CONFIG_BT_CENTERAL, CONFIG_BT_BROADCASTER, CONFIG_BT_OBSERVER. 面向连接的中心角色默认使能观察者角色, 外围角色默认使能广播者角色. 创建应用程序的第一步通常是决定需要哪种角色并从那里开始. 蓝牙 Mesh 是个稍微特殊的场景, 至少有观察者和广播者角色, 并可能需要外围角色. 将在后面的小结详细描述.

8. 外围角色

大多数基于 Zephyr 的 BLE 设备都是 外围角色设备. 意味着他们能执行可连接广播并公开一个或多个 GATT 服务. 在使用 bt_gatt_service_register() API 注册服务后, 应用程序通常会使用 bt_le_adv_start( ) API 开启可连接广播.

在树中有几个外围角色可用的示例应用程序, 如: samples/bluetooth/peripheral_hr.

9. 中心角色

基于 Zephyr 的设备, 中心角色可能不会像外围角色那么常见, 但还是在 Zephyr 中得到了很好的支持. 中心角色设备不接受来自其他设备的连接, 而是扫面可用的外围设备并选一个进行连接. 一旦连接上, 中心角色设备将作为 GATT 客户端, 首先执行发现有效的服务, 然后访问一个或多个支持的服务.

使用 bt_le_scan_start( ) API 去发现要连接到应用程序的设备, 等待找到合适的设备(使用扫描回调), 使用 bt_le_scan_stop( ) 停止扫描, 然后使用 bt_conn_create_le( ) 连接设备. 如果中心角色设备希望保持自动重新连接外围设备, 则应该使用 bt_le_set_auto_conn( ) API.

在树中有几个中心角色可用的示例应用程序, 如: samples/bluetooth/central_hr.

10. 观察者角色

观察者角色设备将使用 bt_le_scan_start( ) API 扫描设备, 并不会去连接任何一个. 而是接收发现设备的广播数据, 可选的将其与接收信号强度(RSSI)组合.

11. 广播者角色

广播者角色设备将使用 bt_le_adv_start( ) API 广播指定的广播数据, 但是广播的类型是不可连接的, 如: 其它设备将不能连接它.

12. 连接

Zephyr 协议栈使用结构体 bt_conn 抽象的表示连接到其它设备. 这个结构体的内部不会公开给应用程序, 但是可以使用 bt_conn_get_info( ) API 获取有限的信息(如: 远端地址). 连接对象被引用计数, 当存储连接指针的时间更长, 应用程序将使用 bt_conn_ref( ) API, 因为这可以确保对象保持有效(即使连接断开). 类似的, 使用 bt_conn_unref( ) API 释放连接的引用计数.

13. 安全

为了实现在两个蓝牙设备间的安全关系, 需要使用配对过程. 这个过程通过 GATT 服务的安全属性隐式的触发, 或在连接对象上显式使用 bt_conn_security( ) API.

为了达到更高的安全等级, 防止中间人(MITM)攻击,推荐在配对过程中使用一些带外(OOB)信道. 如果设备有足够的用户界面, 这个信道就是用户自己. 使用 bt_conn_auth_cb_register( ) API 注册设备的能力. 传递给这个 API 的 bt_conn_auth_cb 结构体有一组可选回调, 可以在配对过程中使用——如果设备缺少某些特性, 可以将相应的回调设置为 NULL. 例如: 设备没有输入, 但有一个显示, passkey_entry 和 passkey_confirm 回调将被设置为 NULL, 但是 passkey_display 将被设置为能够向用户显示的 passkey 的回调函数.

根据本地和远程的安全需求和能力, 有四种可能的安全级别:

BT_SECURITY_LOW         //没有加密, 没有认证
BT_SECURITY_MEDIUM   //加密, 没有认证(没中间人保护)
BT_SECURITY_HIGH        //加密, 认证(使用4.0 和 4.1 中遗留的配对方式)
BT_SECURITY_FIPS         //加密, 认证(使用4.2 中的 LE 安全连接特色)

Note: Mesh 有自己的安全解决方案, 通过 provisioning 过程. 过程类似于配对, 不同的是它使用 Mesh 特定的 API 来完成.

14. 逻辑链路适配层(L2CAP)

逻辑链路适配层(L2CAP)使能面向连接通道, 通过配置选项使能通道: CONFIG_BT_L2CAP_DYNAMIC_CHANNEL. 通道支持透明的分段和重组, 也支持基于流控, 使其适用于数据流.

通道的实体由结构体 bt_l2cap_chan 表示, 该结构包含 bt_l2cap_chan_ops 结构中的回调函数, 用于通知通道何时连接, 断开连接或加密何时更改. 此外, 它还包含一个 recv 回调函数, 每当接收到数据时, 都会调用该函数. 如果处理是异步的, 则通过返回 0 或者使用 bt_l2cap_chan_recv_complete( ) API 将这种方式接收的数据标记为已处理.

Note: recv 回调函数直接从 RX 线程调用, 因此不允许被阻塞.

发送数据, 可以使用 bt_l2cap_chan_send( ) API, 注意, 如果没有 credits 有效时可能会阻塞, 一旦有credits 有效时就会恢复.

可以使用 bt_l2cap_server_register( ) API 注册服务器, 该 API 传递 bt_l2cap_server 结构体, 该结构体通知服务器应该监听什么 psm, 所要求的的安全级别 sec_level 和 accept 回调, 后者被调用 来授权传入的连接请求和分配通道实例.

客户端通道可以使用 bt_l2cap_chan_connect( ) API 初始化, 使用 bt_l2cap_chan_disconnect( ) API 断开连接. 注意, 后者还可以断开由服务器创建的通道实例.

15. GATT

GATT 层管理服务数据库, 为服务注册和属性声明提供 API.

使用 bt_gatt_service_register( ) API 注册服务, 该 API 接受 bt_gatt_service 结构体,该结构体提供服务包含的属性列表. 宏 BT_GATT_SERVICE( ) 可用于声明一个服务.

属性的声明可以使用 bt_gatt_attr 结构体声明或使用以下宏:

BT_GATT_PRIMARY_SERVICE( )           //声明一个主服务
BT_GATT_SECONDARY_SERVICE( )      //声明一个次要服务
BT_GATT_INCLUDE_SERVICE( )          //声明一个包含服务
BT_GATT_CHARACTERISTIC( )           //声明一个特征
BT_GATT_DISCRIPTOR( )                //声明一个描述
BT_GATT_ATTIBUTE( )                   //声明一个属性
BT_GATT_CCC( )                          //声明一个客户端特征配置
BT_GATT_CEP( )                          //声明一个特征扩展属性
BT_GATT_CUD( )                        //声明一个特征用户格式

每个属性包含一个描述其类型的 uuid, 一个 read 回调函数, 一个 write 回调函数和一个许可的集合. 如果属性的许可不允许读写操作, 则 read 和 write 回调函数设置为 NULL.

Note: 属性的 read 和 write 回调函数直接从 RX 线程调用, 因此不允许被阻塞.

可以使用 bt_gatt_notify( ) API 通知属性值的更改, 另外还有 bt_gatt_notify_cb( ), 当需要知道数据在空口传输的确切时刻, 可以在其中传递回调函数. 指示由 bt_gatt_indicate( ) API支持.

客户端过程使能, 使用配置可选项: CONFIG_BT_GATT_CLIENT.

使用 bt_gatt_discover( ) API 可以开启发现过程,该 API 使用 bt_gatt_dicover_params 结构体描述发现的类型. 设置 uuid 字段为仅发现匹配的属性时, 该参数充当过滤器, 相反, 将其设置为 NULL 允许发现说有服务.

Note: 不支持缓存发现的服务.

读过程由 bt_gatt_read( ) API 支持, 该 API 将 bt_gatt_read_params 结构体作为参数. 在参数中可以设置一个或多个属性, 但是设置多个句柄需要一个选项: CONFIG_BT_GATT_READ_MULTIPLE.

写过程由 bt_gatt_write( ) API 支持, 该 API 将 bt_gatt_write_params 结构体作为参数. 如果写操作不需要响应 bt_gatt_write_without_response( ) 或 bt_gatt_write_without_response_cb( ) API 将被使用,则可以使用后者,后者的工作原理类似于 bt_gatt_notify_cb.

可以使用 :cpp:func’bt_gatt_subscribe’ API 启动对通知和指示的订阅, 该API将bt_gatt_subscribe_params 作为参数. 支持对同一属性的多个订阅, 因此可以为同一属性触发多个notify 回调。可以使用 bt_gatt_unsubscribe( ) API 删除订阅.

Note: 当订阅被删除时, 将调用数据设置为 NULL 的 notify 回调函数.

16. Mesh

Mesh 对于所需的 GAP 角色来说有点特殊. 默认情况下, Mesh 需要同时启用观察者和广播者角色. 如果需要可选的 GATT 代理特性, 那么还应该启用外围角色.

16.1. 永久存储(Persistent storage)

蓝牙主机堆栈使用设置子系统实现永久存储到 Flash. 这需要有一个 Flash 驱动程序和Flash 上的一个指定的“存储”分区. 需要的一组典型配置选项如下所示:
CONFIG_BT_SETTINGS =y
CONFIG_FLASH =y
CONFIG_FLASH_PAGE_LAYOUT =y
CONFIG_FLASH_MAP =y
CONFIG_FCB =y
CONFIG_SETTINGS =y
CONFIG_SETTINGS_FCB =y

一旦启用, 应用程序将负责在初始化蓝牙(使用 bt_enable( ) API )之后调用 settings_load( ).

Zphyr_Bluetooth相关推荐

最新文章

  1. linux查看网卡速度
  2. 3. Nest Provider
  3. python 调用c++
  4. 零基础如何学好Python?这2点一定要明白
  5. php抓取多个网页合并,PHP 使用 CURL 同步抓取多个网页
  6. plot函数_时间序列:python移动窗口函数前言
  7. c语言 二进制输出_C语言编译器
  8. 谁说国产操作系统没救了? | 人物志
  9. C语言求一个文件的长度,求二进制文件的长度
  10. HeadFirstJava——14_数据结构
  11. Unity 导入高分辨率图片
  12. 网页中插入文本编辑器
  13. Introduction to NLP
  14. [转载] iOS直播相关,感觉有点用
  15. 利用 pinyin4j 把汉字转化为拼音
  16. Linux系统如何下载CityScape/KITTI-STEP数据集
  17. 计算机显示器的视频接口有什么不同,电脑显示器用VGA还是HDMI好有什么区别
  18. 一年中所有节日的排列顺序_【一年中的节日按时间顺序】时间顺序的春节风俗...
  19. Yukon中椭圆弧对象的使用方法
  20. WebView清除缓存的有效方法

热门文章

  1. 无秘借道“友秘”上架苹果商店
  2. Selenium自动化测试框架基础学习(元素表单操作)(Selenium操作)
  3. Elastic 极客时间 阮一鸣 学习笔记_入门
  4. c语言编程入门题库,级程序设计基础题库(c语言)(..更新).doc
  5. [小说连载]张小庆,在路上(6)- 真心话和大冒险
  6. 上海大学生计算机一级考试时间,2019上海各大学期末考试时间安排 什么时候期末考试...
  7. 不疯魔不成活系列【1】
  8. Heap size 80869K exceeds notification threshold (51200K)
  9. cad卸载不干净_流氓软件卸不干净?这7款超强软件卸载神器专治各种流氓软件!...
  10. 传奇3的WIL文件格式