0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Apache NimBLE v1.5版本的BLE HCI层设计

RTThread物联网操作系统 来源:RTThread物联网操作系统 作者:Leno_yan 2022-07-22 10:03 次阅读

作者信息

本文主要分析了 Apache NimBLE v1.5 版本的 BLE HCI 层设计,并分析了官方仓库自带 UART 对接例程;关于 BLE 层次结构可以先看一下这篇参考文档

NimBLE 目录结构

 1NimBLE
 2├───apps/*Bluetooth示例应用程序*/
 3├───docs/*官方文档及API说明*/
 4├───ext
 5├───nimble
 6│├───controller/*Controller实现*/
 7│├───doc/*当前包含transport层说明文档*/
 8│├───drivers/*Nordic系列Phy驱动*/
 9│├───host/*HostStack(主机控制器)实现*/
10│├───include
11│├───src
12│└───transport/*HCI传输抽象层*/
13└───porting/*OS抽象层及系统配置*/

  • 观察目录,可以看出 NimBLE 是实现了 Host 与 Controller 分离的,在官方介绍中也有说明。

  • nimble 的 Host 可以跑在任一芯片上;Controller 则限制比较多,需要有特殊的硬件以及驱动支持。

  • nimble/controller 则是 Controller 相关代码;nimble/host 对应 Host 代码;nimble/transport 就是 HCI 层代码;这一版本 nimble/doc 下还包含了 HCI 层的说明文档 transport.md。

  • 顺带一提,porting 目录下是 OS 抽象层及系统配置;rt-thread 的移植就实现在 porting/npl 下。

NimBLE HCI 层

主机控制接口层(Host Controller Interface,简写 HCI):HCI是可选的,主要用于2颗芯片实现BLE协议栈的场合(一个当作 Host 一个当作 Controller),用来规范两者之间的通信协议和通信命令等。

NimBLE HCI 层主要是了解 nimble/transport 下的内容。首先看一下官方文档 nimble/doc/transport.md 中对 transport 层的说明,主要看下面这张图:

8e5c0054-08e7-11ed-ba43-dac502259ad0.png

HCI 层包括这4接口:

Host 从 Controller 端接收接口 ble_transport_to_hs_evtble_transport_to_hs_acl;以及 Host 向 Controller 发送接口 ble_transport_to_ll_cmd ble_transport_to_ll_acl

在目录下,官方流出的接口定义主要包含在下面几个文件中:


					
1transport
2└───inlucde
3├───nimble
4│└───transport
5│└───monitor.h
6├───transport_impl.h
7└───transport.h

其中比较重要的是 transport_impl.h 文件,从名字也可以看出这是一个需要实现的接口定义文件,其主要内容如下:


					

						
1/*InitfunctionstobeimplementedfortransportactingasHS/LLside*/
2externvoidble_transport_ll_init(void);
3externvoidble_transport_hs_init(void);
4/*APIstobeimplementedbyHS/LLsideoftransports*/
5externintble_transport_to_ll_cmd_impl(void*buf);
6externintble_transport_to_ll_acl_impl(structos_mbuf*om);
7externintble_transport_to_hs_evt_impl(void*buf);
8externintble_transport_to_hs_acl_impl(structos_mbuf*om);

这些接口在 transportinclude imble ransportmonitor.h 中被引用:


					
 1staticinlineint
 2ble_transport_to_ll_cmd(void*buf)
 3{
 4returnble_transport_to_ll_cmd_impl(buf);
 5}
 6staticinlineint
 7ble_transport_to_ll_acl(structos_mbuf*om)
 8{
 9returnble_transport_to_ll_acl_impl(om);
10}
11staticinlineint
12ble_transport_to_hs_evt(void*buf)
13{
14returnble_transport_to_hs_evt_impl(buf);
15}
16staticinlineint
17ble_transport_to_hs_acl(structos_mbuf*om)
18{
19returnble_transport_to_hs_acl_impl(om);
20}

