蓝牙模块使用

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

051.6.7 蓝牙模块使用

#include "reg52.h"
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

uint count = 0;
uchar recv;

sbit LED1 = P1^0;
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED4 = P1^3;
sbit BEEP = P1^6;
sbit JDQ1 = P2^0;

void Delay_xms(uint xms)    //@11.0592MHz
{
    uchar data i, j;

    while (xms)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}

void UART_Send_Byte(uchar send_byte)
{
    SBUF = send_byte;
    while (TI == 0);
    TI = 0;
}

void UART_Send_Str(uchar *send_str)
{
    while (*send_str != '\0')
    {
        UART_Send_Byte(*send_str++);
    }
}

void main()
{
    SCON = 0x50;
    PCON &= 0x7F;
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = 0xFD;
    TL1 = 0xFD;
    TR1 = 1;
    ES = 1;
    EA = 1;                    // 总中断打开

    while (1)
    {
        for (count = 0; count < 26; count++)
        {
            UART_Send_Byte('a' + count);
        }

        UART_Send_Byte('\n');
        UART_Send_Str("i am wfeng!\r\n");
        Delay_xms(1000);
    }
}

/*****************
串口的中断处理函数
*****************/
void UART_Routine(void) interrupt 4
{
    if (1 == RI)
    {
        RI = 0;
        recv = SBUF;

        switch (recv)
        {
            case 0x1:
                LED1 = 0;
                break;

            case 0x2:
                LED2 = 0;
                break;

            case 0x3:
                LED3 = 0;
                break;

            case 0x4:
                LED4 = 0;
                break;

            case 0x5:
                P1 |= 0x0F;
                break;

            case 0x6:
                BEEP = 0;
                break;

            case 0x7:
                BEEP = 1;
                break;

            case 0x8:
                JDQ1 = 0;
                break;

            case 0x9:
                JDQ1 = 1;
                break;

            default:
                break;
        }
    }
}

一、蓝牙模块使用

本实验使用JDY-31 SPP蓝牙串口模块,把手机与STC89C52之间的无线通信转换成普通UART收发。单片机程序沿用串口接收实验,不需要因为传输介质从USB数据线变成蓝牙而改变SCONSBUFTIRI等串口逻辑。

整体通信链路如下:

手机蓝牙助手
     │  Bluetooth Classic SPP无线链路
     ▼
JDY-31蓝牙协议与串口转换
     │  TTL电平UART:TXD、RXD
     ▼
STC89C52:P3.0/RXD、P3.1/TXD
     │
     ├─ 接收命令01~09:控制LED、蜂鸣器、继电器
     └─ 主动发送a~z和字符串:显示在手机蓝牙助手中

“透明传输”表示模块尽量保持用户数据字节不变:手机发出的0x01经过蓝牙链路后,从模块TXD输出为UART字节0x01;单片机发出的ASCII字符串也会被模块转发给手机。模块负责蓝牙协议和无线收发,单片机仍只面对UART字节流。

1.1 接线方式

视频使用的是带底板、排针和稳压电路的JDY-31模块,板背面标注Power: 3.6V~6VLEVEL: 3.3V。基础接线如下:

JDY-31底板引脚连接位置作用
VCC开发板+5V给模块底板供电
GND开发板GND电源回路和UART共同参考地
TXDSTC89C52的P3.0/RXD模块向单片机发送数据
RXDSTC89C52的P3.1/TXD模块接收单片机发送的数据
STATE基础实验可不接指示蓝牙连接状态
EN基础实验按模块默认状态使用模块使能或相关控制引脚,具体以底板说明为准
JDY-31底板                              STC89C52开发板

VCC  ───────────────────────────────→  +5V
GND  ───────────────────────────────→  GND
TXD  ───────────────────────────────→  P3.0/RXD
RXD  ←───────────────────────────────  P3.1/TXD

数据方向:发送端TXD必须连接到对方接收端RXD

为什么TX和RX必须交叉连接

TXD是本设备的数据输出,RXD是本设备的数据输入。两个输出端接在一起会争用线路,两个输入端接在一起又没有任何设备驱动信号;正确关系只能是“一端发送→另一端接收”。

模块发送:JDY-31 TXD ─→ MCU RXD
模块接收:JDY-31 RXD ←─ MCU TXD

如果只需要手机控制单片机,可以只连接模块TXD→MCU RXD;但本实验还要让手机看到单片机主动发送的字符串,所以两个方向都要连接。

为什么必须共地

UART的高、低电平都是相对于GND测量的。没有公共地时,即使TXD导线已经连接,接收方也无法获得稳定的电压参考:

发送端输出电压 = TXD相对于发送端GND的电压
接收端判断电平 = RXD相对于接收端GND的电压

两端GND连接后,才建立共同的0V参考

