【单片机】超声波测距模块

前言

大家好,我是林白柏;

希望你看完之后,能有所收获,不足请指正!

PS:本文提到的模块都使用正点原子的stm32开发板战舰驱动,模块用的某宝现成的模块。


模块介绍(可跳过)

模块长这个亚子

image-20220421083535208

模块内部电路框图如下

image-20220421082112952

模块原理图如下

image-20220421082513703

探头工作原理参考超声波传感器是如何形成超声波的

模块使用MAX232做电压转换,驱动超声波发射探头;

模块接收电路原理参考超声波接收电路原理解析

经过模块内部MCU的处理,将接口封装成简单的Trig和Echo两根线,用户只需要了解通讯协议,便可以实现超声波测距的功能;

当模块内部MCU接收到Trig上的超过10us高电平时,会向发射电路循环发送8个40kHz的脉冲并检测接收电路是否有回波,一旦检测到回波信号,便在Echo上输出高电平。

Echo上高电平持续时间和测量距离成正比,计算公式为距离(m)=高电平时间*声速(340m/s)/2,模块最大测量距离为4m。

超声波时序图如下

image-20220421083321738

模块使用

模块接口只有四个脚:地、电源、Trig、Echo。

电源需要接+5V,因为模块的发射电路用到芯片MAX232,工作电压4.5V-5.5V;此处需要考虑stm32gpio的耐压值,别把io烧了;

Trig脚连接到stm32的通用gpio且配置为下拉输出,用于产生15us高电平触发信号;

产生触发信号后,测量Echo脚上高电平持续时间(回响信号),根据公式距离(m)=高电平时间*声速(340m/s)/2可以算出距离;测量回响信号有两种方法(大差不差):

  1. 使用定时器的输入捕获,触发输入捕获时,定时器会将计数值拷贝到特定的寄存器,只需要读取捕获上升沿时和捕获下降沿时的计数,两个计数差值便是高电平持续时间;Echo脚需要连接stm32的定时器引脚TIM_CH;

    例如STM32F103ZET6的PB0、PB1、PB8、PB9

image-20220421223001439

  1. 使用外部中断、上升沿触发,当中断触发时,启动定时器计数,并设置为下降沿触发;下降沿中断触发时,定时器计数值即为高电平持续时间;

本次使用方法1。

代码:h文件

头文件声明了两个函数:初始化、产生触发信号;初始化时,注册回调函数,用于接收返回的距离值,单位mm。

typedef void ( * ultrasonic_event_handle )( uint16_t * value_mm );

extern void ultrasonic_ranging_init( ultrasonic_event_handle event_handle );

extern void ultrasonic_ranging_trig_measure( void );

代码:c文件

源文件中分为两部分:初始化,中断服务函数。

初始函数,初始化io、定时器、中断,并保存回调函数指针

static ultrasonic_event_handle m_event_handle;
static ultrasonic_receive_state_t rx_state;

void ultrasonic_ranging_init( ultrasonic_event_handle event_handle  ) {
	GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_ICInitTypeDef  TIM_ICInitStructure;  

    //[ 与硬件相关 ]
	//初始化时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

	//gpio
	//Trig脚,初始化为输出,初始化完成后拉低
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOG, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOG,GPIO_Pin_14);
	//Echo脚,初始化为下拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOB,GPIO_Pin_0);

    //定时器TIM3_CH3,1us计数加1,10ms溢出,输入捕获
    TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出  
    TIM_TimeBaseStructure.TIM_Prescaler =(72-1);    //预分频器,1M的计数频率,1us加1.       
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿触发捕获
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; 
    TIM_ICInitStructure.TIM_ICFilter = 0x03;
    TIM_ICInit(TIM3, &TIM_ICInitStructure);

    TIM_Cmd(TIM3,ENABLE );    //使能定时器3

	//初始化TIM3中断
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);

    TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);

	//[ 与硬件无关 ]
    m_event_handle = event_handle;//保存回调函数
	rx_state.cnt = 0;//内部变量,中断服务函数中标记状态
	rx_state.reserved = 0;
	rx_state.rx_start = 0;
}

触发信号产生函数,拉高15us后拉低

void ultrasonic_ranging_trig_measure( void ) {
    GPIO_SetBits(GPIOG,GPIO_Pin_14);
    delay_us(15);
    GPIO_ResetBits(GPIOG,GPIO_Pin_14);
}

定时器TIM3中断服务函数

void TIM3_IRQHandler(void)
{ 		    	 
    uint16_t d_val = 0;

    
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)//计数溢出中断,因为
	{
        if( rx_state.rx_start ) {
            //模块最大测量距离为4m,根据公式【距离(m)=高电平时间*声速(340m/s)/2】可计算出4m时高电平时间为23.529ms,而定时器配置为10ms溢出,所以溢出时需要记录
            if(rx_state.cnt<14) {
                rx_state.cnt++;
            }
			else {
			    rx_state.cnt = 0;
                rx_state.rx_start = 0;
            }
        }
	}
	if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)//echo脚触发的中断
	{	  
		if(UR_READ_DATA_PIN)//上升沿捕获,读取echo脚电平,为高说明是上升沿
		{
		    TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);
            TIM_SetCounter(TIM3,0);//计数清零
            rx_state.rx_start = 1;//捕获上升沿,标记开始接收回响信号
		}else //下降沿捕获
		{
		    uint16_t rt_val_mm = 0;
            
            d_val  = TIM_GetCapture3(TIM3);
			TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);

            //测量结束,根据公式计算距离,并通过回调函数将距离返回给应用程序
            d_val  = d_val + ( rx_state.cnt * 10000 );
            //rt_val_mm = d_val/10000 * 340 / 2;//测试距离=(高电平时间*声速(340M/S))/2
            rt_val_mm = d_val * 17 / 1000;
            rx_state.cnt = 0;
            rx_state.rx_start = 0;
            
            m_event_handle( &rt_val_mm );
		}				 		     	    					   
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);	 	    
}

代码使用方法

初始化时,应用程序注册一个回调函数,后续通过回调函数接收距离值

//以下使用演示

static  void ultrasonic_evt_cb( uint16_t * value_mm ) {//定义回调函数
    _LOG_DEBUG("[ur] measurement = %d mm\n\n", *value_mm);
}

int main(void){
    ultrasonic_ranging_init( ultrasonic_evt_cb );//将回调函数指针传给初始化函数
    
    while(1) {
        ultrasonic_ranging_trig_measure();
        delay_ms(2000);
    }
}

代码获取

共用代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/common

驱动代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/component

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值