☰
蓝牙模块+STC单片机:无线串口透传从选型配置到代码联调全攻略
2026/10/14 17:43:25 网站建设 项目流程

简介:该资源是一套基于STC单片机的蓝牙通信例程包,重点演示蓝牙主从模块的配置与数据收发,适合嵌入式入门开发者及需要快速集成蓝牙功能的工程师。压缩包共21个文件,主要包含C源文件、头文件、Hex固件以及Keil工程辅助文件,串口驱动、系统初始化和主流程分别由不同源码实现,Hex文件可直接烧录验证。整个包仅29KB,结构紧凑,方便通读。资源已经实测通过,能稳定建立蓝牙模块与单片机之间的无线链路;通过阅读串口中断处理、主从角色配置等代码,可掌握UART初始化、数据收发及蓝牙调试助手的联调方法,对缩短项目排错时间很有帮助。目前已有394人学习下载,适合作为单片机蓝牙开发的入门参考资料,也可根据硬件引脚简单修改后移植到其他STC型号。

1. 蓝牙模块加STC单片机:为什么这套组合仍是无线串口方案的入门首选

做单片机小项目做到一定阶段,总会遇到一个绕不开的需求:设备不能老插着线,得用手机无线控制。某开发者第一次做这个需求时,在一众无线方案里挑花了眼——WiFi模块要配路由器、LoRa要买成对模块、NRF24L01还要自己写协议栈。最后发现,一个十几块钱的蓝牙模块,配上一块STC单片机,就是当前成本最低、上手最快、资料最多的无线透传方案。手机开蓝牙连上模块,数据就变成了单片机串口上的普通收发,代码里根本不需要关心无线协议。这套组合适合做调试串口、传感器数据回传、小型遥控器之类的场景,也是很多高校实验室和电子爱好者入门无线通信的第一站。文章围绕“蓝牙模块stc程序”这个主题,讲讲选型、AT指令、接线、代码和联调翻车经验,照着步骤做就能跑通。

2. 蓝牙模块选型与AT指令配置:HC-05为什么是入门首选

2.1 常见蓝牙模块型号对比:HC-05、HC-06、JDY-31的差异与选型理由

市面上能和STC单片机搭配的蓝牙串口模块,最常见的就是HC-05、HC-06、JDY-31这三款,它们的核心用法都是把蓝牙数据转成串口TTL电平,和单片机连接后就是一对“无线串口线”。区别主要在角色配置和价格上。

HC-05是主从一体模块,既可以被手机搜索连接,也能主动去连接别的蓝牙设备。它有两个工作模式:AT指令配置模式和透传模式。AT模式用来改名称、改配对密码、改波特率,透传模式就是正常收发数据。HC-06则是从机模块,只能被手机搜索和连接,也支持AT指令,但指令集比HC-05简单一些。某开发者第一次用HC-06时还纳闷为什么进不了AT模式,后来才发现HC-06在未配对状态下通电就是AT模式,配对后就变成透传了,跟HC-05的按键进AT模式完全是两回事。

JDY-31是后来出的白菜价模块,也是主从一体,但它的AT指令格式和HC系列不太一样。HC系列用“AT+NAME=xxx”这种带等号的写法,JDY-31用“AT+NAMExxx”这种不带等号的写法。如果习惯了一套指令到处套,很容易在这上面翻车。给个选型结论:如果要和手机APP通信,HC-05是最稳的选择,资料多、踩坑案例多、AT指令格式通用;如果要和另一个单片机蓝牙模块做双机通信,HC-05或JDY-31这类主从一体模块才够用,HC-06从机做不了主动连接。

选型还有一个容易被忽略的点:模块工作电压。HC-05的VCC标称5V供电,板载稳压芯片给蓝牙芯片供3.3V;HC-06和JDY-31很多是3.3V供电版本,直接接STC单片机开发板的5V脚位可能烧模块。买模块时确认一下丝印或咨询商家,供电错了第一件事就是冒烟。

表:三款常见蓝牙串口模块关键参数对比