看完这个文件,大概明白了,之前官方文档图中提到的 HCI 4个接口,在这里与对应的 _impl() 接口绑定了起来。而 impl() 接口就是官方提供给开发者具体实现对接的接口。

由于 Host 和 Contoller 是双向交互的,所以发送与接收 HCI 包接口是要完整实现的,也就是大多数情况下上述 4 个 *impl() 接口都需要全部实现。

分析 UART 对接 HCI 层官方例程


官方提供了一个使用 UART 对接 HCI 层的例程,源码文件为

nimble/transport/uart/src/hci_uart.c

首先找到熟悉的接口:代码中显式实现了 ble_transport_to_hs_evt_impl 以及 ble_transport_to_hs_acl_impl ,这两个接口中基本上就是使用 uart 向 host 发送数据包,涉及到某个板子 uart 发送数据的具体细节,这里不过多关注。

看完上面两个接口,其实 HCI 中向 Host 发送数据包功能算实现完了,且当前文件中没有实现其他的 impl 接口,很容易能想到,这是写的 Controller 端的 HCI 层代码。

有了这个定性信息,可以开始分析如何从 uart 中接收 Host 层发来的数据包。

通过 uart 初始化函数找到 uart 接收回调函数:


					
 
1//uart初始化函数
2rc=hal_uart_init_cbs(MYNEWT_VAL(BLE_TRANSPORT_UART_PORT),
3hci_uart_tx_char,NULL,
4hci_uart_rx_char,NULL);

					

						

							

								
 
1//uart接收回调函数
2staticinthci_uart_rx_char(void*arg,uint8_tdata)
3{
4hci_h4_sm_rx(&hci_uart_h4sm,&data,1);
5return0;
6}

可以看到每接收一个字符,都使用 hci_h4_sm_rx 进行接收。该函数声明在 transportcommonhci_h4include imble ransporthci_h4.h 文件下,是关于 H4 的一个函数,大概看一下具体定义,接收字符后有一个组帧判断的过程。看一下 hci_h4.h 下比较关键的两个接口:


					
 

							
1voidhci_h4_sm_init(structhci_h4_sm*h4sm,
2conststructhci_h4_allocators*allocs,
3hci_h4_frame_cb*frame_cb);
4inthci_h4_sm_rx(structhci_h4_sm*h4sm,constuint8_t*buf,uint16_tlen);

hci_h4_sm_init() 中出现了一个 hci_h4_frame_cb *frame_cb 参数,这是一个函数指针参数,初步猜测用于回调函数的注册。且在

hci_uart.c 代码中,也找到了 hci_h4_sm_init() 相关调用:


					
 

							

								
244:hci_h4_sm_init(&hci_uart_h4sm,&hci_h4_allocs_from_hs,hci_uart_frame_cb)

这里将 hci_uart_frame_cb 注册成了回调函数,源码中定义如下:


					
 
 1staticint
 2hci_uart_frame_cb(uint8_tpkt_type,void*data)
 3{
 4switch(pkt_type){
 5caseHCI_H4_CMD:
 6returnble_transport_to_ll_cmd(data);
 7caseHCI_H4_ACL:
 8returnble_transport_to_ll_acl(data);
 9default:
10assert(0);
11break;
12}
13return-1;
14}

hci_uart_frame_cb 基本上是对一个完整的 HCI 包的处理,根据不同的类型使用 ble_transport_to_ll_cmdble_transport_to_ll_acl 传输给 Link Layer 层( Link Layer 是 Controller 上的一个层次,更加确定这是 Controller 上的 HCI 层实现 )。

结合 hci_uart.c 中对 hci_h4_sm 的使用,以及对 hci_h4_sm 相关接口源码的分析,hci_h4_sm 其实是官方提供的一个类似保证包完整性的东西,用于判断一帧完整的 HCI 数据包,并且提供组包完成回调函数的机制。

看到这里,大概脉络应该已经捋清楚了。这是一个 Controller 上的 UART HCI 层对接实现:

1、向 Host 发送 HCI 包:主要通过显式实现 ble_transport_to_hs_evt_impl 以及 ble_transport_to_hs_acl_impl 接口实现,具体何时被调用,协议栈已经自动处理好。

