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

前言

        此次做的内容是使用 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/21790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超市管理系统设计1——基本功能设计

超市管理系统基础功能类设计 1. 概述 本设计文稿提供一个基础的超市管理系统&#xff0c;包含基本的功能设计。该系统将管理商品、顾客、员工和交易记录&#xff0c;不需要接入数据库&#xff0c;通过文件存储数据&#xff0c;并满足面向对象编程的基本要求&#xff08;继承、…

FCCL:Learn from others and Be yourself in Heterogeneous Federated Learning

CVPR2022,通过在公开数据集上的logits相似性(同类相近、异类原理)来迁移客户端知识,通过上轮模型和初始模型延缓遗忘。 论文地址:openaccess code: 作者开源 贡献 提出了一种新颖的联邦学习方法,称为 FCCL(联邦互相关和持续学习)。 为异构联邦学习制定了一种简单有效…

java设置图片透明度

在Java中&#xff0c;你可以使用BufferedImage类来处理图片的透明度。BufferedImage类提供了setRGB方法&#xff0c;你可以通过这个方法设置每个像素的透明度。 以下是一个简单的例子&#xff0c;演示了如何设置图片的全局透明度&#xff1a; import javax.imageio.ImageIO; i…

面试题:说一下 http 报文都有哪些东西?

面试题&#xff1a;说一下 http 报文都有哪些东西&#xff1f; HTTP 是传输超文本&#xff08;实际上除了 HTML&#xff0c;可以传输任何类型的文件&#xff0c;如视频、音频、文本等&#xff09;的协议&#xff0c;是一组用于浏览器-服务器之间数据传输的规则。 HTTP 位于 OS…

IO进程线程(四)文件IO之文件属性、目录操作

一、文件属性信息 &#xff08;一&#xff09;stat函数 1. 定义 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>int stat(const char *pathname, struct stat *statbuf);功能&#xff1a;获取文件的属性信息参数&#xff1a;pathname…

摸鱼大数据——Hive函数14

14、开窗(开列)函数 官网链接&#xff1a;Window Functions - Apache AsterixDB - Apache Software Foundation 14.1 基础使用 开窗函数格式: 开窗函数 over(partition by 分组字段名 [order by 排序字段名 asc|desc] [rows between 开窗开始 and 开窗结束]) ​ partition b…

【问题随记】System policy prevents Wi-Fi scans,解决连接 WIFI 需要权限的问题

问题随记 System policy prevents Wi-Fi scans&#xff0c;每次打开我的开发板连接 wifi 都会出现下面的弹窗&#xff0c;这也阻挡了我的WIFI自动连接&#xff0c;然后就需要连上屏幕&#xff0c;输入 wifi 密码&#xff0c;这样才能进行 VNC、SSH 等一系列的连接。 问题解决 …

【运维项目经历|026】Redis智能集群构建与性能优化工程

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

【SVG 生成系列论文(九)】如何通过文本生成 svg logo?IconShop 模型推理代码详解

SVG 生成系列论文&#xff08;一&#xff09; 和 SVG 生成系列论文&#xff08;二&#xff09; 分别介绍了 StarVector 的大致背景和详细的模型细节。SVG 生成系列论文&#xff08;三&#xff09;和 SVG 生成系列论文&#xff08;四&#xff09;则分别介绍实验、数据集和数据增…

2024码蹄杯初赛 拔河(非二分解法)

AK选手前来补充一发邪典&#xff08;水数据&#xff09;写法 题面&#xff1a; 简单来说就是给你一个序列&#xff0c;让你选择一段连续区间&#xff0c;使得这个区间平均值最大&#xff0c;同时区间长度大于等于F。 很显然对于区间求和直接用前缀和优化到O(1)&#xff0c;但是…

SRS压测--SRS-Bench

SRS压测--SRS-Bench 简介编译和使用&#xff1a;问题1:问题2:问题3: Player for Live(**直播播放压测**)Publisher for Live or RTC(**直播或会议场景推流压测**)4.Multipel Player or Publisher for RTC(会议场景的播放压测)5.DVR 录制场景&#xff1a;6.RTC Plaintext(压测RT…

git checkout file 撤销对该文件的所有修改

git checkout file 撤销对该文件的所有修改 有时忘记修改了哪些文件,如下命令找到修改的文件 git status -s M linux-4.19.y/fs/jffs2/acl.cM linux-4.19.y/fs/jffs2/acl.hM linux-4.19.y/fs/jffs2/background.cM linux-4.19.y/fs/jffs2/build.cM linux-4.19.y/fs/jffs2/compr.…

jar包部署到服务器,修改jar包配置文件

jar包部署到服务器 打包项目1.jar包分离2.整体打包配置文件配置文件分离整体打包修改配置文件 打包项目 maven项目打包有两种&#xff0c;一是将自己的项目和依赖包分离&#xff0c;二是打包成一个jar包 1.jar包分离 需要在pom文件中引入依赖 <build><finalName&…

CS算法(一)—— 算法原理1

SAR成像专栏目录 CS —— chirp scaling,调频变标。 CS算法基于Papoulis提出的Scaling原理,通过对距离向LFM回波信号进行频率调制,实现了信号的尺度变换(变标)或平移。基于这种原理,可以通过相位相乘替代时域插值来完成随距离变化的RCMC。此外,由于是在二维频域进行数据处…

Docker基础篇之将本地镜像发布到私有库

文章目录 1. Docker Registry简介2. 将本地镜像推送到私有库 1. Docker Registry简介 Docker Registry是官方提供的工具&#xff0c;可以用于构建私有镜像仓库。 2. 将本地镜像推送到私有库 下载Docker Registry docker pull registry现在我们可以从镜像中看到下载的Regist…

【加密与解密】【01】网络安全体系

网络通信OSI模型 物理层&#xff08;Physical&#xff09;链路层&#xff08;DataLink&#xff09;网络层&#xff08;Network&#xff09;传输层&#xff08;Transport&#xff09;会话层&#xff08;Session&#xff09;表示层&#xff08;Presentation&#xff09;应用层&a…

stm32 h5 串口采用DMA循环BUFF接收数据

当使用STM32H5系列的MCU进行串口&#xff08;USART&#xff09;通信&#xff0c;并希望使用DMA&#xff08;Direct Memory Access&#xff09;进行循环缓冲区&#xff08;Circular Buffer&#xff09;接收数据时&#xff0c;你需要进行以下配置步骤&#xff1a; 初始化串口&…

【轻松搞定形象照】助你打造编程等级考试、竞赛专属二寸靓照,报名无忧,展现最佳风采!

更多资源请关注纽扣编程微信公众号 ​ 在数字化时代&#xff0c;拍照似乎变得轻而易举&#xff0c;但当我们需要一张特定规格的一寸照片时&#xff0c;事情就变得复杂起来。随着编程等级考试和各类信息学竞赛的日益临近&#xff0c;不少考生都为了一张符合要求的一寸照片而忙…

抽屉式备忘录(共25041字)

Sing Me to Sleep <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>与妖为邻的备忘录</title&g…

pytorch学习day4

一、卷积层&#xff08;Convolution Layers&#xff09; 卷积层是卷积神经网络&#xff08;CNN&#xff09;中的核心组件&#xff0c;用于提取输入数据的特征。它们通过应用卷积运算来捕捉局部的空间特征&#xff0c;非常适合处理图像和视频等具有空间结构的数据。 1. 卷积层的…