模块型号主从角色供电电压AT指令风格典型场景
HC-05主从一体5V(板载稳压)带等号,指令集完整手机透传、双机通信
HC-06仅从机3.3V(部分版本5V兼容)简化版,配对状态变化影响手机单向透传
JDY-31主从一体3.3V~5V(版本差异大)不带等号,格式不同低成本透传、小批量

2.2 进入AT模式的三步操作与关键参数设置:名称、密码、波特率怎么改

拿到HC-05,第一件事不是急着接单片机,而是先把模块配置好。HC-05进入AT模式有个标准流程:按住模块上的小按键的同时给模块上电,这时模块的指示灯会慢闪(大概2秒一次),表示已经处于AT指令模式。如果直接上电,模块指示灯快闪,那是透传模式,此时发AT指令不会收到“OK”响应。

进入AT模式后,把模块的TXD、RXD、GND分别接USB转TTL工具的RXD、TXD、GND,注意交叉接线,然后把USB转TTL工具的跳线帽或拨码开关拨到5V供电档位。打开串口调试助手,波特率先设成模块默认的38400,数据位8、停止位1、无校验,打开串口后发送“AT\r\n”,能收到“OK”就说明进入状态了。这里有个细节:AT指令的结尾必须带回车换行,大部分调试助手在“发送”旁边有这个选项,勾上就行。

接下来是三个必改参数。改模块名称,是指手机蓝牙列表里显示的名字,多设备区分时尤其有用。改配对密码,默认是1234,做产品原型时最好改掉,否则别人也能连你的设备。改波特率是核心——HC-05默认38400,但STC单片机常用11.0592MHz晶振,跑9600波特率误差率刚好接近零,所以一般会把蓝牙模块也改成9600,两边配置一致才能稳定通信。常用操作命令如下。

AT+NAME=MyDev01 AT+PSWD=8888 AT+UART=9600,0,0

命令含义与参数说明:

  • AT+NAME=MyDev01:把模块名称改为MyDev01,手机搜索蓝牙时显示这个名字,避免和别人的HC-05重名连错。
  • AT+PSWD=8888:把配对密码改为8888,手机第一次连接时要求输入,改掉默认的1234更安全。
  • AT+UART=9600,0,0:第一个参数9600是目标波特率,第二个0表示停止位是1位,第三个0表示无校验位。这个参数必须和STC单片机串口初始化配置保持一致,否则后面透传全是乱码。
  • “AT+UART”改完波特率后,模块内部会立刻切换,调试助手的串口参数也要同步改成9600,否则再也收不到“OK”响应了。

配置完成后重新上电,模块进入透传模式,指示灯变成快闪,手机就能搜索到刚才设置的名称。某开发者的习惯是每次拿到新模块先把名称改成项目代号、波特率统一设为9600,这个习惯在后面对接STC程序时省去了大量排查时间。改完波特率后模块和调试助手的串口参数要同步更新,这是新手最容易忽略的衔接点。

3. 硬件接线与STC串口初始化:先让单片机收到来自蓝牙的字节

3.1 STC与HC-05的最小接线:交叉连接、共地、供电三件事缺一不可

STC单片机和HC-05蓝牙模块连接,本质上就是一组TTL串口的对接。STC的TXD引脚要接蓝牙模块的RXD,STC的RXD引脚要接蓝牙模块的TXD,交叉接线这条规则说过一万遍还是会有人接反。某开发者见过一个“最高级”的错误:所有线都接对了,就是忘了把两边的GND连在一起,结果模块指示灯常亮,手机也连上了,但数据一个字节都收不到。TTL串口通信是电平比较,参考地不一致,信号就全是悬空的。

供电问题在这里也要再强调一遍。HC-05标称5V供电,所以可以直接引STC最小系统板上的5V引脚给模块供电,模块板载稳压会把5V降成3.3V给蓝牙芯片。但蓝牙模块的TXD引脚输出的电平是以3.3V为高电平的,STC单片机的RXD引脚一般能兼容3.3V高电平(STC15系列和STC8系列都支持),不放心的话可以先跑最小代码验证,确认能收到再继续往后面加逻辑。

