使用STM32F103C8T6与蓝牙模块HC-05连接实现手机蓝牙控制LED灯

Gitee工程地址:https://gitee.com/WYW3541/Mobile-phone-Bluetooth-control-based-on-STM32-and-HC-05icon-default.png?t=N7T8https://gitee.com/WYW3541/Mobile-phone-Bluetooth-control-based-on-STM32-and-HC-05

导言:

在现代智能家居系统中,远程控制设备变得越来越普遍和重要。本文将介绍如何利用STM32F103C8T6单片机和蓝牙模块HC-05实现远程控制LED灯的功能。通过这个简单的项目,可以学会如何将嵌入式系统与蓝牙通信技术相结合,实现远程控制的应用。

目录

导言:

准备工作:

硬件设计:

HC-05蓝牙串口模块介绍:

引脚:

手机蓝牙APP:

物理连接:

通信协议:

AT指令:

蓝牙测试软件:

​编辑

推荐资料: 

软件设计:

手机APP和蓝牙通过串口接发通信:

使用HC-05控制mcu

代码实现:

最终实现:


准备工作:

在开始之前,确保已经准备好以下材料:

  • 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引脚说明
1STATE状态引出引脚(未连接时输出低电平,连接时输出高电平)
2RXD接收端
3TXD发送端
4GND模块供电负极
5VCC模块供电正极
6EN使能端,需要进入命令模式时接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连接好后,插上电脑可以一键直接获得当前这个蓝牙模块的信息,需要的可以直接去我的资源里自取。

资源链接:

蓝牙测试软件-HC-05AT测试版

推荐资料: 

蓝牙模块(HC-05/HC-06)详解

两个蓝牙模块HC-05的主从机匹配

蓝牙模块HC-05使用指南

软件设计:

手机APP和蓝牙通过串口接发通信:

  1. 机寻找蓝牙,并填写配对码
  2. SPP蓝牙串口连接对应蓝牙
  3. 发送数据,串口接收,串口发送,手机接收

使用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 接收中断标志位
    }
}
  1. #include "stm32f10x.h":包含 STM32F10x 系列芯片的头文件。
  2. #include <stdio.h>:包含标准输入输出头文件。
  3. #include <stdarg.h>:包含可变参数列表的头文件。
  4. #include "serial.h":包含串口相关的头文件。
  5. uint8_t Serial_RxData;:定义一个无符号8位整型变量 Serial_RxData,用于存储串口接收到的数据。
  6. uint8_t Serial_RxFlag;:定义一个无符号8位整型变量 Serial_RxFlag,用于表示串口接收标志位。
  7. void Serial_Init(void):串口初始化函数。
  8. void Serial_SendByte(uint8_t Byte):发送一个字节数据到串口的函数。
  9. void Serial_SendArray(uint8_t *Array, uint16_t Length):发送一串数据到串口的函数。
  10. void Serial_SendString(char *String):发送一个字符串到串口的函数。
  11. uint32_t Serial_Pow(uint32_t X, uint32_t Y):计算 X 的 Y 次方的函数。
  12. void Serial_SendNumber(uint32_t Number, uint8_t Length):发送一个数字到串口,指定长度的函数。
  13. int fputc(int ch, FILE *f):重定向标准输出函数。
  14. void Serial_Printf(char *format, ...):格式化发送字符串到串口的函数。
  15. uint8_t Serial_GetRxFlag(void):获取串口接收标志位的函数。
  16. uint8_t Serial_GetRxData(void):获取串口接收到的数据的函数。
  17. 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灯

  • 35
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄奕子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值