HC-05蓝牙模块的介绍:
蓝牙模块说到底就是一个无线的串口通信模块,将串口通信无线化,所以理论上只要你懂的串口通信,那么蓝牙模块的使用对你来说也就不是什么大的问题。
市面上常见的蓝牙模块主要有:主从一体式,和单从模式的。蓝牙模块的主从模式只是对于蓝牙与其他设备的连接而言,对于数据传输是不会产生任何影响的。这两种蓝牙模块与其他设备之间的通信都是双向的,既可以发送数据也可以接收数据。我使用的这一款是主从一体式的,所以在使用之前要做一些准备。首先我们要准备一个USB转TTL的模块,因为通信电平的不匹配,所以需要这个模块转换通信电平以保证可以使蓝牙模块和电脑可以正常的进行通信。接线方式:(usb转ttl模块与蓝牙模块)
这里需注意,这个蓝牙模块的电源只能接入+5V电压
蓝牙模块具有两种工作模式:命令响应工作 模式和自动连接工作模式,在自动连接工作模式下模块又可分为主(Master)、从(Slave)和回环(Loopback)三种工作角色。当模块处于自动连接工作模式时,将自动根据事先设定的方式连接的数据传输;当模块处于命令响应工作模式时能执行下述所有AT命令,用户可向模块发送各种AT指令,为模块设定控制参数或发布控制命令。:
AT指令模式,在这个模式之下我们可以通过电脑端的串口助手对蓝牙模块发出一些设置指令,查询模块的状态,设置蓝牙模式、名称等等。蓝牙模块上电后是不会自己进入AT指令模式,会进入上一次在AT指令设置的模式,并在上一次AT指令的所有设置之下工作。在蓝牙模块与单片的连接之下的通信我们是用不到AT指令模式,AT指令模式只是用于我们在使用模块之前设置对蓝牙模块进行我们所需要的设置。这种模式也只是使用蓝牙模块进行简单的通信控制不需要其他较为复杂的操作,我们也可以通过软件的方式进如蓝牙模块的AT指令模式,在线对蓝牙模块进行一些设置,简单的应用场景这种用法就足以解决我们的问题,没必要搞得那么复杂。
如何进入AT指令模式呢?通过控制模块外部引脚 (PIO11)输入电平,可以实现模块工作状态的动态转换。蓝牙模块的原理图如下:
我们只需要在蓝牙模块上电之前一直按住S1按键,上电后松开就可以使蓝牙模块进入AT指令模式。
蓝牙模块上面有一个指示灯,可以提示蓝牙模块当前状态:led指示蓝牙连接状态,快闪表示没有蓝牙连接,慢闪表示进入AT模式,双闪表示蓝牙已连接并打开了端口。
AT命令格式为:波特率38400,8个数据位,1个停止位,无校验。每条指令输入完成后,必须按一下回车键再发送!
模块与电脑连接好之后可以发送“OK”测试模块的好坏,我自己在发送AT指令之后始终收到不到任何响应,查了好多资料,尝试了很多方法都没有用,到最后发现是自己的模块坏了。串口通信的RXD和TXD数据端在空闲的状态之下表现为高电平,我那个坏了的蓝牙模块就有一个端口表现为低电平,这我才发现它是坏的。
我们需要模块出于“从角色”查询设置后就完成了蓝牙模块与手机连接的准备了。
附件中有完整的AT指令表可以下载后试试其他AT测试指令。
平衡车之蓝牙模块
平衡车中主要用蓝牙模块进行PID的调参,控制小车前进后退转向等。
将蓝牙模块与单片机连接好上电之后模块指示灯进入快闪就可以用MiniBalance APP连接蓝牙模块(可以通过AT指令查询模块名字或者设置模块名字),配对码一般是1234 。连接好之后就可以进行通信了。
usart3.c文件
#include "usart3.h"
u8 Usart3_Receive;
/**************************************************************************
Function: Usart3 initialization
Input : bound:Baud rate
Output : none
函数功能:串口3初始化
入口参数:bound:波特率
返回 值:无
**************************************************************************/
void uart3_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能UGPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能USART3时钟
//USART3_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//USART3_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
//Usart3 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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(USART3, &USART_InitStructure); //初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART3, ENABLE); //使能串口3
}
/**************************************************************************
Function: Receive interrupt function
Input : none
Output : none
函数功能:串口3接收中断
入口参数:无
返回 值:无
**************************************************************************/
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收到数据
{
static int uart_receive=0;//蓝牙接收相关变量
static u8 Flag_PID,i,j,Receive[50];
static float Data;
uart_receive=USART_ReceiveData(USART3);
Usart3_Receive=uart_receive;
if(uart_receive==0x59) Flag_velocity=2; //低速挡(默认值)
if(uart_receive==0x58) Flag_velocity=1; //高速档
if(uart_receive>10) //默认使用
{
if(uart_receive==0x5A) Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=0;//刹车
else if(uart_receive==0x41) Flag_front=1,Flag_back=0,Flag_Left=0,Flag_Right=0;//前
else if(uart_receive==0x45) Flag_front=0,Flag_back=1,Flag_Left=0,Flag_Right=0;//后
else if(uart_receive==0x42||uart_receive==0x43||uart_receive==0x44)
Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=1; //右
else if(uart_receive==0x46||uart_receive==0x47||uart_receive==0x48)
Flag_front=0,Flag_back=0,Flag_Left=1,Flag_Right=0; //左
else Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=0;//刹车
}
if(uart_receive<10) //备用app为:MiniBalanceV1.0 因为MiniBalanceV1.0的遥控指令为A~H 其HEX都小于10
{
Flag_velocity=1;//切换至高速档
if(uart_receive==0x00) Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=0;//刹车
else if(uart_receive==0x01) Flag_front=1,Flag_back=0,Flag_Left=0,Flag_Right=0;//前
else if(uart_receive==0x05) Flag_front=0,Flag_back=1,Flag_Left=0,Flag_Right=0;//后
else if(uart_receive==0x02||uart_receive==0x03||uart_receive==0x04)
Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=1; //左
else if(uart_receive==0x06||uart_receive==0x07||uart_receive==0x08) //右
Flag_front=0,Flag_back=0,Flag_Left=1,Flag_Right=0;
else Flag_front=0,Flag_back=0,Flag_Left=0,Flag_Right=0;//刹车
}
// if(Usart3_Receive==0x7B) Flag_PID=1; //APP参数指令起始位 // {
// if(Usart3_Receive==0x7D) Flag_PID=2; //APP参数指令停止位 // }
// if(Flag_PID==1) //采集数据
// {
// Receive[i]=Usart3_Receive;
// i++;
// }
// printf("数据长度%d\r\n",i);
// if(Flag_PID==2) //分析数据
// {
// if(Receive[3]==0x50) PID_Send=1; // 0x50 P
// else if(Receive[1]!=0x23) // ASCLL 0x23 # 程序不能接收一次性发送所有数据
// {
// for(j=i;j>=4;j--)
// {
// Data+=(Receive[j-1]-48)*pow(10,i-j); //pow(x,y) 计算x的y次幂,,十六进制数减去0x30(48)化为10进制数
// }
// switch(Receive[1])
// {
// case 0x30: Balance_Kp=Data;break; // 0 1-8是app内发送数据的序号
// case 0x31: Balance_Kd=Data;break; //1
// case 0x32: Velocity_Kp=Data;break; //2
// case 0x33: Velocity_Ki=Data;break; //3
// case 0x34: Turn_Kp=Data;break;
// case 0x35: Turn_Kd=Data;break;
// case 0x36: break; //预留
// case 0x37: break; //预留
// case 0x38: break; //预留
// }
// }
// Flag_PID=0;
// i=0;
// j=0;
// Data=0;
// memset(Receive, 0, sizeof(u8)*50);//数组清零
// }
}
}
在实际使用当中发现这个app在线调参不是那么好用,所以就屏蔽掉了。也可能是自己的问题,说实话自己在实际的使用过程当中发现的问题还是挺多的,后期会继续优化。
APP一次发送PID参数的格式为:
{x:参数大小}
X是参数的序号。
单片机往APP发送的数据要结尾要加上“$”。
/**************************************************************************
Function: Send data to APP
Input : none
Output : none
函数功能:向APP发送数据
入口参数:无
返回 值:无
**************************************************************************/
void APP_Show(void)
{
static u8 flag;
int Encoder_Left_Show,Encoder_Right_Show,Voltage_Show;
Voltage_Show=(Voltage-1110)*2/3; if(Voltage_Show<0)Voltage_Show=0;if(Voltage_Show>100) Voltage_Show=100; //对电压数据进行处理
Encoder_Right_Show=Velocity_Right*1.1; if(Encoder_Right_Show<0) Encoder_Right_Show=-Encoder_Right_Show; //对编码器数据就行数据处理便于图形化
Encoder_Left_Show=Velocity_Left*1.1; if(Encoder_Left_Show<0) Encoder_Left_Show=-Encoder_Left_Show;
flag=!flag;
if(PID_Send==1) //发送PID参数,在APP调参界面显示
{
printf("{C%d:%d:%d:%d:%d:%d:%d:%d:%d}$",(int)Balance_Kp,(int)Balance_Kd,(int)Velocity_Kp,(int)Velocity_Ki,(int)Turn_Kp,(int)Turn_Kd,0,0,0);//打印到APP上面
PID_Send=0;
}
else if(flag==0) // 发送电池电压,速度,角度等参数,在APP首页显示
printf("{A%d:%d:%d:%d}$",(int)Encoder_Left_Show,(int)Encoder_Right_Show,(int)Voltage_Show,(int)Angle_Balance); //打印到APP上面
else //发送小车姿态角,在波形界面显示
printf("{B%d:%d:%d}$",(int)Pitch,(int)Roll,(int)Yaw); //x,y,z轴角度 在APP上面显示波形
//可按格式自行增加显示波形,最多可显示五个
}
全部评论