Bootstrap

STM32F103开发板与超声波测距并oled显示(可拓展至多个超声波)

最近在做一个比赛需要用到超声波测距,代码都写好了,就顺手写了这一篇博客,以供大家参考,实测有效(我使用的是STM32F103C8T6以及3个超声波测距模块HC-sr04),如果有高手觉得代码可以更加优化,或有疏漏之处欢迎交流,指正。

一、超声波测距模块简介

超声波测距模块是一种利用声波的反射和传播时间来计算距离的装置。它通常由发送器和接收器两部分组成,发送器将超声波发送到目标物体上,接收器则接收从目标物体反射回来的超声波。通过测量超声波从发送到返回的时间,可以计算出物体与测距模块之间的距离。

二、使用超声波测距模块

确保电源供电正常,一般为5V。

  • 在单片机或其他控制设备中编写程序,给超声波测距模块的Trig引脚输入一个高电平信号。这个高电平信号需要持续至少10微秒,以触发测距功能。
  • 当Trig引脚接收到高电平信号后,超声波测距模块会自动发送8个40KHz的声波。
  • 在超声波发射后,需要等待回波引脚(Echo)变为高电平,这表示超声波已经碰到物体并反射回来被模块接收到。这个高电平的持续时间就是超声波从发射到返回的时间。
  • 在回波引脚(Echo)变为高电平后,需要开始计时,直到回波引脚变为低电平为止。这个时间就是超声波从发射到返回的总时间。
  • 根据超声波测距模块的工作原理,测试距离 = (高电平时间 * 声速(340M/S) )/2。这个公式可以根据实际情况进行修改和调整,以确保测量结果的准确性和可靠性。

注意接线:超声波1 的 trig 接PA10 echo接PA11(如果想使用其他的引脚,请在hcsr04.h和timer.h文件中分别修改,具体见代码部分)

超声波代码部分(注意有头文件,配合定时器代码使用

#include "stm32f10x.h"                  // Device header
#include "HCSR04.h"
#include "Timer.h"
#include "Delay.h"

uint16_t Time1;
uint16_t Time2;
uint16_t Time3;
void HCSR04_Init()//每个超声波的T,E脚初始化
{
	RCC_APB2PeriphClockCmd(Trig_RCC1, ENABLE);
	RCC_APB2PeriphClockCmd(Trig_RCC2, ENABLE);
	RCC_APB2PeriphClockCmd(Trig_RCC3, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = Trig_Pin1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Trig_Port1, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = Echo_Pin1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Echo_Port1, &GPIO_InitStruct);
/****/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = Trig_Pin2;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Trig_Port2, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = Echo_Pin2;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Echo_Port2, &GPIO_InitStruct);
/***********/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = Trig_Pin3;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Trig_Port3, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = Echo_Pin3;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(Echo_Port3, &GPIO_InitStruct);	
	
	GPIO_ResetBits(Trig_Port1, Trig_Pin1);
	GPIO_ResetBits(Trig_Port2, Trig_Pin2);
	GPIO_ResetBits(Trig_Port3, Trig_Pin3);
}


uint16_t HCSR04_GetValue1()//超声波1
{
	GPIO_SetBits(Trig_Port1, Trig_Pin1);
	Delay_us(45);//为了超声波能完全发出
	GPIO_ResetBits(Trig_Port1, Trig_Pin1);
	Timer_Init();
	Delay_ms(80);
	return ((Time1 * 0.0001) * 34000) / 2;//计算距离定时器计数*每次计数时间*声速/2
}

/**************************************/

uint16_t HCSR04_GetValue2()//超声波2
{
	GPIO_SetBits(Trig_Port2, Trig_Pin2);
	Delay_us(45);
	GPIO_ResetBits(Trig_Port2, Trig_Pin2);
	Timer_Init();
	Delay_ms(80);
	return ((Time2 * 0.0001) * 34000) / 2;
}

/************************************/
uint16_t HCSR04_GetValue3()//超声波3
{
	GPIO_SetBits(Trig_Port3, Trig_Pin3);
	Delay_us(45);
	GPIO_ResetBits(Trig_Port3, Trig_Pin3);
	Timer_Init();
	Delay_ms(80);
	return ((Time3 * 0.0001) * 34000) / 2;
}
/************************************/