接线顺序方面,我的习惯是先接GND,再接VCC,最后接TXD和RXD。先共地再接电源能减少带电插拔造成的干扰和损坏风险。有些现成的蓝牙模块底板是排针引出,直接杜邦线连到STC开发板即可,不用焊接。

连接完成后的检查清单如下:

  • GND与GND相连,VCC与5V相连
  • STC的TXD引脚连蓝牙模块的RXD引脚
  • STC的RXD引脚连蓝牙模块的TXD引脚
  • 蓝牙模块指示灯正常亮起(未配对时快闪)

硬件连接完成且手机能连上蓝牙模块后,就可以进入STC单片机侧的程序编写了。这套接线的核心思路是:蓝牙模块对STC来说就是多了一个可用串口收发的外设,程序层面与有线串口没有区别。

3.2 STC串口初始化代码:定时器1产生9600波特率,中断接收不丢字节

STC单片机用串口和蓝牙模块通信,最常用的方案是串口1加定时器1做波特率发生器。以STC15系列为例,12T模式下用11.0592MHz晶振,定时器1初值设为0xFD即可产生9600波特率。这个初值的计算逻辑是:波特率 = 定时器溢出率 / 4,而溢出率取决于时钟分频和重装初值,11.0592MHz这个晶振频率专门为串口通信设计,因为它能精确分频出9600、4800这些常用波特率,误差率接近零。

程序里除了初始化串口,还要开接收中断。中断方式接收比查询方式可靠得多:查询方式需要主循环不断去读接收标志位,在高频外设交互时非常容易丢字节;中断方式则是数据到达一字节就进一次中断,把数据存进缓冲区,主循环再去处理。蓝牙模块发来的数据是断续的短帧,用中断接收、主循环解析的结构最舒服。

#include "STC15F2K60S2.H" #define BUF_SIZE 64 unsigned char rxBuf[BUF_SIZE]; unsigned char rxHead = 0; unsigned char rxCount = 0; void UART1_Init(void) { SCON = 0x50; // 串口1模式1,8位UART,允许接收 TMOD = 0x20; // 定时器1工作于模式2,8位自动重装 TH1 = 0xFD; // 11.0592MHz,12T模式,9600波特率初值 TL1 = 0xFD; TR1 = 1; // 启动定时器1 ES = 1; // 使能串口1中断 EA = 1; // 使能总中断 } void UART1_SendByte(unsigned char dat) { SBUF = dat; while (TI == 0); TI = 0; } void UART1_Isr(void) interrupt 4 { if (RI) // 接收完成标志 { RI = 0; rxBuf[rxHead] = SBUF; rxHead = (rxHead + 1) % BUF_SIZE; if (rxCount < BUF_SIZE) { rxCount++; } } } void main(void) { UART1_Init(); while (1) { if (rxCount > 0) { // 从缓冲区取出一字节处理 } } }

代码逻辑说明:

  • SCON = 0x50这项配置把串口1设置为模式1,即10位帧格式(起始位1位、数据8位、停止位1位),同时打开了接收使能位REN。
  • 定时器1配置为模式2后,每次溢出会自动重装TH1的初值,省去了在中断里手动重新赋值的麻烦,误差也更小。
  • 接收中断函数里把SBUF中的数据存入环形缓冲区,rxHead记录写入位置,rxCount记录当前缓冲区里有多少字节待处理。主循环读走数据后要将rxCount减1,这段代码里省略了具体的消费逻辑,实际项目可以按协议解析。
  • 用了环形缓冲区的写法,更贴近真实工程。未使用数组越界风险更低,但要注意缓冲区的大小要大于应用中单次收发的最大数据长度。

常用参数调整参考:

  • 如果改用22.1184MHz晶振,初值要重新计算
  • 如果蓝牙模块波特率改用38400,则TH1初值改为0xFA
  • 缓冲区大小根据实际协议帧调整,典型值32、64、128字节

