物联网操作系统Zephyr(蓝牙篇)之6.0 zephyr os中的bt stack概述

 Zephyr物联网操作系统专栏汇总 


Zephyr os中的蓝牙协议栈还有完整的BLE和部分 经典蓝牙的host.

1.支持的蓝牙features

1.1.Zephyr 中的蓝牙协议栈兼容蓝牙5.0.

高度可配置:*功能、缓冲区大小/计数、堆栈大小等。

可移植到由Zephyr支持的所有架构(包括大端和小端、对齐风格等)。

支持主机和控制器构建的所有组合:

  • UART、SPI和USB物理传输的控制器(HCI)
  • 仅限UART、SPI和IPM上的主机(共享内存)
  • 组合(主机+控制器)

1.2.蓝牙-SIG合格

  • 在nordic硬件上的控制器
  • 在所有层上定期运行一致性测试

1.3.支持BLE controller(LE链路层)

  • 无限的角色和连接计数,支持所有的角色
  • 并发多协议支持
  • 智能调度角色,以尽量减少重叠
  • 便携式设计到任何开放的BLE无线电,目前支持北欧半导体nRF51和nRF52,以及专有无线电
  • 支持小端和大端环境架构,并抽象了硬实时细节,以便可以将它们封装在特定于硬件的模块中。
  • 对不同物理传输上的控制器(HCI)构建的支持

1.4.支持蓝牙主机

  • 具有所有可能的LE角色的通用访问配置文件(GAP):外围和中心;观察者和广播
  • GATT(通用属性配置文件):服务器(要作为一个传感器)、客户端(若要连接到传感器)
  • 配对支持,包括来自蓝牙4.2提供的安全连接功能 
  • 非易失性存储支持,以永久存储蓝牙特定的设置和数据
  • -蓝牙网格支持:中继节点、Friend节点、低功耗节点(LPN)和GATT代理功能;均支持两个供应载体(PB-ADV和PB-GATT);高度可配置,适合于小至16kRAM设备
  • IPSP/6LoWPAN,用于蓝牙LE上的IPv6连接
  • 基本的蓝牙BR/EDR(经典)支持:通用访问配置文件(GAP)、L2CAP、串口仿真(RFCOMM协议)、服务发现协议(SDP)。
  • 清除HCI驱动程序抽象:3线(H:5)和5线(H:4)UART;SPI;本地控制器支持作为一个虚拟的HCI驱动程序
  • 使用多个流行的控制器进行验证

2.蓝牙协议栈架构

这部分主要来说明zephyr中的蓝牙协议栈架构。

Zephyr主要支持蓝牙低能耗(BLE),即蓝牙规格的低功耗版本。zephyr对BR/EDR主机部分的支持也很有限。在整个架构文档中,我们交替使用BLE。

2.1 BLE层

BLE分三层

  • Host
  • controller
  • Radio hardware

Host controller interface蓝牙规范描述了主机必须与控制器通信的格式。这称为主机控制器接口(HCI)协议。HCI可以在一系列不同的物理传输上实现,如UART、SPI或USB。该协议定义了主机可以发送给控制器的命令和它可以预期返回的事件,以及需要通过无线传输的用户和协议数据的格式。HCI确保不同的主机和控制器实现可以以标准方式进行通信,从而可以组合来自不同供应商的主机和控制器。

单芯片配置

在这种配置中,单个微控制器实现了所有三层和应用程序本身。这也可以被称为片上系统(SoC)实现。在这种情况下,BLE主机和BLE控制器直接通过RAM中的函数调用和队列进行通信。蓝牙规范并没有说明如何在这个单芯片配置中实现HCI,也没有说明HCI命令、事件和两者之间的数据流如何是特定于实现的。这种配置非常适合那些需要较小的占用空间和最低可能的功耗的应用程序和设计,因为所有设备都运行在单个IC上。

双芯片配置:

该配置使用两个独立的IC,一个运行应用程序和主机,另一个运行控制器和无线电硬件。这有时也称为连接芯片配置。当使用Zephyr OS作为控制器时,此配置允许更广泛的主机组合。由于HCI确保了主机和控制器实现之间的互操作性,当然包括Zephyr自己的BLE主机和控制器,因此Zephyr控制器的用户可以选择使用他们喜欢的任何平台上运行的任何主机。例如,主机可以是运行在任何能够支持Linux的处理器上的Linux BLE主机堆栈(BlueZ)。主机处理器当然也可以运行Zephyr和Zephyr操作系统主机。相反,也支持将运行Zephyr主机的IC与不运行Zephyr的外部控制器相结合。

构建类型

