STM32进阶之HC-SR04超声波测距

前言

本实验旨在学习和理解HC-SR04超声波测距模块的工作原理,并利用stm32F103单片机完成一个超声波测距方案。HC-SR04超声波测距模块是一种常用的距离测量传感器,通过发送超声波脉冲并接收其回波来测量物体与传感器之间的距离。

超声波测距原理

利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:

HC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。
所以实现超声波测距就需要俩个条件:

  • 发射和接收的时间差
  • 超声波传输的速率
  • HC-SR04工作原理

    HC-SR04模块的电气参数如示:

  • HC-SR04模块的实物图如示:

  • HC-SR04模块的实物图如示:

  • 有四个引脚:

    Vcc:+5V电源供电
    Trig:输入触发信号(可以触发测距)
    Echo:传出信号回响(可以传回时间差)
    Gnd:接地
    用Trig和Echo引脚实现测距的流程:
    1.通过Trig输出一段至少10us的高电平(脉冲),触发一次测距,超声波在传输的过程中Echo一直输出高电平。
    2.在Trig脉冲输出后,立即检测Echo引脚的电平,测出Echo高电平持续的时间t,t就是超声波在所测距离一个来回所需时间。
    测距时序图如示:

  • OLED屏幕
    OLED(Organic Light Emitting Diode,有机发光二极管)是一种能够自发光的显示技术,广泛应用于单片机开发中的显示模块。它具有高对比度、快速响应、低功耗等优点,适用于小尺寸显示和低功耗应用。
    同时,可以将程序中的某些重要参数直接输出到OLED屏幕当中,这样就无需使用串口助手,方便了程序的调试

  • 工作原理

    OLED显示屏由许多微小的有机发光二极管组成,每个像素点都可以自发光。当施加电压时,有机材料中的电子和空穴结合,产生光,从而形成图像。OLED不需要背光源,因此可以实现更薄、更轻、更柔性的显示器。

实验示例

使用标准库

HC-SR04模块代码

HC.c#include "HC.h"
 

#include "Delay.h"
#include "stm32f10x.h" 
#include "sys.h"
 
#define HCSR04_PORT     GPIOB
#define HCSR04_CLK      RCC_APB2Periph_GPIOB
#define HCSR04_TRIG     GPIO_Pin_11
#define HCSR04_ECHO     GPIO_Pin_10
 
u16 msHcCount = 0; 
 
void HC_Init(void)
{  
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
 
    GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;      
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
 
    GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO;     
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
    GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);    
 
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   
 
    TIM_DeInit(TIM2);
    TIM_TimeBaseStructure.TIM_Period = (1000-1); 
    TIM_TimeBaseStructure.TIM_Prescaler =(72-1); 
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);          
 
    TIM_ClearFlag(TIM4, TIM_FLAG_Update);  
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);    
 
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
 
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;             
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       
    NVIC_Init(&NVIC_InitStructure);
 
    TIM_Cmd(TIM4,DISABLE);     
}
 


 
 
static void OpenTimerForHc()  
{
    TIM_SetCounter(TIM4,0);
    msHcCount = 0;
    TIM_Cmd(TIM4, ENABLE); 
}
 
 
static void CloseTimerForHc()    
{
    TIM_Cmd(TIM4, DISABLE); 
}
 
 
void TIM4_IRQHandler(void)  
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)  
   {
       TIM_ClearITPendingBit(TIM4, TIM_IT_Update  ); 
       msHcCount++;
   }
}
 
 
 
  u32 GetEchoTimer(void)
{
    u32 t = 0;
    t = msHcCount*1000;
    t += TIM_GetCounter(TIM4);
    TIM4->CNT = 0;  
    Delay_ms(50);
    return t;
}
 
  float HC_Get(void )
{
    int 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();        
       lengthTemp = ((float)t/58.0);//cm
       sum = lengthTemp + sum ;
 
   }
    lengthTemp = sum/5.0;
    return lengthTemp;

}

HC.h

#ifndef __HC_H
#define __HC_H    
#include "sys.h"
 
void HC_Init(void);
static void OpenTimerForHc(void);
static void CloseTimerForHc(void);
void TIM4_IRQHandler(void);
u32 GetEchoTimer(void);
float HC_Get(void );
 
#define TRIG_Send  PBout(11)
#define ECHO_Reci  PBin(10)
#endif

 

OLED模块代码

OLED.c

#include "stm32f10x.h"
#include "OLED_Font.h"

/*引脚配置*/
#defin OLED_W_SCL(x)        GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x)        GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    OLED_W_SCL(1);
    OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
    OLED_W_SDA(1);
    OLED_W_SCL(1);
    OLED_W_SDA(0);
    OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
    OLED_W_SDA(0);
    OLED_W_SCL(1);
    OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        OLED_W_SDA(Byte & (0x80 >> i));
        OLED_W_SCL(1);
        OLED_W_SCL(0);
    }
    OLED_W_SCL(1);    //额外的一个时钟,不处理应答信号
    OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
    OLED_I2C_Start();
    OLED_I2C_SendByte(0x78);        //从机地址
    OLED_I2C_SendByte(0x00);        //写命令
    OLED_I2C_SendByte(Command); 
    OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
    OLED_I2C_Start();
    OLED_I2C_SendByte(0x78);        //从机地址
    OLED_I2C_SendByte(0x40);        //写数据
    OLED_I2C_SendByte(Data);
    OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
    OLED_WriteCommand(0xB0 | Y);                    //设置Y位置
    OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));    //设置X位置高4位
    OLED_WriteCommand(0x00 | (X & 0x0F));            //设置X位置低4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
    uint8_t i, j;
    for (j = 0; j < 8; j++)
    {
        OLED_SetCursor(j, 0);
        for(i = 0; i < 128; i++)
        {
            OLED_WriteData(0x00);
        }
    }
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{          
    uint8_t i;
    OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);        //设置光标位置在上半部分
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i]);            //显示上半部分内容
    }
    OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);    //设置光标位置在下半部分
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);        //显示下半部分内容
    }
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
    uint8_t i;
    for (i = 0; String[i] != '\0'; i++)
    {
        OLED_ShowChar(Line, Column + i, String[i]);
    }
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
    uint32_t Result = 1;
    while (Y--)
    {
        Result *= X;
    }
    return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i;
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
    uint8_t i;
    uint32_t Number1;
    if (Number >= 0)
    {
        OLED_ShowChar(Line, Column, '+');
        Number1 = Number;
    }
    else
    {
        OLED_ShowChar(Line, Column, '-');
        Number1 = -Number;
    }
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i, SingleNumber;
    for (i = 0; i < Length; i++)                            
    {
        SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
        if (SingleNumber < 10)
        {
            OLED_ShowChar(Line, Column + i, SingleNumber + '0');
        }
        else
        {
            OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
        }
    }
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i;
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
    }
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
    uint32_t i, j;
    
    for (i = 0; i < 1000; i++)            //上电延时
    {
        for (j = 0; j < 1000; j++);
    }
    
    OLED_I2C_Init();            //端口初始化
    
    OLED_WriteCommand(0xAE);    //关闭显示
    
    OLED_WriteCommand(0xD5);    //设置显示时钟分频比/振荡器频率
    OLED_WriteCommand(0x80);
    
    OLED_WriteCommand(0xA8);    //设置多路复用率
    OLED_WriteCommand(0x3F);
    
    OLED_WriteCommand(0xD3);    //设置显示偏移
    OLED_WriteCommand(0x00);
    
    OLED_WriteCommand(0x40);    //设置显示开始行
    
    OLED_WriteCommand(0xA1);    //设置左右方向,0xA1正常 0xA0左右反置
    
    OLED_WriteCommand(0xC8);    //设置上下方向,0xC8正常 0xC0上下反置

    OLED_WriteCommand(0xDA);    //设置COM引脚硬件配置
    OLED_WriteCommand(0x12);
    
    OLED_WriteCommand(0x81);    //设置对比度控制
    OLED_WriteCommand(0xCF);

    OLED_WriteCommand(0xD9);    //设置预充电周期
    OLED_WriteCommand(0xF1);

    OLED_WriteCommand(0xDB);    //设置VCOMH取消选择级别
    OLED_WriteCommand(0x30);

    OLED_WriteCommand(0xA4);    //设置整个显示打开/关闭

    OLED_WriteCommand(0xA6);    //设置正常/倒转显示

    OLED_WriteCommand(0x8D);    //设置充电泵
    OLED_WriteCommand(0x14);

    OLED_WriteCommand(0xAF);    //开启显示
        
    OLED_Clear();                //OLED清屏
}

 

OLED.h

#ifndef __OLED_H
#define __OLED_H

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

此外,还需要OLED_Fnot.h的文件,用于定义显示字符:

主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "HC.h"
 
uint32_t Length;
float length;
 
 
int main(void)
{
    
  OLED_Init();
  OLED_ShowString(1,1,"Length:");
  OLED_ShowString(2,10,"cm");
  HC_Init();
 
while(1)
    {
        length =HC_Get(); 
        Length = length*100; //eg:54.25->5425
        OLED_ShowNum(2,4,Length/1000,1);  // ?
        OLED_ShowNum(2,5,Length%1000/100,1); //?
        OLED_ShowString(2,6,".");
        OLED_ShowNum(2,7,Length%100/10,1); //?
        OLED_ShowNum(2,8,Length%10/1,1);  //?
        
  }
}
实验效果

总结

1. 使用STM32控制HC-SR04超声波传感器进行测距实验,可以有效测量目标物体与传感器之间的距离。
2. 在实验过程中,需要注意使用适当的引脚进行连接,确保传输信号的稳定性和准确性。
3. 在编写程序时,需要使用定时器来控制超声波模块的工作时间,以及捕获定时器的计数值以计算出距离。
4. 需要了解定时器的工作原理和使用方法,确保能够正确地设置定时器的时钟周期和计数模式。
5. 在数据处理过程中,需要使用速度和距离的关系公式来计算实际的距离值。
6. 还需要注意超声波传感器的特性和测距范围,避免超出传感器的测量能力。
7. 在实验过程中,可以通过多次测量取平均值的方式来提高测距的准确性。
8. 需要注意信号和噪声的干扰,可以使用滤波器来处理不符合要求的信号。
9. 需要注意超声波传感器的工作环境,避免受到其他物体的干扰,导致测量结果不准确。
10. 可以根据实际应用需求,对测距实验进行进一步的优化和改进,例如增加数据显示和记录功能,实现自动控制等。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: STM32HC-SR04超声波测距是一种常见的测距方法。HC-SR04是一种超声波传感器,可以通过发送超声波并接收回波来测量距离。STM32是一种微控制器,可以通过编程控制HC-SR04传感器,并处理测量结果。通过将HC-SR04传感器连接到STM32微控制器上,并编写相应的程序,可以实现精确的距离测量。这种方法在机器人、智能家居、自动化控制等领域得到广泛应用。 ### 回答2: STM32HC-SR04超声波模块的组合被广泛用于测距领域。使用超声波波束可以测量近距离的物体,无需接触,具有高精度、高速度和可靠性等特点,因此它已成为许多应用场合中测量距离、检测物体在距离上的位置和机器人导航等方面的重要工具。 其中,STM32作为一种嵌入式微控制器,具有高性能、低功耗、丰富的外设接口等特点,可以用于控制超声波模块的发送接收、采集返回信号和计算物体距离等功能。最常用的实现方法是使用GPIO口控制超声波发射器,然后使用外部中断或定时器捕获输入来获取超声波信号的回波时间,然后根据声速和回波时间计算物体距离。此外,还需要注意芯片的时钟与等待函数的设置,以便实现准确的距离测量。 而HC-SR04超声波模块是一种低成本的测距传感器,也被称为超声波距离传感器,它包含一个发射器和接收器。它通常被用于实现测量距离和避障功能,特别是在无人机和机器人的控制中应用非常广泛。此外,它还可与其他传感器配合使用,例如加速度计和陀螺仪,来实现在三维空间中的定位和导航。 总之,STM32HC-SR04超声波距离传感器的组合是一种高性能、低成本的测距解决方案,在各种嵌入式系统中都有广泛的应用。 ### 回答3: 在应用中,单片机stm32和超声波传感器hc-sr04常被用到进行距离的测量。首先,hc-sr04超声波传感器利用超声波的发射和接收原理实现测量距离,该传感器通过发射一个超声波脉冲,并测量该脉冲从发射后到接收时所经过的时间来计算所测量的距离。 相应地,在单片机stm32的应用中,首先需要配置相关的GPIO口,将超声波传感器的发射器和接收器与单片机的GPIO口相映射。其次,在程序中需要编写相应的中断服务程序,以便及时地获取超声波的发射和接收信号,通过计算时间差来测量距离。 在嵌入式系统编程中,通常使用定时器和中断服务程序实现对超声波传感器的控制和距离的测量。在stm32中,可以使用定时器来发出超声波,然后在相应的中断函数中判断接收到的超声波信号是否合法,并计算超声波传播的时间差,以得到测量距离。 而在使用hc-sr04传感器时,需要注意的是传感器需要使用5V电源,stm32通常需要使用3.3V电源,因此需要转换电平。此外,读取hc-sr04传感器的输出信号时,需要设置stm32的GPIO口为输入模式,并启动外部中断,同时需要按照hc-sr04传感器的指令进行相应的输入输出控制。 总而言之,stm32hc-sr04超声波传感器可以实现精准的距离测量。在程序实现上,需要编写相应的中断服务程序,并配合使用定时器等硬件模块进行控制和计时,实现测量距离的功能。同时在使用时需要注意硬件电源的兼容和GPIO的设置等问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值