主要问题
1. 蓝牙模块的连接问题
2. 蓝牙模块的工作模式
3. CUBEMX 配置串口注意事项
4. 两个模块数据传输异常
前言
因为最近都在做基于STM32,MPU6050的手势控制机器人,遇到了无线数据传输的问题,正好手上有几个蓝牙模块,就用蓝牙模块来传输数据,但是并没有想象的那么顺利,最主要的还是两个模块串口传输数据的问题,一直得不到解决,因为简简单单的串口就OK。
首先是实物连接的问题
蓝牙模块与STM32的连接只需要四根线就好,VCC,GND,RX,TX,VCC,提供3.3或者5V,这块基本没有问题
蓝牙模块的工作模式
- AT指令
- 透传
AT
按住蓝牙模块的按钮,然后上电,之后在松开就可以进入AT指令模式,这是时候可以对模块进行必要的设置,比如波特率等,也可以查询模块的相关信息,AT指令要以\R\N结尾,而且此时的波特率固定为38400,常用的AT指令在下面啦!!
透传
正常上电,模块上的灯闪得比较快,这个时候就是透传,当使用两个模块相互通信的时候要先进入AT模式,将其中一个设置为主机,另外一个设置为从机,这样上电之后主机会自动连接从机。
cubemx 的配置
这个很简单,就是正常的配置串口,但是切记波特率要与蓝牙模块的波特率一致,透传模式下默认是9600,可以在AT指令中自行修改
如果是指定一边发送,另外一边接受的话,接受的一段 需要开启中断,发送的一段可以不开启,就是普通的阻塞模式。
今天最主要的不是上面的内容,这些都很简单,主要是两个模块的数据发送与接受的处理方式,这个是今天笔记的主要对象
首先来看发送端的
发送端的数据我放到了一个数组里,通过发送数组将数据发送出去
由于串口HAL库本身没有数组的发送函数,所以需要自己写一个数组发送的函数,原理是通过反复的调用串口发送函数来实现的,那么问题来了,
为什么不能使用printf函数来实现呢?
如果使用printf函数来实现的话,也可以传输十六进制的数据,但是,你传输出去之后,到接受端就会变成十进制,这可能是我自己软件的问题,但是网上大佬也是这么说的,所以最好使用HAL库自带的函数来实现,这里会有一个问题,就是串口调试助手可能会显示异常,但是不影响,因为你需要的是接收端能够正常的显示,发送端主要能正确的把数据发送就好。
发送端串口偶尔异常,还没探究具体的原因。
/*下面的函数时对数据进行打包处理,将一个完整的数组通过数组发送函数传输出去*/
void ack(int x,int y,int z)
{
Inverse_X (x);Inverse_Y (y);Inverse_Z (z);uint8_t str[12]= {
0};str[0] = 0xAA;str[1] = 0xBB;str[2] = X_H ;str[3] = X_L ;str[4] = Y_H ;str[5] = Y_L ;str[6] = Z_H ;str[7] = Z_L ;str[8] = X_flag ;str[9] = Y_flag ;str[10] =Z_flag ;str[11] = 0xCC;UART_SendStr (str,12);
}
这块代码是重点,因为串口发送的数据是8位的,也就是只能传输0-255,这样的256个数字,超过范围的就不能正常的显示,但是MPU6050的角度纵不能只到255度吧。得到300度甚至更多,所以有时候8位就不能满足要求啦,我的处理方法是将数据分为高位和低位,同时由于串口传输无符号数据,负数传输会出现错误。我的解决办法是通过判断是否为负数,然后设置一个标志位,通过置位标志位来判断是否是负数。
超出范围以及负数的处理办法如下
/*用于判断X的值是否超过255,以及X的正负*/
void Inverse_X (int x)
{
/*如果X的值大于0不大于255,则高位直接存放X的值,低位不存数据,标志为不置1*/if((x>= 0) && (x < 255)){
X_H = x;X_L = 0;X_flag = 0;}/*如果超过255,则将数据分为高低两位存储,高位存放255,低位存放剩余的数,标志位不置位*/else if(x>255){
X_H = 255;X_L = x-255;X_flag = 0;}/*如果数据的值低于-255,则同样分为高低两位,但是这两位存放的是正数,标志位置一,表示为负数*/else if ( x <= -255){
X_H = 255;X_L = -x-255;X_flag = 1;}/*如果数据的值在0到255之间,则高位存数据的正数,标志位置一*/else {
X_H = -x;X_L = 0;X_flag = 1;}
}
主函数发送三个数据
ack (300,-6,-200);
发送端的处理基本结束,接下来是接收端
/*三个变量用于存放目标数据,这里时MPU6050的角度*/
static int X=0,Y=0,Z=0;
/*数据接收函数*/
void Openmv_Receive_Data(uint16_t Com_Data)
{
/*循环体变量*/uint8_t i;/*计数变量*/static uint8_t RxCounter1=0;//计数/*数据接收数组*/static uint16_t RxBuffer1[50]={
0};/*数据传输状态位*/static uint8_t RxState = 0; /*对数据进行校准,判断是否为有效数据*/if((RxState==0)&&(Com_Data==0xAA)) //0x2c帧头{
RxState=1;RxBuffer1[RxCounter1++]=Com_Data;}else if((RxState==1)&&(Com_Data==0xBB)) //0x12帧头{
RxState=2;RxBuffer1[RxCounter1++]=Com_Data;}/*对数据进行处理*/else if(RxState==2){
/*接受的数据存放到数组里*/ RxBuffer1[RxCounter1++]=Com_Data;if(RxCounter1>=30||Com_Data == 0xCC) //RxBuffer1接受满了,接收数据结束{
RxState=3;/*X,Y,Z都是有高位与低位构成的,这里将高位与低位加到一起即可*/X = RxBuffer1[2]+RxBuffer1[3];Y = RxBuffer1[4]+RxBuffer1[5];Z = RxBuffer1[6]+RxBuffer1[7];/*处理数据的符号,传输的过程不能传输符号,通过标志位的形式解决这个问题*/X = Inverse (RxBuffer1 [8],X);Y = Inverse (RxBuffer1 [9],Y);Z = Inverse (RxBuffer1 [10],Z);printf("X = %d\r\n",X);printf("Y = %d\r\n",Y);printf("Z = %d\r\n",Z); }}else if(RxState==3)//检测是否接受到结束标志{
if(RxBuffer1[RxCounter1-1] == 0xCC){
//RxFlag1 = 0;RxCounter1 = 0;RxState = 0;}else //接收错误{
printf ("erro");RxState = 0;RxCounter1=0;for(i=0;i<10;i++){
RxBuffer1[i]=0x00; //将存放数据数组清零}}} else //接收异常{
RxState = 0;RxCounter1=0;for(i=0;i<10;i++){
RxBuffer1[i]=0x00; //将存放数据数组清零}
// printf ("接收异常00\r\n");}
}
上面是一个串口数据的接收函数,具体的接收方式已经在里面哟注释啦!
还有一个函数时对正负数的标志位进行处理的函数,如下
/*符号处理*/
int Inverse(uint8_t x,int X)
{
/*判断标志为来解决符号问题,标志为为1,说明是负数,对数据进行求相反数*/if(x==0)X = X;else X = -X;printf("******%d\r\n",X);return X ;
}
最后是串口中断的回调函数
/*回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
uint16_t tempt //定义临时变量存放接受的数据if(huart->Instance==USART1){
tempt=USART1_RXbuff;Openmv_Receive_Data(tempt);}
HAL_UART_Receive_IT(&huart1,&USART1_RXbuff,1);
}
接收端记得在主函数中开启串口接收中断,否则不能进入中断。
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&USART1_RXbuff,1);/* USER CODE END 2 */
为了防止以后忘记怎么弄,来做一个笔记。
同时感谢这位大哥提供的数据处理思路哦