搞过嵌入式开发的朋友都知道,蓝牙模块在无线通信中扮演着重要角色。我最近用STM32做了个双蓝牙通信的项目,踩了不少坑,也积累了些经验。先说硬件准备,你需要两个HC-05蓝牙模块(主从一体)、一块STM32开发板(我用的是STM32F103ZET6)、USB转TTL模块,还有杜邦线若干。
蓝牙模块的供电要注意,虽然3.3V也能工作,但实测5V供电更稳定。连接时记住一个铁律:模块的RX接开发板的TX,模块的TX接开发板的RX。我第一次接反了,调试了半天才发现问题。两个蓝牙模块我都用USB转TTL先单独配置,这样出了问题好排查。
进入AT指令模式是关键:给模块上电时按住按键,直到指示灯变成慢闪(约2秒一次)。这时候波特率要设为38400,这是出厂默认值。我在串口助手发送"AT",收到"OK"回应才确认进入AT模式成功。如果没反应,检查接线和波特率设置,这个环节最容易出问题。
配置主从模式是整个项目的核心。我建议先用串口助手单独配置每个模块,成功后再接入STM32。主模块配置如下:
AT+ORGL恢复出厂设置AT+ROLE=1设置为主模式AT+PSWD="1234"设置配对密码AT+UART=115200,0,0设置通信波特率从模块配置类似,只是角色设为从模式(AT+ROLE=0)。这里有个细节:主从模块的波特率和密码必须完全一致。我第一次测试时主模块用了115200波特率,从模块用的9600,结果死活连不上。
配置完成后,需要绑定从模块地址。先用AT+ADDR?查询从模块地址,返回格式类似"98d3:31:3006c0"。在主模块上发送AT+BIND=98d3,31,3006c0(注意把冒号换成逗号)。这个步骤我反复试了三次才成功,地址格式一定要严格匹配。
我的方案是用串口2连接主蓝牙(发送数据),串口3连接从蓝牙(接收数据)。这样设计避免了单一串口既要收又要发的冲突。硬件连接如下:
初始化代码有几个关键点:
c复制// USART2初始化示例
void USART_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// 配置TX(PA2)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置RX(PA3)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 串口参数配置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
}
数据收发部分我设计了两个功能:通过USART2主动发送数据,通过USART3中断接收数据。发送函数比较简单,就是遍历数组发送每个字节:
c复制void USART2_SENDDATA(u8 *data, u16 size){
for(u16 i=0; i<size; i++){
USART_SendData(USART2, data[i]);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET);
LCD_ShowNum(30,70+i*15,data[i],2,16); // 在LCD显示发送数据
}
}
接收端用了中断方式,这是更实用的方案。我在USART3的中断服务函数中实现了数据接收:
c复制u8 USART3_RX_BUF[20]; // 接收缓冲区
uint8_t len=0; // 接收计数
void USART3_IRQHandler(void){
if(USART_GetITStatus(USART3, USART_IT_RXNE)){
USART3_RX_BUF[len++] = USART_ReceiveData(USART3);
if(len>=10){ // 假设每次接收10字节
for(int i=0; i<10; i++){
LCD_ShowNum(100,70+i*15,USART3_RX_BUF[i],2,16);
}
len=0; // 重置计数器
}
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
调试时我遇到了数据错位的问题,后来发现是中断服务函数里没及时清除中断标志。另一个坑是波特率误差,长时间通信会出现累积误差导致数据错误。解决方法是在USART初始化后加入波特率校准:
c复制// 波特率校准
USART_OverSampling8Cmd(USART2, DISABLE); // 16倍过采样
USART_SetPrescaler(USART2, 1); // 预分频值
在实际项目中,我总结了几个典型问题及解决方法:
蓝牙无法进入AT模式
主从模块配对失败
STM32通信不稳定
数据丢失或错乱
我用示波器抓取了通信波形,发现当电源噪声较大时,数据误码率明显上升。后来在蓝牙模块的VCC和GND之间加了0.1uF的去耦电容,问题就解决了。这个经验告诉我,无线通信项目不仅要关注软件配置,硬件环境同样重要。