基于STM32的HC-05蓝牙模块使用及常见问题(STM32-手机-Arduino使用HC-05进行蓝牙通讯)
最近在学习STM32开发板,在使用HC-05蓝牙模块时遇到了很多问题,没有驱动,串口通讯异常等等,在此期间借鉴了大量CSDN博主的文章,作为回报,我将最终可以正常运行的程序,配置方法以及可能出现的问题分享给大家。大家有问题也可以留言,尽量回复!
本文分为三个模块:
- Arduino与HC-05连接(与手机进行蓝牙通讯)
- STM32与HC-05连接(与手机进行蓝牙通讯)
- STM32与Arduino透过HC-05通讯
已更新
目录
*为了能够阅读之后的模块,需要你先阅读下面文字:
研究HC-05可以不用太在意HC-05具体硬件体系和软件构成,但需要至少知道以下几点:
- HC-05有两个工作模式,正常模式(用于蓝牙通讯)和AT模式(用于配置蓝牙模块参数),二者之间不能通过软件方法切换(不是不能实现而是实现需要通过断电上电实现),正常上电会进入正常模式,但若在上电前保持PIO11为高则会进入AT模式(一般HC-05上会有一个按钮,按住按钮上电相当于上电前保持PIO11为高,能进入AT模式);
- AT模式相当于HC-05模块成为一个终端,可以与开发板进行串口通信,所有AT指令从开发板发向HC-05,HC-05反馈操作结果给开发板,AT指令可以设置蓝牙模块名称,配对密码,通讯频率等;
- 正常模式HC-05相当于一根串口通信线缆,将与之配对的设备和开发板的通信串口相连接。
1.Arduino与HC-05连接(与手机进行蓝牙通讯)
此模块目的是将Arduino作为一个收发中继,将HC-05发来的信息原封不动的发给电脑,并将电脑发来的信息原封不动的发给Arduino,主要有两个步骤:
第一步:配置Arduino模块
将以下程序烧录进Arduino,解释可以先不看;
[code]#include <SoftwareSerial.h> // 设置Arduino软件串口,10-RX,11-TX // Pin10为RX,接HC05的TXD // Pin11为TX,接HC05的RXD SoftwareSerial BT(10, 11); char val; void setup() { Serial.begin(38400); //初始化Arduino串口,波特率自定,这里选38400 Serial.println("BT is ready!"); //测试与PC之间串口是否正常,正常则显示上述文字,异常则显示乱码 BT.begin(38400); // HC-05的AT模式默认通信波特率为38400 pinMode(13,OUTPUT); pinMode(8,INPUT); //用来使能HC-05并读取HC-05状态,这里没用到 } void loop() { if (Serial.available()) { val = Serial.read(); BT.print(val); //将PC发来的数据存在val内,并发送给HC-05模块 } if (BT.available()) { val = BT.read(); Serial.print(val); //将HC-05模块发来的数据存在val内,并发送给PC } }
连接HC-05和Arduino开发板:
HC-05 | Arduino |
TXD | Pin10 |
RXD | Pin11 |
VCC | VCC |
GND | GND |
可选(这里没有用到) | |
STATE | Pin8 |
EN-M | Pin13 |
接完线,烧好程序,将Arduino断电,按住HC-05模块上的按钮或者将PIO11接在VCC上,将Arduino与PC连接,发现HC-05模块指示灯2秒周期慢闪,即进入AT模式。
打开PC机上的串口助手,设置波特率为38400,结束符选择Both NL & CR,若显示“BT is ready”则证明串口通信可用,若无显示则按下Arduino上的Reset键,否则请检查线路和程序。
串口通信成功后,通过串口助手发送“AT”,如果一切正常将收到“OK”。若无反馈,请检查HC-05指示灯情况,并重新连接,若仍然无反馈,则可能因为串口助手不支持自动结束符,需要输入“AT\r\n”或在结尾按下回车,并在接下来所有AT指令后都加上“\r\n”或按下回车。若反馈为“ERROR:(0)”,那就再发一次,并检查输入的是否是"AT",其前后是否有包括回车符在内的其他符号。
常用的AT指令有
- AT+NAME(咨询修改蓝牙名称),若发送“AT+NAME”,则会反馈蓝牙名称(部分品牌不会反馈),大多数默认叫做HC-05,如果想更改名称,则需要发送“AT+NAME=XXX”(XXX代表你想要的名称);
- AT+ROLE(咨询修改主从状态),若发送“AT+ROLE”,则会反馈蓝牙模块的主从状态,若返回为“+ROLE:1”则为主机,若返回为“+ROLE:0”则为从机,可以使用“AT+ROLE=1”修改蓝牙模块为主机状态,亦可以使用“AT+ROLE=0”修改蓝牙模块为从机状态;
- AT+CMODE(咨询修改连接模式),若发送“AT+CMODE”,则会反馈蓝牙模块的连接模式,若返回为“+CMODE:1”则不进行地址绑定,若返回为“+CMODE:0”则需要地址绑定,修改类似AT+ROLE,格式为“AT+CMODE=1/0”;
- AT+PSWD(咨询修改配对密码),若发送“AT+PSWD”,则会反馈蓝牙模块的配对密码,一般默认是1234,修改方法同AT+NAME,格式为“AT+PSWD=XXXX”(XXXX为新密码);
- AT+ADDR(咨询蓝牙模块地址),若发送“AT+ADDR”,则会反馈蓝牙模块的地址,一般是三段用冒号分开的字符串;
- AT+BIND(修改绑定连接地址),CMODE为0时可以使用该指令修改绑定的连接地址,即发送“AT+BIND=XX,XX,XX”,注意这里需要将地址中的冒号改为逗号;
- AT+ORGL(复位蓝牙设置),发送“AT+ORGL”即可情况之前的设置;
- AT+RMAAD(清空配对列表),发送“AT+RMAAD”即可情况沛对列表。
根据自己的需要按照上面的AT指令修改蓝牙模块参数,一般需要修改的是蓝牙名称,配对密码,一般HC-05出厂默认为从模式、指定地址连接,所以一般不需要修改其他参数。
第二步:与手机的通信实验
在手机上下载一个蓝牙串口助手,各大应用商店都有,自行选择。
设置好蓝牙模块,断开串口助手,将Arduino断电,重新上电与PC连接,HC-05进入正常模式(指示灯快速连闪),打开PC上的串口助手。
打开手机上的蓝牙寻找你命名的蓝牙模块(有时候刷新不出来,只显示了MAC地址,可以试着连一下,输入配对密码后,如果是你要测试的就会显示名称并且连接成功),连接配对,配对成功后HC-05上的指示灯将进入2s周期的快速双闪。
若成功则可以打开手机上的蓝牙串口助手选择蓝牙模块,试着发送一些简单数据,观察PC上的串口助手是否能够收到数据,若不能检查HC-05状态,连线以及PC串口助手的设置。
可能出现的问题&解决方法
至此,HC-05的简单应用就完成了,Arduino封装了串口通信驱动,一般情况下不会出现问题,蓝牙模块(HC-05和手机的蓝牙)之间通信一般都会自动协商波特率,基本上也不会出现什么问题。若存在问题,则很有可能是1.AT模式配置错误或根本就没有进入AT模式,请详细阅读上面的说明,检查连线;2.串口助手设置错误,串口助手实际上非常简单,就是调用了WriteFile和ReadFile这个有时间我也会发一个解析,但问题就出在这里,有些串口助手没有设置自动添加结束符,有些串口助手获取输入是允许存在回车符的,所以需要自行检验串口助手的发送特征,并按照上述的格式进行发送。
手机发送->电脑接收->电脑发送->手机接收
2.STM32与HC-05连接(与手机进行蓝牙通讯)
这里实现的功能与上一模块相同,使用STM32作为中继,将PC发来的数据发给HC-05,将HC-05接收到的数据发给PC。与Arduino不同,STM32需要自行编写串口驱动,当然也可视HC-05的相关函数为驱动,其关键在与如何编写双串口通讯的驱动。所以这里分为三个步骤,第一步,编写STM32驱动程序(这里使用了一块LCD显示收发信息,亦可不要);第二步,调试配置HC-05模块;第三步,通讯测试。
第一步:编写STM32驱动程序
为了实现中继功能需要编写两个串口驱动,串口1用于与PC通讯,串口2用于与HC-05通讯。驱动的方案有很多种,可以是双中断,也可以是非中断接收方法,输出可以是重构printf函数+额外定义u2_printf函数,或者使用带全局变量的printf函数。这里用的发送方法是重写printf函数,加入一个全局变量以控制向不同串口发送数据;接收方面二者都为中断接收,buffer区20个字节自动覆盖,主程序500ms超时,做的并不是很完善,但足够使用,大家可以根据需要自行修改。
这里使用的是一块STM32F103ZE的开发板,键盘、LED等其他驱动程序均使用样例所用的驱动。主要修改/添加的文件是usart和stm32f10x_it,其代码如下:
usart.h(提供两个初始化函数)
[code]#ifndef __USART_H #define __USART_H #include "stm32f10x.h" void USART1_Int(u16 baud); //初始化串口1,波特率=baud void USART2_Int(u16 baud); //初始化串口2,波特率=baud #endif
usart.c(内含初始化函数,重写printf函数,发送串口选择全局变量)
[code]#include <stdio.h> #include "stm32f10x.h" #include "usart.h" //#include "led.h" //调试时使用 //重写printf #ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */ //全局变量,用来控制发送的串口 int USART_PRINTF_FLAG = 2; //初始化串口1 void USART1_Int(u16 baud) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); // USART_DeInit(USART1); //USART1_TX PA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX PA.10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //Usart1 NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //中断等级3.3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //激活中断 NVIC_Init(&NVIC_InitStructure); /* USARTx configured as follow: - BaudRate = baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); } //初始化串口2 void USART2_Int(u16 baud) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); USART_DeInit(USART2); //¸´Î»´®¿Ú2 //USART2_TX PA.2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART2_RX PA.3 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //Usart1 NVIC ÅäÖà NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //中断等级2.2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //激活中断 NVIC_Init(&NVIC_InitStructure); /* USARTx configured as follow: - BaudRate = baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); } //重写printf函数 PUTCHAR_PROTOTYPE { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ //USART_SendData(USART1, (uint8_t) ch); //串口2发送直至标志复位 if (USART_PRINTF_FLAG == 2) { while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //LED3_OFF; //LED2_OFF; //测试用 USART_SendData(USART2,(uint8_t)ch); } //串口1发送直至标志复位 else if (USART_PRINTF_FLAG == 1) { while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); //LED3_ON; //LED2_ON; //测试用 USART_SendData(USART1,(uint8_t)ch); } else { //LED2_OFF; //LED3_ON; //测试用 } return ch; }
stm32f10x_it.c
[code]//... //放在变量定义区 uint8_t buffer1[20]; uint8_t buffer2[20]; int reccount1=0; int reccount2=0; //... //... //结束符之前插入 //串口1接收中断 void USART1_IRQHandler(void) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { buffer1[reccount1]=USART_ReceiveData(USART1); reccount1++; if(reccount1>=20)reccount1=0; } //buffer1最大20字节,无超时,溢出则覆盖 } //串口2接收中断 void USART2_IRQHandler(void) { if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET) { buffer2[reccount2]=USART_ReceiveData(USART2); reccount2++; if(reccount2>=20)reccount2=0; } //buffer2最大20字节,无超时,溢出则覆盖 } //...
主程序中使用了一块LCD屏幕,并调用了两个按键,若不想使用可以按照注释修改。
main.c
[code]#include <stdio.h> #include "stm32f10x.h"//改成对应芯片驱动的头文件 #include "led.h" #include "delay.h" #include "key.h" #include "timer.h" #include "beep.h" #include "usart.h" #include "adc.h" #include "lcd.h" extern __IO uint16_t ADC_ConvertedValue; //获取串口接收buffer和计数 extern uint8_t buffer1[20]; extern uint8_t buffer2[20]; extern int reccount1; extern int reccount2; // extern int USART_PRINTF_FLAG; float ADC_ConvertedValueLocal; int main(void) { int j; LED_Init(); KEY_Init(); SysTick_Init(); BEEP_Init(); USART1_Int(9600); //可以修改,关系到PC上串口助手的波特率 USART2_Int(38400); //建议不修改 LCD_Init(); //主循环 while(1) { //LCD显示+键盘控制方案 POINT_COLOR=RED; LCD_ShowString(30,50,200,16,16,"buffer1="); LCD_ShowString(30,70,200,16,16,buffer1);//显示目前buffer1内容 LCD_ShowString(30,90,200,16,16,"buffer2="); LCD_ShowString(30,110,200,16,16,buffer2);//显示目前buffer2内容 LCD_ShowString(30,150,200,16,16,"S1= BUFFER1 TO USART2"); //按键1功能:将buffer1内容发给串口2 LCD_ShowString(30,170,200,16,16,"S2= CLEAR BUFFERS"); //按键2功能:清除两个buffer if(!S1) { Delay_ms(10); if(!S1) { while(!S1); USART_PRINTF_FLAG=2; printf(buffer1); } } if(!S2) { Delay_ms(10); if(!S2) { while(!S2); for(j=0;j<20;j++) { buffer1[j]=' '; buffer2[j]=' '; } } } //中继(无操作方案) /* USART_PRINTF_FLAG=2; printf(buffer1); Delay_ms(500); USART_PRINTF_FLAG=1; printf(buffer2); */ Delay_ms(500); reccount1=0; reccount2=0;//接收超时 } }
第二步:配置调试HC-05模块
将STM32与计算机通过USB连接,将HC-05的接口与STM32开发板的usart2接口连接,与Arduino开发板相同,RXD-TXD,TXD-RXD,VCC-VCC,GND-GND。烧录好程序,断开连接,按住HC-05上的按钮上电。观察到2s周期慢闪,打开PC上的串口助手。
PC串口助手设置波特率为9600(或者修改的波特率),发送AT 回车(注意这个串口助手的输入栏是接收回车的,需要手动加回车符),LCD方案可以在STM32的LCD上看到buffer1为AT,按下S1,可以看到反馈为OK,即可开始配置HC-05模块,配置用AT指令等与Arduino相同。
接收情况第三步:通讯测试
完成配置,关闭PC上的串口助手,断电,再次上电,观察到HC-05上的LED快闪,打开PC串口助手,手机上的蓝牙串口助手。连接到该HC-05模块,观察到HC-05上的LED以2s周期双闪,即可开始发送数据。
PC发送手机串口接收与发送 STM32接收与发送
3.STM32与Arduino透过HC-05通讯
- STM32控制HC-05蓝牙模块进行通信
- 使用HC-05蓝牙模块控制Arduino开发板
- 基于STM32的无线通信模块使用——HC_05蓝牙串口
- hc-05蓝牙模块与arduino连接实现手机控制蓝牙智能小车
- HC-05蓝牙模块调试笔记以及使用正点原子例程无法检测到蓝牙模块原因分析
- 【ARDUINO】HC-05蓝牙不配对问题
- 用Android手机通过蓝牙模块HC-06连接Arduino串口输出
- Arduino+HC-05蓝牙模块AT模式设置
- HC-05蓝牙模块基本使用
- arduino mega2560与HC-06蓝牙模块使用
- [Arduino]HC-06蓝牙模块使用小结
- 【Android开发 蓝牙通信】手机蓝牙与下位机HC-05蓝牙模块通信系统
- 常见的出现login incorrect有下面几个原因: 1)大小写键盘问题,Ubuntu不认小键盘,所以一定要关掉NumLock,使用字母键盘上面的数字按键进行输入。
- 使用air进行移动app开发常见功能和问题(一)
- 如何配置和配对两个HC-05蓝牙模块作为主设备和从设备
- 使用AIR进行移动APP开发常见功能和问题(上)
- 【arduino】通过Esp8266-01模块实现的WiFi通信(3)使用TCP协议进行局域网通信(client篇)
- 使用CVS进行版本控制——CVS的配置、使用及常见问题
- 使用air进行移动app开发常见功能和问题(二)
- 基于Android中Webview使用自定义的javascript进行回调的问题详解