STM32——蓝牙模块双串口控制led

一、项目整体功能介绍

本项目实现了 STM32 与蓝牙模块 + 电脑串口 的双串口交互功能:

  1. 手机蓝牙调试助手 发送指令,通过蓝牙模块控制 STM32 上的 LED 亮灭;
  2. 电脑串口助手 可以实时接收到控制指令与反馈信息,实现调试与监控;
  3. 采用双串口架构: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. 蓝牙模块通信流程

  1. 手机蓝牙调试助手搜索并连接名为PEI的蓝牙模块,输入密码1234完成配对;
  2. 手机发送字符指令01,蓝牙模块通过 TX 引脚将数据发送到 STM32 的 USART2_RX(PA3);
  3. USART2 收到数据后触发接收中断,自动进入USART2_IRQHandler
  4. 中断服务函数读取指令,控制 LED 亮灭,并通过printf将指令和状态反馈发送到电脑串口。

2. 电脑串口通信流程

  1. 电脑串口助手发送字符指令01,通过 CH340 发送到 STM32 的 USART1_RX(PA10);
  2. USART1 收到数据后触发接收中断,自动进入USART1_IRQHandler
  3. 中断服务函数读取指令,控制 LED 亮灭,并通过printf回显数据和状态。

3. 关键说明

  • 双串口互不干扰:USART1 负责与电脑通信,USART2 负责与蓝牙模块通信,两个中断独立运行,不会互相阻塞;
  • 波特率匹配 :USART2 波特率必须与蓝牙模块最终配置的9600一致,否则会出现乱码;
  • printf 重定向:所有调试信息都通过 USART1 发送到电脑串口,方便同时监控蓝牙和电脑的控制指令。

串口配置蓝牙模块

手机端蓝牙助手发送信息

电脑端串口助手接收信息

相关推荐
xiangw@GZ4 小时前
智能锁TouchKey的抗干扰设计【2】-软件算法
嵌入式硬件
iCxhust4 小时前
微机原理实践教程(C语言篇)---A001闪烁灯
c语言·开发语言·汇编·单片机·嵌入式硬件·51单片机·微机原理
一起搞IT吧4 小时前
相机Camera日志实例分析之二十:相机Camx【照片后置4800/5000/6400万拍照】单帧流程日志详解
android·嵌入式硬件·数码相机·智能手机
笨笨饿5 小时前
69_如何给自己手搓一个串口
linux·c语言·网络·单片机·嵌入式硬件·算法·个人开发
FreakStudio9 小时前
MicroPython 内核开发者直接狂喜!这个 Claude 插件市场,把开发全流程做成了「对话式外挂」
python·单片机·嵌入式·面向对象·并行计算·电子diy
天诚智能门锁9 小时前
天诚公租房管控平台CAT.1人脸猫眼智能锁助力青神县公租房管理
人工智能·嵌入式硬件·物联网·智能家居·智能硬件
m0_3771081412 小时前
5月1日zzz
单片机
踏着七彩祥云的小丑12 小时前
嵌入式——认识电子元器件——晶体谐振器系列
单片机·嵌入式硬件