1

head:
title: 手把手教你玩转蓝牙模块(原理+驱动)

description:

手把手教你玩转蓝牙模块(原理+驱动)

作为嵌入式开发工程师,蓝牙模块怎能少呢?

蓝牙模块广泛应用在各种电子器件,比如手机、蓝牙耳机/音箱、蓝牙手环、扫地机器人,等等。大家在学嵌入式的时候,玩过的智能家居、智能小车、无人机,都有蓝牙模块的应用。

所以,蓝牙模块的学习势在必行。

蓝牙模块的学习其实也没大家想的那么难,只需要你玩好串口就行了,再加上会一些 AT 指令,你就可以称得上蓝牙高手了。但关于蓝牙协议栈,那学起来估计没一年半载下不来。

对于初学者来讲,只需要懂得如何使用这个蓝牙模块,就已经绰绰有余了。

1. 源码下载及前置阅读

本文首发 良许嵌入式网https://www.lxlinux.net/e/ ,欢迎关注!

本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):

https://www.lxlinux.net/e/stm32/bluetooth-turorial.html

如前面所述,玩蓝牙模块就是玩串口,所以大家一定要先把串口玩好。如果你不懂得如何玩串口,可以看下面这篇文章:

STM32串口接收不定长数据(接收中断+超时判断):https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-usin...

如果你是个零基础的小白,连 STM32 都没见过,我也给你准备了一个保姆级教程,手把手教你搭建好 STM32 开发环境,并教你如何下载程序,简直业界良心!

零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginne...

如果你连代码都不知道怎么烧录到 STM32 的,可以参考下文,提供了 5 种代码烧录方式:

STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to...

2. 蓝牙模块介绍

2.1 型号介绍

现在市面上流行的蓝牙模块有很多,例如广州汇承公司的蓝牙模块应用非常的广泛,它们公司生产的 HC 系列的蓝牙模块如下图所示:

型号主/从机通信协议工作频段通信距离嵌入方式空中速率尺寸
HC-02从机蓝牙2.0 / 蓝牙4.02.4G10米贴片2Mbps26.9 13 2 mm
HC-04从机蓝牙2.0 / 蓝牙4.02.4G10米贴片60KB/s18.5 13 2 mm
HC-05主从机一体蓝牙2.02.4G10米贴片2Mbps27.0 13 2 mm
HC-06主从机一体蓝牙2.02.4G10米贴片2Mbps27.0 13 2 mm
HC-08主从机一体蓝牙4.02.4G80米贴片 / 焊接1Mbps26.9 13 2 mm
HC-09主从机一体蓝牙4.02.4G60米贴片 / 焊接3KB/s18.5 13 2 mm
HC-42主从机一体蓝牙5.02.4G40米贴片 / 焊接1Mbps / 2Mbps26.9 13 2 mm

这些蓝牙模块,在主/机、工作频段、通信距离、空中速率等方面都存在差异,大家可以根据自己的业务需求进行选择。

对于初学者而言,HC-08 是一款非常适合入门的蓝牙模块,本文就是以 HC-08 作为对象,介绍蓝牙模块的玩法。

HC-08 采用 TI CC25540 芯片方案,蓝牙 BLE4.0 主从一体,通过 BLE 的软件连接,传输速率 1Mbps ,传输距离 80m ,低功耗,详细参数如下:

2.2 引脚介绍

HC-08 蓝牙模块是通过串口与单片机进行通信,这个模块既可以作为主机也可以作为从机(通过 AT 指令配置)。有些蓝牙模块不支持主机(如 HC-02 、HC-04),所以在使用时需要注意区分。

HC-08 蓝牙模块实物图如下所示:

可以看到,HC-08 模块一共有 6 个引脚,下面详细介绍各个引脚的作用。

  • STATE:状态输出引脚。未连接时,则为低电平。连接成功时,则为高电平。可以在程序中作指示引脚使用;
  • RXD:串口接收引脚。接单片机的 TX 引脚(如果是5V MCU,需串联一个 220R 电阻);
  • TXD:串口发送引脚。接单片机的 RX 引脚;
  • GND:接地电源;
  • VCC:输入 3.2~6V 的电源(注意,上面一层邮票口的模块不能接 5V 的电源,需要底板降压至 3.3V);
  • KEY:主机用于清除配对的从机地址记忆(需要拉高电平 200ms 以上)。
上面的「连接」是指模块通过蓝牙协议连接上主机或从机,并非物理意义上的连接。下同。

正常通信下,只需接 RXD、TXD、GND、VCC 四条线就够了。

蓝牙模块上还有一个 LED灯和一个小按键 (按键控制着引脚 KEY )。默认情况下,当 LED灯闪烁时表示蓝牙模块当前为从机,正在等待连接。而长亮的时候就代表已经有主机连接上该模块,可以正常进行透传通讯了。

当按键按下后,主机将清除已被记录的从机地址。另外,也可使用 AT+CLEAR 指令,实现「主机清除已记录的从机地址」的功能。

注意,在硬件接线的时候蓝牙模块的 TXD 要和单片机的 RXD 相连接,蓝牙模块的 RXD 要和单片机的 TXD 相连接,也就是所谓的「交叉接线」。

3. 基本玩法

3.1 主/从机模式

3.1.1 主机模式

当蓝牙模块处于主机模式的时候,可以与一个从机进行连接。在此模式下可以对周围设备进行搜索并选择需要连接的从机进行连接。理论上,一个蓝牙主端设备,可同时与 7 个蓝牙从端设备进行通讯。

一个具备蓝牙通讯功能的设备,可以在两个角色之间进行切换。比如:平时工作在从机模式,等待其它主机来连接;在需要时,可转换为主机模式,向其它设备发起连接。一个蓝牙设备以主机模式发起连接时,需要知道对方的蓝牙地址,配对密码等信息,配对完成之后,可直接发起连接。

3.1.2 从机模式

当蓝牙模块处于从机模式的时候,只能被主机搜索,不能主动搜索。从机与主机连接以后,也可以和主机进行发送和接收数据。

3.3.3 两种工作模式有什么区别呢?

主机是指能够搜索别人并主动建立连接的一方,从机则不能主动建立连接,只能等待主机连接自己。

  • 如何进入从机模式?

HC-08 上电之后,默认情况下就是从机模式。如果需要手动配置,可以使用 AT+ROLE=S 指令。

  • 如何进入主机模式?

我们需要通过 AT 指令 AT+ROLE=M 来设置蓝牙模块为主机模式。

3.2 什么是AT指令?

AT 指令(AT Commands)最早是由发明拨号调制解调器的贺氏公司为了控制拨号调制解调器而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号调制解调器基本退出市场,但是 AT 指令被保留了下来。

在嵌入式开发中,经常是使用 AT 命令去控制各种通讯模块,比如 WiFi 模块、蓝牙模块、GPRS 模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送 AT 指令给通讯模块,模块接收到数据之后回应响应的数据。

3.3 常用的AT指令

AT 指令分为四种类型:

类型格式功能
测试指令AT + < X > = ?查询设置命令或内部程序设置的参数及其取值范围
查询指令AT + < X > ?返回参数的当前值
设置指令AT + < X > = < ... >设置用户自定义的参数值
执行指令AT + < X >执行受模块内部程序控制的变参数不可变

蓝牙模块的 AT 指令相较于 ESP8266 要少非常多,后者有近百条。现在举例一些常用指令,并使用这些指令一步一步的通过串口实现蓝牙模块的收发数据。

