一、项目整体功能介绍
本项目实现了 STM32 与蓝牙模块 + 电脑串口 的双串口交互功能:
- 手机蓝牙调试助手 发送指令,通过蓝牙模块控制 STM32 上的 LED 亮灭;
- 电脑串口助手 可以实时接收到控制指令与反馈信息,实现调试与监控;
- 采用双串口架构:USART1 负责与电脑通信,USART2 负责与蓝牙模块通信,互不干扰。
二、蓝牙模块 AT 指令配置详解
1. 配置前准备
- 使用 CH340 USB 转串口模块,将蓝牙模块的 TX 接 CH340 的 RX,RX 接 CH340 的 TX,VCC 接 5V,GND 接 GND,蓝牙模块的EN接CH340的3.3V;
- 打开串口调试助手,波特率先设为 115200(HC-05 默认波特率为 9600,部分模块出厂为 115200,按实际情况调整);
- 发送
AT,收到OK即表示模块进入 AT 模式,可进行配置。
2. 完整配置指令解析
| 指令 | 作用 | 说明 |
|---|---|---|
AT |
测试通信 | 收到 OK 表示模块正常 |
AT+UART? |
查询当前波特率 | 查看模块当前通信参数 |
AT+UART=115200,0,0 |
设置波特率 115200,无校验,1 停止位 | 临时修改,断电恢复默认 |
AT+NAME="PEI" |
设置蓝牙名称为PEI |
手机搜索蓝牙时显示的设备名 |
AT+ROLE=0 |
设置为从机模式 | 仅被动等待手机连接,适合本项目 |
AT+CMODE=1 |
任意地址连接模式 | 无需绑定特定设备,方便手机连接 |
AT+PSWD=1234 |
设置配对密码为1234 |
手机连接时需输入该密码 |
AT+UART=9600,0,0 |
最终设置波特率为 9600 | 与 STM32 的 USART2 波特率一致 |
AT+RESET |
复位模块 | 保存配置并重启生效 |
关键说明 :最后设置的波特率为
9600,所以 STM32 中与蓝牙模块通信的 USART2 必须配置为 9600 波特率,否则会出现乱码或无法通信。
三、硬件连接说明
| STM32 引脚 | 功能 | 连接对象 |
|---|---|---|
| PA9(USART1_TX) | 发送 | CH340 的 RX(电脑串口) |
| PA10(USART1_RX) | 接收 | CH340 的 TX(电脑串口) |
| PA2(USART2_TX) | 发送 | 蓝牙模块的 RX |
| PA3(USART2_RX) | 接收 | 蓝牙模块的 TX |
四、代码详解
1.main.c 主程序文件
#include "stm32f10x.h"
#include "main.h"
#include "usart.h"
#include "stdio.h"
#include "led.h"
// 软件延时函数(约1ms延时)
void delay(uint16_t time)
{
uint16_t i = 0;
while(time --)
{
i = 12000;
while(i --);
}
}
int main()
{
// 1. 配置中断分组(整个工程只配置一次,2位抢占+2位子优先级)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 2. 初始化LED引脚(PA1)
LED_Init();
// 3. 初始化USART1(与电脑串口通信,波特率115200)
my_usart_init();
// 4. 初始化USART2(与蓝牙模块通信,波特率9600)
my_usart2_init();
// 上电后向电脑串口发送一条欢迎信息
printf("hello\r\n");
// 主循环:所有逻辑交给中断处理,无需额外代码
while(1)
{
}
}
2.usart.c 双串口初始化与中断服务函数详解
① USART1 初始化(电脑串口通信)
void my_usart_init(void)
{
GPIO_InitTypeDef GPIOInitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 开启GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 配置PA9(USART1_TX)为复用推挽输出
GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_9;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIOInitStruct);
// 配置PA10(USART1_RX)为上拉输入
GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIOInitStruct);
// 配置USART1参数:波特率115200,8N1模式,收发使能
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
// 开启USART1接收中断(RXNE)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置USART1中断优先级(抢占0,子0)
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
②USART2 初始化(蓝牙模块通信)
void my_usart2_init(void)
{
GPIO_InitTypeDef GPIOInitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 开启GPIOA和USART2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// 配置PA2(USART2_TX)为复用推挽输出
GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_2;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIOInitStruct);
// 配置PA3(USART2_RX)为上拉输入
GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIOInitStruct);
// 配置USART2参数:波特率9600,与蓝牙模块一致,8N1模式,收发使能
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART2, &USART_InitStruct);
USART_Cmd(USART2, ENABLE);
// 开启USART2接收中断(RXNE)
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// 配置USART2中断优先级(抢占0,子0)
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
③通用串口发送函数
// 发送单个字节
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
{
USART_SendData(USARTx, Data);
// 等待发送完成(TXE标志置1)
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
// 发送字符串
void My_Usart_Send_String(USART_TypeDef* USARTx, char * str)
{
uint16_t i = 0;
do
{
My_Usart_Send_Byte(USARTx,*(str+i));
i++;
}while(*(str+i) != '\0');
// 等待整个字符串发送完成(TC标志置1)
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
④printf 重定向函数(输出到 USART1)
int fputc(int ch, FILE * p)
{
// 将printf输出重定向到USART1(电脑串口)
USART_SendData(USART1, (u8)ch);
// 等待发送完成
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
⑤USART1 中断服务函数(电脑串口指令控制 LED)
void USART1_IRQHandler()
{
static char str;
if(USART_GetITStatus(USART1, USART_IT_RXNE) !=RESET)
{
// 读取电脑串口发来的字符
str = USART_ReceiveData(USART1);
// 向电脑串口回显收到的数据
printf("receive data: %c \r\n",str);
// 收到'0',点亮LED(PA1低电平有效)
if(str == '0')
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
printf("led is on\r\n");
}
// 收到'1',熄灭LED
if(str == '1')
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
printf("led is off\r\n");
}
// 清除中断标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
⑥USART2 中断服务函数(蓝牙模块指令控制 LED)
void USART2_IRQHandler()
{
static char str;
if(USART_GetITStatus(USART2, USART_IT_RXNE) !=RESET)
{
// 读取蓝牙模块发来的字符
str = USART_ReceiveData(USART2);
// 向电脑串口回显收到的数据(监控蓝牙指令)
printf("receive data: %c \r\n",str);
// 收到'1',点亮LED
if(str == '1')
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
printf("led is on\r\n");
}
// 收到'0',熄灭LED
if(str == '0')
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
printf("led is off\r\n");
}
// 清除中断标志位
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
}
五、项目工作流程详解
1. 蓝牙模块通信流程
- 手机蓝牙调试助手搜索并连接名为
PEI的蓝牙模块,输入密码1234完成配对; - 手机发送字符指令
0或1,蓝牙模块通过 TX 引脚将数据发送到 STM32 的 USART2_RX(PA3); - USART2 收到数据后触发接收中断,自动进入
USART2_IRQHandler; - 中断服务函数读取指令,控制 LED 亮灭,并通过
printf将指令和状态反馈发送到电脑串口。
2. 电脑串口通信流程
- 电脑串口助手发送字符指令
0或1,通过 CH340 发送到 STM32 的 USART1_RX(PA10); - USART1 收到数据后触发接收中断,自动进入
USART1_IRQHandler; - 中断服务函数读取指令,控制 LED 亮灭,并通过
printf回显数据和状态。
3. 关键说明
- 双串口互不干扰:USART1 负责与电脑通信,USART2 负责与蓝牙模块通信,两个中断独立运行,不会互相阻塞;
- 波特率匹配 :USART2 波特率必须与蓝牙模块最终配置的
9600一致,否则会出现乱码; - printf 重定向:所有调试信息都通过 USART1 发送到电脑串口,方便同时监控蓝牙和电脑的控制指令。
串口配置蓝牙模块

手机端蓝牙助手发送信息

电脑端串口助手接收信息