初始化代码写好后,可以先写一段最简单的回环代码验证硬件链路:单片机收到任何字节都原封不动发送回去。这个测试的意义在于快速区分是硬件问题还是程序问题,收到回显就说明“蓝牙模块→单片机→蓝牙模块→手机”整条链路口是通的。

4. 手机与单片机通话:透传调试与协议设计,让APP端的数据变成单片机变量

4.1 串口调试助手与蓝牙配对:用虚拟串口打通开发环境

代码写好后,最理想的联调方式不是直接用手机APP,而是先用PC端的串口调试助手配合“蓝牙虚拟串口”功能。具体操作是:先用手机或电脑的蓝牙设置配对HC-05模块,然后在PC端安装蓝牙串口服务程序,配对成功后系统会分配一个新的COM口,打开串口调试助手连接这个虚拟COM口,就能像操作有线串口一样给蓝牙模块发数据。

这种联调方式的好处是调试成本极低——串口调试助手可以发送十六进制字节、可以定时发送、可以保存收发日志,这些功能在手机APP还没开发时就完成了绝大部分通信验证。某开发者在做一个传感器数据回传项目时,先在PC端用串口助手模拟手机发指令,把指令解析逻辑全部验证完,再写APP,整个过程省了大量反复烧录单片机的次数。

联调时要确认串口助手的波特率设置与蓝牙模块一致。如果前期按第2章的配置把蓝牙模块波特率改成了9600,这里串口调试助手也要选9600。打开串口后,给蓝牙模块发一个固定字符,观察串口助手的接收窗口是否出现单片机回复的内容。这能验证蓝牙链路是否通畅,以及单片机程序是否按照预期工作。

这里有一个经验性判断:串口助手收发正常后,再换手机APP测试,如果此时出现问题,往往不是蓝牙链路的问题,而是手机端的蓝牙串口库配置问题。这样可以快速缩小排查范围。

4.2 单片机透传程序扩展:按帧解析指令,用字符串匹配控制LED

串口收发跑通之后,下一步是按协议解析数据。最常见的做法是定义简单的字符指令:手机端发送“LED_ON\r\n”,单片机收到后点亮LED;发送“LED_OFF\r\n”,关闭LED。指令帧结构越简单越好,一般包含固定字符串和结尾的换行符,解析时按行匹配。

CH340之类USB转TTL工具的串口助手发送时,默认支持勾选“发送新行”,这会让每条指令末尾带一个换行符,正好作为帧结束标志。蓝牙透传模式下,手机APP的发送缓冲区最后也要手动加上换行符,否则单片机端很难确定一帧数据从哪里结束。

