简介:正点原子ATK-HC05蓝牙模块资料,面向STM32嵌入式开发者和物联网项目工程师,系统梳理了基于Bluetooth V2.0+EDR的串口蓝牙模块的完整使用路径。资源以压缩包形式提供,约33.2MB,文件目录未在下载页展示;不过从已有说明看,内容涵盖模块硬件设计原理图、AT命令集、STM32串口初始化及收发示例、设备发现/配对/连接流程,以及常见问题排查指南,兼顾理论讲解与项目实操。已有2058人浏览学习,适合需要在中低速无线通信场景下快速集成蓝牙功能的开发者。资料在介绍UART/SPI/I2C接口接法的同时,还会指导如何通过AT指令配置工作模式、配对和通信速率,帮助读者理解蓝牙底层交互机制,从而在智能家居、工业控制、便携设备等项目中完成从硬件连线到无线数据传输的全流程搭建。资料将AT指令配置、串口通信与连接管理串联起来,形成从入门到上手的系统参考。
1. 为什么STM32项目里还在用正点原子ATK-HC05这类串口蓝牙模块
手头有个基于STM32F103的小型数据采集设备,当初选无线方案时,团队里有人提议直接用BLE模组,理由是协议新、功耗低。后来评估了一圈,还是把正点原子ATK-HC05放了上去。原因不复杂:这类基于Bluetooth 2.0 + EDR的串口蓝牙模块走的是SPP(Serial Port Profile)协议,对MCU来说它就是一根无线的串口线,没有协议栈移植成本,也没有GATT服务开发的负担。正点原子资料里把原理图、AT命令集、STM32标准库示例代码、排错指南都铺全了,对要快速交付或做毕设、课程设计的场景来说,这是最短路径。适合谁?手里有STM32开发板、要接手机蓝牙调试助手做无线透传、又不想碰BLE协议栈的工程师。
2. 硬件链路:电源、电平与接线——ATK-HC05和STM32的物理层细节
ATK-HC05核心是CSR BC417方案,Bluetooth 2.0 + EDR,SPP协议,这些参数决定了它的硬件设计思路和接线方式。很多人第一次拿到模块,直接按“蓝牙串口”四个字把四根线一连,能通,但后面调试AT命令、出现乱码、连不上手机时,问题往往不在代码,而在物理层。
2.1 供电与电源噪声
资料里的原理图显示模块板载3.3V稳压,VCC允许3.6V到6V输入,所以可以直接从STM32板的5V引脚取电,不要用3.3V引脚去喂它。模块射频发射时电流峰值可以到40mA以上,如果稳压前级是从USB口拉的3.3V,会把MCU的供电一起拉垮。我一般会在VCC和GND之间并联一个10uF电解电容,靠近模块引脚放置,避免射频突发电流在长线上造成压降。
正点原子ATK-HC05常见是模块加底板的套装,底板上有USB转串口和按键,这种版本直接用USB供电调试AT命令很方便。但注意:接到STM32时不要同时插USB线和接单片机串口,两个发送端会在同一根线上打架,轻则收不到数据,重则烧坏IO口。
2.2 电平匹配与引脚定义
STM32的IO是3.3V,模块的串口逻辑电平也是3.3V,TXD和RXD可以直连。但如果手头模块是别人给的、或带电平转换芯片的底板版本,要先确认底板上的转换方向。有些二手模块是5V电平版本,直连STM32会把IO打坏,上电前用万用表量一下模块TXD的空闲电平,3.3V是安全的,5V就要加电阻分压。
典型的接线方式如下,我一般挂在USART3上,把USART1留给调试打印:
| STM32引脚 | ATK-HC05引脚 | 说明 |
|---|---|---|
| PB10 (USART3_TX) | RXD | MCU发、模块收,交叉连接 |
| PB11 (USART3_RX) | TXD | 模块发、MCU收 |
| 5V | VCC | 模块输入电源,3.6-6V |
| GND | GND | 必须共地,否则逻辑电平无参考 |
| PA4 | KEY | 拉高进入AT命令模式,平时拉低 |
| PB1 | STATE | 连接状态输出,空闲低电平、连接高电平 |
代码里把引脚定义写成宏,方便换板子时只动一处:
#define HC05_USART USART3 #define HC05_USART_CLK RCC_APB1Periph_USART3 #define HC05_GPIO_CLK RCC_APB2Periph_GPIOB #define HC05_TX_PIN GPIO_Pin_10 #define HC05_RX_PIN GPIO_Pin_11 #define HC05_KEY_GPIO_CLK RCC_APB2Periph_GPIOA #define HC05_KEY_PORT GPIOA #define HC05_KEY_PIN GPIO_Pin_4 #define HC05_STATE_GPIO_CLK RCC_APB2Periph_GPIOB #define HC05_STATE_PORT GPIOB #define HC05_STATE_PIN GPIO_Pin_1引脚宏定好后,初始化GPIO时把TX配成复用推挽输出、RX配成浮空输入,KEY配成推挽输出并默认拉低。这里有个很多人踩过的坑:如果之前用底板调试过AT命令,模块里可能已经被改成了非默认波特率,首次接到STM32时先按模块当前波特率通信,或者干脆通过底板恢复出厂设置,避免第一轮就怀疑代码有问题。
2.3 PCB布线与天线处理
洞洞板或PCB布线时,天线区域下方不要走长平行线,尽量远离晶振、电感和DC-DC电感。模块天线端垂直悬空朝外,不要贴在外壳金属面或大块覆铜上,否则蓝牙信号会被吸走,表现为传输距离骤降、偶尔断连。资料里的PCB布局说明对这些有明确指引,实际项目中影响的是连接稳定性和误码率。
3. AT命令实操:进入AT模式、参数固化与常见失败原因
AT命令是控制HC05的主要手段。资料里给了完整的命令集,但实际调试时大部分人卡在“发AT没反应”这一步,而不是命令本身。先把进入AT模式的条件说清楚。
3.1 两种工作模式与进入方法
HC05有两个工作状态:数据透传状态和AT命令状态。上电后默认是透传状态,此时串口收到的数据直接走蓝牙发出去;要让模块识别AT命令,必须先把KEY引脚拉高再上电,或者在上电后、未连接蓝牙时给KEY一个高电平脉冲。正点原子底板上有独立按键,按住再上电就是AT模式。判断模式的简单方法是看LED闪法:慢闪(约2秒一次)表示在AT模式等待命令,连上手机后变成快闪,这个现象可以用来快速确认当前状态。
进入AT模式后,发一条AT\r\n,正常会回OK。如果没有任何回显,按这个顺序排查:串口波特率是否和模块当前一致(模块默认9600,老版本常见38400);KEY是否确实拉高;发送时有没有带回车换行,很多串口助手默认不带换行,导致命令没有被解析;模块是否处于已连接状态,连接状态下不会响应AT命令。
3.2 高频命令表与参数说明
下面这张表是正点原子资料里最常用到的命令,按实际使用频率排了序:
| 命令 | 参数示例 | 作用 |
|---|---|---|
AT | 无 | 测试通信,返回OK |
AT+NAME | AT+NAME=HC05-X | 修改模块名称,手机搜索时显示 |
AT+ROLE | AT+ROLE=1 | 设置主从角色,0从机、1主机 |
AT+PSWD | AT+PSWD=8888 | 设置配对密码,默认1234 |
AT+UART | AT+UART=115200,0,0 | 设置串口波特率、停止位、校验位 |
AT+CMODE | AT+CMODE=0 | 配对模式,0指定地址、1任意地址 |
AT+BIND | AT+BIND=0014,03,050607 | 主机模式下绑定从机地址 |
AT+RMAAD | 无 | 清除配对记录 |
AT+RESET | 无 | 软复位,参数生效 |
注意AT+ROLE和AT+BIND这一组。默认模块是从机模式,手机直接搜名字就能连,这是大多数场景的用法。如果要做两个模块之间的串口透传,比如STM32采集端和数据接收端各挂一个HC05,就必须一个设主机、一个设从机,主机绑定从机地址,两边波特率和密码要一致。AT+BIND的地址格式和手机蓝牙地址显示格式不一样,要把冒号换成分号和逗号:地址00:14:03:05:06:07写成0014,03,050607,中间两位一组反着写,这个格式问题卡住过不少人。
3.3 波特率修改与回包解析
修改波特率的命令要特别小心:AT+UART=115200,0,0执行完并复位后,模块立刻按新波特率工作,如果此时调试助手没同步改成115200,之后就再也收不到任何响应,看起来就像模块变砖了。恢复办法是短接模块上的复位电路或者重新上电,并在上电后立刻用旧波特率发AT测试。我一般开发阶段保持9600不动,等整个链路调通后再统一改。
给一段标准的AT命令发送代码,基于第2章的USART3配置:
void HC05_SendATCmd(char *cmd) { printf("AT> %s", cmd); while (*cmd) { while (USART_GetFlagStatus(HC05_USART, USART_FLAG_TXE) == RESET); USART_SendData(HC05_USART, (uint8_t)(*cmd++)); } // 等待最后一个字节发送完成 while (USART_GetFlagStatus(HC05_USART, USART_FLAG_TC) == RESET); } // 使用示例 HC05_SendATCmd("AT\r\n"); // 测试通信 HC05_SendATCmd("AT+ROLE=0\r\n"); // 设为从机 HC05_SendATCmd("AT+PSWD=8888\r\n");这段代码只负责发送,不处理返回值。实际项目里应该在后面串一个500ms超时等待,把回包收下来判断是否包含“OK”,再决定下一步执行什么命令。回包解析要按行做,HC05一次可能回多个CRLF分隔的字符串,不要用简单的strstr去匹配整个缓冲区。
另一个容易被忽略的点是AT+UART的最后两个参数:停止位和校验位。绝大多数情况下填0,0,但如果你的STM32串口初始化里配了奇偶校验或2位停止位,这里必须和MCU侧完全一致,否则会出现“能连上但数据全乱”的现象。这种问题用示波器看串口波形最直观,没有示波器就把模块接回底板用USB串口助手验证。
提示:绝大多数“hc06蓝牙模块at无响应”的问题,也适用于这里。HC06和HC05的AT指令集有差异,HC06不支持主从切换和绑定,如果发HC05的命令给HC06,自然不会有响应。
4. STM32串口驱动与收发状态机:从标准库到中断处理
STM32和ATK-HC05之间本质是串口通信,核心工作是三件事:初始化串口、发送数据、接收数据。正点原子资料里给了标准库的示例工程,下面这套逻辑是类似项目里常用的精简写法,删掉了调试打印和LED闪烁干扰,方便移植。
4.1 串口初始化与中断配置
USART3挂载在APB1总线上,波特率默认9600,8位数据、无校验、1位停止位,和HC05出厂参数对齐:
void HC05_UART_Init(uint32_t baudrate) { GPIO_InitTypeDef gpio; USART_InitTypeDef usart; NVIC_InitTypeDef nvic; RCC_APB2PeriphClockCmd(HC05_GPIO_CLK, ENABLE); RCC_APB1PeriphClockCmd(HC05_USART_CLK, ENABLE); // TX: PB10 复用推挽输出 gpio.GPIO_Pin = HC05_TX_PIN; gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(HC05_GPIO_PORT, &gpio); // RX: PB11 浮空输入 gpio.GPIO_Pin = HC05_RX_PIN; gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(HC05_GPIO_PORT, &gpio); usart.USART_BaudRate = baudrate; usart.USART_WordLength = USART_WordLength_8b; usart.USART_StopBits = USART_StopBits_1; usart.USART_Parity = USART_Parity_No; usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None; usart.USART_Mode = USART_Mode_RX | USART_Mode_TX; USART_Init(HC05_USART, &usart); nvic.NVIC_IRQChannel = USART3_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 2; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); USART_ITConfig(HC05_USART, USART_IT_RXNE, ENABLE); USART_Cmd(HC05_USART, ENABLE); }这段初始化把TX配成复用推挽、RX配成浮空输入,这是STM32串口最常用的GPIO配置。中断优先级设成2,给主程序和其他外设留出调整空间。收到AT命令返回时,把回包打印出来。
4.2 接收中断与环形缓冲
接收数据用中断方式,不要在main函数里轮询RXNE标志,否则蓝牙数据包稍微密集一点就会丢字节。中断服务函数只做一件事:把收到的字节放进环形缓冲,由主循环取走解析:
#define RX_BUF_SIZE 256 static uint8_t rx_buf[RX_BUF_SIZE]; static volatile uint16_t rx_head = 0; static volatile uint16_t rx_tail = 0; void USART3_IRQHandler(void) { if (USART_GetITStatus(HC05_USART, USART_IT_RXNE) != RESET) { uint8_t byte = USART_ReceiveData(HC05_USART); uint16_t next = (rx_head + 1) % RX_BUF_SIZE; if (next != rx_tail) { rx_buf[rx_head] = byte; rx_head = next; } // 如果缓冲区满,直接丢弃新字节,保留未读数据 } }环形缓冲的好处是中断开销极小,主循环可以等空闲时一次性处理完一批数据,不会因为处理逻辑太慢导致后续字节丢失。缓冲区大小256字节对HC05的串口速率来说够用,如果单包数据超过200字节,可以考虑改成1024。
4.3 数据发送的两种方式
发送数据有两种做法。简单场景用阻塞发送,适合小数据量:
void HC05_SendData(uint8_t *buf, uint16_t len) { for (uint16_t i = 0; i < len; i++) { while (USART_GetFlagStatus(HC05_USART, USART_FLAG_TXE) == RESET); USART_SendData(HC05_USART, buf[i]); } while (USART_GetFlagStatus(HC05_USART, USART_FLAG_TC) == RESET); }阻塞发送会卡住CPU,如果主循环里还有其他实时任务,比如PID控制、LED扫描,就要改成中断发送或DMA发送。用DMA时,把串口TX配置成DMA请求源,发送前关掉对应DMA通道的EN位、填充内存地址和长度、再重新打开,原子性比中断逐字节发送好。
主循环里的处理逻辑一般是先查环形缓冲有没有完整的一行数据,有就按AT命令回复格式解析,或者打包进应用层协议。HC05透传模式下收到的数据没有边界,要自己在协议层定义帧头和长度,不要指望蓝牙帮你分包。
5. 掉线重连与STATE引脚检测:HC05在STM32项目里的可靠性收尾
HC05在实验室环境很好用,一旦做成设备放到现场,最头疼的是“手机离开蓝牙范围再回来,连不上了”。这一章讲项目交付前一定要做的处理:利用STATE引脚做链路检测,配合自动重连机制,把模块从“能通”用到“可交付”。
5.1 STATE引脚轮询检测
连接状态用STATE引脚判断。模块空闲时输出低电平,与手机建立连接后拉高,断开后恢复低。轮询周期建议100ms到200ms,不要在主循环里不加延时地死读:
uint8_t link_connected = 0; uint32_t last_check = 0; void HC05_LinkCheckTask(void) { if (millis() - last_check < 100) return; last_check = millis(); if (GPIO_ReadInputDataBit(HC05_STATE_PORT, HC05_STATE_PIN) == Bit_SET) { if (!link_connected) { printf("[HC05] link established\r\n"); link_connected = 1; } } else { if (link_connected) { printf("[HC05] link lost\r\n"); link_connected = 0; } } }把状态变化打印出来,方便串口调试时观察连接和断开的准确时间点。注意到一个问题:手机蓝牙断开后STATE引脚不会立刻拉低,实测有几百毫秒到一秒的延迟,所以不要用它做精确的实时链路判断,只做状态监测和重连触发。
5.2 断线后的自动重连逻辑
断线后的处理要分情况。如果模块本身还活着,只是蓝牙链路断了,HC05在从机模式下会重新进入可发现状态,手机再次配对即可。如果是被手机主动取消配对,模块会记住旧配对信息,此时需要发AT+RMAAD清除后重新配对。
更稳妥的做法是在主循环里做超时管理:
uint8_t link_connected = 0; uint32_t last_check = 0; void HC05_LinkCheckTask(void) { if (millis() - last_check < 100) return; last_check = millis(); if (GPIO_ReadInputDataBit(HC05_STATE_PORT, HC05_STATE_PIN) == Bit_SET) { if (!link_connected) { printf("[HC05] link established\r\n"); link_connected = 1; } } else { if (link_connected) { printf("[HC05] link lost\r\n"); link_connected = 0; } } }这个状态机只做一件事情:在无连接状态持续一段时间后触发一次AT+RESET。复位后模块重新上电初始化,比反复尝试发送数据更干净,而且能清掉偶发的内部死锁状态。断线后盲目往串口写数据,模块在无连接状态下不会把数据发出去,反而可能造成缓冲区堆积。
最后一个实用技巧:利用STATE引脚做低功耗唤醒。设备平时让HC05处于非连接状态,只有手机靠近并连上后STATE拉高,MCU才从停止模式唤醒,开始采集和发送数据。这样一来,蓝牙模块不连的时候MCU不用空转,设备整体功耗可以压到毫安级,比单纯靠透传数据判断连接状态省电得多。
本文还有配套的精品资源,点击获取