资讯详情

STM32传感器外设集 -- 蓝牙(HC-05)+超声波(hc-sr04)

前言


一、模块介绍

1.连接图

蓝牙模块 引脚 超声传感器 引脚
GND GND GND GND
VCC 3.3 VCC 5V
EN PA7 TG PB13
RST PA5 EG PB12
TX PB11
RX PB10

2 蓝牙(HC-05)

3.超声波(hc-sr04)

距离传感器的核心是两个。将电信号转换为40发射器 KHz超声波脉冲。接收器监控发射的脉冲。如果接收到它们,它将产生输出脉冲,其宽度可用于确定脉冲传播的距离。太简单了!

三、代码编写

使用串口三和两个定时器

usart3.c

#include "delay.h" #include "usart3.h" #include "stdarg.h"     #include "stdio.h"     #include "string.h"   #include "timer.h"  //串口接收缓存区   u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];     //接收缓冲,最大USART3_MAX_RECV_LEN个字节. u8  USART3_TX_BUF[USART3_MAX_SEND_LEN];    //发送缓冲,最大USART3_MAX_SEND_LEN字节  //通过判断连续两个字符之间的时差不大于10ms决定是否是连续数据. /如果两个字符的接收间隔超过10个ms,认为这不是一个连续数据.也就是超过10ms没有接收到 //任何数据,这意味着接收已经完成. /////接收到的数据状态 //[15]:0,未收到数据;1.收到一批数据. //[14:0]:接收到的数据长度 vu16 USART3_RX_STA=0;       void USART3_IRQHandler(void) {  u8 res;         if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)///接收数据  {     res =USART_ReceiveData(USART3);      if((USART3_RX_STA&(1<<15))==0)///收到的一批数据尚未处理,不再接收其他数据   {     if(USART3_RX_STA<USART3_MAX_RECV_LEN) //也可以接收数据    {     TIM_SetCounter(TIM3,0)//清空计数器              ///清空计数器     if(USART3_RX_STA==0)     ////中断使能定时器7      {      TIM_Cmd(TIM3,ENABLE);//使能定时器7     }     USART3_RX_BUF[USART3_RX_STA  ]=res; //记录接收到的值      }else     {     USART3_RX_STA|=1<<15;    //强制标记接收完成    }    }  }                    }      //初始化IO 串口3 //pclk1:PCLK1时钟频率(Mhz) //bound:波特率    void usart3_init(u32 bound) {     NVIC_InitTypeDef NVIC_InitStructure;  GPIO_InitTypeDef GPIO_InitStructure;  USART_InitTypeDef USART_InitStructure;   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口3时钟使能    USART_DeInit(USART3);  //复位串口3    //USART3_TX   PB10   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出   GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10         //USART3_RX   PB11   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;///浮空输入   GPIO_Init(GPIOB, &GPIO_InitStructure);  //初始化PB11    USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;  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(USART3, &USART_InitStructure); ///初始化串口 3      USART_Cmd(USART3, ENABLE);                    //使能串口     //使接收中断   USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//打开中断       //设置中断优先级  NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //子优先级3  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //IRQ通道使能  NVIC_Init(&NVIC_InitStructure); ///根据指定参数初始化VIC寄存器      TIM3_Init(99,7199);  //10ms中断  USART3_RX_STA=0;  //清零  TIM_Cmd(TIM3,DISABLE);   ///关闭定时器7  }  //串口3,printf 函
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节
void u3_printf(char* fmt,...)  
{  
	u16 i,j; 
	va_list ap; 
	va_start(ap,fmt);
	vsprintf((char*)USART3_TX_BUF,fmt,ap);
	va_end(ap);
	i=strlen((const char*)USART3_TX_BUF);		//此次发送数据的长度
	for(j=0;j<i;j++)							//循环发送数据
	{
	  while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕   
		USART_SendData(USART3,USART3_TX_BUF[j]); 
	} 
}

 

usart3.h

#ifndef __USART3_H
#define __USART3_H	 
#include "sys.h"  
#define USART3_MAX_RECV_LEN		600					//最大接收缓存字节数
#define USART3_MAX_SEND_LEN		600					//最大发送缓存字节数
#define USART3_RX_EN 			1					//0,不接收;1,接收.

extern u8  USART3_RX_BUF[USART3_MAX_RECV_LEN]; 		//接收缓冲,最大USART3_MAX_RECV_LEN字节
extern u8  USART3_TX_BUF[USART3_MAX_SEND_LEN]; 		//发送缓冲,最大USART3_MAX_SEND_LEN字节
extern vu16 USART3_RX_STA;   						//接收数据状态

void usart3_init(u32 bound);				//串口2初始化 
void u3_printf(char* fmt,...);
#endif


hc05.c

#include "delay.h" 			 
#include "usart.h" 			 
#include "usart3.h" 			 
#include "hc05.h" 

#include "string.h"	 
#include "math.h"
//初始化ATK-HC05模块
//返回值:0,成功;1,失败.
u8 HC05_Init(void)
{
	u8 retry=10,t;	  		 
	u8 temp=1;
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//使能PORTA
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 // 端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 //上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化A15
	 
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;				 // 端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA4

	GPIO_SetBits(GPIOA,GPIO_Pin_7);
 	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	HC05_KEY=1;
	HC05_LED=1; 
	
	usart3_init(9600);	//初始化串口2为:9600,波特率.
	
	while(retry--)
	{
		HC05_KEY=1;					//KEY置高,进入AT模式
		delay_ms(10);
		u3_printf("AT\r\n");		//发送AT测试指令
		HC05_KEY=0;					//KEY拉低,退出AT模式
		for(t=0;t<10;t++) 			//最长等待50ms,来接收HC05模块的回应
		{
			if(USART3_RX_STA&0X8000)break;
			delay_ms(5);
		}		
		if(USART3_RX_STA&0X8000)	//接收到一次数据了
		{
			temp=USART3_RX_STA&0X7FFF;	//得到数据长度
			USART3_RX_STA=0;			 
			if(temp==4&&USART3_RX_BUF[0]=='O'&&USART3_RX_BUF[1]=='K')
			{
				temp=0;//接收到OK响应
				break;
			}
		}			    		
	}		    
	if(retry==0)temp=1;	//检测失败
	return temp;	  
}	 
//获取ATK-HC05模块的角色
//返回值:0,从机;1,主机;0XFF,获取失败.							  
u8 HC05_Get_Role(void)
{	 		    
	u8 retry=0X0F;
	u8 temp,t;
	while(retry--)
	{
		HC05_KEY=1;					//KEY置高,进入AT模式
		delay_ms(10);
		u3_printf("AT+ROLE?\r\n");	//查询角色
		for(t=0;t<20;t++) 			//最长等待200ms,来接收HC05模块的回应
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)break;
		}		
		HC05_KEY=0;					//KEY拉低,退出AT模式
		if(USART3_RX_STA&0X8000)	//接收到一次数据了
		{
			temp=USART3_RX_STA&0X7FFF;	//得到数据长度
			USART3_RX_STA=0;			 
			if(temp==13&&USART3_RX_BUF[0]=='+')//接收到正确的应答了
			{
				temp=USART3_RX_BUF[6]-'0';//得到主从模式值
				break;
			}
		}		
	}
	if(retry==0)temp=0XFF;//查询失败.
	return temp;
} 							   
//ATK-HC05设置命令
//此函数用于设置ATK-HC05,适用于仅返回OK应答的AT指令
//atstr:AT指令串.比如:"AT+RESET"/"AT+UART=9600,0,0"/"AT+ROLE=0"等字符串
//返回值:0,设置成功;其他,设置失败.							  
u8 HC05_Set_Cmd(u8* atstr)
{	 		    
	u8 retry=0X0F;
	u8 temp,t;
	while(retry--)
	{
		HC05_KEY=1;					//KEY置高,进入AT模式
		delay_ms(10);
		u3_printf("%s\r\n",atstr);	//发送AT字符串
		HC05_KEY=0;					//KEY拉低,退出AT模式
		for(t=0;t<20;t++) 			//最长等待100ms,来接收HC05模块的回应
		{
			if(USART3_RX_STA&0X8000)break;
			delay_ms(5);
		}		
		if(USART3_RX_STA&0X8000)	//接收到一次数据了
		{
			temp=USART3_RX_STA&0X7FFF;	//得到数据长度
			USART3_RX_STA=0;			 
			if(temp==4&&USART3_RX_BUF[0]=='O')//接收到正确的应答了
			{			
				temp=0;
				break;			 
			}
		}		
	}
	if(retry==0)temp=0XFF;//设置失败.
	return temp;
} 
///
//通过该函数,可以利用USMART,调试接在串口3上的ATK-HC05模块
//str:命令串.(这里注意不再需要再输入回车符)
void HC05_CFG_CMD(u8 *str)
{					  
	u8 temp;
	u8 t;		  
	HC05_KEY=1;						//KEY置高,进入AT模式
	delay_ms(10);
	u3_printf("%s\r\n",(char*)str); //发送指令
	for(t=0;t<50;t++) 				//最长等待500ms,来接收HC05模块的回应
	{
		if(USART3_RX_STA&0X8000)break;
		delay_ms(10);
	}									    
	HC05_KEY=0;						//KEY拉低,退出AT模式
	if(USART3_RX_STA&0X8000)		//接收到一次数据了
	{
		temp=USART3_RX_STA&0X7FFF;	//得到数据长度
		USART3_RX_STA=0;
		USART3_RX_BUF[temp]=0;		//加结束符		 
		printf("\r\n%s",USART3_RX_BUF);//发送回应数据到串口1
	} 				 
}