unsigned char lineBuf[16]; unsigned char lineLen = 0; void ProcessCommand(void) { if (lineLen == 5 && lineBuf[0] == 'L' && lineBuf[1] == 'E') { // 两条指令长度一致,比较第三位区分 if (lineBuf[2] == 'D' && lineBuf[3] == '_' && lineBuf[4] == 'O' && lineBuf[5] == 'N') { LED = 0; // 点亮LED } else if (lineBuf[2] == 'D' && lineBuf[3] == '_' && lineBuf[4] == 'O' && lineBuf[5] == 'F' && lineBuf[6] == 'F') { LED = 1; // 关闭LED } } }

代码逻辑说明:

  • 中断接收函数把每个字节存入lineBuf,lineLen记录当前行长度。
  • 当收到换行符(0x0A)时,调用ProcessCommand处理当前行的内容,同时清空lineLen做下一帧的接收。
  • 上面的判断逻辑用了固定长度匹配,简单直接,适合指令数量不多的小项目。如果指令多,可以用strcmp或switch-case加字符串匹配,在代码可读性和扩展性上更好。

给协议设计一个边界:指令长度不要超过单片机的缓冲区大小,否则长数据会把缓冲区写穿。设计协议时约定最大帧长,接收端做长度限制,超出就丢弃,这是避免不稳定因素的常见手段。

void UART1_Isr(void) interrupt 4 { if (RI) { RI = 0; unsigned char dat = SBUF; if (dat == '\n') { ProcessCommand(); lineLen = 0; } else if (lineLen < sizeof(lineBuf)) { lineBuf[lineLen++] = dat; } else { lineLen = 0; // 超长直接丢弃整帧 } } }

这段代码是对上一段的中断接收逻辑升级:把收到换行符作为一帧结束标志,遇到超长数据时直接清空而不是继续写入导致数组越界。这个细节能让整个指令识别逻辑在数据杂乱时也不会出现缓冲区溢出的问题。

4.3 手机端调试工具选型与参数匹配:波特率、字符编码之间的三个坑

手机端的蓝牙串口工具选哪家不重要,重要的是会用。某开发者见过太多求助信息里,手机APP连接蓝牙后收到的全是乱码或加密符号,最后排查发现全是串口号匹配问题。

第一个坑是波特率不匹配。手机端串口工具默认会连上蓝牙模块的波特率,但有些工具界面里波特率是可以手动改的,如果工具默认值和你模块配置的9600不一样,收到的就是乱码。操作时先把工具里的波特率手动设置成与模块配置一致,重新断开再连接一次,让工具重新读取配置。

第二个坑是字符编码。部分手机蓝牙串口工具有“ASCII模式”和“Hex模式”的切换。单片机端发的是ASCII字符串“LED_OK”,而手机端用Hex模式显示,看到的就是“4C45445F4F4B”这种十六进制文本,语义上没错,但人看着就是乱码。调试时统一用ASCII模式显示。

第三个坑是手机APP的“发送新行”选项。PC端串口助手的这个选项很明确,手机工具则藏在设置菜单里,默认可能是关闭的。蓝牙模块透传模式下,单片机按行解析指令时,如果手机端发“LED_ON”不带换行符,单片机就一直等不到帧结束标志。这几个坑都是配置层面的问题,排查优先级应该排在代码之前。

5. 蓝牙联调避坑:5个高频翻车现场,现象、原因与解决路径

5.1 AT指令“发了个寂寞”:上电时序不对,模块根本没进入AT模式

现象:HC-05接USB转TTL工具,串口助手打开后发AT指令没有任何响应,模块指示灯要么快闪要么常亮。

原因:模块没有进入AT模式。HC-05在按键上电时才会进入AT指令模式,通电后再按按键无效。很多新手是先上电再按键,模块当然没有反应。

解决:断开电源,按住模块上的小按键不松手,然后给模块上电,看到指示灯慢闪后松手。此时再发AT指令就能收到OK响应。JTAG调试口如果占用了TXD和RXD,也要检查是否冲突。

5.2 透传全是乱码:波特率三方不匹配

现象:手机连上蓝牙模块,发送字符,单片机串口助手上收到一堆乱码符号,偶尔能识别出一两个正确字符。

原因:蓝牙模块波特率、STC单片机波特率、手机工具波特率三者不一致。常见于模块先前被人改过波特率,你不清楚具体值,按默认38400或9600发,两边就对不上。

解决:回到AT模式,发“AT+UART=9600,0,0”把模块重新设为9600,然后让单片机和手机工具都统一到9600。如果已经忘了模块里是什么设置,可以用USB转TTL工具穷举常见波特率(9600、19200、38400、57600、115200),逐个试发AT指令,找到能收到OK响应的那个值。

5.3 手机能连上但单片机收不到任何数据

现象:蓝牙模块能被手机搜索、配对、连接,指示灯常亮,但给单片机发数据没有任何反应,串口助手收不到字节。

原因:大多数情况是TXD和RXD接反了。STC发数据到蓝牙模块的RXD,蓝牙模块发数据给STC的RXD,两边各自接的是对方的TXD。自查时把杜邦线拔下来,用万用表量一下模块上丝印TXD和RXD的位置,确认和STC板上的引脚对应关系。

解决:对调TXD和RXD两根线。还有一个可能原因是GND没接,前面提过参考地不统一的问题,检查GND是否与STC的GND相连。

5.4 指示灯常亮但手机搜索不到模块

现象:模块上电后指示灯常亮,手机蓝牙列表里找不到HC-05的设备名称。

原因:模块处于已配对状态。某些HC-05版本在上电后如果处于连接状态或已跳过发现模式,是不会广播名称的;也可能是模块处于AT模式,AT模式下部分版本不会进入可发现状态。

解决:断开一切连接状态,尝试长按模块按键直到指示灯回到快闪或慢闪状态。如果还不行,把模块重新复位或重新进入AT模式后再退出,让模块重新进入可发现状态。手机端蓝牙列表也建议先清掉之前的配对记录,否则系统不会重新搜索。

5.5 通信距离只有几米,走到客厅就断连

现象:模块贴脸测试没问题,离开发板隔两道墙就频繁断连,或蓝牙连上但数据严重延迟。

原因:蓝牙模块的天线方向、所在PCB板的环境干扰、以及供电不足是三个主要因素。模块天线区域下方有金属物体或直接贴近大面积的STC开发板地铜箔,作用距离会明显缩短。供电方面,如果用STC开发板上的AMS1117之类稳压芯片供电,电流余量不够时模块发射功率会掉。

解决:把蓝牙模块的天线区域远离STC板上大面积覆铜区域,最好用排线或杜邦线把模块架空几厘米。供电尽量从5V引脚取电,不要从普通IO口偷电。必要时给模块单独加一个100uF电电容,稳定瞬态电流。

6. 进阶技巧:用回环测试验证无线链路质量,顺手给程序加一个信号灯反馈

项目做到后期,通信稳定性会变成主要矛盾。某开发者的习惯是给设备固件里加一个“心跳回环”功能:单片机收到手机端发来的“PING”后,立即回发“PONG”,手机端统计发送与收到的比例,就能量化无线链路的丢包率。这个测试做一次,能直观地暴露出蓝牙链路的问题——如果PINGPONG测试丢包率高于1%,优先排查硬件布局问题,而不是把时间花在调协议上。

unsigned char CompareLine(const char *str) { unsigned char i; for (i = 0; i < lineLen; i++) { if (lineBuf[i] != str[i]) { return 0; } } return (lineLen > 0) ? 1 : 0; } void ProcessCommand(void) { if (CompareLine("PING")) { UART1_SendString("PONG\r\n"); } else if (CompareLine("LED_ON")) { LED = 0; UART1_SendString("LED_OK\r\n"); } else if (CompareLine("LED_OFF")) { LED = 1; UART1_SendString("LED_OK\r\n"); } }

代码逻辑说明:

  • CompareLine函数把当前缓冲区的数据和参数字符串做逐字节比较,返回是否匹配。这样指令处理就变成了直观的字符串匹配,可读性比逐字节判断好很多。
  • UART1_SendString是循环发送一串字符的封装,内部逐字节调用前面的UART1_SendByte。
  • 发送“LED_OK”作为指令执行回执,手机端可以根据是否收到回执判断指令是否被执行成功,这在做APP时非常有用。

验证链路质量时,在手机端用串口工具连发100条“PING”指令,然后统计收到多少条“PONG”。对比不同摆放位置、不同天线朝向下的丢包率,把数据留下来。这个阶段积累的测试习惯,在后续做更复杂的无线项目时会直接受用。链路质量达标后,再往项目里加状态上报、OTA式固件参数下发这些功能,心里就有底了。

做到这一步,“蓝牙模块stc程序”这个组合对你来说已经不再是黑匣子:你知道模块怎么配、线怎么接、代码怎么写、问题怎么查,整个链路从手机到单片机都是自己的掌控范围内。某开发者的血泪经验是:遇到蓝牙类问题,永远先查物理连接、再查波特率、最后才动代码,这条排查顺序能省掉很多无意义的烧录次数。这套技术栈之后无论换什么单片机、换什么蓝牙模块,骨架都不会变——串口配置、帧协议、回环验证这三板斧,到哪里都通用。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询