序号AT指令(小写 x 表示参数)作用默认状态主/从生效
1AT检测串口是否正常工作-M/S
2AT+RX查看模块基本参数-M/S
3AT+DEFAULT恢复出厂设置-M/S
4AT+RESET模块重启-M/S
5AT+VERSION获取模块版本、 日期-M/S
6AT+ROLE=x主/从角色切换SM/S
7AT+NAME=xxx修改蓝牙名称HC-08M/S
8AT+ADDR=xxxxxxxxxxxx修改蓝牙地址硬件地址M/S
9AT+RFPM=x更改无线射频功率0(4dBm)M/S
10AT+BAUD=xx,y修改串口波特率9600,NM/S
11AT+CONT=x是否可连接0(可连)M/S
12AT+AVDA=xxx更改广播数据-S
13AT+MODE=x更改功耗模式0S
14AT+AINT=xx更改广播间隔320M/S
15AT+CINT=xx,yy更改连接间隔6,12M/S
16AT+CTOUT=xx更改连接超时时间200M/S
17AT+CLEAR主机清除已记录的从机地址-M
18AT+LED=xLED 开/关1M/S
19AT+LUUID=xxxx搜索 UUIDFFF0M/S
20AT+SUUID=xxxx服务 UUIDFFE0M/S
21AT+TUUID=xxxx透传数据 UUIDFFE1M/S
22AT+AUST=x设置自动进入睡眠的时间20S

常用AT指令说明

请注意,只有当蓝牙模块未连接上主/从机,通过串口发送的数据才会被识别为 AT 指令。否则一旦连接上主/从机,则发送的字符串则被视为普通数据,直接透传给对方。

  • 测试指令

    指令响应功能说明
    ATOK测试指令最基础的测试指令

当模块连接上 MCU 之后,我们不知道模块是否连接到位、是否有虚连、模块是否正常工作,我们可以发送 AT 这条指令进行测试,如果接收到 OK 响应,则代表模块一切正常,可以进行后续的操作了。

  • 模块重启指令

    指令响应功能说明
    AT+RESETOK重启蓝牙模块蓝牙模块会自动重启,重启 200ms 后可执行新的操作
  • 恢复出厂设置指令

    指令响应功能说明
    AT+DEFAULTOK恢复出厂设置注:不会清除主机已记录的从机地址!若要清除,请在未连线状态下使用 AT+CLEAR 指令进行清除。
    蓝牙模块会自动重启,重启 200ms 后可进行新的操作。
  • 更改功耗模式指令

    指令响应功能说明
    AT+MODE=?0/1/2获取当前功耗模式获取当前功耗模式
    AT+MODE=0OK更改功耗模式(仅限从机)全速功耗模式(出厂默认)
    AT+MODE=1OK更改功耗模式(仅限从机)一级节能模式
    AT+MODE=2OK更改功耗模式(仅限从机)二级节能模式(睡眠模式)

节能模式说明:

一级节能模式是模块最主要的低功耗模式,可为透传提供低功耗待机,也可以作为低功耗的广播数据;

二级节能模式是睡眠模式,在睡眠下时不可发现、不可连接,串口唤醒后可发现、可连接。

两种节能模式都可以通过串口发送 1 个字节以上的数据来唤醒,但唤醒后前面几个字节的数据可能会乱码。

  • 修改模块角色指令

    指令响应功能说明
    AT+ROLE=?Master/Slave获取当前模块的主从机状态获取当前的蓝牙模块为主机/从机模式
    AT+ROLE=MMaster设置为主机模式设置后模块将自动重启,重启 200ms 后可进行新的操作
    AT+ROLE=SSlave设置为从机模式设置后模块将自动重启,重启 200ms 后可进行新的操作
  • 设置 LED 开/关指令

    指令响应功能说明
    AT+LED=?OK+LED=?查询LED工作模式查询LED当前的工作模式
    AT+LED=0OK+LED=0设置LED工作模式设置LED的工作模式为关闭
    AT+LED=1OK+LED=1设置LED工作模式设置LED的工作模式为打开