Zephyr软件栈作为一个RTO是高度可配置的,特别是在构建过程中,BLE子系统可以通过多种方式进行配置,以仅包括减少RAM和ROM占用空间以及功耗所需的功能和层。这是

  • 仅controller构建:当构建为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_SPLIT =y (if using the open source Link Layer)

  • 仅host构建

        一个ZephyrOS主机构建将包含应用程序和BLE主机,以及一个HCI驱动程序(UART或SPI),以与外部控制器芯片接口。此类型的构建将设置以下Kconfig选项值:

– CONFIG_BT =y
– CONFIG_BT_HCI =y
– CONFIG_BT_CTLR =n
除了用于仅控制器构建的样本外,所有位于样本/蓝牙中的样本都可以作为仅主机构建。
  • 组合构建

这包括应用程序、主机和控制器,它专门用于单芯片(SoC)配置。此类型的构建将设置以下Kconfig选项值:

– CONFIG_BT =y
– CONFIG_BT_HCI =y
– CONFIG_BT_CTLR =y
– CONFIG_BT_LL_SW_SPLIT =y (if using the open source Link Layer)

除了用于控制器构建的样本外,所有位于样本/蓝牙中的样本都可以组合构建

 

 此配置并不限于使用ZephyrOS主机,如图像右侧所示。我们确实可以使用许多现有的GNU/Linux发行版中的一个,其中大多数包括Linux自己的BLE主机(BlueZ),通过UART或USB将其连接到ZephyrOS控制器构建的一个或多个实例。BlueZ作为一个主机,同时支持多个控制器,这些应用程序需要多个BLE无线电同时操作,但共享同一主机堆栈。

HOST

蓝牙主机实现了所有的高级协议和配置文件,最重要的是,它为应用程序提供了一个高级的API。下图描述了主机的主要协议和配置文件层。

主机堆栈的最底层有一个所谓的HCI驱动程序,负责抽象出HCI传输的细节。它提供了一个基本的API,用于将数据从控制器传递到主机,反之亦然。

可能HCI处理之上最重要的块是通用访问配置文件(GAP)。GAP通过定义BLE使用的四个不同角色简化了蓝牙LE访问:

  1. 面向连接的角色
  2. 外围设备(如智能传感器,通常具有有限的用户界面)
  3. 中心设备

无连接角色

  1. 广播者(发送BLE广播)
  2. 观察者(扫描BLE广告)

每个角色都带有自己的构建时配置选项:CONFIG_BT_PERIPHERAL、CONFIG_BT_CENTRAL、CONFIG_BT_BROADCASTER和CONFIG_BT_OBSERVER。对于面向连接的角色,中央设备隐式支持观察者角色,而外围设备则隐式支持广播者角色。通常,创建应用程序的第一步是决定需要哪些角色并从那里开始。蓝牙网是一种稍微特殊的情况,至少需要观察者和广播员角色,也可能还需要外围设备角色

外围角色

大多数基于Zephyr的BLE设备很可能是外围角色设备。这意味着他们执行可连接的广告并公开一项或多项GATT服务。使用bt\u gatt\u service\u register()API注册服务后,应用程序通常会使用bt_le_adv_start()API启动可连接广告。
树中有几个外围示例应用程序,例如samples/bluetooth/peripheral\u hr。

中心角色

对于基于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。

观察者角色

观察者角色观察者角色设备将使用bt_le_scan_start()API来扫描设备,但它不会连接到其中的任何一个设备。相反,它将简单地利用已找到的设备的广告数据,可选地将其与接收到的信号强度(RSSI)相结合。

广播员角色

设备将使用bt_le_adv_start()API来宣传特定的广告数据,但广告的类型将是不可连接的,即其他设备将无法连接到它。

连接

连接处理和相关的api可以在连接管理部分中找到。

