HC-SR04超声波传感器测距(学习笔记)

1、任务

利用HC-SR04超声波传感器进行测距实验,通过stm32单片机板载的按键实现手动触发测距,将测得的距离显示在OLED屏上。

2、工作原理

a.单片机IO向HC-SR04的Trig接口发送一个持续时间大于10us高电平信号;

b.HC-SR04接收到来自单片机的信号后,自动发送8个频率为4KHz的方波,方波遇到障碍物后会反射,HC-SR04会自动检测反射回来的信号;

c.HC-SR04的Trig接口接收到单片机高电平信号自动发送方波的同时,Echo接口向单片机IO口发送高电平信号,当HC-SR04接收到返回信号时,Echo接口向单片机IO发送低电平信号,高电平持续的时间就是超声波从发射到返回的总时间T,此时空气中的声速为v,则测得的距离S=(T*v)/2。

3、硬件

a.硬件:HC-SR04传感器、OLED显示屏、stm32F103C8T6单片机

b.接线:VCC——单片机5V                    GND——单片机GND 

              Trig——PA6(TIM3_CH1)          Echo——PA7(TIM3_CH2)

 4、代码实现

代码思路:利用定时器中断获取高电平持续时间,将TIM3重装载周期设为1000次,每次1us,当Echo上升沿时开启TIM3,TIM3将每1ms进入中断,在中断函数中记录中断次数,多少次中断就是多少ms,再获取TIM3的计数寄存器值,寄存器值为0~1000,即0us~1000us。

定时时间:Tout=((arr+1)*(psc+1))/Tclk

                  Tclk:TIMx的输入时钟频率(单位MHz,一般是72MHz)
                  Tout:TIMx的溢出时间,即定时时间(单位us)
                  arr:自动重装载寄存器周期的值,自定
                  psc:时钟频率除数的预分频值

hcsr.c

#include "hcsr.h"
#include "sys.h"
#include "delay.h"

//定时时间=((arr+1)*(psc+1))/72MHz=1000us=1ms
u32 arr=1000-1;
u32 psc=72-1;

u32 HcCount=0;  //ms计数

//初始化
void HcsrInit(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    //使能RCC时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    //PA6 Trig 发送电平引脚
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;         
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;  
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOA, GPIO_Pin_6);

    //PA7 Echp 接收电平引脚
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    GPIO_ResetBits(GPIOA,GPIO_Pin_7);

    //PA0 KEY 
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_0);

    //定时器TIM3初始化
    TIM_TimeBaseStructure.TIM_Period=arr;
    TIM_TimeBaseStructure.TIM_Prescaler=psc;
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;       //不分频
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;   //向上计数
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);                    //允许更新中断
    TIM_Cmd(TIM3,DISABLE);                                      //失能时钟外设(先关掉,后面开)

    //中断管理
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;             //开启TIM3时钟中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          //响应优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能中断
    NVIC_Init(&NVIC_InitStructure);

}

//定时器TIM3中断服务程序
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)            //检查TIM3是否发生中断,TIM3开启后1ms进一次中断
    {
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update);              //清除更新中断标志
        HcCount++;                                              //溢出的时间,一次1ms
    }
}

//打开定时器
void OpenTimerForHc()                   
{
        TIM_SetCounter(TIM3,0);	//清除计数
        HcCount = 0;
        TIM_Cmd(TIM3, ENABLE);  //使能TIM2外设
}

//关闭定时器
void CloseTimerForHc()                  
{
        TIM_Cmd(TIM3, DISABLE); //使能TIM2外设
}

//获取定时器时间
u32 GetTimer(void)
{
    u32 t = 0;
    t = HcCount*1000;           //转化为us
    t += TIM_GetCounter(TIM3);  //未溢出的时间,单位us
    TIM2->CNT = 0;              //定时器计数寄存器值清0
    delay_ms(50);
    return t;
}

hcsr.h

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

#define TRIG_Send PAout(6)
#define ECHO_Reci PAin(7)
#define KEY PAin(0)

void HcsrInit(void);
void TIM3_IRQHandler(void);
u32 GetTimer(void);
void OpenTimerForHc(void);
void CloseTimerForHc(void);

#endif

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "hcsr.h"
#include "OLED.h"

int main(void)
{	  			

	float distance = 0;
	u32 time=0;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	OLED_Init();
	delay_init();
	HcsrInit();
	
	OLED_ShowString(1,3,"Distance-cm:");

	while(1)
	{
		//当按键按下触发传感器测距
		if(KEY==0)
    	{
			delay_ms(20);
			if(KEY==0)
			{
				//防止按键长按连触
				while(KEY==0);

				//向传感器发出信号,开始测距
				TRIG_Send=1;
				delay_us(20);
				TRIG_Send=0;

				//捕获高电平持续时间
				while(ECHO_Reci==0);
				OpenTimerForHc();		//捕获上升沿开启时钟
				while(ECHO_Reci==1);
				CloseTimerForHc();		//捕获下降沿关闭时钟
			}
    	}

		time = GetTimer();				//获取高电平持续时间
		distance =time/58.3;			//计算距离
		OLED_ShowNum(3,7,distance,4);	//OLED显示
		
	}
}

5、效果

 

6、说明

参考文章:传感器系列(一)——超声波测距传感器 HC—SR04模块 

                  超声波测距为什么除以58