找回密码
 立即注册

使用微信账号登录

只需一步,快速开始

蓝牙协议栈初始化流程介绍(以CSR8311为例)

2020-8-7 08:38| 发布者: 蓝牙协议栈开发| 查看: 2231| 评论: 0|原作者: 于忠军

摘要: 一. 声明本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。第二篇:Transport层介绍,主要介 ...

一. 声明

本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:

第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。

第二篇:Transport层介绍,主要介绍蓝牙协议栈跟蓝牙芯片之前的硬件传输协议,比如基于UART的H4,H5,BCSP,基于USB的H2等

第三篇:传统蓝牙controller介绍,主要介绍传统蓝牙芯片的介绍,包括射频层(RF),基带层(baseband),链路管理层(LMP)等

第四篇:传统蓝牙host介绍,主要介绍传统蓝牙的协议栈,比如HCI,L2CAP,SDP,RFCOMM,HFP,SPP,HID,AVDTP,AVCTP,A2DP,AVRCP,OBEX,PBAP,MAP等等一系列的协议吧。

第五篇:低功耗蓝牙controller介绍,主要介绍低功耗蓝牙芯片,包括物理层(PHY),链路层(LL)

第六篇:低功耗蓝牙host介绍,低功耗蓝牙协议栈的介绍,包括HCI,L2CAP,ATT,GATT,SM等

第七篇:蓝牙芯片介绍,主要介绍一些蓝牙芯片的初始化流程,基于HCI vendor command的扩展

第八篇:附录,主要介绍以上常用名词的介绍以及一些特殊流程的介绍等。

另外,开发板如下所示,对于想学习蓝牙协议栈的最好人手一套。以便更好的学习蓝牙协议栈,相信我,学完这一套视频你将拥有修改任何协议栈的能力(比如Linux下的bluez,Android下的bluedroid)。

------------------------------------------------------------------------------------------------------------------------------------------

CSDN学院链接(进入选择你想要学习的课程):https://edu.csdn.net/lecturer/5352?spm=1002.2001.3001.4144

蓝牙交流扣扣群:970324688

Github代码:https://github.com/sj15712795029/bluetooth_stack

入手开发板:https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22329603896.18.5aeb41f973iStr&id=622836061708

------------------------------------------------------------------------------------------------------------------------------------------

二.传统蓝牙初始化介绍

估计这个小节开始你就会对蓝牙协议栈有一点感觉了,感觉好像也没有那么困难,这个小节我们来进行初始化的讲解,完成这个小节后,你就惊奇的发现蓝牙设备竟然能被搜索到了·

笔者自己也摸索出了一个不写一行代码,就能把蓝牙芯片弄到被搜索到的方法,那么就让我们开始本小节吧(以CSR8X11蓝牙芯片为例,如果是不同的芯片,那么HCI vendor command部分会有所不同,我们会在后续说明更多的蓝牙芯片的vendor command)!

每个蓝牙协议栈初始化command的顺序略有不同,这些是无所谓的·以我们的蓝牙协议栈抓的Snoop为例,整个步骤如下

总结起来如下:

1)首先保证硬件连接正常,这点没有什么好说的,你们就当作废话吧,哈哈,但是我还是要提两点注意事项:

*Transport对应的硬件接线:如果是走H4的transport,那么蓝牙芯片的TX/RX/CTS/RTS需要分别接到MCU的RX/TX/RTS/CTS,如果是H5跟BCSP的话,那么只接TX/RX就行了(注意此部分接线方式有的芯片有特殊要求,那么会有所变动),如果是H2,那么没啥好讲的,插到USB口就行了,SDIO同理

*蓝牙Enable/Reset pin,就是硬件使能蓝牙芯片

2)对应Transport驱动(UART/USB/SDIO等)的初始化

3)持续发送HCI_RESET直到芯片回应,接收到command complete with command opcode

4)发送Vendor command,那么什么是vendor command呢,就是芯片原厂自己扩展的HCI command,来设定芯片的一些行为,比如CSR的叫做BCCMD/PSKEY,TI的叫做BTS,BCM的叫做HCD等,我们在后续详细介绍这些

---------------------------------------------------------------------------------------------------------------------------------

从下面开始就是HCI core文档的command部分的使用了,每个协议栈定义的会有所不同,我的是最简化的。

---------------------------------------------------------------------------------------------------------------------------------

5)发送Read Buffer Size command,接收到command complete with command opcode

6)发送 Read BT address command,接收到command complete with command opcode.

7)发送 Write code command,接收到command complete with comand opcode.

8)发送 Change Local Name command,接收到command complete with comand opcode.

9)发送 Write page timeout command,接收到command complete with comand opcode.

10)发送 Write set Event Mask command,接收到command complete with comand opcode.

11)发送 Write Write Simple Pairing command,接收到command complete with comand

12)发送Write Scan Enable command,接收到command complete with comand opcode.

下面我们就步骤来详细说下:

Step 1)保证硬件连接正常

Step 2)对应Transport驱动(UART/USB/SDIO等)的初始化

我们以UART,Transport H4为例,首先看下STM32F103的UART初始化

/******************************************************************************
 * func name   : hw_uart_bt_init
 * para        : baud_rate(IN)  --> Baud rate of uart1
 * return      : hw_uart_bt_init result
 * description : Initialization of USART2.PA0->CTS PA1->RTS PA2->TX PA3->RX
******************************************************************************/
uint8_t hw_uart_bt_init(uint32_t baud_rate,uint8_t reconfig)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    if(reconfig == 0)
        ringbuffer_init(&bt_ring_buf,bt_rx_buf,BT_RX_BUF_SIZE);
    /* Enable USART2,GPIOA,DMA1 RCC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    USART_DeInit(USART2);
    USART_Cmd(USART2, DISABLE);
    /* Initialize the GPIOA0,GPIOA1,GPIOA2,GPIOA3 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    /* Data format :1:8:1, no parity check, hardware flow control */
    USART_InitStructure.USART_BaudRate = baud_rate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    /* Enable USART interrupts, mainly for idle interrupts */
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=BT_PREE_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = BT_SUB_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    /* Initializes USART2 to enable USART,USART idle interrupts, and USART RX DMA */
    USART_Init(USART2, &USART_InitStructure);
    USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
    USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE);
    USART_Cmd(USART2, ENABLE);
    /* Initializes DMA and enables it */
    hw_memset(&DMA_UART2,0,sizeof(DMA_InitTypeDef));
    DMA_DeInit(DMA1_Channel6);
    DMA_UART2.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;
    DMA_UART2.DMA_MemoryBaseAddr = (uint32_t)bt_dma_rx_buf;
    DMA_UART2.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_UART2.DMA_BufferSize = BT_DMA_BUF_SIZE;
    DMA_UART2.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_UART2.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_UART2.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_UART2.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_UART2.DMA_Mode = DMA_Mode_Normal;
    DMA_UART2.DMA_Priority = DMA_Priority_Medium;
    DMA_UART2.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel6, &DMA_UART2);
    DMA_Cmd(DMA1_Channel6, ENABLE);
    return BT_ERR_OK;
}

下面我们以Linux UART Transport H4为例

uint8_t hw_uart_bt_init(uint32_t baud_rate,uint8_t reconfig)
{
    speed_t baudrate;
    int flags = O_RDWR | O_NOCTTY;
 
    struct termios toptions;
    if(baud_rate == 115200)
        baudrate =  B115200;
    else
        baudrate =  B921600;
    if(reconfig == 0)
    {
        ringbuffer_init(&bt_ring_buf,bt_rx_buf,BT_RX_BUF_SIZE);
    }
 
    printf("phybusif_open /dev/ttyUSB0\n");
 
    uart_if.phyuart_fd = open("/dev/ttyUSB0", flags);
    if (uart_if.phyuart_fd == -1)
    {
        printf("ERROR:Unable to open port /dev/ttyUSB0\n");
        return -1;
    }
 
    printf("uart_if.phyuart_fd %d\n",uart_if.phyuart_fd);
    if (tcgetattr(uart_if.phyuart_fd, &toptions) < 0)
    {
        printf("ERROR:Couldn't get term attributes\n");
        return -1;
    }
 
    cfmakeraw(&toptions);
 
    // 8N1
    toptions.c_cflag &= ~CSTOPB;
    toptions.c_cflag |= CS8;
 
    toptions.c_cflag |= CREAD | CLOCAL | CRTSCTS;
    toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
    toptions.c_cflag &= ~PARENB;
 
    toptions.c_cc[VMIN]  = 1;
    toptions.c_cc[VTIME] = 0;
 
    if(tcsetattr(uart_if.phyuart_fd, TCSANOW, &toptions) < 0)
    {
        printf("ERROR:Couldn't set term attributes\n");
        return -1;
    }
 
    if (tcgetattr(uart_if.phyuart_fd, &toptions) < 0)
    {
        printf("ERROR:Couldn't get term attributes\n");
        return -1;
    }
    cfsetospeed(&toptions, baudrate);
    cfsetispeed(&toptions, baudrate);
 
 
    if( tcsetattr(uart_if.phyuart_fd, TCSANOW, &toptions) < 0)
    {
        printf("ERROR:Couldn't set term attributes\n");
        return -1;
    }
 
    return BT_ERR_OK;
 
}

Step 3)发送HCI Reset以及回应,HCI reset格式如下

什么参数都没有,我们来直接看下Wireshark直接抓的btsnoop

回应如下:

Step 4)发送Vendor command

我们在这里先不具体讲vendor command,只是先列出CSR8X11 vendor command的raw data

/*************************************************************************************************
HCI command  PACKETS Format:
	opcode 16 bit
	para total len 8 bit
	para 0
**************************************************************************************************/
 
/*************************************************************************************************
BCCMD PACKETS Format:
           |	  type   |	length   |	seqno   |		varid	|	status   |	payload···	|
 uint 16   |	    1    |       2     |      3     |       4       |     5      |        6~        |
**************************************************************************************************/
/*************************************************************************************************
BCCMD PS PACKETS Format:
           |	  header  |	key   |	length    |	stores      |		ps value  	|
 uint 16   |	    1-5   |    6    |      7     |       8        |      9~           |
**************************************************************************************************/
// minimal CSR init script to configure PSKEYs and activate them
uint8_t csr8x11_initscript[] =
{
    //  Set PSKEY_DEEP_SLEEP_STATE never deep sleep
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x02, 0x00, 0x03, 0x70, 0x00, 0x00, 0x29, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
    //  Set ANA_Freq to 26MHz
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x03, 0x00, 0x03, 0x70, 0x00, 0x00, 0xfe, 0x01, 0x01, 0x00, 0x08, 0x00, 0x90, 0x65,
 
    //  Set CSR_PSKEY_ANA_FTRIM 0x24 for csr8811
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x04, 0x00, 0x03, 0x70, 0x00, 0x00, 0xf6, 0x01, 0x01, 0x00, 0x08, 0x00, 0x24, 0x00,
 
    // Set CSR_PSKEY_DEFAULT_TRANSMIT_POWER 0x4
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x05, 0x00, 0x03, 0x70, 0x00, 0x00, 0x21, 0x00, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00,
 
    // Set CSR_PSKEY_MAXIMUM_TRANSMIT_POWER 0x4
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x70, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00,
 
    // Set CSR_PSKEY_BLE_DEFAULT_TRANSMIT_POWER 0x4
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x07, 0x00, 0x03, 0x70, 0x00, 0x00, 0xc8, 0x22, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00,
 
    // Set CSR_PSKEY_BDADDR
    0x19, 0xc2, 0x02, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x03, 0x70, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x08, 0x00, 0x20, 0x00, 0x99, 0x1a, 0x86, 0x00, 0x1d, 0x00,
 
    // Set CSR_PSKEY_PCM_CONFIG32
    0x15, 0xc2, 0x02, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x70, 0x00, 0x00, 0xb3, 0x01, 0x02, 0x00, 0x08, 0x00, 0x80, 0x08, 0x80, 0x18,
 
    // Set CSR_PSKEY_PCM_FORMAT 0x60
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x70, 0x00, 0x00, 0xb6, 0x01, 0x01, 0x00, 0x08, 0x00, 0x60, 0x00,
 
    // Set CSR_PSKEY_USER_LOW_JITTER_MODE
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x0b, 0x00, 0x03, 0x70, 0x00, 0x00, 0xc9, 0x23, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
 
    //  Set HCI_NOP_DISABLE
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x03, 0x70, 0x00, 0x00, 0xf2, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
 
    // Set UART baudrate to 921600
    0x15, 0xc2, 0x02, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x70, 0x00, 0x00, 0xea, 0x01, 0x02, 0x00, 0x08, 0x00,0x0e,0x00,0x00,0x10,/*0x1b, 0x00, 0x40, 0x77,*/
 
    //  WarmReset
    0x13, 0xc2, 0x02, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

Step 5)发送Read Buffer Size command,接收到command complete with command opcode

