基于51单片机的超声波液位测量与控制系统

基于51单片机液位控制器

(仿真+程序+原理图+PCB+设计报告)

功能介绍

具体功能:

1.使用HC-SR04测量液位,LCD1602显示;

2.当水位高于设定上限的时候,对应声光报警报警,继电器控制水泵停止加水;

3.当水位低于设定上限的时候,对应声光报警报警,继电器控制水泵开始加水;

4.按键可以设置液位上下限;

​演示视频:

基于51单片机的超声波液位测量与控制系统

程序

cs 复制代码
/***************************************************************
基于51单片机的超声波水位监测报警系统
具体水泵控制和排水控制
***微信公众号:木子单片机****
***************************************************************/
#include <reg52.h>
#include <intrins.h>		// 包含循环移位:_cror_
#include "main.h"     
//----------------------------------------------------------------------

uchar code TabNumASCII[10] =    {'0','1','2','3','4','5','6','7','8','9'};

bool	g_flag = isNo;		//用于标记超时(65.536ms) 	  
bool	g_flag05s = isNo;	//用于标记0.52秒  
uchar 	ucCount = 0;			//用于计数0.52秒     
    
uint	uiH = 80;			//设定的最高报警水位 H
uint	uiL = 30;			//设定的最低报警水位 L
uint	uiD = 100; 			//检测探头到水库底部的距离 D 

bool	g_flagSwitch = isNo;		//控制阀门连续开启间隔延时(保护)标志
bool	g_flagBeepTimer = isNo;	//定时提醒标志
		

//-----------------------------------------------------------------------
// 延时10us
void delay10us(void)		//@12MHz
{
	unsigned char i;

	_nop_();
	i = 2;
	while (--i);
}

// 延时100us
void delay100us(void)		//@12MHz
{
	uchar i;

	_nop_();
	i = 47;
	while (--i);
}

// 延时125us
void delay125us(void)		//@12MHz
{
	unsigned char i;
	i = 60;
	while (--i);
}

// 延时5ms
void delay5ms(void)		//@12.000MHz
{
	unsigned char i, j;

	i = 10;
	j = 183;
	do
	{
		while (--j);
	} while (--i);
}

// 延时500ms
void delay500ms(void)		//@12MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 205;
	k = 187;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

//-----------------------------------------------------------------------
//初始化IO端口			
void initIO(void)					
{
	P0 = 0xff;
	P1 = 0xff;
	P2 = 0xff;
	P3 = 0xff;
}

// 初始化定时器0,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer0(void)		
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0;				//定时器初值清零
	TH0 = 0;				//定时器初值清零
	//TR0 = 1;   			//开定时器0
    ET0 = 1;  			//开定时器0中断
    EA = 1;     			//开总中断    	
	
}

// 初始化定时器1,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer1(void)		//50毫秒@12.000MHz
{	
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0xB0;		//设置定时初值
	TH1 = 0x3C;		//设置定时初值	
	TR1 = 1;		//定时器1开始计时
	ET1 = 1;  	//开定时器0中断
}

//-----------------------------------------------------------------------
//定时器0中断
void zd0(void) interrupt 1 		 
{
	
	g_flag = isYes;							//中断溢出标志,g_flag = isYes超过测距范围
	if(++ucCount >= 8)
	{
		ucCount = 0;
		g_flag05s = isYes;					//g_flag05s = isYes定时0.52秒到,用于测量周期延时
	}
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	
}


//定时器1中断 定时50ms
void tm1_isr() interrupt 3 using 1
{
	static uchar count = DATA_switchTime;	//50ms的200倍 = 10S
	static uchar uiCount = 1200;				//			= 1分钟		
	static uint uiCount_BeepTimer = DATA_BeepTimer;

	TL1 = 0xB0;		//设置定时初值
	TH1 = 0x3C;		//设置定时初值

	if (g_flagSwitch == isNo)
	{
		if (count-- == 0)               //50ms * 200 -> 10s
		{
			count = DATA_switchTime;
			g_flagSwitch = isYes;
			// TR1 = 0;
		}
	}	
	
	if(g_flagBeepTimer == isNo)
	{
		if (uiCount-- == 0)               //= 1分钟
		{
			uiCount = 1200;
			if(uiCount_BeepTimer-- == 0)
			{
				uiCount_BeepTimer = DATA_BeepTimer; 
				g_flagBeepTimer = isYes;
				// TR1 = 0;
			}			
			
		}

	}
}

//-----------------------------------------------





//外部中断1
void exint1() interrupt 2 
{
    EX1 = 0; 			//关闭当前中断
	TR0 = 0;   			//关闭时器0
	
}
//-----------------------------------------------------------------------
  