hc05.h

#ifndef __HC05_H
#define __HC05_H	 
#include "sys.h" 

#define HC05_KEY  	PAout(7) 		//蓝牙控制KEY信号
#define HC05_LED  	PAin(5)		//蓝牙连接状态信号
  
u8 HC05_Init(void);
void HC05_CFG_CMD(u8 *str);
u8 HC05_Get_Role(void);
u8 HC05_Set_Cmd(u8* atstr);	     
#endif  




HC_SR04.c

#include "HC_SR04.h"
#include "timer.h"
#include "delay.h"

//	 

void HC_SR04_IO_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 // 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.12
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;				 // 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.13
	
}

u16 TIM2_UPDATA = 0;
u32 temp = 0;
u16  Get_SR04_Distance(void)
{

   SR04_Trlg=1;       //触发信号是高电平脉冲,宽度大于10us
   delay_us(20);
   SR04_Trlg=0;
   while(!SR04_Echo); //等待高电平
	 TIM_SetCounter(TIM2,0);  //重填计数器值
	 TIM2_UPDATA = 0;         //溢出次数清零
   while(SR04_Echo);  //等待低电平
	 TIM_Cmd(TIM2,DISABLE);  //暂时关闭定时器,保证数据正确性
   temp = (int)(((double)(TIM_GetCounter(TIM2) + (7200* TIM2_UPDATA)))/72/2);  //得到高电平总时间,单位us(定时器的计数值加上溢出的值才是高电平的时间),除以2是计算单程的时间
	 //(7200* TIM2_UPDATA这里为溢出的时间us,可以转换为100* TIM2_UPDATA,因为在公式后面除以了72。可最终理解为TIM2_UPDATA个100us)
	 TIM_Cmd(TIM2,ENABLE);
	 return temp;
}
 


HC_SR04.h

#ifndef __HC_SR04_H
#define __HC_SR04_H	 
#include "sys.h"


#define SR04_Trlg PBout(12)// PB12
#define SR04_Echo PBin(13)// PB13	

void HC_SR04_IO_Init(void);
u16  Get_SR04_Distance(void);



	 				    
#endif

timer.c

#include "timer.h"
#include "delay.h"
#include "sys.h"

//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器2

void TIM2_Init(u16 arr,u16 psc)
{	 
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能TIM2时钟
	
	//初始化定时器2	 
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  

	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//允许更新中断 ,
	
  TIM_Cmd(TIM2,ENABLE ); 	//使能定时器2

}
//定时器2中断服务程序	               主要记录溢出次数

extern u16 TIM2_UPDATA;
extern u8 state_machine;
void TIM2_IRQHandler(void)
{ 
				if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
				{
					  TIM2_UPDATA++;//当回响信号很长时,记录溢出次数,每加一次代表加100us
						TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志位
			  }
}

