蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 –传统蓝牙搜索演示以及实现原理[通俗易懂]

零.概述主要介绍下蓝牙协议栈开发板跑传统蓝牙搜索AT指令以及上位机操作步骤,以及原理一.声明本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:第一篇:蓝牙综合介绍,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。第二篇:Transport层介绍,主要介绍蓝牙协议栈跟蓝牙芯片之前的硬件传输协议,比如基于UART的H4,H5,BCSP,基于USB的H2等第三篇:传统蓝牙controller介绍,主要介绍传统蓝牙芯片的介绍,包括射频层(R

大家好,又见面了,我是你们的朋友全栈君。

零. 概述

主要介绍下蓝牙协议栈开发板跑传统蓝牙搜索AT指令以及上位机操作步骤,以及原理

一. 声明

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

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

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

第二篇: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)。

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

————————————————————————————————————————-

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

蓝牙学习目录https://blog.csdn.net/XiaoXiaoPengBo/article/details/107727900

————————————————————————————————————————–

演示视频点击我(!!!!!!!!!!!!!!!!)

二. STM32蓝牙协议栈封装使用AT command实现搜索

使用步骤操作如下:

步骤 1)准备好代码,从github下载下来最新的代码(在上面有介绍Github连接)

步骤 2)连接好硬件(把模组插好,ST-LINK接上,TYPE-C debug先接上,按下按钮可以看到蓝色电源等亮起)

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

步骤 3)打开Keil工程文件夹下的project\stm32f10x_bb_csr8x11_bt\stm32f10x_bb_csr8x11.uvprojx,然后编译下载

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

此部分注意几点:

  • 下载需要ST-LINK驱动,我已经放在下载资料中的软件工具文件夹中
  • STM32 F1的pack要有,我已经放在软件工具文件夹中的MDK下,没有没安装过要安装下,名字如下:

   蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

  • 下载的debug要选ST-LINK

     蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

  • 下载的时候要勾选Use micro lib

     蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

步骤4)打开串口工具(我用的是XCOM),然后做初始化动作,在发送串口敲BT_START,点击发送,出来以下log就证明初始化通过了,我们就可以来进行搜索动作了注意一点:不能勾选发送新行,否则会解析错误

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

步骤5)然后敲BT_INQUIRY就能搜索到设备了

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

三.使用我们自己写的上位机来实现搜索

步骤跟AT的1)2)3)一样,我们从第四步开始讲解

打开我们工程源码1-BLUETOOTH\mcu_bt_tool\mcu_bt_tool\mcu_bt_tool\bin\Debug中的mcu_bt_tool.exe,当然你也可以直接用VS2010打开工程

步骤 1)打开串口

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

步骤 2)点击蓝牙开启按钮(此步骤跟AT 命令BT_START一样的效果,就是实现蓝牙初始化)

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

步骤 3) 等待初始化完成点击搜索按钮,你就发现可以搜索到蓝牙了

蓝牙协议栈开发板 STM32F1 跑蓝牙协议栈 --传统蓝牙搜索演示以及实现原理[通俗易懂]

另外:使用上位机的时候注意几点:

① mcu_bt_tool.exe你如果想把可执行文件拿到别的路径单独执行,那么必须要把Newtonsoft.Json.dll跟exe放在同一个路径下,因为上位机是跟STM32用json沟通的

② 因为目前搜索是开启的EIR,带RSSI的,所以他会重复性上来同一个设备,我没做根据同一个蓝牙地址做显示过滤,如果有兴趣的人可以加上这一块

四. 串口工具AT command以及上位机实现搜索的原理

步骤 1)Type C uart debug口的tx,rx初始化

/******************************************************************************
 * func name   : hw_uart_debug_init
 * para        : baud_rate(IN)  --> Baud rate of uart1
 * return      : hw_uart_debug_init result
 * description : Initialization of USART1.PA9->TX PA10->RX
******************************************************************************/
uint8_t hw_uart_debug_init(uint32_t baud_rate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    /* Enable RCC clock for USART1,GPIOA,DMA1 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* Initialization GPIOA9 GPIOA10 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    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_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Data format :1:8:1, no parity check, no 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_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

    /* Enable USART interrupts, mainly for idle interrupts */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=DEBUG_PREE_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = DEBUG_SUB_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Initializes USART1 to enable USART, USART idle interrupts and USART RX DMA */
    USART_Init(USART1, &USART_InitStructure);
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    USART_Cmd(USART1, ENABLE);

    /* Initializes DMA and enables it */
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uart1_rev_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = UART1_MAX_REV;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

    DMA_Cmd(DMA1_Channel5, ENABLE);

    return HW_ERR_OK;

}

可以看到TX我们就是普通的实现发送,RX我们用串口空闲中断+DMA的方式来实现接受串口工具以及上位机的发送指令,然后串口中断的实现原理是这样:

/******************************************************************************
 * func name   : USART1_IRQHandler
 * para        : NULL
 * return      : NULL
 * description : Interrupt handler for usart1
******************************************************************************/
void USART1_IRQHandler(void)
{

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        /* Without this, the interrupt cannot be cleared and continues into the interrupt */
        USART_ReceiveData(USART1);
        uart1_rev_len =UART1_MAX_REV-DMA_GetCurrDataCounter(DMA1_Channel5);

        if(uart1_rev_len != 0)
        {
            /* Call the parse function */
		shell_parse(uart1_rev_buffer);
            hw_memset(uart1_rev_buffer,0,sizeof(uart1_rev_buffer));
        }
        /* Clear the interrupt and reset DMA */
        USART_ClearITPendingBit(USART1,USART_IT_IDLE);
        uart1_dma_enable(DMA1_Channel5);
    }
}