使用指令关闭LED后再打开,需要重启蓝牙模块才能生效

  • 修改蓝牙地址指令

    指令响应功能说明
    AT+ADDR=?(当前蓝牙模块MAC地址)获取蓝牙模块MAC地址地址必须为 12 位的 0~F 数字或大写字符,即 16 进制字符。
    AT+ADDR=xxxOKsetADDR修改蓝牙模块的MAC地址建议不要修改模块的 MAC 地址,避免冲突
  • 查看软件版本指令

    指令响应功能说明
    AT+VERSIONHC-08V3.1, 2017-07-07获取软件版本和发布日期获取软件版本和发布日期
  • 查看当前基本参数

    指令响应功能说明
    AT+RXName:HC-08查询模块的基本参数蓝牙名是用户设定的名字
    Role:Slave 模块角色(主/从)
    Baud:9600,NONE 串口波特率,校验位
    Addr:xx,xx,xx,xx,xx,xx 蓝牙地址
    PIN :000000 蓝牙密码(密码无效)
  • 修改蓝牙名称指令

    指令响应功能说明
    AT+NAME=?OK+NAME=HC-08获取蓝牙当前名称获取蓝牙模块的当前名称
    AT+NAME=xxxOKsetNAME设置蓝牙名称设置蓝牙模块的名称
  • 设置模块是否可连接指令

    指令响应功能说明
    AT+CONT=?Connectable/Non-Connectable获取蓝牙模块当前是否可连接设置可连接性,不可连接时主要用于广播数据
    AT+CONT=0OK设置“可连接”成功设置“可连接”成功
    AT+CONT=1OK设置“不可连接”成功设置“不可连接”成功
    模式主机从机
    可连接中心(Central)可连接,连线后进入普通透传模式外设(Peripheral)可连接,连线后进入普通透传模式
    不可连接观察者(Observer)当前模块不能连接到其它模块或者设备,但是会自动扫描 HC-08 从机的广播数据包,固定 2s 刷新一次广播者(Broadcaster)不会和主机连接,但可以结合低功耗模式 1,实现广播数据包发送
  • 设置连接超时指令

    指令响应功能说明
    AT+CTOUT=?OK+CTOUT=200(默认)查询连接超时时间查询连接超时时间,单位 10ms,范围 10~3200(100ms~32s)。默认为200
    AT+CTOUT=100OK+CTOUT=100设置连接超时时间设置连接超时时间为100ms
  • 设置自动进入睡眠时间指令

    指令响应功能说明
    AT+AUST=?OK+AUST=20查询自动睡眠定时(默认 20s,该时间越小则越省电)设置的时间范围为:1s~300s 。在低功耗模式下,激活模块后如无操作,x 秒后将返回用户原先所设置的低功耗模式
    AT+AUST=100OK+AUST=100设置自动睡眠时间设置自动睡眠时间为100s
  • 修改串口波特率指令

    指令响应功能说明
    AT+BAUD=xx,yxx, y设置串口设置串口,参数如下表格
    AT+BAUD=?9600,NONE查询串口设置查询串口设置

    xx 为串口波特率,y 为校验位

    参数串口波特率(xx)参数校验位(y)
    12001200bpsN无校验NONE
    24002400bpsE偶校验EVEN
    48004800bpsO奇校验ODD
    96009600bps(默认波特率)
    1920019200bps
    3840038400bps
    5760057600bps
    115200115200bps

3.4 通信示意图

4. 项目实战

4.1 硬件准备

  • STM32核心板

本文使用的是 STM32F103C8T6 最小系统板,价格很便宜,普遍 5~8 元。

<img src="https://lxlinux.superbed.verylink.top/item/656d4d04c458853aef905452.jpg" style="zoom:50%;" />

  • ST-Link V2

ST-Link 是一种用于 STM32 微控制器的调试和编程工具,它可以通过 SWD 或 JTAG 接口与开发板进行通信。一般价格在 6~8 元左右。

<img src="https://lxlinux.superbed.verylink.top/item/65222a41c458853aefb2ce8a.jpg" style="zoom:50%;" />

  • USB转TTL

