首页 > 其他分享 >蓝牙通信--STM32读取超声波传感器并在手机APP上显示

蓝牙通信--STM32读取超声波传感器并在手机APP上显示

时间:2024-07-21 18:25:07浏览次数:16  
标签:USART -- APP 蓝牙 STM32 InitStructure 串口 GPIO USART3

1.实物接线

本设计主要是用HC-SR04超声波传感器测量距离,通过串口3经过HC-05蓝牙芯片发送到蓝牙调试助手APP上显示。

STM32管脚模块管脚
3V3超声波VCC
PB7超声波ECHO
PB6超声波TRIG
GND超声波GND
3V3串口3 VCC
PB10串口3 RXD
PB11串口3 TXD
GND串口3 GND

2.涉及的知识点

        本次主要设计串口操作、蓝牙数据收发原理和超声波测距原理

2.1 串口通信

        串口通信在嵌入式开发中无处不在,需要两根数据线就可以完成数据的通信,串口有三种通信方式:单工通信、半双工通信和全双工通信,STM32的串口分别使用TX和RX作为通信数据线,所以可以同时收发数据,属于全双工通信,不过通信速率不高,最高可以达到62.5Kbps,一般常用的就是9600bps和115200bps这俩,比IIC的低速还要低(100K),更不如SPI了。

串口通信协议如下:

我们一般使用的是8位字长的,从图中可以看到串口的一帧数据包括:起始位+数据帧+校验位+停止位。

起始位:起始位的时钟信号是0(低电平)。

数据帧:数据帧主要包括8bit的数据

校验位:我们使用无校验模式

停止位:停止位的时钟信号是1(高电平)

2.2 蓝牙通信原理

        我们用的HC-05蓝牙串口通信模块,是基于 Bluetooth Specification V2.0 带 EDR 蓝牙协议的,可以说是低端的蓝牙模块了,现在都发展到V5.4了,传输速率和安全性得到了极大的提高。

        蓝牙的工作频段是2.4GHz,这个频段是国际通用免费的,无需申请即可直接使用,像WiFi和zigbee都是这个频段的,缺点就是如果附近有其他的2.4G频段的设备,容易造成信号干扰,不过现在很多模组厂家都会做相应的抗干扰措施。

        通过串口和单片机通信,所以它的收发数据速率受单片机的限制,因为它的传输速率最高可以达到138Kbps,而STM32最高才能到62.5Kbps。

2.3 超声波测距原理

        超声波测距原理是在超声波发射端发出超声波,在发射超声波的同时开始计时,超声波在空气中传播,在传播的时刻碰到障碍物,就会返回一个信号给超声波接收端,超声波接收端接收到信号后立即停止计时,这时候会有一个时间差T,而超声波也就是声音在空气中传播的速度为340m/s,通过公式距离S=340 x T/ 2,即可计算出待测距离是多少。我们使用的HC-SR04测距范围是2cm-400cm。采用STM32的I/O口触发方式测距,模块的Trig引脚接到STM32的任意一个GPIO上发出至少10us的高电平信号,通过模块的Echo引脚连接单片机的另一个GPIO口输出一个高电平,然后开始计时,模块会自动发送8个40KHz的方波信号,自动检测是否有信号返回,当有信号返回,高电平持续时间就是超声波从发射到返回的时间,也就是上面的时间差T,然后用上面的公司就可以算出距离S。

3. 代码实现

3.1 串口代码

//串口3的初始化
void USART3_Init(uint32_t  baud)
{
    GPIO_InitTypeDef   GPIO_InitStructure;
    NVIC_InitTypeDef   NVIC_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    
    //1.打开USRAT3外设时钟 + GPIOB外设时钟  PB10-U3_TX  PB11-U3_RX
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    //2.配置GPIO引脚 PB10 PB11  引脚模式需要设置为复用模式
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_AF;             //复用模式
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_NOPULL;         //无上下拉
    GPIO_InitStructure.GPIO_OType    = GPIO_OType_PP;            //推挽输出
    GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_100MHz;        //引脚速率    
    GPIO_InitStructure.GPIO_Pin      = GPIO_Pin_11|GPIO_Pin_10;  //引脚编号
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    //3.把GPIO引脚的功能进行复用  复用为USART3  需要调用两次
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3);
    
    //4.配置USART3  波特率  停止位  数据位  校验位
    USART_InitStructure.USART_BaudRate = baud;                      //波特率  9600bps
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;     //数据位  8bit
    USART_InitStructure.USART_StopBits = USART_StopBits_1;          //停止位  1bit
    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(USART3,&USART_InitStructure);
    
    //5.指定USART3的中断源  接收到数据就发生中断
    USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
    
    //6.配置NVIC 指定中断通道+中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;            //中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;        //响应优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能通道
    NVIC_Init(&NVIC_InitStructure);    
    
    //7.打开串口
    USART_Cmd(USART3,ENABLE);
}

串口代码主要分为七步,上面代码都有相应的序号标注:

1.打开USRAT3外设时钟 + GPIOB外设时钟。因为我们的串口3是接在了PB10和PB11上,所以要想使用串口除了打开串口3时钟外,还必须打开GPIOB的时钟。

 2.配置GPIO引脚 PB10 PB11 ,引脚模式需要设置为复用模式。因为此时的PB10和PB11不是作为普通IO管脚的,是作为串口的,所以引脚模式必须要配置为复用模式。

3.把GPIO引脚的功能进行复用 ,复用为USART3 。因为这里是把PB10和PB11用作引脚的第二功能,而不是默认普通管脚(IO功能),所以必须调用复用库函数,而且是每个管脚都要调用。

4.配置USART3 的:波特率 、停止位 、数据位 、校验位。这个就是2.1提到的串口的数据通信流程。

5.指定USART3的中断源 ,接收到数据就发生中断。我们是通过中断方式使用串口3的,所以必须配置中断源。

6.配置NVIC 指定中断通道+中断优先级。有中断必然要配置中断优先级,不然程序里如果有两个中断STM32就不知道先响应哪个中断了。

7.打开串口。配置完成后要调用库函数把串口使能打开。

下面是串口中断函数:

void USART3_IRQHandler(void)
{
    uint16_t recv_data = 0;
    //检测中断是否发生
    if( USART_GetITStatus(USART3,USART_IT_RXNE) != RESET )
    {
        USART_ClearITPendingBit(USART3,USART_IT_RXNE); //清除中断标志
        
        recv_data = USART_ReceiveData(USART3); //把接收到的数据保存到变量中
        
        USART_SendData(USART3,recv_data); //把收到的数据发送
        while( USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET ); //等待数据发送完成    
    }
}

上面说了,我们是通过中断方式操作串口3的接收和发送,所以必须写中断函数,这个中断的函数名字不能随便改,必须用STM32规定好的名字,不是你想写啥就写啥的,这个在STM32的启动文件里找,启动文件就是那个.s文件,这个文件就是用汇编写的,一般不需要我们改动。

我们这个串口3的中断函数名字在273行,前面那个EXPORT代表的是可以在其他文件引用,后面的[WEAK]是弱定义的意思,这是汇编指令,感兴趣的可以深入研究一下,若定义就是它这里的这个优先级低,如果在其他文件你自己实现了这个函数,那么程序就会执行你写的那个函数,而不会来启动文件执行这个中断函数。

串口3的中断函数很简单,就是判断它的接收数据状态是否完成如果完成了就直接返回0,如果没有完成则继续接收。

3.2 蓝牙的AT指令操作

注意,蓝牙这是没有代码需要实现的,因为它接的是STM32的串口3,它的数据接收就是串口3的数据发送了。但是蓝牙模块拿到手还不能直接和手机app实现蓝牙通信,因为你要配置一下才可以。先把蓝牙模块和USB转TTL用线连上,我用的是淘宝买的10块钱的CH340的USB转TTL,这个质量一般,但是够用了,如果想用更好的建议CP2102。

CH340模块蓝牙HC-05模块
5VVCC
TXDRXD
RXDTXD
GNDGND

接好线后用手按住蓝牙模块上面的黑色小按键(HC-06没有黑色的按键,注意区分),再去把CH340模块插到电脑上。然后观察发现蓝牙灯是2S闪烁一次,此时是配置模式。模块买回来第一次连接串口助手的时候,在配置蓝牙的时候一定要将串口助手按这个要求配置:设置波特率 38400,数据位 8 位,停止位 1 位,无校验位。

配置的话请参考这篇博客:

https://gitcode.csdn.net/6628b5eec46af9264276a8da.html?dp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NTczNDYxLCJleHAiOjE3MjIwOTQ5NjQsImlhdCI6MTcyMTQ5MDE2NCwidXNlcm5hbWUiOiJ3ZWl4aW5fNDEwMTE0NTIifQ.tivQx3Y9LsUe_NtvUGf_WhBFKBIL1GB7z-hBUKz_czY

不过跟手机APP通信的话,波特率要改成115200,我们的程序里也是115200,这点要注意。配置好以后就按第一节说的把蓝牙HC-05和STM32串口3接线就可以了。

3.3 超声波测距代码

#include "stm32f10x.h"
#include "Delay.h"


#define Echo GPIO_Pin_6		//HC-SR04模块的Echo脚接GPIOB6
#define Trig GPIO_Pin_5		//HC-SR04模块的Trig脚接GPIOB5

uint64_t time=0;			//声明变量,用来计时
uint64_t time_end=0;		//声明变量,存储回波信号时间

void HC_SR04_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);	//启用GPIOB的外设时钟	
	GPIO_InitTypeDef GPIO_InitStructure;					//定义结构体
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		//设置GPIO口为推挽输出
	GPIO_InitStructure.GPIO_Pin = Trig;						//设置GPIO口5
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//设置GPIO口速度50Mhz
	GPIO_Init(GPIOB,&GPIO_InitStructure);					//初始化GPIOB
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;			//设置GPIO口为下拉输入模式
	GPIO_InitStructure.GPIO_Pin = Echo;						//设置GPIO口6
	GPIO_Init(GPIOB,&GPIO_InitStructure);					//初始化GPIOB
	GPIO_WriteBit(GPIOB,GPIO_Pin_5,0);						//输出低电平
	Delay_us(15);											//延时15微秒
}

int16_t sonar_mm(void)									//测距并返回单位为毫米的距离结果
{
	uint32_t Distance,Distance_mm = 0;
	GPIO_WriteBit(GPIOB,Trig,1);						//输出高电平
	Delay_us(15);										//延时15微秒
	GPIO_WriteBit(GPIOB,Trig,0);						//输出低电平
	while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);		//等待低电平结束
	time=0;												//计时清零
	while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);		//等待高电平结束
	time_end=time;										//记录结束时的时间
	if(time_end/100<38)									//判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
	{
		Distance=(time_end*346)/2;						//计算距离,25°C空气中的音速为346m/s
		Distance_mm=Distance/100;						//因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
	}
	return Distance_mm;									//返回测距结果
}

        超声波测距的原理前面已经介绍过了,TRIG是声波发出引脚,至少发出10us的高电平后,同时ECHO的引脚会拉高,并且开始计时,模块内部会自动发出8个40KHZ的矩形波,这种连发8个矩形波使超声波测距模块的“超声特征”变得独一无二,从而使接收端能够将发射模式与环境超声噪声区分开,不会造成干扰。然后如果在38ms内声波没有返回,则模块认为此次测量无效,就不会记录此次测量的距离。如果38ms内声波返回了,那么就把ECHO电平由刚才的高电平拉低。我们利用STM32的内部定时器TIIM3进行计时,这样可以更准确,因为TIM3的通道2是接在了PB5上,所以我们把ECHO引脚接到PB5。

上面的代码也很简单,就是先让TRIG维持15us的高电平,然后ECHO低电平的时候计时清零,然后当ECHO变高的时候开始计时,计算ECHO高电平维持的时间就行了,因为TIMER3的中断周期是10us,至于这个10us怎么来的,这个是TIMER3的配置,定时器代码我就不贴了,这个你要去看定时器的相关知识。用这个时间再去除以100,就变成了ms,所以得到的距离数值就是毫米了。

4.实物效果

我先用串口1把数据打印在串口助手上,验证了超声波测距的结果没问题

        然后再把串口改成串口3,接到蓝牙上,同时在手机上打开蓝牙调试器,然后手机搜索蓝牙,我这个蓝牙模块名字叫:HC-05。搜索到后点击连接,配对密码是默认密码:1234。这个密码我没改。然后就可以在蓝牙APP上看到传过来的距离数据。