void TIM3_Init(u16 arr,u16 psc)
{	 
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能TIM2时钟
	
	//初始化定时器3	 
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  

	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//允许更新中断 ,
	
  TIM_Cmd(TIM3,ENABLE ); 	//使能定时器2
   


}

extern vu16 USART3_RX_STA;
//定时器3中断服务程序		    
void TIM3_IRQHandler(void)
{ 	
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//是更新中断
	{	 			   
		USART3_RX_STA|=1<<15;	//标记接收完成
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIM7更新中断标志    
		TIM_Cmd(TIM3, DISABLE);  //关闭TIM7 
	}	    
}

timer.h

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"


void TIM2_Init(u16 arr,u16 psc);
void TIM3_Init(u16 arr,u16 psc);

#endif

main.c

#include "delay.h"
#include "sys.h"
#include "timer.h"
#include "HC_SR04.h"
#include <stdio.h>
#include <math.h>
#include "usart.h"	
#include "hc05.h"
#include "usart3.h"
//STM32超声波测距
int  SAFET_Distance  = 300;         //安全距离
float  Distance = 0;  //距离
//显示ATK-HC05模块的主从状态
void HC05_Role_Show(void)
{
	
	if(HC05_Get_Role()==1)printf("ROLE:Master \n");	//主机
	else printf("ROLE:Slave \n");			 		//从机
//	/*4. 设置蓝牙的名称*/
//	if(HC05_Set_Cmd("AT+NAME=HEATR"))printf("4 蓝牙名称设置失败!\r\n");
//	else printf("4 蓝牙名称设置为 HEATR \r\n");
//	/*5. 设置蓝牙配对密码*/
//	if(HC05_Set_Cmd("AT+PSWD=1234"))printf("5 蓝牙配对密码设置失败!\r\n"); //密码必须是4位
//	else printf("5 蓝牙配对密码设置为 1234 \r\n");
}
//显示ATK-HC05模块的连接状态
void HC05_Sta_Show(void)
{												 
	if(HC05_LED)printf("STA:Connected \n");			//连接成功
	else printf("STA:Disconnect \n");	 			//未连接				 
}	
int main(void)
{
    char displaytemp[16];       //开启字符串空间
	int bobao_delay;
	u16 time=0;
	u16 count = 0;
	u8 reclen=0;
	u8 key = 0;
	delay_init();	    //延时函数初始化
 uart_init(115200);
	HC_SR04_IO_Init();  //超声波模块GPIO初始化
	delay_ms(500);       //上电瞬间加入一定延时在初始化
	TIM2_Init(7199,0);    //以10KHz计数,定时100us
	delay_ms(1000);			//等待蓝牙模块上电稳定
	while(HC05_Init()) 		//初始化ATK-HC05模块  
	{
		printf("ATK-HC05 Error!\n"); 
		delay_ms(500);
		printf("Please Check!!!\n"); 
		delay_ms(100);
	}
	HC05_Role_Show();
	delay_ms(100);
	USART3_RX_STA=0;		
    while(1)
    {
			time++ ;

				Distance = (Get_SR04_Distance() * 331) * 1.0/1000;   //Get_SR04_Distance()返回单程声波传输时间 us,转换为秒=时间*10^(-6);331m/s等于331000mm/s,
			 //最终换算为Distance =Get_SR04_Distance()*10^(-6)*331000=(Get_SR04_Distance() * 331) * 1.0/1000;
			
			 delay_ms(10);
			
			 if(time==300)
			 {
				 u3_printf(" %.1f",Distance);		//发送到蓝牙模块
				 printf("%.1f\n",Distance);
				 HC05_Sta_Show();
				 time=0;
			 }	
			 
			 if(USART3_RX_STA&0X8000)			//接收到一次数据了
			 {
			
				reclen=USART3_RX_STA&0X7FFF;	//得到数据长度
					USART3_RX_BUF[reclen]=0;	 	//加入结束符
				printf("%s\n",USART3_RX_BUF);//显示接收到的数据
				USART3_RX_STA=0;	 
			  }	

    }
    
}

四、 蓝牙APP测试工具

 链接:

APP连接https://pan.baidu.com/s/1LOWmq_fsPrx7TskBymwxlQ?pwd=heru%C2%A0 提取码:heru 


总结

标签: 传感器pwd12

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台