这玩意儿大家应该非常熟悉了,通常我们用它来打印单片机 log。

当然,配合上位机(比如串口调试助手),我们也可以使用它对一些模组进行调试,比如:wifi模块、4G模组、蓝牙,等等。

当然价格也很便宜,普遍 5~8 元。

如果对这个工具使用不熟悉的小伙伴,可以阅读下文:

零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginne...

<img src="https://lxlinux.superbed.verylink.top/item/6523c525c458853aef551a73.jpg" style="zoom:67%;" />

  • 蓝牙模块

本文使用的是 HC-08 蓝牙模块,价格普遍 5~20 元左右。

<img src="https://lxlinux.superbed.verylink.top/item/6576bf2ec458853aefd0c3b7.jpg" style="zoom: 25%;" />

4.2 PC串口助手调试

在项目开始前,我们需要使用 USB 转 TTL 工具对我们的蓝牙模块进行测试,确保蓝牙模块工作正常。

硬件接线如下表格所示:

HC-08USB转TTL
RXDTXD
TXDRXD
GNDGND
VCC3V3

线路接好之后如下图所示:

线路接好之后,将 USB 转 TTL 工具插入电脑,在串口助手输入指令 AT ,模块正常情况下会返回 OK

<img src="https://lxlinux.superbed.verylink.top/item/6577c680c458853aef84dbf1.jpg" style="zoom: 50%;" />

接着我们输入 AT+VERSION ,获取蓝牙模块的版本信息,结果如下:

<img src="https://lxlinux.superbed.verylink.top/item/6577c72ec458853aef874003.jpg" style="zoom: 50%;" />

在使用串口调试蓝牙模块的过程中,有可能会出现波特率正常、驱动正常、蓝牙模块正常,且可以被手机连接上,但是输入 AT 指令,却没有返回的情况,出现这种情况可以试试换个串口助手。

4.3 硬件接线

蓝牙模块确认正常之后,我们就可以使用单片机通过编程的方式来操作蓝牙模块。

本文使用 串口2 连接蓝牙,串口1 连接 USB 转 TTL 来打印 log。

HC-08STM32USB转TTL
VCC3.3VVCC
RXDA2
TXDA3
GNDGNDGND
A10TXD
A9RXD

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章 STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to...

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

接好之后像这样,我这里使用了面包板,用公对母的杜邦线将他们串在了一起

4.4 蓝牙收发代码编写

蓝牙模块通过串口与 MCU 进行通讯,所以第一步需要先做好串口的配置。

关于串口的配置,我写过一篇文章手把手教你玩串口,大家可以移步下文查看:

STM32串口接收不定长数据(接收中断+超时判断):https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-usin...

具体代码如下:

uint8_t bt_uart_rx_buf[BT_RX_BUF_SIZE];
uint8_t bt_uart_tx_buf[BT_TX_BUF_SIZE];
uint16_t bt_uart_rx_len = 0;

void bt_init(uint32_t baudrate)
{
    bt_uart_handle.Instance          = BT_INTERFACE;                 /* BT */
    bt_uart_handle.Init.BaudRate     = baudrate;                     /* 波特率 */
    bt_uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 数据位 */
    bt_uart_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */
    bt_uart_handle.Init.Parity       = UART_PARITY_NONE;             /* 校验位 */
    bt_uart_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收发模式 */
    bt_uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 无硬件流控 */
    bt_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 过采样 */
    HAL_UART_Init(&bt_uart_handle);                                  /* 使能BT */
}

void bt_rx_clear(void)
{
    memset(bt_uart_rx_buf, 0, sizeof(bt_uart_rx_buf));              //清空接收缓冲区
    bt_uart_rx_len = 0;                                             //接收计数器清零
}

void BT_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_RXNE) != RESET){      //获取接收RXNE标志位是否被置位
        if(bt_uart_rx_len >= sizeof(bt_uart_rx_buf))                        //如果接收的字符数大于接收缓冲区大小,
            bt_uart_rx_len = 0;                                             //则将接收计数器清零
        HAL_UART_Receive(&bt_uart_handle, &receive_data, 1, 1000);          //接收一个字符
        bt_uart_rx_buf[bt_uart_rx_len++] = receive_data;                    //将接收到的字符保存在接收缓冲区
    }

    if (__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_IDLE) != RESET)      //获取接收空闲中断标志位是否被置位
    {
        printf("recv: %s\r\n", bt_uart_rx_buf);                             //将接收到的数据打印出来
        bt_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&bt_uart_handle);                         //清除UART总线空闲中断
    }
}

通过这几个函数,我们就可以读取蓝牙返回的数据,并保存在数组 bt_uart_rx_buf 里。

如果需要通过串口向蓝牙模块发送数据,可以使用下面函数:

void bt_send(char *fmt, ...)
{
    va_list ap;
    uint16_t len;
    
    va_start(ap, fmt);
    vsprintf((char *)bt_uart_tx_buf, fmt, ap);
    va_end(ap);
    
    len = strlen((const char *)bt_uart_tx_buf);
    HAL_UART_Transmit(&bt_uart_handle, bt_uart_tx_buf, len, HAL_MAX_DELAY);
}

至此,蓝牙模块的初始化、发送、接收部分就做好了。

在 main 函数里,我们可以先调用 bt_init() 函数进行初始化,然后调用 bt_send() 函数发送数据,如下:

int main(void)
{
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                             /* 初始化延时函数 */
    usart_init(115200);                         /* 串口1波特率设为115200 */
    bt_init(9600);                              /* 串口2波特率设为9600 */

    printf("蓝牙实验……\r\n");

    while(1)
    {
        bt_send("bt send\r\n");
        delay_ms(1000);
    }
}

4.5 通过手机蓝牙助手发送数据到电脑的串口助手

接着我们打开电脑串口软件。设置串口助手波特率 115200 (你们不一定要用我这款,随便的串口助手都可以),选择串口号,最后打开串口开始准备接收数据。

这个串口工具接收的是 MCU 串口 1 的数据,也就是 log 。蓝牙接收到数据后,我们使用串口 1 打印到下面的串口助手里。

<img src="https://lxlinux.superbed.verylink.top/item/6577caedc458853aef93c636.jpg" style="zoom:50%;" />

然后打开手机蓝牙助手准备开始调试,点击蓝牙模块开始连接。没有蓝牙助手的同学,可以在前文找到下载地址。

<img src="https://lxlinux.superbed.verylink.top/item/6577cbd2c458853aef969a92.jpg" style="zoom:33%;" />

<img src="https://lxlinux.superbed.verylink.top/item/6577cc42c458853aef980238.jpg" style="zoom:33%;" />

到这里,我们就完成了 MCU 通过蓝牙将数据透传到手机 APP (蓝牙助手)。

当然,我们也可以通过手机 APP 向蓝牙发送数据,MCU 接收到透传的数据之后通过串口助手打印在电脑上。比如我们给蓝牙模块发送数据 111 、aaa 、123123。

<img src="https://lxlinux.superbed.verylink.top/item/6577cdfdc458853aef9e08c0.jpg" style="zoom:50%;" />

可以看到串口助手成功接收到 111 、aaa 、123123,这些数据。

<img src="https://lxlinux.superbed.verylink.top/item/6577ce1ec458853aef9e6ba0.jpg" style="zoom:50%;" />

到此,蓝牙模块的调试就完成了。

5. 小结

通过学习和实践,希望大家都能够了解并掌握蓝牙模块的使用方法,从而更好地应用于嵌入式开发。无论是智能小车还是开发物联网设备,蓝牙模块都能成为您的得力助手,让我们一起玩转蓝牙模块吧!

另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!


良许
1k 声望1.8k 粉丝