下面看下Read Buffer Size command(OGF=0x04)的封包格式

抓取Wireshark btsnoop如下:

回应如下:

Sep 6)发送 Read BT address command,接收到command complete with command opcode.

下面看下Read BT address command(OGF=0x04)的封包格式

抓取Wireshark btsnoop如下:

回应如下:

Step 7)发送 Write code command,接收到command complete with comand opcode.

下面看下Write cod command(OGF=0x04)的封包格式

抓取Wireshark btsnoop如下:

回应如下:

Step 8)发送 Change Local Name command,接收到command complete with comand opcode.

下面看下Change Local Name command(OGF=0x03)的封包格式

参数这个地方有点特别,特别贴下,不管你的名字是多长的参数,比如叫做Wireless_link,但是都要凑够248byte,这是SIG的core文档定义,但是有一点,我自己试验过,不用248byte也行,但是最好还是按照SIG规范来,这个CSR8311芯片没有问题不代表其他芯片就可以。

抓取Wireshark btsnoop如下:

回应如下:

Step 9)发送 Write page timeout command,接收到command complete with comand opcode.

下面看下Write page timeout command(OGF=0x03)的封包格式

参数如下:

抓取Wireshark btsnoop如下:

回应如下:

Step 10)发送Set Event Mask,接收到command complete with comand opcode.

下面看下Set Event Mask(OGF=0x03)的封包格式

参数:

Event_Mask:芯片产生的时间掩码

抓取Wireshark封包:

回应如下:

Step 11)发送Write Simple Pairing command,接收到command complete with comand opcode.

下面看下Write Simple Pairing(OGF=0x03)的封包格式

参数:

Simple_Pairing_mode:设置SSP的参数

抓取Wireshark封包:

回应如下:

此部分代码配置在bt_conf.h中

/***********   bluetooth function option **************************************/
/** BT_PBUF_TRANSPORT_H2 = 0x01,BT_PBUF_TRANSPORT_H4 = 0x02,BT_PBUF_TRANSPORT_H5 = 0x03,BT_PBUF_TRANSPORT_BCSP = 0x04,*/
#define BT_ENABLE_SNOOP 1
#define BT_TRANSPORT_TYPE 0x02
#define BT_ENABLE_SSP 0   /************这个地方配置SSP********************/
/* IO_CAP_DISPLAY_ONLY->0x00 IO_CAP_DISPLAY_YES_NO->0x01 IO_CAP_KEYBOARD_ONLY->0x02 IO_CAP_NOINPUT_OUTPUT->0x03 */
#define BT_SSP_IOCAP_CONF 3
#define BT_CLASS_OF_DEVICE 0x200408;
#define BT_LOCAL_NAME "BT_DEMO"
#define BT_PIN_CODE "0000"
#define BT_TIMER_COUNT 64 /* TIMER COUNT */

此部分如果配置为1,那么就默认SSP配对,如果是0,那么就是PINCODE配对

Step 12)发送Write Scan Enable command,接收到command complete with comand opcode.

下面看下Write Scan enablecommand(OGF=0x03)的封包格式

参数如下:

抓取Wireshark btsnoop如下:

回应如下:

讲到这里蓝牙协议栈的初始化基本就完成了·,但是每个芯片的vendor command部分稍微有差异(也就是Step 4),后续我们再慢慢扩展更多的芯片,谢谢大家


路过

雷人

握手

鲜花

鸡蛋

最新评论

小黑屋|手机版|我爱蓝牙网 - 52Bluetooth

GMT+8, 2024-4-29 18:53 , Processed in 0.156716 second(s), 17 queries , Gzip On, MemCached On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

返回顶部