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

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

超声波测距原理
利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:在这里插入图片描述

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

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

HC-SR04模块的实物图如示:
参数 描述
工作电压 DC 5V
工作电流 小于15mA
工作频率 40kHz
探测距离范围 2cm - 400cm
探测角度 15度
脉冲宽度 10微秒以上
输出信号 TTL电平信号,与目标距离成反比
模块尺寸 45mm × 20mm × 15mm

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); //?

}
}
实验效果
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值