//读LCD忙状态并等待忙状态结束
void LCD_waitNotBusy(void)
{
	IO_LCD_Data = 0xFF; 
	io_LCD_RS = 0;
	io_LCD_RW = 1;
	io_LCD_E = 0;
	_nop_();
	_nop_();
	io_LCD_E = 1;
	while(IO_LCD_Data & 0x80); //检测如果是忙信号,一直等到不忙
}

//给LCD写指令
void LCDWriteCommand(uchar command,bool ifReadBusy) //ifReadBusy = 1 时先进行忙检测
{
	if (ifReadBusy == isReadBusy) LCD_waitNotBusy(); //根据需要检测忙
	IO_LCD_Data = command;
	io_LCD_RS = 0;
	io_LCD_RW = 0;	
	io_LCD_E = 0;
	_nop_();
	_nop_();
	io_LCD_E = 1;	
}

//给LCD写数据
void LCDWriteData(uchar dat) 
{
	LCD_waitNotBusy(); //等到不忙
	IO_LCD_Data = dat;
	io_LCD_RS = 1;
	io_LCD_RW = 0;
	io_LCD_E = 0; 
	_nop_();
	_nop_();
	io_LCD_E = 1;
}




// 初始化LCD1602液晶显示屏
void initLCD1602(void) 
{
	uchar	i;	
	IO_LCD_Data = 0;								// 数据端口清零
	for(i = 0; i < 3; i++)						// 设置三次显示模式
	{
		LCDWriteCommand(0x38,isNotReadBusy);	// 不检测忙信号
		delay5ms();
	}
	
	LCDWriteCommand(0x38,isReadBusy); // 设置显示模式,检测忙信号
	LCDWriteCommand(0x08,isReadBusy); // 关闭显示
	LCDWriteCommand(0x01,isReadBusy); // 显示清屏
	LCDWriteCommand(0x06,isReadBusy); // 显示光标移动设置
	LCDWriteCommand(0x0F,isReadBusy); // 显示开及光标设置
}



//按指定位置显示一个字符
void putOneCharToLCD1602(uchar line, uchar position, uchar ucData)
{

	line &= DATA_LineMax;
	position &= DATA_PositionMax;
	if (line == DATA_LineTow) position |= 0x40; 			//当要显示第二行时地址码+0x40;
	position |= 0x80; 									//设置两行显示格式 D7 = 1;
	LCDWriteCommand(position, isReadBusy); 			//发送命令 设置字符地址
	LCDWriteData(ucData); 							 	//写入字符的数据	
}

//按指定位置显示一串字符
void putLineCharsToLCD1602(uchar line, uchar position, uchar count, uchar code *ucData)
{
	uchar i;
	for(i = 0; i < count; i++)							//连续显示单个字符
	{
		putOneCharToLCD1602(line, position + i, ucData[i]);	
	}
}


//按指定位置连续显示三个字符(三位数字)
void	putThreeCharToLCD1602(uchar line, uchar position, uint uiNumber)
{
	uiNumber %= 1000;
	putOneCharToLCD1602(line, position, TabNumASCII[uiNumber / 100]);
	putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 / 10]);
	putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 % 10]);	
	
}
	


// 按键检测子程序,有键按下返回键端口数据,无键返回0
uchar GetKey(void)
{	
    uchar KeyTemp = (IO_KEY | DATA_KEY_ORL);		//获取按键端口数据	
	
	if( KeyTemp != DATA_KEY_Null )				// 如果不为空
	{
		uchar CountTemp = 0;
		do
		{
			delay125us();
			if(KeyTemp != (IO_KEY | DATA_KEY_ORL)) return 0;	//在延时期间检测键,如果不稳定保持则退出	
			
		} while(++CountTemp > Data_Key20msCountMax); 				// 延时20ms去抖动 
		
		while((IO_KEY | DATA_KEY_ORL) != DATA_KEY_Null); 	//等键释放		
		
		return KeyTemp;	// 有键按下返回键端口数据
	}
	
	return 0;	// 无有效键返回0
}



//加一
uchar  INC_Number(uchar Number, uchar Min, uchar Max)
{
	if(Number >= Max) return Min; else return (++ Number);
		
}

//减一
uchar  DEC_Number(uchar Number, uchar Min, uchar Max)
{
	if(Number <= Min) return Max; else return (-- Number);
		
}

