JDY-31
JDY-31蓝牙模块,就是下面这么个小玩意。某宝买大概就七八块,超过十块的不要买。
JDY一共有6根引脚,而我们想要让它工作的话只需要接两根线即可,那就是VCC和GND,给VCC接3.6V~6V的电压(推荐是5V),GND接地,然后JDY31就开始工作了,我们的手机打开蓝牙就能找到JDY的蓝牙了。
配对后我们的手机就可以和JDY31进行通信了,但是仅仅是和JDY31通信,这没有什么意义,所以我们会将JDY31和单片机进行连接,这样我们就可以用手机通过蓝牙去控制单片机了。
我们把JDY31的RXD接到我们STM32F103的TXD,而TXD接到我们STM32F103的RXT(推荐使用USART1的资源,GPIOA的9号引脚和10号引脚,因为JDY的工作电压较高,STM32F103一般的引脚受不了,而USART1资源的引脚能够接受5V电压,具体可以参考引脚定义表)
这样,只要我们的手机通过蓝牙和JDY31连接之后,并且JDY31和STM32F103通过串口连接之后,只要手机通过蓝牙助手发送串口数据,然后JDY就会直接收到并且发送给STM32F103。而STM32F103通过串口发送的数据也会经由JDY31来传输到我们的手机上。
所以蓝牙通信没那么难,或者说是因为有像JDY31这些集成度高的模块,使得我们的蓝牙通信变得简单。
剩下两个引脚是STATE和EN,简单来说EN没有用,而我们连接上来自JDY的蓝牙之后,STATE会输出高电平,可以外接一个LED来直观的查看蓝牙是否连接。不过其实没有太大必要,因为JDY自带一个小红灯,等待连接的时候小红灯会闪烁,而连接上之后小红灯常亮。
AT命令控制JDY31
上面讲述了怎么通过JDY31来进行蓝牙通信。
而如果我们想要修改JDY的一些配置信息(例如蓝牙名称,连接的密码之类的),我们就需要使用AT命令来对JDY进行控制。
其实AT命令听起来挺酷,实际上就是一堆特定的字符串罢了。
例如我要查询版本号,那么我直接让STM32F103通过串口发送字符串“AT+VERSION\r\n”即可,也就是指令再加上“\r\n”。然后JDY31就会通过串口把版本号再通过串口发过来。本质上还是串口通信。
总的指令就上面九条没什么可说的,下面就简单说一下带参数的指令。
波特率这条指令可以加上参数来修改波特率,不过不是自由设置的,例如我要设置9600的波特率,那么我发送的指令就应该是“AT+BAUD<4>\r\n”,其他波特率的参数可以参考上面的图。
设置配对密码,默认是1234,把要修改的密码当作参数发送即可。
蓝牙名称设置,这个会稍微有些延迟,要等一等,手机上搜索到的蓝牙名称才会更新。
上面说了,一般情况下,JDY31连接上蓝牙之后,STATE会发出高电平,如果我们将这个串口状态输出失能,那么STATE就会和EN一样没有任何用处了。
通过STM32F103对JDY-31使用串口进行AT命令
接下来的讲解配合下面代码食用。
打开串口没什么可说的,直接复制我之前关于USART的文章的代码即可。
这边为了串口输出方便,我加入了stdio库以及重写了fputc,这是为了能够直接使用printf来进行串口的输出,keil5还需要像下图这样配置。
然后我们需要做的就是直接发送AT指令即可,需要注意的是连续两条指令之间需要延时一段时间,否则JDY31反应不过来(我猜的,总之就是JDY会忽略一些指令)
得到的结果在代码下面的图中。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include <stdio.h>
//中断函数
void USART1_IRQHandler(void){
//判断数据接收标志位
if(SET==USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){
uint16_t data=USART_ReceiveData(USART1); //读取出接收的数据
printf("%c",data);
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除数据接收标志位
}
}
void sendbyte(uint16_t Data){
//发送数据
USART_SendData(USART1,Data);
//等待数据发送完毕
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
//这里重写fputc使得可以直接使用printf对串口进行输出
int fputc(int ch, FILE *f){
sendbyte(ch);
return ch;
}
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
itd.GPIO_Pin=GPIO_Pin_9; //TX引脚
itd.GPIO_Speed=GPIO_Speed_2MHz; //这个随意
GPIO_Init(GPIOA,&itd);
itd.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
itd.GPIO_Pin=GPIO_Pin_10; //RX引脚
GPIO_Init(GPIOA,&itd);
USART_InitTypeDef uitd;
uitd.USART_BaudRate=9600; //波特率
uitd.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件流控制
uitd.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //串口模式
uitd.USART_Parity=USART_Parity_No; //校验
uitd.USART_StopBits=USART_StopBits_1; //停止位长度
uitd.USART_WordLength=USART_WordLength_8b; //传输的字长
USART_Init(USART1,&uitd);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启USART接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断配置
NVIC_InitTypeDef nitd;
nitd.NVIC_IRQChannel=USART1_IRQn;
nitd.NVIC_IRQChannelCmd=ENABLE;
nitd.NVIC_IRQChannelPreemptionPriority=2;
nitd.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&nitd);
USART_Cmd(USART1,ENABLE); //上电USART
printf("AT+NAME\r\n"); //查询蓝牙名字
Delay_ms(100);
printf("AT+LADDR\r\n"); //查询mac地址
Delay_ms(100);
printf("AT+BAUD\r\n"); //查询波特率
Delay_ms(100);
printf("AT+PIN\r\n"); //查询蓝牙连接密码
Delay_ms(100);
printf("AT+ENLOG\r\n"); //查询状态输出使能状态
Delay_ms(100);
printf("AT+VERSION\r\n"); //查询版本
Delay_ms(100);
printf("AT+NAME<JDY>\r\n"); //更改蓝牙名称,但是反应比较慢,得等一下子才会更新
while(1){
}
}
忽略最后一行,那是蓝牙断开后,JDY发送的。
JDY与手机进行蓝牙通信
其实蓝牙通信听起来挺酷,实际上你手机连上蓝牙之后,所谓蓝牙通信实际上就是利用串口来发送信息。因此我们直接使用printf输出内容就行,然后手机就能收到,不过我们需要使用蓝牙助手才能看得到,我用的是某宝商家提供的,大家随便找一个都行,甚至微信小程序里都有。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include <stdio.h>
//中断函数
void USART1_IRQHandler(void){
//判断数据接收标志位
if(SET==USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){
uint16_t data=USART_ReceiveData(USART1); //读取出接收的数据
printf("%c",data);
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除数据接收标志位
}
}
void sendbyte(uint16_t Data){
//发送数据
USART_SendData(USART1,Data);
//等待数据发送完毕
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
int fputc(int ch, FILE *f){
sendbyte(ch);
return ch;
}
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
itd.GPIO_Pin=GPIO_Pin_9; //TX引脚
itd.GPIO_Speed=GPIO_Speed_2MHz; //这个随意
GPIO_Init(GPIOA,&itd);
itd.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
itd.GPIO_Pin=GPIO_Pin_10; //RX引脚
GPIO_Init(GPIOA,&itd);
USART_InitTypeDef uitd;
uitd.USART_BaudRate=9600; //波特率
uitd.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件流控制
uitd.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //串口模式
uitd.USART_Parity=USART_Parity_No; //校验
uitd.USART_StopBits=USART_StopBits_1; //停止位长度
uitd.USART_WordLength=USART_WordLength_8b; //传输的字长
USART_Init(USART1,&uitd);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启USART接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断配置
NVIC_InitTypeDef nitd;
nitd.NVIC_IRQChannel=USART1_IRQn;
nitd.NVIC_IRQChannelCmd=ENABLE;
nitd.NVIC_IRQChannelPreemptionPriority=2;
nitd.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&nitd);
USART_Cmd(USART1,ENABLE); //上电USART
while(1){
printf("Hello World");
Delay_ms(2000);
}
}
这是串口助手收到的完整信息,包括了蓝牙的连接到断开。
这是手机的蓝牙助手里的信息,RX开头的是收到的信息,TX开头的是手机这里发送的。
资源获取
如果大家想要蓝牙特供版的串口助手以及安卓手机版的蓝牙助手,可以关注我的公众号“折途想要敲代码”回复关键词“蓝牙”即可免费获取。