供电电压与UART信号电平必须分开理解

视频中的JDY-31带底板模块应把VCC接5V,因为底板输入范围为3.6~6V,5V是推荐值。直接把这块底板接到3.3V,可能因板载稳压器压差而导致内部电压不足、启动不稳定。

但“VCC接5V”不等于“RXD/TXD也是5V逻辑”。该底板标注的UART逻辑电平是3.3V:

电源侧:VCC输入5V ─→ 底板稳压电路 ─→ 给JDY-31核心供电

信号侧:TXD/RXD仍按照约3.3V逻辑工作

还要区分两种产品形态:

产品形态典型VCC范围说明
JDY-31裸SMD核心模块1.8~3.6V,推荐3.3V不能直接把5V接到核心VCC
视频中的带底板模块3.6~6V,推荐5V底板带供电处理,VCC可接开发板5V

STC89C52工作在5V时,P3.1/TXD输出高电平可能接近5V,而JDY-31的RXD属于3.3V逻辑输入。视频所用成品底板直接连接能够完成实验,但换用其他批次或裸模块时,不能默认RXD耐受5V;应核对底板是否带电平保护,必要时在MCU TXD→模块RXD方向增加电阻分压或电平转换器。

典型电阻分压可以这样理解:

MCU TXD 5V ── R上 ──┬──→ JDY-31 RXD约3.3V
                     │
                    R下
                     │
                    GND

V_RXD = 5V × R下 / (R上 + R下)

例如取R上=1kΩR下=2kΩ,理想输出约为:

$ V_{RXD}=5\times\frac{2}{1+2}\approx3.33V $

反方向的JDY-31 TXD→MCU RXD输出约3.3V,是否能被5V单片机稳定识别为高电平,要检查单片机输入高电平门限。当前开发板实验可以正常通信,但不能把这一结果无条件套用到其他芯片。

接线和上电前应再次确认模块背面丝印。外形相似的HC-05、HC-06、JDY-30、JDY-31以及裸模块可能具有不同供电范围和引脚顺序,不能只按模块颜色或排针数量判断。

1.2 实验演示

实验包含手机、JDY-31、STC89C52开发板、板载LED与蜂鸣器,以及外接继电器模块。代码仍使用上一讲的命令表:

手机以HEX发送单片机收到的字节执行动作
010x01点亮LED1
020x02点亮LED2
030x03点亮LED3
040x04点亮LED4
050x05熄灭全部LED
060x06打开蜂鸣器
070x07关闭蜂鸣器
080x08继电器吸合
090x09继电器释放

视频中主要演示了01、02、03、05、06、07、08、0904同样由代码支持。这里控制的是蜂鸣器,不是风扇;继电器只是一个电控机械开关,是否接风扇或其他负载取决于继电器触点侧的实际接线。

手机还会收到单片机主循环主动发送的字母和字符串:

单片机UART发送 ─→ JDY-31 RXD ─→ 蓝牙无线链路 ─→ 手机APP显示

而手机按钮命令走反方向:

手机APP发送 ─→ 蓝牙无线链路 ─→ JDY-31 TXD ─→ 单片机RXD与串口中断

这说明通信是双向的。蓝牙模块并不理解“开灯”“蜂鸣器”这些业务含义,它只转发01~09等字节;真正把命令解释成硬件动作的是单片机中的switch(recv)

自定义按钮

蓝牙助手通常允许长按按钮并配置按钮名称与发送内容。例如建立“全部关灯”按钮:

按钮名称:全部关灯
发送模式:HEX
发送内容:05

点击按钮后,APP发送一个字节0x05,而不是ASCII字符'5'对应的0x35。其他按钮可以依照命令表分别配置01~09

如果希望APP使用普通文本发送数字,则代码中的分支应匹配case '1'case '9',或者先把ASCII数字转换成数值;发送端格式与接收端协议必须一致。

1.3 思维导图

JDY-31属于Bluetooth Classic SPP串口透传模块。出厂常见名称为JDY-31-SPP,默认配对密码为1234,默认UART波特率为9600;名称、密码或波特率若已通过AT命令调整,应以模块当前配置为准。

Android获取与安装蓝牙助手

课程资料中提供了安卓蓝牙助手APK,也可以在可信应用商店搜索支持SPP和HEX收发的蓝牙串口工具。选择软件时重点确认:

功能用途
支持Bluetooth Classic SPP能连接JDY-31,而不只是BLE设备
支持HEX发送可直接发送01~09命令字节
支持接收显示查看单片机返回的字符串
支持自定义按钮把常用命令做成“开灯、关灯”等按钮

从课程资料安装APK时,可以通过USB、可信网盘或系统文件管理器把文件传入手机,再按Android提示临时允许对应来源安装未知应用。安装完成后应关闭该权限。聊天软件或特定浏览器只是文件转交方式,不是安装APK的必需条件;不要安装来源不明或被二次修改的APK。

