当前位置: 首页> 教育> 锐评 > 基于stm32的双蓝牙主从通信—双蓝牙主从配置、串口配置、串口接收处理浮点数(附测试代码)

基于stm32的双蓝牙主从通信—双蓝牙主从配置、串口配置、串口接收处理浮点数(附测试代码)

时间:2025/7/15 5:13:10来源:https://blog.csdn.net/m0_74800695/article/details/139320314 浏览次数:0次

前言

        此次做的内容是使用 mpu6050 无线控制小车的运动。在做的过程中发现需要用到双蓝牙进行两个板子之间的通信,将主板mpu6050检测的数据传输至从板上从而控制车的移动。

1、配置双蓝牙主从通信

        以下是转载博主 不怨天,不尤人 的主从配置方法,简洁明了:

        首先让蓝牙进入AT模式
        先按住蓝牙上的微动开关,然后给蓝牙上电。蓝牙上的红灯慢闪表示进入AT模式。
        进行蓝牙AT指令配置
        1、打开两个串口调试助手,选好COM口、波特率选38400,数据位为8,停止位为1。
        2、恢复两个蓝牙的默认设置(最好选择文本模式发送AT命令):AT+ORGL\r\n
(\r\n代表一个回车,在每一条AT指令之后都要加一个回车)。
        3、【(A)主机配置】蓝牙名字配置:AT+NAME=YI(名字任意)
        4、【(A)主机配置】蓝牙模式配置:AT+ROLE=1(主机模式)
        5、【(A)主机配置】蓝牙密码配置:AT+PSWD=1234(密码任意)
        6、【(B)从机配置】蓝牙名字配置:AT+NAME=YI(名字要一致)
        7、【(B)从机配置】蓝牙模式配置:AT+ROLE=0(从机模式)
        8、【(B)从机配置】蓝牙密码配置:AT+PSWD=1234(密码要一致)
        9、蓝牙地址的绑定,通过串口助手查询B蓝牙的地址:AT+ADDR?(很多查询都是指令后面加问号、回车,但是有一些东西是不能查询的,比如名字等)
        10蓝牙A绑定蓝牙B的地址,给蓝牙A(主蓝牙)发送指令:AT+BIND=(B的地址) ,注意在绑定地址的时候要把查询到的地址中的冒号换成逗号,例如98d3:51:fd8103,应该换成98d3,51,fd8103。
        11、按照相同的方式,查询A的地址,让B绑定A的地址。
        12、蓝牙的连接模式配置:AT+CMODE=0(0是指定蓝牙地址连接模式,设置为0才能自动的连接绑定的地址)
        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。        
        原文链接:https://blog.csdn.net/weixin_42994525/article/details/82622405

配置好之后,当蓝牙上电的时候,红灯快闪表示没匹配到地址。过几秒后出现搁几秒红灯快闪两下,就表示匹配成功了。测试方法也非常简单:

使用 ttl 将主从蓝牙连接在电脑不同的端口上,使用上位机在一个端口发送数据,另一个端口便能接收到数据。

2、串口配置

        我使用的是stm32c8t6,由于要使用ttl在串口1进行烧录代码,所以主从板都使用串口2进行收发数据。主板串口2的配置如下,重定义了 printf() 函数:

