蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1)

这篇具有很好参考价值的文章主要介绍了蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于蓝牙协议栈的理解,最好的办法是找一个最简单的开源协议栈进行学习,BTStack整个协议栈都是C语言编写,非常适合刚入门的同学来学习借鉴。借鉴卫东上老师的蓝牙视频教程。

BTStack协议栈学习。首先来看一下,对于硬件操作,它是如何来进行处理的。在上篇文章中曾说过,在main函数里面它会调用硬件相关的代码,调用操作系统相关的代码。在BTStack中,可以搜索一下main.c,将会发现有很多main.c,都是为于port目录下面。

Main.c (port\esp32\components\btstack)
Main.c (port\ez430-rf2560\src)
Main.c (port\libusb)
Main.c (port\libusb-intel)
Main.c (port\max32630-fthr\src)
Main.c (port\msp-exp430f5438-cc2564b\src)
Main.c (port\msp430f5229lp-cc2564b\src)
Main.c (port\nrf5-zephyr)
Main.c (port\nrf5x)
Main.c (port\pic32-harmony\src)
Main.c (port\posix-h4)
Main.c (port\posix-h4-atwilc3000)
Main.c (port\posix-h4-da14581)
Main.c (port\posix-h4-da14585)
Main.c (port\posix-h4-zephyr)
Main.c (port\posix-h5)
Main.c (port\posix-h5-bcm)
Main.c (port\raspi)
Main.c (port\samv71-xplained-atwilc3000)
Main.c (port\stm32-f103rb-nucleo)
Main.c (port\stm32-f4discovery-cc256x\eclipse-template\src)
Main.c (port\stm32-l053r8-em9304\cubemx-l053r8-em9304\src)
Main.c (port\wiced-h4)
Main.c (port\wiced-h5)
Main.c (port\windows-h4)
Main.c (port\windows-h4-zephyr)
Main.c (port\windows-winusb)
Main.c (port\windows-winusb-intel)

看一下windows,有Main.c (port\windows-h4)、Main.c (port\windows-winusb),使用的是usb口的蓝牙模块。注意后h4表示5线串口的蓝牙模块。

分析Main.c 中的main函数,按照上一篇文章中总结出来的框架,首先找到硬件操作的相关代码,然后再看操作系统先关的代码

1. 硬件相关的代码:

a.使用usb口

分析Main.c (port\windows-winusb)

// setup USB Transport
transport = hci_transport_usb_instance();

const hci_transport_t * hci_transport_usb_instance(void) {
  return &hci_transport_usb;  //返回hci_transport_usb的结构体
}

hci_transport_usb的结构体定义如下:

// get usb singleton
static const hci_transport_t hci_transport_usb = {
  /* const char * name; */ "H2_WINUSB",
  /* void (*init) (const void *transport_config); */ &usb_init,
  /* int (*open)(void); */ &usb_open,
  /* int (*close)(void); */ &usb_close,
  /* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler,
  /* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now,
  /* int (*send_packet)(...); */ &usb_send_packet,
  /* int (*set_baudrate)(uint32_t baudrate); */ NULL,
  /* void (*reset_link)(void); */ NULL,
#ifdef ENABLE_SCO_OVER_HCI
  /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config,
#else
  /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
#endif
};

在hci_transport_usb结构体中,有初始化函数、有open函数、有注册函数、有发送包的函数等等,这些函数应该就是用来操作硬件的。

在main函数中,返回了一个结构体,以后将使用transport 这个结构体去操作硬件——从硬件里面得到数据或把数据发给硬件。

以上使用的是USB口,如果我使用的是串口呢?硬件操作的相关代码又是怎样的?

b. 使用串口

分析Main.c (port\windows-h4)

main

  const btstack_uart_block_t * uart_driver = btstack_uart_block_windows_instance();

  const hci_transport_t * transport = hci_transport_h4_instance(uart_driver);  //同样是返回一个transport结构体 

  // configure and return h4 singleton

  const hci_transport_t * hci_transport_h4_instance(const btstack_uart_block_t * uart_driver) {
    btstack_uart = uart_driver;
    return &hci_transport_h4;//返回hci_transport_h4的结构体
  }

hci_transport_h4结构体是什么样的呢?定义如下:

static const hci_transport_t hci_transport_h4 = {
    /* const char * name; */                                        "H4",
    /* void   (*init) (const void *transport_config); */            &hci_transport_h4_init,
    /* int    (*open)(void); */                                     &hci_transport_h4_open,
    /* int    (*close)(void); */                                    &hci_transport_h4_close,
    /* void   (*register_packet_handler)(void (*handler)(...); */   &hci_transport_h4_register_packet_handler,
    /* int    (*can_send_packet_now)(uint8_t packet_type); */       &hci_transport_h4_can_send_now,
    /* int    (*send_packet)(...); */                               &hci_transport_h4_send_packet,
    /* int    (*set_baudrate)(uint32_t baudrate); */                &hci_transport_h4_set_baudrate,
    /* void   (*reset_link)(void); */                               NULL,
    /* void   (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
};

注意:btstack_uart这个参数是用来干嘛的呢?

BTStack支持多种接口的蓝牙模块,比如USB口、3线串口、5线串口。对于3线串口和5线串口,它们之间有什么差别呢?

对于3线串口,它只有三条线:TxD、RxD、GND。5线串口比三线串口多了两条线:CTS、RTS,用来控制流量。
使用三线串口和无线串口传输同一个数据时,它们使用的协议不一样。

蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1),蓝牙,蓝牙,BLE

假设图中红色的部分就是要发送的数据,当使用三线串口时可能给它加上头部、尾部后再发送给硬件,当使用五线串口时可能将数据直接发给硬件。

从这个地方可以产出,无论是三线串口还是五线串口,它们的底层硬件操作都是一样的。因此在硬件的这一层,又抽象出了一个结构体:uart_driver。使用该结构体来操作硬件。

H5协议只是将数据加上各种头部和各种尾部,H4协议也只是对数据进行了某种处理。

因此在main函数中,首先得到了uart_driver,然后再将该结构体作为hci_transport_h4_instance的参数传进去。

看一下hci_transport_h4_open()函数:

hci_transport_h4_open

   btstack_uart->open();//直接调用了btstack_uart的open函数。

从中可以看出,H4、H5协议通过 btstack_uart_block_t结构体来操作硬件。

2. 操作系统相关的代码

a.windows操作系统

分析Main.c (port\windows-winusb)

main

  btstack_run_loop_init(btstack_run_loop_windows_get_instance());

通过btstack_run_loop_windows_get_instance()来获取一个结构体,

/**
* Provide btstack_run_loop_windows instance
*/
const btstack_run_loop_t * btstack_run_loop_windows_get_instance(void){
  return &btstack_run_loop_windows;
}

btstack_run_loop_windows结构体定义如下:定义了操作系统先关的循环函数。

static const btstack_run_loop_t btstack_run_loop_windows = {
    &btstack_run_loop_windows_init,
    &btstack_run_loop_windows_add_data_source,
    &btstack_run_loop_windows_remove_data_source,
    &btstack_run_loop_windows_enable_data_source_callbacks,
    &btstack_run_loop_windows_disable_data_source_callbacks,
    &btstack_run_loop_windows_set_timer,
    &btstack_run_loop_windows_add_timer,
    &btstack_run_loop_windows_remove_timer,
    &btstack_run_loop_windows_execute,
    &btstack_run_loop_windows_dump_timer,
    &btstack_run_loop_windows_get_time_ms,
};

b. linux操作系统

Main.c (port\posix-h4)

main

  btstack_run_loop_init(btstack_run_loop_posix_get_instance());

通过btstack_run_loop_posix_get_instance()来获取一个结构体

/**
* Provide btstack_run_loop_posix instance
*/
const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){
  return &btstack_run_loop_posix;
}

btstack_run_loop_posix结构体定义如下:定义了操作系统先关的循环函数。

static const btstack_run_loop_t btstack_run_loop_posix = {
    &btstack_run_loop_posix_init,
    &btstack_run_loop_posix_add_data_source,
    &btstack_run_loop_posix_remove_data_source,
    &btstack_run_loop_posix_enable_data_source_callbacks,
    &btstack_run_loop_posix_disable_data_source_callbacks,
    &btstack_run_loop_posix_set_timer,
    &btstack_run_loop_posix_add_timer,
    &btstack_run_loop_posix_remove_timer,
    &btstack_run_loop_posix_execute,
    &btstack_run_loop_posix_dump_timer,
    &btstack_run_loop_posix_get_time_ms,
};

3. 在主循环中读取数据、处理数据,它是如何用代码来实现的呢?

例如:如果我使用H5协议的话,从硬件中得到数据,需要将这个数据的头部去掉,才能得到真正的数据。如果我使用H4协议的话,从硬件中得到的数据就是真正的数据。

如果我使用usb协议的话,得到的数据又需要作另一种处理。从这个地方就可以看出某种端倪来,什么端倪呢?得到数据和处理数据应该绑定在一起。

即使用A协议得到数据,就使用process_A来处理;使用B协议来得到数据,就使用process_B来处理。在BTStack中,又抽象处理另外一个结构体,该结构体就是btstack_data_source。

蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1),蓝牙,蓝牙,BLE

上面已经提到操作系统相关的代码时,在结构体btstack_run_loop中它有一个函数指针void (*add_data_source)(btstack_data_source_t * data_source),就是给这个循环添加一个数据来源。这个数据来源里面有文件句柄或handle、process函数。以后在循环里面,它可以通过文件句柄或handle中获取数据,得到数据后马上调用它里面的process函数。问题来了,btstack_data_source结构体是在什么时候创建的?显然应该在打开硬件设备时,就会创建这个结构体,并且把这格结构体添加到btstack_run_loop中。

4.数据如何进行处理的呢?

process函数就是数据处理的起点,前面已经说过,对数据的处理分为两部分:一部分是蓝牙协议栈中各个层次的处理,另一个部分是APP的处理。
data_source结构体中有process函数,process函数就是数据处理的起点,在这里会干什么事情呢?它会调用各个层的处理函数,也会调用APP的处理函数

看一下函数usb_process_event_in,在里面会做什么事情呢?
usb_process_event_in
  // notify uppper 通知更上面的层次
  packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, bytes_read);
  packet_handler是一个函数指针,static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler;
  该函数指针在哪里被设置呢?
  static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size))

{
    log_info("registering packet handler");
    packet_handler = handler;
  }
在硬件相关的结构体hci_transport_usb里面,有一个注册函数usb_register_packet_handler

蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1),蓝牙,蓝牙,BLE

对于usb蓝牙模块,它使用hci_transport_h2_winusb.c文件中抽象出来的hci_transport_usb结构体,在这个结构体里面有usb_register_packet_handler函数,
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
  log_info("registering packet handler");
  packet_handler = handler; 对函数指针packet_handler进行赋值。
}
此处的handler是什么?就需要看看谁调用了register_packet_handler函数指针,调用了register_packet_handler函数指针,就相当于调用了usb_register_packet_handler函数
在hci.c文件中的hci_init函数中调用了register_packet_handler函数指针。
hci_init
  // register packet handlers with transport
  transport->register_packet_handler(&packet_handler); 从这个地方可以看出,上面的handler就是hci.c文件中的packet_handler
  再看一下参数packet_handler
  static void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
    hci_dump_packet(packet_type, 1, packet, size);
    switch (packet_type) {
      case HCI_EVENT_PACKET:
        event_handler(packet, size);
        break;
      case HCI_ACL_DATA_PACKET:
        acl_handler(packet, size);
        break;
  #ifdef ENABLE_CLASSIC
      case HCI_SCO_DATA_PACKET:
        sco_handler(packet, size);
        break;
  #endif
      default:
        break;
      }
   }

在主循环中,得到数据之后,会调用btstack_data_source中的process函数,在process函数中最终会进行packet_handler = handler这样的赋值操作。最终会调用到hci.c中的packet_handler函数。在该函数中将数据分为了三类:HCI_EVENT_PACKET、HCI_ACL_DATA_PACKET、HCI_SCO_DATA_PACKET。根据类别的不同调用不同的处理函数。

event_handler(packet, size);
hci_emit_event
  // dispatch to all event handlers 分发给所有的事件处理器,那么这些event handler保存在event_handlers链表中。
  btstack_linked_list_iterator_t it;
  btstack_linked_list_iterator_init(&it, &hci_stack->event_handlers);从链表中将handler一个个取出来,调用那些结构体中的callback函数来处理那些数据。
  while (btstack_linked_list_iterator_has_next(&it)){
    btstack_packet_callback_registration_t * entry = (btstack_packet_callback_registration_t*) btstack_linked_list_iterator_next(&it);
    entry->callback(HCI_EVENT_PACKET, 0, event, size);
  }

问题:谁向hci_stack->event_handlers链表中放入handler的呢?

从图中可以看出,上面的各个层都调用了hci_add_event_handler,上面的各层如果对数据感兴趣的话就调用hci_add_event_handler函数在链表中添加自己的处理函数。

void hci_add_event_handler(btstack_packet_callback_registration_t * callback_handler){
  btstack_linked_list_add_tail(&hci_stack->event_handlers, (btstack_linked_item_t*) callback_handler); 向event_handler链表中添加了btstack_packet_callback_registration_t结构体,这个结构体是什么样的呢?
}

typedef struct {
  btstack_linked_item_t item;
  btstack_packet_handler_t callback;  有callback函数,刚好与上面对应起来。
} btstack_packet_callback_registration_t;

5. 谁来启动数据传输?

hci_power_control会启动蓝牙模块,向蓝牙模块发送第一个数据,以后所有的数据都会在主循环中进行的,收到数据后将调用process函数。文章来源地址https://www.toymoban.com/news/detail-840564.html