2、从 Host 接收 HCI 包:主要是使用的 hci_h4 中的组包接口,hci_h4_sm 即一个组包的状态机实例,通过 hci_h4_sm_rx 接收 uart 接收到的字符,在判断 hci 包完整接收时调用提前注册好的 回调函数 hci_h4_frame_cb 。hci_h4_frame_cb 里则实现了将 uart 接收到的包传递给 LL 层,进而 Controller 可以对 Host 传下来的命令或数据做出响应动作。

完整的 HCI 层实现


Controller 使用 UART 做 HCI 层数据传输

开始说到无论在什么情况下都要完成 HCI 层中 4个主要接口的实现,当前 hci_uart.c 中只找到了两个接口的实现,还有另外两个接口在哪呢。使用全局搜索在 nimblecontrollersrcle_ll.c 下找到了另外两个接口的实现:


					

						
 1/*TransportAPIsforLLside*/
 2int
 3ble_transport_to_ll_cmd_impl(void*buf)
 4{
 5returnble_ll_hci_cmd_rx(buf,NULL);
 6}
 7int
 8ble_transport_to_ll_acl_impl(structos_mbuf*om)
 9{
10returnble_ll_hci_acl_rx(om,NULL);
11}

因为目前默认在 Controller 端实现 HCI 层,剩下未实现的两个接口在 Controller 源码下实现了。

Host 使用 UART 做 HCI 层数据传输

那么假设当前需要实现 Host 端的 HCI 层,且还是需要使用 UART 对接。那么需要实现对接 UART 的接口应该刚好和 hci_uart.c 中相反。

需要在对接源码中,实现 ble_transport_to_ll_cmd_implble_transport_to_ll_acl_impl 发送接口,具体同样也是使用 UART 发送包。相应的,在 nimblehostsrcle_hs.c 下找到了以下两个接口的实现:


					

						

							
 1/*TransportAPIsforHSside*/
 2int
 3ble_transport_to_hs_evt_impl(void*buf)
 4{
 5returnble_hs_hci_rx_evt(buf,NULL);
 6}
 7int
 8ble_transport_to_hs_acl_impl(structos_mbuf*om)
 9{
10returnble_hs_rx_data(om,NULL);
11}

也就实现了 Host端 HCI 层的完整搭建。

  • 在使用 UART 对接 HCI 层时,是默认当前环境只跑了 Host 或 Controller 中的一端,因此 nimblecontrollersrcle_ll.c 和nimblehostsrcle_hs.c 不会同时参与编译,自然也不会引起一些 *impl() 接口重复定义的错误。

不需要其他接口进行 HCI 传输

当 Host 与 Controller 跑在同一环境中,不需要对接具体的数据传输接口, nimblecontrollersrcle_ll.cnimblehostsrcle_hs.c 同时参与编译,此时无需再编写额外的代码,即可完整实现整个 HCI 层。

总结

在较早的版本中,NimBLE 对接 HCI 层需要实现以下接口(参考文档):


					

						

							

								
 1intble_hci_trans_hs_cmd_tx(uint8_t*cmd);
 2intble_hci_trans_hs_acl_tx(structos_mbuf*om);
 3voidble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn*cmd_cb,
 4void*cmd_arg,
 5ble_hci_trans_rx_acl_fn*acl_cb,
 6void*acl_arg);
 7uint8_t*ble_hci_trans_buf_alloc(inttype);
 8voidble_hci_trans_buf_free(uint8_t*buf);
 9intble_hci_trans_set_acl_free_cb(os_mempool_put_fn*cb,void*arg);
10intble_hci_trans_reset(void);

可以看出来有一些麻烦,除了处理数据传输方向,还有处理数据包的内存申请与释放,实现起来较为复杂。