(蓝牙的图片我找不到了,后面补上)

5.获取源码

源码和具体操作步骤请加微信,白嫖党勿伸手。

标签:USART,--,APP,蓝牙,STM32,InitStructure,串口,GPIO,USART3
From: https://blog.csdn.net/weixin_41011452/article/details/140470277

相关文章

  • 【C++11】lambda表达式
    目录lambda表达式function包装器bind绑定lambda表达式我们之前如果要对一组数据排序,我们可以调用sort,并且传一个仿函数对象或者函数指针或者调用库中的类模板并实例化成对象,比如:boolLess(inta,intb){ returna<b;}structGreater{ booloperator()(inta......
  • 用Python生成图形
    在Python中生成图形可以使用多种库,具体取决于你想要生成的图形类型。以下是一些常见的库和它们通常用于创建的图形类型:Matplotlib -最常用的绘图库,适合创建各种静态、动态以及交互式的可视化图表。Seaborn -基于Matplotlib,提供更高级的数据可视化功能,特别适用于统计图形。......
  • 学编程框架的建议
    前段时间,一位学Java的朋友问我:学长你好,请问怎么能够根据前台用户的输入,自动创建和修改数据库的表呢?在我看的视频教程中,都是先根据需求设计并且手动创建好库表,再去做增删改查。现在数据库表的字段是不固定的,就不知道怎么能动态地操作库表了。这个问题并不难,所有的创建库表操作......
  • E. Rudolf and k Bridges
    链接https://codeforces.com/problemset/problem/1941/E题目思路比较容易想到的一道题(但是之前想了好久hhh)。首先题意是对一连串的桥的代价求和,不难想到是前缀和求区间最小。那么就可以拆分这题为每行处理。容易想到dp,而且dp的过程也比较简单:设dp[i]是到达i的最小代价,那么对......
  • 大语言模型和API网关
    一、大模型能力和WAF配置员今天试验了百度大模型文心一言,大模型可以通过学习键值对内容,输出较为安全的正则表达式:1)以往的WAF配置员就是针对无校验的键值对,提供正则表达式以白名单的方式进行安全防护,防止外部的恶意输入。  可以参考华为产品:https://support.huawei.com/hedex......
  • ubuntu_安装cuda
    1.下载CUDAToolkithttps://developer.nvidia.com/cuda-toolkit-archive2.按照命令下载,安装3.环境变量vim~/.bashrc最后面添加exportPATH="/usr/local/cuda-12.1/bin:$PATH"exportLD_LIBRARY_PATH="/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH"退出,刷新source~......
  • Linux - 网络状态工具ss命令详解
    ss是SocketStatistics的缩写。ss命令可以用来获取socket统计信息,它显示的内容和netstat类似。但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快。当服务器的socket连接数量变得非常大时,无论是使用netstat命令还是直接cat/proc......
  • vue3 ts 项目增加eslint插件实现命令行报错提示和vscode 报错提示,eslint 最新版本9.x
    快速开始安装eslintyarnaddeslint-D然后运行初始化eslintnpxeslint--init接着上面命令会自动生成一个新文件eslint.config.jseslint.config.jsimportglobalsfrom"globals";importpluginJsfrom"@eslint/js";importtseslintfrom"typescript-eslint......
  • 2024.7.20
    模拟赛昨天的题解还在咕。。。今天的又来了。。。T1SimpleMath2签到题,推一推式子就好了。\[\lfloor{\frac{a^b}{c}}\rfloor\modc=x\]\[\lfloor{\frac{a^b}{c}}\rfloor=k\timesc+x\]\[\frac{a^b-(a^b\modc)}{c}=k\timesc+x\]\[a^b-(a^b\modc)=k......
  • 暑假集训csp提高模拟2
    赛时rank11,T130,T20,T320,T420T1活动投票摩尔投票模板题点此查看代码#include<bits/stdc++.h>#include<bits/extc++.h>//usingnamespace__gnu_pbds;//usingnamespace__gnu_cxx;usingnamespacestd;#defineinfile(x)freopen(x,"r",stdin)#define......