#include "sys.h"
#include "usart2.h"	  //
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ int handle; }; FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      while((USART2->SR&0X40)==0);//循环发送,直到发送完毕   USART2->DR = (u8) ch;      return ch;
}
#endif u8 USART_RX_BUF2[USART_REC_LEN];
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA2=0;       //接收状态标记	  void uart2_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);	//使能USART2时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//使能GPIOA时钟USART_DeInit(USART2);  //复位串口2//USART1_TX   GPIOA.2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2//USART1_RX	  GPIOA.3初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3 //Usart2 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;  //抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3NVIC_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(USART2, &USART_InitStructure); //初始化串口2USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断USART_Cmd(USART2, ENABLE);                    //使能串口2 }void USART2_IRQHandler(void)                	//串口2中断服务程序
{u8 Res;if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾){Res =USART_ReceiveData(USART2);	//读取接收到的数据if((USART_RX_STA2&0x8000)==0)//接收未完成{if(USART_RX_STA2&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA2=0;//接收错误,重新开始else USART_RX_STA2|=0x8000;	//接收完成了 }else //还没收到0X0D{	if(Res==0x0d)USART_RX_STA2|=0x4000;else{USART_RX_BUF2[USART_RX_STA2&0X3FFF]=Res ;USART_RX_STA2++;if(USART_RX_STA2>(USART_REC_LEN-1))USART_RX_STA2=0;//接收数据错误,重新开始接收	  }		 }}   		 } }

        从板的串口配置如下,编写了 u2_printf() 函数:

#include "usart2.h"
#include "sys.h"
#include <stdarg.h>char  USART2_RX_BUF[USART2_REC_LEN]; //?óê??o3?,×?′óUSART_REC_LEN??×??ú.??×??ú?a??DD·? 
u16 USART2_RX_STA;         		//?óê?×′ì?±ê??	void uart2_init(u32 baudrate)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//ê1?üUSART2£?GPIOAê±?óGPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//?′ó?í?íìê?3?GPIO_Init(GPIOA, &GPIO_InitStructure);//3?ê??ˉGPIOA.2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//????ê?è?GPIO_Init(GPIOA, &GPIO_InitStructure);//3?ê??ˉGPIOA.3  //Usart1 NVIC ????NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//?à??ó??è??3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//×óó??è??3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQí¨μàê1?üNVIC_Init(&NVIC_InitStructure);	//?ù?Y???¨μ?2?êy3?ê??ˉVIC??′??÷//USART 3?ê??ˉéè??USART_InitStructure.USART_BaudRate = baudrate;//′??ú2¨ì??êUSART_InitStructure.USART_WordLength = USART_WordLength_8b;//×?3¤?a8??êy?Y??ê?USART_InitStructure.USART_StopBits = USART_StopBits_1;//ò???í£?1??USART_InitStructure.USART_Parity = USART_Parity_No;//?T????D£?é??USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//?Tó2?têy?Yá÷????USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//ê?·¢?£ê?USART_Init(USART2, &USART_InitStructure); //3?ê??ˉ′??ú2USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//?a??′??ú?óêü?D??USART_Cmd(USART2, ENABLE);                    //ê1?ü′??ú2
}void USART2_IRQHandler(void)                	//′??ú2?D??·t??3ìDò
{u8 Res;if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //?óê??D??(?óê?μ?μ?êy?Y±?D?ê?0x0d 0x0a?á?2){Res =USART_ReceiveData(USART2);	//?áè??óê?μ?μ?êy?Yif((USART2_RX_STA&0x8000)==0)//?óê??′íê3é{if(USART2_RX_STA&0x4000)//?óê?μ?á?0x0d{if(Res!=0x0a)USART2_RX_STA=0;//?óê?′í?ó,??D??aê?else USART2_RX_STA|=0x8000;	//?óê?íê3éá? }else //?1??ê?μ?0X0D{	if(Res==0x0d)USART2_RX_STA|=0x4000;else{USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;USART2_RX_STA++;if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//?óê?êy?Y′í?ó,??D??aê??óê?	  }		 }}   		 } }//自定义串口2 的printf 函数
char UART2_TX_BUF[200];
void u2_printf(char* fmt, ...)    //无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
{u16 i, j;va_list ap;          //va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。va_start(ap, fmt);   //va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束vsprintf((char*)UART2_TX_BUF, fmt, ap);	// 把生成的格式化的字符串存放在这里va_end(ap);i = strlen((const char*)UART2_TX_BUF);              //此次发送数据的长度for(j = 0; j < i; j++)                                                    //循环发送数据{while((USART2->SR & 0X40) == 0);                    //循环发送,直到发送完毕USART2->DR = UART2_TX_BUF[j];}
}

 3、浮点数的收发

发送数据

        主板先检测到mpu的数据后将数据转为带两位小数的浮点数,并在每个数据前加上了类似包头的东西来分辨数据,通过 printf() 函数,由串口2发送至从设备。

	while(1){mpu_dmp_get_data(&pitch,&roll,&yaw);			//得到姿态角即欧拉角temp=MPU_Get_Temperature();								//得到温度值MPU_Get_Accelerometer(&aacx,&aacy,&aacz);	//得到加速度传感器数据MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);	//得到陀螺仪数据printf("P%.2fR%.2fY%.2f\r\n", pitch, roll, yaw);extract_data_from_buffer();oled_show();delay_ms(200);}	

接收数据

        从板通过串口2的中断服务函数接收完数据后,通过 extract_data_from_buffer() 函数处理接收到的数据。

        sscanf() 会对存储接收到数据的数组 USART2_RX_BUF 进行处理。将字符串转化为浮点数。

   "P%fR%fY%f" 这个格式字符串指定了要在输入字符串中匹配一个 P,然后是一个浮点数,再匹配一个 R,然后是一个浮点数,再匹配一个 Y,然后是一个浮点数。

   pitch, roll, yaw:这些是指向 float 类型变量的指针,sscanf 会将解析出的浮点数存储到这些变量中。

工作流程

  1. sscanf 函数将 USART2_RX_BUF 中的字符串与 "P%fR%fY%f" 进行匹配;
  2. sscanf 查找字符串中的 P,并将其后的浮点数读取到 pitch 指向的变量中;
  3. sscanf 查找字符串中的 R,并将其后的浮点数读取到 roll 指向的变量中;
  4. sscanf 查找字符串中的 Y,并将其后的浮点数读取到 yaw 指向的变量中;
  5. 最后对接收到的数据进行校验,返回的数字是接收到数据的个数,判断是否为3。
u8 extract_data_from_buffer(float *pitch, float *roll, float *yaw)
{u8 sta=0;if(USART2_RX_STA&0x8000)//串口1接收到数据{if (sscanf(USART2_RX_BUF, "P%fR%fY%f", pitch, roll, yaw) == 3){USART2_RX_STA=0;sta=1;}}return sta;
}

关键字:基于stm32的双蓝牙主从通信—双蓝牙主从配置、串口配置、串口接收处理浮点数(附测试代码)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: