1. 硬件准备与蓝牙模块基础配置
搞过嵌入式开发的朋友都知道,蓝牙模块在无线通信中扮演着重要角色。我最近用STM32做了个双蓝牙通信的项目,踩了不少坑,也积累了些经验。先说硬件准备,你需要两个HC-05蓝牙模块(主从一体)、一块STM32开发板(我用的是STM32F103ZET6)、USB转TTL模块,还有杜邦线若干。
蓝牙模块的供电要注意,虽然3.3V也能工作,但实测5V供电更稳定。连接时记住一个铁律:模块的RX接开发板的TX,模块的TX接开发板的RX。我第一次接反了,调试了半天才发现问题。两个蓝牙模块我都用USB转TTL先单独配置,这样出了问题好排查。
进入AT指令模式是关键:给模块上电时按住按键,直到指示灯变成慢闪(约2秒一次)。这时候波特率要设为38400,这是出厂默认值。我在串口助手发送"AT",收到"OK"回应才确认进入AT模式成功。如果没反应,检查接线和波特率设置,这个环节最容易出问题。
2. 主从模式配置实战
配置主从模式是整个项目的核心。我建议先用串口助手单独配置每个模块,成功后再接入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(注意把冒号换成逗号)。这个步骤我反复试了三次才成功,地址格式一定要严格匹配。
3. STM32多串口配置技巧
我的方案是用串口2连接主蓝牙(发送数据),串口3连接从蓝牙(接收数据)。这样设计避免了单一串口既要收又要发的冲突。硬件连接如下:
- 蓝牙1:RX-PA3, TX-PA2(USART2)
- 蓝牙2:RX-PB11, TX-PB10(USART3)
初始化代码有几个关键点:
- 时钟使能要正确:USART2挂载在APB1,GPIO在APB2;USART3也在APB1
- 中断优先级配置:我用了NVIC_PriorityGroup_2,抢占优先级和响应优先级各2位
- 波特率设置:必须和蓝牙模块配置的115200一致
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);
}
4. 数据收发与调试实战
数据收发部分我设计了两个功能:通过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); // 预分频值
5. 常见问题排查指南
在实际项目中,我总结了几个典型问题及解决方法:
-
蓝牙无法进入AT模式
- 检查按键是否在通电时按下
- 确认波特率是38400
- 尝试发送"AT"时换行符用\r\n还是\n
-
主从模块配对失败
- 确认两模块的ROLE设置正确(1主0从)
- 检查密码和波特率完全一致
- 主模块BIND地址格式是否正确(冒号变逗号)
-
STM32通信不稳定
- 检查供电是否充足(建议5V)
- 确认RX/TX线没有接反
- 测试时先降低波特率(如9600)
-
数据丢失或错乱
- 增加接收缓冲区大小
- 在中断服务函数中加入超时判断
- 检查地线是否接好
我用示波器抓取了通信波形,发现当电源噪声较大时,数据误码率明显上升。后来在蓝牙模块的VCC和GND之间加了0.1uF的去耦电容,问题就解决了。这个经验告诉我,无线通信项目不仅要关注软件配置,硬件环境同样重要。