// 检测到有按键后 这里执行按键任务			
void execute_key_task(uchar ucKeyValue)	
{
	uchar state = 0;						//定义调整数据的状态变量
	uchar keyValue = 0;					//定义键值的临时变量
	
	if(ucKeyValue != DATA_KEY_Set) return;	//不是设置键退出
	
	//是设置键继续-----------------------------------------------------
	
	putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm ");	//清零显示当前距离CURRENT		
	putThreeCharToLCD1602(lineOne, 8 + 2, uiD);		//光标调整到调整总距离(检测探头到水库底部的距离"D:000cm")	
	
	while(1)
	{
		keyValue = GetKey();	
		if(keyValue == 0) continue;
		
		switch(keyValue)
		{
			case DATA_KEY_Set:
			{
				// 如果按的是设置键,顺序设置总距离D------高水位H------低水位L------退出
				switch(state)
				{
					case 0:			// 如果是设置总距离状态,改变为设置高水位状态,并显示高水位,实现移动光标到高水位后面
					{
						state = 1;						
						putThreeCharToLCD1602(lineOne, 0 + 2, uiH);						
					}
					break;
					case 1:
					{
						uchar tempMax = uiD - DATA_uiD_Min;
						if(tempMax < 2 + 2) tempMax = 2 + 2;											
						if(uiH > tempMax) 
						{
							uiH = tempMax;
							putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
						}
						else if(uiH < 2 + 2)
						{
							uiH = 2 + 2;	
							putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
						}							
						state = 2;
						putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
					}
					break;
					case 2:
					{
						if(uiL > uiH - 2) 
						{
							uiL = uiH - 2;
							putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
						}
						return;	
						
					}
					break;
				}
				
			}
			break;
			// 如果按的是增加键,改变相应数据并显示
			case DATA_KEY_INC:
			{
				switch(state)
				{
					case 0:
					{
						uiD = INC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);
						putThreeCharToLCD1602(lineOne, 8 + 2, uiD);							
					}
					break;
					case 1:
					{
						uchar tempMax = uiD - DATA_uiD_Min;
						if(tempMax < 2 + 2) tempMax = 2 + 2;
						uiH = INC_Number(uiH, 2, tempMax);						
						putThreeCharToLCD1602(lineOne, 0 + 2, uiH);	
					}
					break;
					case 2:
					{
						uiL = INC_Number(uiL, 0, uiH - 2);	
						putThreeCharToLCD1602(lineTow, 0 + 2, uiL);	
					}
					break;
				}
				
			}
			break;
			// 如果按的是减少键,改变相应数据并显示
			case DATA_KEY_DEC:
			{
				switch(state)
				{
					case 0:
					{
						uiD = DEC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);					
						putThreeCharToLCD1602(lineOne, 8 + 2, uiD);
					}
					break;
					case 1:
					{
						uchar tempMax = uiD - DATA_uiD_Min;
						if(tempMax < 2 + 2) tempMax = 2 + 2;
						uiH = DEC_Number(uiH, 2, tempMax);						
						putThreeCharToLCD1602(lineOne, 0 + 2, uiH);	

					}
					break;
					case 2:
					{
						uiL = DEC_Number(uiL, 0, uiH - 2);	
						putThreeCharToLCD1602(lineTow, 0 + 2, uiL);	

					}
					break;
				}
				
			}
			break;
			
		}
		
	}
	
}



// 蜂鸣器	
void	buzzerCall(void)
{
	uchar	i;
	
		for(i = 0; i < 90; i++)
		{
			io_Buzzer = 0;
			delay100us();
			io_Buzzer = 1;
			delay100us();
			delay100us();		
		}
		delay100us();	
		delay100us();	
}