到了这里,关于蓝牙系列五:最简单的开源蓝牙协议BTStack代码分析(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 蓝牙BLE安全-SSP简单安全配对

    SSP的配对过程由于可以根据设备的IO能力选择不同的关联模型,因此十分灵活,其提供了四种方式:Numeric Comparison、Passkey Entry、Just Works以及Out of Band (OOB) 。这里关联方式的选择实质上对后面的流程是有一定影响的,如Just Works就不需要对Link Key进行验证。 Numeric Comparison : 数值

    2024年02月20日
    浏览(56)
  • 【Bluetooth蓝牙开发】七、BLE协议之L2CAP

    个人主页:董哥聊技术 我是董哥,嵌入式领域新星创作者 创作理念:专注分享高质量嵌入式文章,让大家读有所得!   【所有文章汇总】  

    2023年04月09日
    浏览(33)
  • 车规级耐高温BLE5.2协议串口转蓝牙模块E104-BT53C3产品简介

    蓝牙模块通信接口: UART串口通信 蓝牙模块工作频率:2402~2480MHz 车规级蓝牙模块蓝牙协议:BLE 5.2 通信距离:170m 天线接口:PCB 产品尺寸:23*16mm 产品简介: E104-BT53C3耐高温车规级蓝牙模块 是一款基于蓝牙协议5.2版本的串口转BLE蓝牙模块,蓝牙模块具有耐高温、体积小、功耗

    2024年02月10日
    浏览(46)
  • 【STM32备忘录】【STM32WB系列的BLE低功耗蓝牙】一、测试广播配置搜不到信号的注意事项

    WB系列是双核单片机,用户写M4,无线协议栈使用M0 新买到手的单片机,需要自己刷入使用的无线协议栈 刷入无线协议栈的途径是通过一个叫FUS的东东,类似于bootloader,这个FUS新买的芯片通常已经刷好,但版本不一定是最新的(如果没有需要自己刷入) 刷入FUS和无线协议栈需要

    2024年03月16日
    浏览(55)
  • 【经典蓝牙】蓝牙AVRCP协议分析

    蓝牙AVRCP协议是蓝牙设备之间音视频的控制协议。定义了音频/视频的控制、浏览、查询、通知等一系列的命令集。常用来蓝牙耳机对手机的音乐进行控制,以及获取手机的音乐信息等场景。AVRCP协议有两个角色,分别是controller(CT)和 target(TG)。CT: 发送控制命令到对端,控制

    2024年02月04日
    浏览(25)
  • Flutter:BLE蓝牙开发

    说明: 使用flutter_blue_plus插件实现低功耗蓝牙开发。 一、添加蓝牙权限: 1.Android网络权限(工程/android/app/src/main/AndroidManifest.xml): 2.iOS蓝牙权限(工程/ios/Runner/Info.plist): 二、实现扫描/连接/接收BLE设备数据: 1.添加flutter_blue_plus插件依赖,在pubspec.yaml中: 2.实现BLE蓝牙设备扫

    2024年02月11日
    浏览(40)
  • Android蓝牙BLE开发

    最近正在研究Android的蓝牙BLE开发学习,以下是自己做的个人总结 首先得说明什么是低功耗蓝牙BLE,BLE的全称为Bluetooth low energy(或称Blooth LE,BLE),从英文全称便可以知晓其是一种低功耗的蓝牙技术,是蓝牙技术联盟设计和销售的一种个人局域网技术,旨在用于医疗保健、运

    2023年04月09日
    浏览(42)
  • 蓝牙BLE学习-安全

    蓝牙标准规定了5种基本的安全服务 身份验证:根据通信设备的蓝牙地址验证其身份。蓝牙不提供本地用户身份验证。 保密性:确保只有授权的设备才能访问和查看传输的数据,防止窃听造成的信息泄露。 授权(Authorization):在允许设备使用某项服务之前,确保该设备已被授权,从

    2024年02月19日
    浏览(42)
  • 蓝牙 - 关于BLE的安全连接

    A Basic Introduction to BLE 4.x Security 引言 Bluetooth Low Energy (BLE)正在迅速成为当今最常用的无线标准之一。同样地,它也越来越多地被用于传输敏感信息的应用中。因此,希望将BLE集成到其产品中的设计者应该了解这项技术的安全特性和限制。本文试图对这些功能做一个基本的概

    2024年02月01日
    浏览(43)
  • Android -BLE 蓝牙模块开发

    Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码) - 简书 前言 万物互联的物联网时代的已经来临,ble蓝牙开发在其中扮演着举重若轻的角色。最近刚好闲一点,抽时间梳理下这块的知识点。 涉及ble蓝牙通讯的客户端(开启、扫描、连接、发送... https://

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包