首页 > 基础资料 博客日记
使用STM32F103C8T6与蓝牙模块HC-05连接实现手机蓝牙控制LED灯
2024-08-11 00:00:08基础资料围观217次
导言:
在现代智能家居系统中,远程控制设备变得越来越普遍和重要。本文将介绍如何利用STM32F103C8T6单片机和蓝牙模块HC-05实现远程控制LED灯的功能。通过这个简单的项目,可以学会如何将嵌入式系统与蓝牙通信技术相结合,实现远程控制的应用。
目录
准备工作:
在开始之前,确保已经准备好以下材料:
- STM32F103C8T6开发板
- HC-05蓝牙模块
- LED灯
- 杜邦线等连接线
- USB转串口模块(用于调试)
硬件设计:
本次设计使用HC-05(JDY-31)无线蓝牙模块实现单片机和手机的无线通信,将其正常工作的频段2.4 GHz ISM,GFSK作为它的调制方式。主控芯片STM32F103 C8T6单片机通过串口连接HC-05,安卓手机端自带蓝牙,通过手机App与单片机蓝牙设备建立配对,蓝牙模块将接收的数据传送给单片机,单片机处理后控制电机的运转和桶盖的开关。工作原理如图3所示。
图3蓝牙工作原理
HC-05蓝牙串口模块介绍:
HC-05是一款常用的蓝牙串口模块,用于在微控制器和其他设备之间建立蓝牙串口通信连接。下面我将详细介绍HC-05蓝牙模块的接口设计,以便将其与STM32F103C8T6微控制器进行通信。
HC-05蓝牙串口模块
引脚:
标号 | PIN | 引脚说明 |
---|---|---|
1 | STATE | 状态引出引脚(未连接时输出低电平,连接时输出高电平) |
2 | RXD | 接收端 |
3 | TXD | 发送端 |
4 | GND | 模块供电负极 |
5 | VCC | 模块供电正极 |
6 | EN | 使能端,需要进入命令模式时接3.3V |
注:或者也可以直接去优信买JDY-31模块,相比HC05更加便宜,使用也没什么区别并且资料全套。
手机蓝牙APP:
这几个都可以,手机应用商店直接搜索就行。
物理连接:
电源供应:HC-05通常需要3.3V电源供应。你可以使用STM32F103C8T6的一块3.3V输出引脚连接到HC-05的VCC引脚,或者使用一个3.3V的稳压芯片。
串口通信:HC-05通过串口与STM32通信。它包含了TX(发送)和RX(接收)引脚,分别用于发送和接收数据。你可以将HC-05的TX引脚连接到STM32的一个USART接收引脚(比如USART1的RX(PA10)引脚),并将HC-05的RX引脚连接到STM32的一个USART发送引脚(比如USART1的TX(PA9)引脚)。
接地:HC-05的GND与STM32的GND相连接
通信协议:
波特率设置:HC-05支持多种波特率,通常默认波特率为9600bps。你可以通过AT指令将其更改为其他波特率,以便与STM32的USART通信波特率匹配。
数据格式:通常情况下,HC-05使用8位数据位、无校验位和1位停止位的数据格式。
AT指令:
HC-05也可以使用AT指令进行配置。在配置之前,首先需要让模块进入配置模式。一般HC-05有一个小按钮。按住小按钮再给蓝牙模块上电,蓝牙模块进入配置模式,此时模块上自带的LED会慢速闪烁。进入配置模式后,就可以用AT指令来配置我们的HC-05了。配置时,用USB转TTL连接HC-05,用串口调试助手发送AT指令进行配置。需要注意的是,HC-05配置模式的波特率固定为38400,如果你给HC-05发送指令,没有收到回复,记得检查一下串口调试助手的波特率是否正确。下面列举一些配置时常用的AT指令
AT
检查HC-05模块连接是否正常,HC-05收到后会回复“OK”
AT+NAME=名字
配置HC-05的名字,配置成功后会返回“OK”
AT+NAME?
询问HC-05的名字。发送后会收到“+NAME:“名字””,换行加“OK”
AT+PSWD=密码
配置HC-05密码,配对时需要用到。配置成功后,会收到“OK”
AT+PSWD?
询问HC-05配对密码。发送后会收到“+PSWD:991102”,换行加“OK”
AT+UART=波特率,停止位,校验
设置HC-05的波特率,其中停止位0表示一位停止位,为1表示两位停止位。校验位为0表示无校验,为1表示奇校验,为2表示偶校验。比如设置115200的波特率,一位停止位,无校验。发送“AT+UART=115200,0,0”即可。配置成功后会返回“OK”
AT+UART?
询问HC-05波特率。发送后会收到“+UART:波特率,停止位,校验”,换行加“OK”
配置完成功后,断电重新上电,HC-05按照配置好的名字,配对密码和波特率开始工作。此时LED快闪。
蓝牙测试软件:
推荐一个特别好用的蓝牙测试软件,将蓝牙模块与HC05连接好后,插上电脑可以一键直接获得当前这个蓝牙模块的信息,需要的可以直接去我的资源里自取。
资源链接:
推荐资料:
软件设计:
手机APP和蓝牙通过串口接发通信:
- 机寻找蓝牙,并填写配对码
- SPP蓝牙串口连接对应蓝牙
- 发送数据,串口接收,串口发送,手机接收
使用HC-05控制mcu
stm32f103c8t6自带一个led灯,使用PC13引脚就行了,
切记尽量避免使用PB3、PB4,因为PB3和PB4在默认情况下是做JTAG调试用的。如果需要将其当普通GPIO使用,需要关闭JTAG调试功能,否则会发现普通的GPIO初始化程序无法正常使用PB3和PB4
代码实现:
led.c
#include "stm32f10x.h" // Device header
#include "LED.h" // Device header
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PA,PD端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PA.8 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.8
GPIO_ResetBits(GPIOA,GPIO_Pin_8); //PA.8 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED1-->PD.2 端口配置, 推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOB,GPIO_Pin_8); //PD.2 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //LED1-->PD.2 端口配置, 推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOB,GPIO_Pin_9); //PD.2 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //LED1-->PD.2 端口配置, 推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOC,GPIO_Pin_13); //PD.2 输出高
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h" // Device header
#define LED0 PCout(13) // PA8
#define LED1 PBout(8) // PB8
#define LED2 PBout(9) // PB9
#define BUZ PAout(8) // PA8
void LED_Init(void);//初始化
#endif
usart1.c
#include "stm32f10x.h" // 包含 STM32F10x 系列芯片的头文件
#include <stdio.h> // 包含标准输入输出头文件
#include <stdarg.h> // 包含可变参数列表的头文件
#include "serial.h" // 包含串口相关的头文件
uint8_t Serial_RxData; // 定义一个无符号8位整型变量 `Serial_RxData`,用于存储串口接收到的数据
uint8_t Serial_RxFlag; // 定义一个无符号8位整型变量 `Serial_RxFlag`,用于表示串口接收标志位
// 串口初始化函数
void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能 USART1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能 GPIOA 时钟
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个 GPIO 初始化结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // GPIO 模式为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // GPIO 引脚为 PA9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO 速度为 50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 GPIOA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // GPIO 模式为上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // GPIO 引脚为 PA10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO 速度为 50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 GPIOA
USART_InitTypeDef USART_InitStructure; // 定义一个 USART 初始化结构体变量
USART_InitStructure.USART_BaudRate = 9600; // 波特率为 9600
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发模式
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 1 个停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8 位数据位
USART_Init(USART1, &USART_InitStructure); // 初始化 USART1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能 USART1 接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置 NVIC 中断优先级
NVIC_InitTypeDef NVIC_InitStructure; // 定义一个 NVIC 初始化结构体变量
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // USART1 中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级为 1
NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC
USART_Cmd(USART1, ENABLE); // 使能 USART1
}
// 发送一个字节数据到串口
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); // 发送数据到 USART1
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送完成
}
// 发送一串数据到串口
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i++) // 遍历数组
{
Serial_SendByte(Array[i]); // 逐个发送数组中的数据
}
}
// 发送一个字符串到串口
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++) // 遍历字符串
{
Serial_SendByte(String[i]); // 逐个发送字符串中的字符
}
}
// 计算 X 的 Y 次方
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--) // Y 次方计算
{
Result *= X;
}
return Result;
}
// 发送一个数字到串口,指定长度
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++) // 遍历指定长度
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0'); // 发送数字的每一位
}
}
// 重定向标准输出函数
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch); // 发送字符到串口
return ch;
}
// 格式化发送字符串到串口
void Serial_Printf(char *format, ...)
{
char String[100]; // 定义一个字符数组用于存储格式化后的字符串
va_list arg;
va_start(arg, format); // 初始化可变参数列表
vsprintf(String, format, arg); // 格式化字符串
va_end(arg); // 结束可变参数列表
Serial_SendString(String); // 发送格式化后的字符串到串口
}
// 获取串口接收标志位
uint8_t Serial_GetRxFlag(void)
{
if (Serial_RxFlag == 1) // 如果串口接收标志位为 1
{
Serial_RxFlag = 0; // 清零串口接收标志位
return 1; // 返回 1
}
return 0; // 否则返回 0
}
// 获取串口接收到的数据
uint8_t Serial_GetRxData(void)
{
return Serial_RxData; // 返回串口接收到的数据
}
// USART1 中断处理函数
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) // 如果 USART1 接收中断标志位为 SET
{
Serial_RxData = USART_ReceiveData(USART1); // 获取接收到的数据
Serial_SendByte(Serial_RxData); // 发送接收到的数据到串口
Serial_RxFlag = 1; // 置位串口接收标志位
USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除 USART1 接收中断标志位
}
}
#include "stm32f10x.h"
:包含 STM32F10x 系列芯片的头文件。#include <stdio.h>
:包含标准输入输出头文件。#include <stdarg.h>
:包含可变参数列表的头文件。#include "serial.h"
:包含串口相关的头文件。uint8_t Serial_RxData;
:定义一个无符号8位整型变量Serial_RxData
,用于存储串口接收到的数据。uint8_t Serial_RxFlag;
:定义一个无符号8位整型变量Serial_RxFlag
,用于表示串口接收标志位。void Serial_Init(void)
:串口初始化函数。void Serial_SendByte(uint8_t Byte)
:发送一个字节数据到串口的函数。void Serial_SendArray(uint8_t *Array, uint16_t Length)
:发送一串数据到串口的函数。void Serial_SendString(char *String)
:发送一个字符串到串口的函数。uint32_t Serial_Pow(uint32_t X, uint32_t Y)
:计算 X 的 Y 次方的函数。void Serial_SendNumber(uint32_t Number, uint8_t Length)
:发送一个数字到串口,指定长度的函数。int fputc(int ch, FILE *f)
:重定向标准输出函数。void Serial_Printf(char *format, ...)
:格式化发送字符串到串口的函数。uint8_t Serial_GetRxFlag(void)
:获取串口接收标志位的函数。uint8_t Serial_GetRxData(void)
:获取串口接收到的数据的函数。void USART1_IRQHandler(void)
:USART1 中断处理函数。
usart2.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
#include "LED.h" // Device header
#include "sys.h" // Device header
#include "HC05.h" // Device header
#include "Delay.h"
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
#endif
HC05.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "HC05.h" // Device header
uint8_t RxSTA = 1; // 定义串口接收状态变量,初始为1,表示准备接收数据
char RxData[100] = "None"; // 定义接收数据缓冲区,初始值为"None"
void HC05_GetData(char *Buf)
{
uint32_t count = 0, a = 0; // 定义计数器和数据索引变量
while (count < 10000) // 进入循环等待接收数据
{
if (Serial_GetRxFlag() == 1) // 如果串口接收标志为1,表示有数据接收到
{
Buf[a] = Serial_GetRxData(); // 将接收到的数据存储在缓冲区Buf中
a ++; // 数据索引自增
count = 0; // 重置计数器
RxSTA = 0; // 将接收状态置为0,表示接收到数据
}
count ++; // 计数器自增
}
}
void HC05_Init()
{
Serial_Init(); // 初始化串口通信
}
void HC05_EnterAT()
{
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 将GPIOA的第0引脚置为高电平,进入AT模式
}
void HC05_ExitAT()
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 将GPIOA的第0引脚置为低电平,退出AT模式
}
void HC05_SendString(char *Buf)
{
Serial_Printf(Buf); // 向HC05模块发送字符串
}
void HC05_proc()
{
HC05_GetData(RxData); // 获取HC05模块接收到的数据
if (RxSTA == 0) // 如果接收状态为0,表示接收到了数据
{
OLED_Clear(); // 清空OLED显示屏
OLED_ShowString(1, 1, "RxData:"); // 在OLED上显示"RxData:"
OLED_ShowString(2, 1, RxData); // 在OLED上显示接收到的数据
if(strstr((const char*)RxData, "led on") != 0) // 如果接收到的数据包含"led on"
{
LED0 = 0; // 控制LED0点亮
LED1 = 0; // 控制LED1点亮
BUZ=1; // 控制蜂鸣器响
Delay_ms(100); // 延时100毫秒
BUZ=0; // 关闭蜂鸣器
}
if(strstr((const char*)RxData, "led off") != 0) // 如果接收到的数据包含"led off"
{
LED0 = 1; // 控制LED0熄灭
LED1 = 1; // 控制LED1熄灭
BUZ=1; // 控制蜂鸣器响
Delay_ms(100); // 延时100毫秒
BUZ=0; // 关闭蜂鸣器
}
memset(RxData,0,100); // 清空接收数据缓冲区
RxSTA = 1; // 将接收状态置为1,表示准备接收新的数据
}
}
HC05.h
#ifndef __HC05_H
#define __HC05_H
#include "LED.h" // Device header
#include "sys.h" // Device header
#include "OLED.h"
#include "string.h" // Device header
extern char RxData[100];
extern uint8_t RxSTA;
void HC05_Init();
void HC05_EnterAT();
void HC05_ExitAT();
void HC05_SendString(char *Buf);
void HC05_GetData(char *Buf);
void HC05_proc(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "HC05.h"
#include "sys.h" // Device header
#include "serial.h" // Device header
#include "string.h" // Device header
#include "ESP01.h"
#include "usart2.h"
int main(void)
{
OLED_Init(); // 初始化OLED显示屏
LED_Init(); // 初始化LED指示灯
HC05_Init(); // 初始化HC05蓝牙模块
// uart2_init(115200); //串口初始化为115200
// ESP01_Init(); // 初始化ESP01模块
OLED_ShowString(1, 1, "RxData:"); // 在OLED上显示"RxData:"
OLED_ShowString(2, 1, RxData); // 在OLED上显示RxData变量内容
BUZ=1; // 控制蜂鸣器响
Delay_ms(100); // 延时100毫秒
BUZ=0; // 关闭蜂鸣器
while (1)
{
HC05_proc(); // 处理HC05模块接收到的数据
// ESP01_proc(); // 处理ESP01模块接收到的数据
}
}
最终实现:
在APP中发送字符串“led on”可以打开LED灯,发送字符串“led off”可以关闭LED灯
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: