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模块 |
5V | VCC |
TXD | RXD |
RXD | TXD |
GND | GND |
接好线后用手按住蓝牙模块上面的黑色小按键(HC-06没有黑色的按键,注意区分),再去把CH340模块插到电脑上。然后观察发现蓝牙灯是2S闪烁一次,此时是配置模式。模块买回来第一次连接串口助手的时候,在配置蓝牙的时候一定要将串口助手按这个要求配置:设置波特率 38400,数据位 8 位,停止位 1 位,无校验位。
配置的话请参考这篇博客:
不过跟手机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