详细讲解如何根据以下api和数据结构将数据发送给手机端void ble_controller_init(uint8_t task_priority) int hci_driver_init(void) int bt_enable(bt_ready_cb_t cb)int bt_le_adv_start(const struct bt_le_adv_param *param,const struct bt_data *ad, size_t ad_len, const struct bt_data *sd, size_t sd_len)int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len,const struct bt_data *sd, size_t sd_len)int bt_le_adv_stop(void)int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb)int bt_le_scan_stop(void)int bt_le_whitelist_add(const bt_addr_le_t *addr)int bt_le_whitelist_rem(const bt_addr_le_t *addr)int bt_le_whitelist_clear(void)int bt_le_set_chan_map(u8_t chan_map[5])int bt_unpair(u8_t id, const bt_addr_le_t *addr)int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info)int bt_conn_get_remote_dev_info(struct bt_conn_info *info)int bt_conn_le_param_update(struct bt_conn *conn,const struct bt_le_conn_param *param)int bt_conn_disconnect(struct bt_conn *conn, u8_t reason)struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer,const struct bt_le_conn_param *param)int bt_conn_create_auto_le(const struct bt_le_conn_param *param)int bt_conn_create_auto_stop(void)int bt_le_set_auto_conn(const bt_addr_le_t *addr,const struct bt_le_conn_param *param)struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer,const struct bt_le_adv_param *param)int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)bt_security_t bt_conn_get_security(struct bt_conn *conn)u8_t bt_conn_enc_key_size(struct bt_conn *conn)void bt_conn_cb_register(struct bt_conn_cb *cb)void bt_set_bondable(bool enable)int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb)int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)int bt_conn_auth_cancel(struct bt_conn *conn)int bt_conn_auth_passkey_confirm(struct bt_conn *conn)int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin)int bt_le_read_rssi(u16_t handle,int8_t *rssi)int bt_get_local_address(bt_addr_le_t *adv_addr)int bt_set_tx_pwr(int8_t power)bt_le_adv_parambt_databt_le_scan_parambt_le_conn_parambt_conn,给出一个详细的例程和注释
05-19
首先,这一系列的 API 涉及到了 BLE 控制器的初始化、广播、扫描、连接等操作,可以用于实现 BLE 设备与手机端的通信。 以下是一个简单的例程,演示如何使用这些 API 发送数据给手机端: ```c #include <zephyr.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #define DEVICE_NAME "My_BLE_Device" #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) #define ADV_LEN 3 // 广播数据 static struct bt_data adv_data[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), BT_DATA_BYTES(BT_DATA_MANUFACTURER_DATA, 0x01, 0x02, 0x03), }; // 扫描回调函数 static void scan_callback(const bt_addr_le_t *addr, s8_t rssi, u8_t adv_type, struct net_buf_simple *buf) { // TODO: 处理扫描到的数据 } void main(void) { int err; struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT( BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, BT_GAP_ADV_FAST_INT_MIN, BT_GAP_ADV_FAST_INT_MAX, NULL); struct bt_le_scan_param scan_param = BT_LE_SCAN_PARAM_INIT( BT_LE_SCAN_TYPE_PASSIVE, BT_LE_SCAN_OPT_NONE, 0, 0); // 初始化 BLE 控制器 err = ble_controller_init(K_PRIO_COOP(2)); if (err) { printk("Failed to initialize BLE controller (err %d)\n", err); return; } // 初始化 HCI 驱动 err = hci_driver_init(); if (err) { printk("Failed to initialize HCI driver (err %d)\n", err); return; } // 启用 BLE 栈 err = bt_enable(NULL); if (err) { printk("Failed to enable Bluetooth (err %d)\n", err); return; } // 开始广播 err = bt_le_adv_start(&adv_param, adv_data, ARRAY_SIZE(adv_data), NULL, 0); if (err) { printk("Failed to start advertising (err %d)\n", err); return; } // 开始扫描 err = bt_le_scan_start(&scan_param, scan_callback); if (err) { printk("Failed to start scanning (err %d)\n", err); return; } // TODO: 发送数据给手机端 // 停止扫描 bt_le_scan_stop(); // 停止广播 bt_le_adv_stop(); // 关闭 BLE 栈 bt_disable(); } ``` 上面的例程中,我们首先定义了一些常量和变量,包括设备名称、广播数据、扫描参数等。在 `main` 函数中,我们依次调用了 BLE 控制器初始化、HCI 驱动初始化、启用 BLE 栈、开始广播和扫描等操作。然后,我们可以在 TODO 注释处添加发送数据的代码。最后,我们停止扫描、停止广播和关闭 BLE 栈。 需要注意的是,在使用这些 API 之前,我们需要在设备的 DTS 文件中配置好蓝牙硬件的信息,并在 Kconfig 中启用蓝牙支持。此外,还需要在 `prj.conf` 文件中添加以下配置: ``` CONFIG_BT=y CONFIG_BT_HCI=y CONFIG_BT_CONN=y CONFIG_BT_L2CAP=y CONFIG_BT_SMP=y CONFIG_BT_GATT=y CONFIG_BT_SCAN=y CONFIG_BT_OBSERVER=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y CONFIG_BT_WHITELIST=y CONFIG_BT_PRIVACY=y ``` 这样,我们才能正确地使用这些 API 来实现 BLE 设备与手机端的通信。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心跳包

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

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

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

打赏作者

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

抵扣说明:

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

余额充值