超声波头文件代码(即#include "HCSR04.h")

#ifndef __HCSR04_H
#define __HCSR04_H

#define Trig_Port1 		GPIOA
#define Trig_Pin1 		GPIO_Pin_10
#define Trig_RCC1		RCC_APB2Periph_GPIOA

#define Echo_Port1 		GPIOA
#define Echo_Pin1 		GPIO_Pin_11
#define Echo_RCC1		RCC_APB2Periph_GPIOA
/**************/
#define Trig_Port2 		GPIOC
#define Trig_Pin2 		GPIO_Pin_13
#define Trig_RCC2		RCC_APB2Periph_GPIOC

#define Echo_Port2 		GPIOC
#define Echo_Pin2 		GPIO_Pin_14
#define Echo_RCC2		RCC_APB2Periph_GPIOC
/************************/
#define Trig_Port3 		GPIOB
#define Trig_Pin3 		GPIO_Pin_13
#define Trig_RCC3		RCC_APB2Periph_GPIOB

#define Echo_Port3 		GPIOB
#define Echo_Pin3 		GPIO_Pin_12
#define Echo_RCC3		RCC_APB2Periph_GPIOB
#include "stm32f10x.h"                

void HCSR04_Init();
uint16_t HCSR04_GetValue1();
uint16_t HCSR04_GetValue2();
uint16_t HCSR04_GetValue3();
#endif 

定时器代码.c(注意头文件)

#include "stm32f10x.h"                 
#include "Timer.h"

extern uint16_t Time1;
extern uint16_t Time2;
extern uint16_t Time3;
void Timer_Init()
{
	Time1 = 0;
	Time2 = 0;
	Time3 = 0;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//选择APB1总线下的定时器Timer2
	
	TIM_InternalClockConfig(TIM2);		//TIM2使用内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//计数模式,此处为向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 7199;		//ARR 1 = 0.0001S
	TIM_TimeBaseInitStructure.TIM_Prescaler = 0;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;		//高级计时器特有,重复计数
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);		//使能中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;		//中断通道选择
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//优先级,同上
	
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);		//打开定时器
}

void TIM2_IRQHandler()		//定时器2的中断函数处理函数,即每次中断发生时要运行的程序
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//中断触发,定时器记一次
	{

		if (GPIO_ReadInputDataBit(Echo_Port1, Echo_Pin1) == 1)//超声波1 E脚电平为高即加1
		{
			Time1 ++;
		}
				if (GPIO_ReadInputDataBit(Echo_Port2, Echo_Pin2) == 1)//超声波2 E脚电平为高即加1
		{
			Time2 ++;
		}
				if (GPIO_ReadInputDataBit(Echo_Port3, Echo_Pin3) == 1)//超声波3 E脚电平为高即加1
		{
			Time3 ++;
		}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);		//清空标志位
	}
}

定时器头文件代码.h

#ifndef __TIMER_H
#define __TIMER_H

#define Trig_Port1 		GPIOA
#define Trig_Pin1 		GPIO_Pin_10
#define Trig_RCC1		RCC_APB2Periph_GPIOA

#define Echo_Port1 		GPIOA
#define Echo_Pin1 		GPIO_Pin_11
#define Echo_RCC1		RCC_APB2Periph_GPIOA
/**************/
#define Trig_Port2 		GPIOC
#define Trig_Pin2 		GPIO_Pin_13
#define Trig_RCC2		RCC_APB2Periph_GPIOC

#define Echo_Port2 		GPIOC
#define Echo_Pin2 		GPIO_Pin_14
#define Echo_RCC2		RCC_APB2Periph_GPIOC
/************************/
#define Trig_Port3 		GPIOB
#define Trig_Pin3 		GPIO_Pin_13
#define Trig_RCC3		RCC_APB2Periph_GPIOB

#define Echo_Port3 		GPIOB
#define Echo_Pin3 		GPIO_Pin_12
#define Echo_RCC3		RCC_APB2Periph_GPIOB

void Timer_Init();

#endif

主函数代码

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "HCSR04.h"
#include "Delay.h"

uint16_t L1;
uint16_t L2;
uint16_t L3;
int main(void)
{
	OLED_Init();
	HCSR04_Init();
	OLED_ShowString(1, 1, "distance:");
	OLED_ShowString(1, 13, "cm");
	OLED_ShowString(2, 1, "distance:");
	OLED_ShowString(2, 13, "cm");
	OLED_ShowString(3, 1, "distance:");
	OLED_ShowString(3, 13, "cm");
	while (1)
	{
	L1 = HCSR04_GetValue1();
		OLED_ShowNum(1, 10, L1, 3);
	L2 = HCSR04_GetValue2();
		OLED_ShowNum(2, 10, L2, 3);
L3 = HCSR04_GetValue3();
		OLED_ShowNum(3, 10, L3, 3);
		Delay_ms(100);
	}
}

显示屏函数我使用的是江科大提供的代码,delay函数应该都是通用的,没有可以删去while循环最后一行的Delay_ms(100);

如果想要全部工程代码,我可以弄一个百度网盘连接,看需求多不多,代码就分享到这了,欢迎评论区交流。这是第一次做博客可能写的不好,但代码没问题。

;