最近在学习STM32开发板,在使用HC-05蓝牙模块时遇到了很多问题,没有驱动,串口通讯异常等等,在此期间借鉴了大量CSDN博主的文章,作为回报,我将最终可以正常运行的程序,配置方法以及可能出现的问题分享给大家。大家有问题也可以留言,尽量回复!
本文分为三个模块:
已更新
目录
1.Arduino与HC-05连接(与手机进行蓝牙通讯)
2.STM32与HC-05连接(与手机进行蓝牙通讯)
3.STM32与Arduino透过HC-05通讯
*为了能够阅读之后的模块,需要你先阅读下面文字:
研究HC-05可以不用太在意HC-05具体硬件体系和软件构成,但需要至少知道以下几点:
此模块目的是将Arduino作为一个收发中继,将HC-05发来的信息原封不动的发给电脑,并将电脑发来的信息原封不动的发给Arduino,主要有两个步骤:
第一步:配置Arduino模块
将以下程序烧录进Arduino,解释可以先不看;
#include
// 设置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指令修改蓝牙模块参数,一般需要修改的是蓝牙名称,配对密码,一般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这个有时间我也会发一个解析,但问题就出在这里,有些串口助手没有设置自动添加结束符,有些串口助手获取输入是允许存在回车符的,所以需要自行检验串口助手的发送特征,并按照上述的格式进行发送。
手机发送->电脑接收->电脑发送->手机接收
这里实现的功能与上一模块相同,使用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(提供两个初始化函数)
#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函数,发送串口选择全局变量)
#include
#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
//...
//放在变量定义区
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
#include
#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上的串口助手。
设置波特率为9600(或者修改的波特率),发送AT 回车(注意这个串口助手的输入栏是接收回车的,需要手动加回车符),LCD方案可以在STM32的LCD上看到buffer1为AT,按下S1,可以看到反馈为OK,即可开始配置HC-05模块,配置用AT指令等与Arduino相同。
第三步:通讯测试
完成配置,关闭PC上的串口助手,断电,再次上电,观察到HC-05上的LED快闪,打开PC串口助手,手机上的蓝牙串口助手。连接到该HC-05模块,观察到HC-05上的LED以2s周期双闪,即可开始发送数据。