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

avatar
作者
筋斗云
阅读量:2

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灯

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!