单片机通过蓝牙数据包与手机通信
前言
作者在7月份进行电赛集训时,除了单片机接LCD触摸屏外,经常要做手机端数据显示和无线控制相关(整花活),常见的无线通信方式如蓝牙,2.4G,wifi等是电赛中的常客,其中蓝牙使用最普遍,因此本文将介绍一款非常实用的蓝牙调试器以及使用方法,也算是做个记录。
一、蓝牙调试器介绍
1.基本介绍
该调试器出自某大佬,原文链接如下:https://www.jianshu.com/p/1a8262492619
这个我们要用的时候直接去应用市场下载即可(注意:此APP基于Android设备)。该调试器具有以下功能:
- 蓝牙设备搜索连接(支持指定名称)
- 普通的串口收发(对话模式,最简单的数据收发)
- 专业调试(自定义界面和数据,通过数据包进行数据收发)
- 按钮控制(自定义按钮及动作)
2.功能体验
首先是蓝牙设备的搜索与连接(搜索指定名称的蓝牙设备可以在设置里打开对应功能),注意搜索时要打开定位功能,否则可能搜索不到设备。搜索到设备后点击右边的+号就能连接。
很常规的串口收发——对话模式,可以设置间隔自动重发和HEX收发。当有数据传输时,界面上端还能显示数据传输速率和丢包率。
高级数据收发——专业调试,可以自定义界面和数据包结构。控件有按钮、开关、滑动条、二维曲线图等,支持bool、char、short、int、float五种数据类型的数据。界面编辑极其简单,就是各种块块,拖拖放放,然后关联数据。关于通信,可以查看通信设置里的说明。
二、实际使用
1.串口配置
使用CubeMX进行串口配置如下:
串口波特率可以设置为38400,这样可以通过单片机发送AT指令配置蓝牙模块;也可以通过串口助手配置好后连接单片机使用。
配置好后生成工程文件,打开。
2.代码实现
(1)数据处理与发送
首先需要把其它数据类型转化为十六进制才能由单片机发送到手机上,方法如下:
首先定义发送数据长度和通信串口,然后定义一个发送数据的结构体,结构体成员跟据需求进行添加。
#define len_s 31
#define UART &huart3
#pragma pack(1)
typedef struct
{
char head;
int f_max;
int f_min;
int fx;
float R_in;
float R_out;
float Au;
float Aux;
char check;
char end;
} Frame;
#pragma pack()
#pragma pack(1)
…
#pragma pack()
这段代码的作用是将结构体中的每个成员的地址相邻,没有这句代码程序会出问题。
定义一个联合体让结构体中的数据转移到数组中。
union Data_send_
{
Frame Frame_data;
uint8_t str_send[len_s];
};
处理数据并发送,此处作者根据实际需要把数据发送封装成一个函数:
union Data_send_ hex;
void SendInfo(int f_max, int f_min, int fx, float R_in, float R_out, float Au, float Aux)
{
//给数组中的变量赋值
hex.Frame_data.f_max = f_max;
hex.Frame_data.f_min = f_min;
hex.Frame_data.fx = fx;
hex.Frame_data.R_in = R_in;
hex.Frame_data.R_out = R_out;
hex.Frame_data.Au = Au;
hex.Frame_data.Aux = Aux;
int sum=0;
uint8_t *p = hex.str_send;
//添加帧头帧尾
hex.str_send[0]=0xA5;
hex.str_send[len_s-1]=0x5A;
for(int i=1;i<len_s-2;i++)
sum=sum+hex.str_send[i];
hex.str_send[len_s-2]=sum%256; //计算并添加校验位
for(int i=0;i<len_s;i++){
HAL_UART_Transmit(UART,(p+i), 1, 1); //通过串口发送数据
while(__HAL_UART_GET_FLAG(UART,UART_FLAG_TC)!=SET); //等待发送结束
}
HAL_Delay(5);
memset(hex.str_send,0x00,len_s); //清空数组
}
(2)数据接收与处理
接收到的数据为16进制数据,需要转化为浮点数或整形才能进行运算。代码如下:
//十六进制数转浮点数,参数为4个八位二进制数
float hex_float(uint8_t a,uint8_t b,uint8_t c,uint8_t d)
{
union _float //定义联合体,十六进制和浮点数地址相同
{
float f;
uint8_t byte[4];
};
float m;
union _float fl;
fl.byte[0]=a;
fl.byte[1]=b;
fl.byte[2]=c;
fl.byte[3]=d;
m=fl.f;
return m;
}
//十六进制数转短整型数,参数为2个八位二进制数
short int hex_int(uint8_t a,uint8_t b)
{
union _int
{
short int _i;
uint8_t byte[2];
};
short int m;
union _int i;
i.byte[0]=a;
i.byte[1]=b;
m=i._i;
return m;
}
数据接收和处理代码如下:
uint8_t receive[len_r];
short KEY0 = 1, KEY1 = 1, WK_UP = 0;
HAL_UART_Receive(UART, (uint8_t *)receive, len_r, 40); //通过串口接收数据
if( receive[0]==0xa5 && receive[len_r-1]==0x5a){ //帧头帧尾均合法
int sum=0;
for(int i=1; i<len_r-2; i++) //计算原数据字节和
sum=sum+ receive[i];
if(sum%256 == receive[len_r-2]){ //取余256得到低八位,与倒数第二位校验位相等
KEY0 = hex_int(receive[1], receive[2]);
KEY1 = hex_int(receive[3], receive[4]);
WK_UP = hex_int(receive[5], receive[6]);
}else return 0;
}