当前版本中官方对 HCI 层进行了重构,在 transport.c 中统一处理了数据包的内存申请与释放;让开发者专注于处理数据的发送与接收方式,并且提供了 H4 类型的 HCI 包接收工具,更加方便开发者的对接使用。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • BLE
    BLE
    +关注

    关注

    12

    文章

    660

    浏览量

    59392
  • HCI
    HCI
    +关注

    关注

    0

    文章

    29

    浏览量

    12948
  • Apache
    +关注

    关注

    0

    文章

    64

    浏览量

    12464

原文标题:NimBLE HCI 层分析

文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何在完成BLE Provisioning后使用Nimble?

    advertising.I (29593) NimBLE: GAP procedure initiated: terminate connection; conn_handle=1 hci_reason=19I (29593
    发表于 06-06 06:18

    使用mesh加手机ble与esp32通信时遇到一个死机问题怎么解决?

    ble_hs_hci_cmd_send at D:/ESP-IDF/components/bt/host/nimble/nimble/nimble/host/src/
    发表于 06-17 06:15

    升级到RTOS SDK v1.5版本编译报错如何解决?

    准备升级到RTOS SDK v1.5版本,在进行工程编译的时候出现问题,cJSON.c使用了floor和pow两个方法,并且该文件#include ,但在链接的时候库中找不到这两个方法的定义,出现
    发表于 07-12 06:10

    主要分析Apache NimBLE v1.5版本BLE HCI设计

    HCI 代码;这一版本 nimble/doc 下还包含了 HCI 的说明文档 transp
    发表于 07-11 10:27

    使用rt-thread UART对接NimBLEHCI设计实现

    本文主要介绍如何使用 RT-Thread UART 对接 NimBLE 中 Host 端的 HCI 传输,实现 RT-Thread NimBLE Host 与
    发表于 07-27 11:27

    主要介绍NimBLE软件包的使用说明

    、配置 NimBLE HCI 支持,选择实际用于连接蓝牙 Control 芯片的串口,如 “uart3”。4、选择使用相应的蓝牙例程:在 Bluetooth Samples 中选择相应的例程。目前
    发表于 08-18 10:22

    ble_hs_hci_cmd_send .函数重入导致申请内存失败死机的原因?怎么解决?

    ble_hs_hci_cmd_send at D:/ESP-IDF/components/bt/host/nimble/nimble/nimble/host/src/
    发表于 02-15 08:34

    NuTiny-EVB-NUC_QFN88 V1.4,网站上提供的资料是v1.5的请问v1.4与v1.5的区别在哪里?

    手上有一块 NuTiny-EVB-NUC_QFN88 V1.4,网站上提供的资料是v1.5的请问v1.4与v1.5的区别在哪里? Sample Code 能否通用?有没有
    发表于 06-16 08:12

    低功耗蓝牙设备接入协议-血糖仪V1.5

    京东低功耗蓝牙设备接入协议-血糖仪V1.5
    发表于 11-19 16:29 0次下载

    ps2解码通讯手册V1.5

    ps2解码通讯手册V1.5,感兴趣的小伙伴们可以瞧一瞧。
    发表于 09-22 12:04 16次下载

    LPC2378 Erratasheet V1.5资料

    LPC2378 Erratasheet V1.5资料,感兴趣的小伙伴们可以看看。
    发表于 11-08 18:13 0次下载

    YX5200-24SS原理图V1.5

    语音芯片YX5200-24SS原理图V1.5
    发表于 11-28 14:16 82次下载

    单片机小精灵v1.5资源下载

    单片机小精灵v1.5资源下载
    发表于 03-29 15:38 103次下载

    ATK 3.5 TFTLCD V1.5图下载

    ATK 3.5 TFTLCD V1.5图下载
    发表于 09-28 14:35 0次下载

    智联物联调试工具分享之串口网络数据调试助手V1.5

    智联物联串口网络数据调试助手V1.5是一款非常好用的调试工具,串口网络数据调试助手V1.5显示流畅,不容易丢数据,支持字符串和十六进制方式显示,和加时间戳分数据包显示。串口网络数据调试助手V1.5
    的头像 发表于 03-27 14:19 2175次阅读
    智联物联调试工具分享之串口网络数据调试助手<b class='flag-5'>V1.5</b>