1、基本功能
HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。
2、工作原理
(1).给超声波模块接入电源和地。
(2).给脉冲触发引脚(trig)输入一个长为20us的高电平方波 。
(3).输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时)
(4).当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长。 (5).根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
(6).利用串口,将超声波测到的数据发送到电脑。
(7).利用串口发送阈值给单片机,当测到的距离小于阈值则报警 。
下面程序:
hcsr04.c文件
#include "hcsr04.h"
#include "delay.h"
#include "stm32f4xx.h"
#include "led.h"
#define HCSR04_PORT GPIOB
#define HCSR04_CLK RCC_APB2Periph_GPIOB
#define HCSR04_TRIG GPIO_Pin_5
#define HCSR04_ECHO GPIO_Pin_6
u16 msHcCount = 0;//ms¼ÆÊý
void Hcsr04Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin=HCSR04_TRIG; //·¢Ë͵çƽÒý½ÅTrig
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //ÆÕͨÊä³öģʽ
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //ÍÆÍìÊä³ö
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
GPIO_InitStructure.GPIO_Pin=HCSR04_ECHO; //·¢Ë͵çƽÒý½ÅTrig
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN; //ÆÕͨÊäÈëģʽ
// GPIO_InitStructure.GPIO_OType=
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);
}
static void OpenTimerForHc() //´ò¿ª¶¨Ê±Æ÷
{
TIM_SetCounter(TIM3,0);//Çå³ý¼ÆÊý
msHcCount = 0;
TIM_Cmd(TIM3, ENABLE); //ʹÄÜTIMxÍâÉè
}
static void CloseTimerForHc() //¹Ø±Õ¶¨Ê±Æ÷
{
TIM_Cmd(TIM3, DISABLE); //ʹÄÜTIMxÍâÉè
}
u32 GetEchoTimer(void) //»ñÈ¡¶¨Ê±Æ÷ʱ¼ä
{
u32 t = 0;
t = msHcCount*1000;//µÃµ½ms
t += TIM_GetCounter(TIM3);//µÃµ½us
TIM3->CNT = 0; //½«TIM3¼ÆÊý¼Ä´æÆ÷µÄ¼ÆÊýÖµÇåÁã
delay_ms(50);
return t;
}
float Hcsr04GetLength(void)
{
u32 t = 0;
int i = 0;
float lengthTemp = 0;
float sum = 0;
while(i!=5)
{
TRIG_Send = 1; //·¢ËͿڸߵçƽÊä³ö
delay_us(20);
TRIG_Send = 0;
while(ECHO_Reci == 0); //µÈ´ý½ÓÊտڸߵçƽÊä³ö
OpenTimerForHc(); //´ò¿ª¶¨Ê±Æ÷
i = i + 1;
while(ECHO_Reci == 1);
CloseTimerForHc(); //¹Ø±Õ¶¨Ê±Æ÷
t = GetEchoTimer(); //»ñȡʱ¼ä£¬·Ö±æÂÊΪ1us
lengthTemp = ((float)t*1.7f);//cm
sum = lengthTemp + sum ;
}
lengthTemp = sum/5.0f;
return lengthTemp;
}
timer.c
#include "timer.h"
#include "led.h"
void TIM3_Int_Init(u16 arr,u16 psc) ////ͨÓö¨Ê±Æ÷3Öжϳõʼ»¯
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///ʹÄÜTIM3ʱÖÓ
TIM_TimeBaseInitStructure.TIM_Period=arr; //ÉèÖÃ×Ô¶¯ÖØÔؼÆÊýÖÜÆÚ
TIM_TimeBaseInitStructure.TIM_Prescaler=psc;//ÉèÖ÷ÖƵϵÊý
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//ÏòÉϼÆÊý
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //ÉèÖÃʱÖÓ·ÖƵÒò×Ó
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); //¶¨Ê±Æ÷²ÎÊý³õʼ»¯
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //ÔÊÐí¶¨Ê±Æ÷3¸üÐÂÖжÏ
TIM_Cmd(TIM3,ENABLE); //ʹÄܶ¨Ê±Æ÷3
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //¶¨Ê±Æ÷3ÖжÏ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;//ÇÀÕ¼ÓÅÏȼ¶1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //×ÓÓÅÏȼ¶3;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//¶¨Ê±Æ÷3ÖжϷþÎñº¯Êý
void TIM3_IRQHandler (void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //Òç³öÖжÏ
{
LED1=!LED1;//DS1·×ª
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //Çå³ýÖжϱê־λ
}
usart.c
#include "sys.h"
#include "usart.h"
//////////////////////////////////////////////////////////////////////////////////
//Èç¹ûʹÓÃucos,Ôò°üÀ¨ÏÂÃæµÄÍ·Îļþ¼´¿É.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos ʹÓÃ
#endif
//¼ÓÈëÒÔÏ´úÂë,Ö§³Öprintfº¯Êý,¶ø²»ÐèҪѡÔñuse MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//±ê×¼¿âÐèÒªµÄÖ§³Öº¯Êý
struct __FILE
{
int handle;
};
FILE __stdout;
//¶¨Òå_sys_exit()ÒÔ±ÜÃâʹÓðëÖ÷»úģʽ
void _sys_exit(int x)
{
x = x;
}
//Öض¨Òåfputcº¯Êý
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //Èç¹ûʹÄÜÁ˽ÓÊÕ
//´®¿Ú1ÖжϷþÎñ³ÌÐò
//×¢Òâ,¶ÁÈ¡USARTx->SRÄܱÜÃâĪÃûÆäÃîµÄ´íÎó
u8 USART_RX_BUF[USART_REC_LEN]; //½ÓÊÕ»º³å,×î´óUSART_REC_LEN¸ö×Ö½Ú.
//½ÓÊÕ״̬
//bit15£¬ ½ÓÊÕÍê³É±êÖ¾
//bit14£¬ ½ÓÊÕµ½0x0d
//bit13~0£¬ ½ÓÊÕµ½µÄÓÐЧ×Ö½ÚÊýÄ¿
u16 USART_RX_STA=0; //½ÓÊÕ״̬±ê¼Ç
//³õʼ»¯IO ´®¿Ú1
//bound:²¨ÌØÂÊ
void uart_init(u32 bound){
//GPIO¶Ë¿ÚÉèÖÃ
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //ʹÄÜGPIOAʱÖÓ
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//ʹÄÜUSART1ʱÖÓ
//´®¿Ú1¶ÔÓ¦Òý½Å¸´ÓÃÓ³Éä
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9¸´ÓÃΪUSART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10¸´ÓÃΪUSART1
//USART1¶Ë¿ÚÅäÖÃ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9ÓëGPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//¸´Óù¦ÄÜ
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //ËÙ¶È50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //ÍÆÍ츴ÓÃÊä³ö
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //ÉÏÀ
GPIO_Init(GPIOA,&GPIO_InitStructure); //³õʼ»¯PA9£¬PA10
//USART1 ³õʼ»¯ÉèÖÃ
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(USART1, &USART_InitStructure); //³õʼ»¯´®¿Ú1
USART_Cmd(USART1, ENABLE); //ʹÄÜ´®¿Ú1
//USART_ClearFlag(USART1, USART_FLAG_TC);
#if EN_USART1_RX
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//¿ªÆôÏà¹ØÖжÏ
//Usart1 NVIC ÅäÖÃ
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//´®¿Ú1ÖжÏͨµÀ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//ÇÀÕ¼ÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //×ÓÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQͨµÀʹÄÜ
NVIC_Init(&NVIC_InitStructure); //¸ù¾ÝÖ¸¶¨µÄ²ÎÊý³õʼ»¯VIC¼Ä´æÆ÷¡¢
#endif
}
void USART1_IRQHandler(void) //´®¿Ú1ÖжϷþÎñ³ÌÐò
{
u8 Res;
#if SYSTEM_SUPPORT_OS //Èç¹ûSYSTEM_SUPPORT_OSΪÕ棬ÔòÐèÒªÖ§³ÖOS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
{
Res =USART_ReceiveData(USART1);//(USART1->DR); //¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý
if((USART_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
{
if(USART_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//½ÓÊÕ´íÎó,ÖØпªÊ¼
else USART_RX_STA|=0x8000; //½ÓÊÕÍê³ÉÁË
}
else //»¹Ã»ÊÕµ½0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØпªÊ¼½ÓÊÕ
}
}
}
}
#if SYSTEM_SUPPORT_OS //Èç¹ûSYSTEM_SUPPORT_OSΪÕ棬ÔòÐèÒªÖ§³ÖOS.
OSIntExit();
#endif
}
#endif
main.c
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "beep.h"
#include "usart.h"
#include "key.h"
//#include "exti.h"
#include "timer.h"
#include "hcsr04.h"
/*************³¬Éù²¨²â¾à*****************/
int main()
{
float length=0; //²âÁ¿µÄ³¤¶È
u8 len; //ÉèÖÃãÐֵʱ·¢ËÍÊý¾ÝµÄ³¤¶È
u8 t,i,j;
u16 times=0;
int table1[]={1,10,100,1000};
long long s1=0;//ãÐÖµ
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é2
delay_init(168); //³õʼ»¯ÑÓʱº¯Êý
LED_Init(); //³õʼ»¯LED¶Ë¿Ú
BEEP_Init(); //³õʼ»¯·äÃùÆ÷¶Ë¿Ú
uart_init(115200);
printf("\r\n´®¿Ú³õʼ»¯³É¹¦£¡\r\n");
Hcsr04Init();
printf("\r\n³¬Éù²¨³õʼ»¯³É¹¦£¡\r\n");
TIM3_Int_Init(5000-1,8400-1);
printf("\r\n¶¨Ê±Æ÷³õʼ»¯³É¹¦£¡\r\n");
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//µÃµ½´Ë´Î½ÓÊÕµ½µÄÊý¾Ý³¤¶È
printf("\r\nÄúÉèÖõı¨¾¯ãÐֵΪ:\r\n");
for(t=0;t//Ïò´®¿Ú1·¢ËÍÊý¾Ý
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//µÈ´ý·¢ËͽáÊø
}
printf("\r\n\r\n");//²åÈë»»ÐÐ
USART_RX_STA=0;
for(j=0;j//½«×Ö·ûת»¯³ÉÊý×Ö
{
s1+=(USART_RX_BUF[len-1-j]-48)*table1[j];
}
t=s1; //½«ãÐÖµ¸øt
s1=0;
for(i=0;i<30;i++) //²âÁ¿µÄ´ÎÊý
{
length = Hcsr04GetLength(); //²âÁ¿
printf("²âµ½¾àÀëΪ:%.3f\n",length);
if(length//±¨¾¯
{
GPIO_SetBits(GPIOF,GPIO_Pin_8); //BEEPÒý½ÅÀ¸ß£¬ µÈͬBEEP=1;
delay_ms(300); //ÑÓʱ300ms
}
else
{
GPIO_ResetBits(GPIOF,GPIO_Pin_8); //·äÃùÆ÷¶ÔÓ¦Òý½ÅGPIOF8ÀµÍ
}
}
}
else
{
times++;
if(times%5000==0)
{
printf("\r\n³¬Éù²¨²â¾à\r\n");
}
if(times%200==0)printf("ÇëÊäÈëãÐÖµ8
}
说明:这个程序,输入阈值之后才会开始测量,我设置的测量30次。可以根据自己需要,进行修改。
/*************更新于2018/1/30**********************/
STM32入门阶段,为期十天的超声波测距模块圆满完成了。从对STM32一无所知到现在略知一二我觉得收获还是很大的。
在我看来,开发STM32需要很规范很系统的框架,也就是说我们学习的时候就要条理清晰,慢慢形成网状的知识框架。比如说,很多源程序的一般步骤都很相似,一般来说第一步就是使能相应的时钟。
学习过程中最难的部分应该就是对原理的理解,比如0波特率,溢出时间的计算等。
一般遇到问题,我觉得先去数据手册或者中文手册比较有效,如果解决不了再去百度或者论坛。
在写程序的时候会遇到各种各样的问题,一般常见的有语法错误比较好查到并改正,但是如果在找使能时钟的函数,或者IO口用错时,很难查出错误,所以一定要细心。2灯的语句去逐步判断,把它放到不同的地方,来检测这个语句到底有没有执行,感觉还比较好用。
接下来我会继续学习新知识,并对之前的做补充更正。
/*************更新于2018/2/1*********************/
/**************欢迎指正*****************/
在超声波完成了基本的测距功能之后,通过串口打印出的数据可以看出,当超声波状态不稳定时,数据有较大的波动,而且有一些超出测量范围的数值。这就说明,测量的精度不够,我们可以通过两种办法解决此问题。其一:滤掉较大或较小的没用的数据,留下在测量范围内的数据。其二:加一些滤波,如平均值滤波卡尔曼滤波等。然后可以通过虚拟示波器将数据显示出来,来对比原始数据与滤波后的数据,最终选择效果最好的滤波。
/**************更新于2018/2/4**********************/
/**************欢迎指正*******************/