iPhone兼容性说明

JDY-31使用经典蓝牙SPP。普通iOS应用不能像Android应用那样随意访问未经过相应认证的经典蓝牙串口配件,因此即使App Store中能搜到“蓝牙串口”软件,也不代表它一定能连接JDY-31。使用iPhone时应先确认软件明确支持该模块;如果需要通用iOS连接,通常应选择BLE串口模块并使用支持其BLE服务的应用。

SPP与BLE不能混淆

项目JDY-31 SPP常见BLE串口模块
蓝牙类型Bluetooth ClassicBluetooth Low Energy
数据抽象串口配置文件SPPGATT服务与特征值
Android支持通常较直接需要BLE应用支持相应服务
iOS通用应用支持受经典蓝牙配件机制限制通常可通过Core Bluetooth访问

手机能在系统设置中看到模块,只表示发现或配对层面可见,不等于任意串口App都能建立数据通道。

1.4 软件使用方式

配对和连接步骤

  1. 按接线表连接模块并给开发板上电,确认模块状态灯工作。
  2. 在Android手机的蓝牙设置中查找JDY-31-SPP或实际广播名称。
  3. 发起配对,未改过配置时输入默认PIN码1234
  4. 打开支持SPP的蓝牙助手,选择已配对的JDY-31并建立连接。
  5. 设置与单片机一致的串口参数。本实验使用9600、8N1。
  6. 打开发送选项,勾选HEX发送;显示时间、显示发送内容可按调试需要开启。
  7. 发送01~09并观察硬件动作,同时查看单片机主动发回的文本。

模块状态灯的具体闪烁方式会因底板版本而异,通常未连接时闪烁,建立蓝牙连接后节奏改变或常亮。应以模块实物说明为准,不要只凭LED颜色判断串口数据一定已经收发成功。

为什么不勾选HEX就没有动作

APP在文本模式中发送字符1时,线路上的字节是ASCII码0x31;代码判断的是数值0x01

HEX模式输入01  ─→ 发送0x01 ─→ 匹配 case 0x1  ─→ LED1点亮
文本模式输入1  ─→ 发送0x31 ─→ 不匹配任何case ─→ default,无动作

所以必须把APP设置与代码协议对应起来。“显示HEX”和“发送HEX”也是两个不同选项:前者只改变接收区怎样显示,后者才决定实际发出的字节内容。

为什么蓝牙模块会影响串口烧录

STC89C52的程序下载也使用P3.0/RXDP3.1/TXD。开发板上的CH340 USB转串口芯片已经连接到这组引脚,再外接JDY-31后,相当于两个串口设备同时挂在同一组线上:

                         ┌← CH340 TXD(下载工具向MCU发送)
MCU P3.0/RXD  ←─────────┤
                         └← JDY-31 TXD(蓝牙模块向MCU发送)

                         ┌→ CH340 RXD(电脑接收MCU数据)
MCU P3.1/TXD  ──────────┤
                         └→ JDY-31 RXD(蓝牙模块接收MCU数据)

一个TXD同时驱动两个RXD通常只是增加输入负载;真正危险的是两个TXD并到MCU的同一个RXD上。当CH340和JDY-31输出不同电平时,会发生信号争用,既可能破坏下载数据,也可能形成不必要的对拉电流。

CH340 TXD输出1 ─┐
                ├─ 同一节点发生冲突 ─→ MCU收到错误波形,下载失败
JDY-31 TXD输出0 ─┘

烧录失败或无法握手时,最稳妥的处理是断电后拔下蓝牙模块的VCC、TXD、RXD,至少应断开模块TXD→P3.0/RXD这条可能主动驱动的线路;烧录完成后再断电恢复接线。不要在开发板和模块通电时随意插拔,避免短路或接错引脚。

若要长期保留两套串口设备,可以增加跳帽、模拟开关或三态缓冲器,在“CH340下载”和“蓝牙通信”之间明确选择;也可以把蓝牙模块接到其他GPIO并使用软件串口,但51单片机的软件串口会占用CPU时间,对波特率和中断时序要求更高。

排查蓝牙通信时,可以按以下顺序检查:

模块供电是否正确
        ↓
TXD/RXD是否交叉、GND是否共地
        ↓
模块名称、PIN和SPP连接是否成功
        ↓
双方波特率是否都是9600、帧格式是否8N1
        ↓
APP是否真的启用了“发送HEX”
        ↓
发送01~09时,单片机RI是否触发、switch是否匹配
        ↓
下载失败时,断开蓝牙模块后重新烧录

掌握这条排查链后,就能把问题区分为供电、接线、电平、蓝牙连接、串口参数、数据格式和程序解析七个层次,而不是只反复点击“连接蓝牙”。