//计算水位
bool CalculatedWaterLevel(void)
{
	uchar 	i = 8 + 2;					//当前水位的数字在LCD屏显示的起点位置
	uint  	uiTime;						//声波传播时间
	ulong 	ulDis;						//实时测量到距离	
	
	uiTime = TH0 << 8 | TL0;	
	ulDis = (uiTime * 3.40) / 200;     	//计算当前测量的距离,单位cm
	
	TH0 = 0;
	TL0 = 0;	
	
	if((ulDis > uiD) || (g_flag == isYes )) 	// ulDis > uiD 超出测量范围;g_flag == isYes超时;
	{	 
		g_flag = isNo;		
		TR0 = 0;
		putLineCharsToLCD1602(lineTow, i, 3, "Err");	// 显示Err 		
		
		//阀门动作:		
		// if(g_flagSwitch == isYes)
		// {		
			// io_Control_Inlet = isio_Control_Inlet_OFF;		
			// io_Control_Outlet = isio_Control_Outlet_ON;
			// g_flagSwitch = isNo;
		// }		
		
		//指示灯:
		ioLed_Red = ! ioLed_Red;							// 三个灯同时快速闪亮
		ioLed_Green = ! ioLed_Green;
		ioLed_Yellow = ! ioLed_Yellow;
		
		// 蜂鸣器叫:	
		if(buzzerCallFlag == isCall)
		{
			buzzerCall();					// 蜂鸣器叫	
		}
		
		return isNo;									// 返回错误信息
	}
	else
	{
		ulDis = uiD - ulDis;					// 当前水位C = 总距离 - 当前检测到的距离
		
		if(ulDis > uiH)						// 如果水位超高
		{
			
			//阀门动作:
			io_Control_Inlet = isio_Control_Inlet_OFF;
			io_Control_Outlet = isio_Control_Outlet_ON;	
			g_flagSwitch = isNo;
			
			//指示灯:
			ioLed_Red = ! ioLed_Red;			// 红灯闪
			ioLed_Green = isLedOFF;		
			ioLed_Yellow = isLedOFF;				
			// 蜂鸣器叫:
			if(ulDis - uiH > (uiD - uiH) / DATA_alarmCoefficient) //当"当前水位"超出最高水位" (("总高度减高水位)除以2的值")时报警
			{
				buzzerCall();					// 蜂鸣器叫
			}
			
		}
		else if(ulDis < uiL)					// 如果水位超低
		{
			//阀门动作:	
			if(g_flagSwitch == isYes)
			{		
				io_Control_Outlet = isio_Control_Outlet_OFF;	
				io_Control_Inlet = isio_Control_Inlet_ON;	
				g_flagSwitch = isNo;
			}	
			
			//指示灯:
			ioLed_Red = isLedOFF;
			ioLed_Green = isLedOFF;
			ioLed_Yellow = ! ioLed_Yellow;	//黄灯闪
			// 蜂鸣器叫:						
			if( uiL - ulDis > uiL / DATA_alarmCoefficient)//uiL / 2 当"当前水位"低于"低水位" "低水位除以2的值"时报警
			{
				buzzerCall();					// 蜂鸣器叫
			}
****/*完整资料
***//***微信公众号:木子单片机****			
			
		}
		else								// 水位在正常范围 
		{	
			ioLed_Red = isLedOFF;
			ioLed_Green = ! ioLed_Green;
			ioLed_Yellow = isLedOFF;	
			
		}
		putThreeCharToLCD1602(lineTow, i, ulDis);
		return isYes;	
		
	}
	return isYes;
}

硬件设计

使用元器件:

单片机:STC89C51;

(注意:单片机是通用的,无论51还是52、无论stc还是at都一样,引脚功能都一样。程序也是一样的。)

9*15万用板;超声波模块;

40脚IC座;4脚排针;

杜邦线;继电器;

LCD1602液晶;103电位器;

USB电源线(电池盒);

16脚IC座;16脚排针;

蜂鸣器;8550三极管;

1k电阻;10k电阻;

10uf电容;30pf电容;

12M晶振;3mmLED;

轻触按键;自锁开关;

DC电源插口;

直流水泵;

导线:若干;

添加图片注释,不超过 140 字(可选)

结构框图:

添加图片注释,不超过 140 字(可选)

设计资料

01仿真图

本设计使用proteus8.9版本设计,仿真和实物有些差异,有对应的仿真程序!具体如图!

添加图片注释,不超过 140 字(可选)

02原理图

本系统原理图采用Altium Designer19设计,具体如图!

添加图片注释,不超过 140 字(可选)

03程序

本设计使用软件keil5版本编程设计!具体如图!

添加图片注释,不超过 140 字(可选)

04设计报告

一万一千字设计报告,仅供参考,具体如下!

添加图片注释,不超过 140 字(可选)

05设计资料

资料获取请看前面演示视频,全部资料包括仿真源文件 、AD原理图、程序(含注释)、PCB、任务书、设计报告、结构框图、实物图、元件清单、讲解视频等。具体内容如下,全网最全! !

操作演示请观看前面演示视频!

点赞分享一起学习成长。

相关推荐
lkbhua莱克瓦2416 分钟前
用C语言实现——一个中缀表达式的计算器。支持用户输入和动画演示过程。
c语言·开发语言·数据结构·链表·学习方法·交友·计算器
The Mr.Nobody43 分钟前
STM32MPU开发之旅:从零开始构建嵌入式Linux镜像
linux·stm32·嵌入式硬件
技术干货贩卖机1 小时前
0基础 | 51单片机 | Proteus仿真
51单片机·proteus·仿真·电路仿真·0基础
lwewan1 小时前
26考研——存储系统(3)
c语言·笔记·考研
阿川!1 小时前
嵌入式软件--stm32 DAY 3
stm32·单片机·嵌入式硬件
#金毛2 小时前
STM32的定时器输出PWM时,死区时间(DTR)如何计算
stm32·单片机·嵌入式硬件
无脑学c++2 小时前
STM32串口重定向:MDK与GCC重定向需重写的不同函数
stm32·单片机·物联网
Invinciblenuonuo3 小时前
STM32八股【6】-----CortexM3的双堆栈(MSP、PSP)设计
stm32·单片机·嵌入式硬件
2401_888859714 小时前
STM32 TIM编码器接口
stm32·单片机·嵌入式硬件
【0931】4 小时前
51单片机中断
单片机·嵌入式硬件