基于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、任务书、设计报告、结构框图、实物图、元件清单、讲解视频等。具体内容如下,全网最全! !
操作演示请观看前面演示视频!
点赞分享一起学习成长。