所用的蓝牙调试助手app可以自由添加控件,包括摇杆、参数微调框、能量槽、开关、Y-T波形图、X-Y波形图等,使用起来很方便,但是数据的接收和处理有些麻烦,app的数据接收与发送格式如下:
通过简单的数据协议来保证接收到的数据不会出现混乱,每一帧数据的包头为0xA5,包尾0x5A,倒数第二位校验位为原数据所有字节之和的低八位。那么就跟据这个协议来写数据的接收和发送程序。
关于app的使用方法可以参考作者的博客:蓝牙调试器-划时代无线调试器_XLazyDog的博客-CSDN博客_蓝牙调试器
目录
1、数据接收
首先就是串口初始化配置
void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStruct);
USART_InitStruct.USART_BaudRate=115200;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_Parity=USART_Parity_No;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStruct);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
}
接下来,写串口中断函数
void USART1_IRQHandler(void)
{
static u8 i;
u8 res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
res=USART_ReceiveData(USART1);
if((res==0xa5)&&(Frame_End_sta==0))//如果接收到数据是帧头并且帧尾标志位为0
Frame_Head_sta=1;//此时接收到为帧头,帧头标志位置1
if(res==0x5a&&Frame_Head_sta==1) //如果已经接收到帧头,且此时接收到数据为0x5A
Frame_End_sta=1;//判定此次为帧尾
if(Frame_Head_sta==1)//已经接收到帧头
{
str[i]=res;//把接收到的数据写进数组
i++;
if(i==len_r)//如果i=接收数组长度,说明一帧数据已经接收完成
{
if(res!=0x5a)//如果最后一个不是帧尾,说明接收错误
{
Frame_Head_sta=0;//帧头帧尾标志位全部置0,重新接收
Frame_End_sta=0;
}
i=0;
}
}
if(str[0]!=0xa5)//如果第一个数据不是帧头,说明接收错误
{
i=0;
Frame_Head_sta=0;//帧头帧尾标志位全部置0,重新接收
Frame_End_sta=0;
}
}
}
这段代码保证了接收到一帧数据的帧头和帧尾是合法的,但无法保证内部数据不出现乱序,所以还要加上对校验位的处理
if(Frame_Head_sta==1&&Frame_End_sta==1)//帧头帧尾全部置1,已经接收到一帧数据
{
if( str[0]==0xa5&& str[len_r-1]==0x5a)//帧头帧尾均合法
{
sum=0;
for(i=1;i<len_r-2;i++)//计算原数据字节和
{
sum=sum+ str[i];
}
if(sum%256== str[len_r-2])//取余256得到低八位,与倒数第二位校验位相等
memcpy(str2, str,len_r);//把数组str中的数据复制到str2
else
{
memset( str,0x00,len_r);//否则数据校验错误,将上次接收到的数据清零,重新接收
Frame_Head_sta=0;//帧头帧尾标志位全部置0,说明接收到的数据已经处理完成,可以进行下一帧数据的接收
Frame_End_sta=0;
}
}
/*将str中数据转移到str2中的目的是为了让str继续回去接收数据,利用str2来进行接下来的运算,
即使接收数据出错,也可以暂时执行上一次接收到的数据*/
else
memset( str,0x00,len_r);//全部清零,重新接收
data_r.joystick_X=hex_int(str2[1],str2[2]);//把接收到的十六进制数据全部转化为所需要的数据类型
data_r.joystick_Y=hex_int(str2[3],str2[4]);
data_r.parameter_1=hex_float(str2[5],str2[6],str2[7],str2[8]);
data_r.parameter_2=hex_float(str2[9],str2[10],str2[11],str2[12]);
data_r.parameter_3=hex_float(str2[13],str2[14],str2[15],str2[16]);
Frame_Head_sta=0;//数据处理完毕,帧头帧尾标志位置0
Frame_End_sta=0;
}
到这里,str2数组中的数据即为需要的一帧合法数据
2、数据处理
接下来处理数据,接收到的数据为16进制数据,现在需要转化为浮点数或整形才能进行运算
float hex_float(u8 a,u8 b,u8 c,u8 d)//十六进制数转浮点数,参数为4个八位二进制数
{
union _float //定义联合体,十六进制和浮点数地址相同
{
float f;
u8 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;
}
short int hex_int(u8 a,u8 b)//十六进制数转短整型数,参数为4个八位二进制数
{
union _int
{
short int _i;
u8 byte[2];
};
short int m;
union _int i;
i.byte[0]=a;
i.byte[1]=b;
m=i._i;
return m;
}
其它类型的数据转换参考以上内容
接收到的十六进制转化为其它数据类型已经解决
下面还需要把其它数据类型转化为十六禁止才能由单片机发送到手机上
首先定义一个发送数据的结构体,结构体成员跟据需求进行添加
#pragma pack(1)
typedef struct
{
char head;
short int Y_T_3;
short int Y_T_4;
short int Y_T_5;
short int power_2;
float Y_T_0;
float Y_T_1;
float Y_T_2;
float power_1;
char check;
char end;
} Frame;
#pragma pack()
#pragma pack(1)
xxxxx
#pragma pack()
这段代码的作用是将结构体中的每个成员变量的地址相邻,没有这句代码程序会出问题,其它具体作用可以百度百科“字节对齐“
成员变量的地址首尾相连之后才能转移到数组中
定义一个联合体让结构体中的数据转移到数组中
union Data_send_
{
Frame Frame_data;
u8 str_send[len_s];
};
union Data_send_ hex;
到这里就完成了其它类型数据转换为可以由单片机发送给手机的十六进制数
3、数据发送
把数据由单片机发送给手机app比较简单,只需要按照协议来处理以下,然后再发送就行了
首先帧头帧尾为特定数据
hex.Frame_data.head=0xA5;
hex.Frame_data.end=0x5A;
校验位的处理
for(i=1;i<len_s-2;i++)
{
sum=sum+hex.str_send[i];
}
hex.Frame_data.check=sum%256;//校验位等于原数据字节之和的低八位
for(i=0;i<len_s;i++)//循环发送数据
{
USART_SendData(USART1,hex.str_send[i]); //通过串口1发送数据
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET); //等待发送完成
}
数据的接收处理发送就完成了