当前位置: 首页 > news >正文

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

基于51单片机液位控制器

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

功能介绍

具体功能:

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

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

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

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

​演示视频:

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

程序

/***************************************************************
基于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;   			//开定时器0ET0 = 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倍 = 10Sstatic 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;     	//计算当前测量的距离,单位cmTH0 = 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、任务书、设计报告、结构框图、实物图、元件清单、讲解视频等。具体内容如下,全网最全! !

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

点赞分享一起学习成长。

相关文章:

  • PMIC PCA9450 硬件原理全解析:为 i.MX 8M 平台供电的“大脑”
  • 23种设计模式-行为型模式之责任链模式(Java版本)
  • 4/24杂想
  • 30分钟算法题完成
  • 使用命令行加密混淆C#程序
  • python中的logging库详细解析
  • AI超级智能体教程(三)---程序调用AI大模型的四种方式(SpringAI+LangChain4j+SDK+HTTP)
  • 二项式分布html实验
  • Low Poly 风格 | 飞机飞行可视化系统
  • 【计算机视觉】CV实战- 深入解析基于HOG+SVM的行人检测系统:Pedestrian Detection
  • 深入解析Mlivus Cloud核心架构:rootcoord组件的最佳实践与调优指南
  • 防火墙技术深度解析:从包过滤到云原生防火墙的部署与实战
  • 软测面经(私)
  • API/SDK
  • 【金仓数据库征文】金仓数据库:开启未来技术脑洞,探索数据库无限可能
  • 【基于Qt的QQMusic项目演示第一章】从界面交互到核心功能实现
  • 基于HTML+CSS实现的动态导航引导页技术解析
  • 数字孪生:从概念到实践,重构未来产业的“虚拟镜像”
  • Java实现背景图片加自适应水印图片
  • Android 编译问题 prebuilts/clang/host/linux-x86
  • 航行警告!黄海南部进行实弹射击,禁止驶入
  • 南方医科大学原副校长宁习洲主动投案,接受审查调查
  • 梁启超“失肾记”的余波:中西医论战与最后的真相
  • 爱奇艺要转型做微剧?龚宇:是误解,微剧是增量业务,要提高投资回报效益
  • 厦门国贸去年营收约3544亿元,净利润同比减少67.3%
  • 特朗普激发加拿大爱国热情:大选提前投票人数创纪录,魁北克分离情绪被冲淡