步骤2)收到串口工具或者上位机的解析函数如下:

uint8_t shell_parse(uint8_t *shell_string)
{
    uint8_t result = HW_ERR_OK;


    cJSON* parse_json = cJSON_Parse((const char *)shell_string);
    uint8_t* func_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"FUNC"))->valuestring;
    uint8_t* operate_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"OPERATE"))->valuestring;
    uint8_t* para1 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM1"))->valuestring;
    uint8_t* para2 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM2"))->valuestring;
    uint8_t* para3 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM3"))->valuestring;
    uint8_t* para4 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM4"))->valuestring;
    uint8_t* para5 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM5"))->valuestring;
    uint8_t* para6 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM6"))->valuestring;

    if(hw_strcmp((const char *)func_value,"BT") == 0)
    {
        result = shell_json_parse(operate_value,para1,para2,para3,para4,para5,para6);
    }
    else
    {
        result = shell_at_cmd_parse(shell_string);
    }

    cJSON_Delete(parse_json);
    return result;

}

在这里我们分两种方式来解析:①AT command(串口工具采用这种方式) ②Json(上位机采用这种方式),如果解析不是我们定义的json格式,那么就自动转变为AT指令的解析

步骤3)AT command解析执行BT_START,以及BT_INQUIRY

uint8_t shell_at_cmd_parse(uint8_t *shell_string)
{

    if(hw_strcmp("BT_START",(const char*)shell_string) == 0)
    {
        HW_DEBUG("SHELL:operate bt start\n");
        bt_start(&bt_app_cb);
        return HW_ERR_OK;
    }

    ........


    if(hw_strcmp("BT_INQUIRY",(const char*)shell_string) == 0)
    {
        HW_DEBUG("SHELL:operate bt inquiry\n");
        bt_start_inquiry(0x30,HCI_INQUIRY_MAX_DEV);
        return HW_ERR_OK;
    }
}

步骤4)接受上位机json指令解析

uint8_t shell_json_parse(uint8_t *operate_value,
                         uint8_t *para1,uint8_t *para2,uint8_t *para3,
                         uint8_t *para4,uint8_t *para5,uint8_t *para6)
{
    if(hw_strcmp((const char *)operate_value,"BT_START") == 0)
    {
        HW_DEBUG("UART PARSE DEBUG:operate BT_START\n");
        bt_start(&bt_app_cb);
        operate_stauts_oled_show("BT",operate_value,"SUCCESS",0,0,0,0,0,0);
        return HW_ERR_OK;
    }

    .....


    if(hw_strcmp((const char *)operate_value,"BT_START_INQUIRY") == 0)
    {
        HW_DEBUG("UART PARSE DEBUG:operate BT_INQUIRY\n");
        bt_start_inquiry(0x30,HCI_INQUIRY_MAX_DEV);
        return HW_ERR_OK;
    }
}

以上步骤3)4)其中bt_start以及bt_start_inquiry就是协议栈函数了,这样就实现了AT command或者json上位机跟蓝牙协议栈的对接。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/128933.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Java经典设计模式之十一种行为型模式(附实例和详解)

    Java经典设计模式之十一种行为型模式(附实例和详解)

    2020年11月12日
  • java中instanceof用法

    java中instanceof用法java中的instanceof运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。用法:result=objectinstanceofclass参数:Result:布尔类型。Object:必选项。任意对象表达式。Class:必选项。任意已定义的对象类。说明:如果object是cla

  • matlab画折线图,标记指定点「建议收藏」

    matlab画折线图,标记指定点「建议收藏」首先,找到你需要标注的点。比如说你有x、y两个列向量构成一条曲线。现在要找最大值点那么用p=find(y=max(y)),那么坐标(x(p),y(p))就是你要找的点咯。2第二步如何标记。我介绍两总方法来标记这个点,但是总体上可以归结为一种方法。(1)利用text(x(p),y(p),’o’,’color’,’g’));这里o表示标注的形状,也可以用*、^等比较好看的符号哟。’g’表示的是颜色。(…

  • 博客群发软件–用 Windows Live Writer完美发布新浪、网易、blogcn、blogbus、cnbl

    博客群发软件–用 Windows Live Writer完美发布新浪、网易、blogcn、blogbus、cnbl前言:当今网络博客、微薄铺天盖地,相信即使一个普通的用户也都注册了很多家品牌的博客或者微薄等,那么困扰着大家一个很大的问题,同时在多家博客发布同样的内容,如果说只是简单的文字还好说,复制粘贴就完事了,但是如果里面包含着图片,那么使用复制粘贴是不可以的,因为诸多博客品牌之间图片是不能共享使用的。研究了一天,终于找到较为完美博客群发软件,那就是微软出品的…

  • 快速排序算法——C/C++

    快速排序算法——C/C++快速排序1、算法思想快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。2、实现原理2.1、设置两个变量low、high,排序开始时:low=0,high=size-1。2.2、整个数组找基准正确位置,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面…

  • Pytest(17)运行未提交的git(pytest-picked)

    Pytest(17)运行未提交的git(pytest-picked)前言我们每天写完自动化用例后都会提交到git仓库,随着用例的增多,为了保证仓库代码的干净,当有用例新增的时候,我们希望只运行新增的未提交git仓库的用例。pytest-picked插件可以

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号