本设计中液晶显示有4个字母,分别为H------容器的最高水位设定值(不能高于实际高度)L------容器的最低水位设定值D-----容器实际高度(可以设置)C-----容器内液体的高度(在实际演示中,障碍物离探头越近,液晶C显示越大,因为障碍物好比液面,离探头近了说明水位高了)特别提醒:如果容器实际高度D你设置为1米,那么C液体的高度最高能测到98cm,因为探头的盲区在2cm左右。如果D设为2米,那么最高能测到1.98m.按键功能分别为:设置键 增加键 减小键 复位键三个指示灯的分别功能为:红色----超过设定的最高水位H 黄色-----低于设定的最低水位L 绿色----最高H和最低L中间本文采用AT89C52单片机系统实现了水塔水位的自动控制,设计出一种低成本、高实用价值的水塔水位控制器。该系统具有水位检测、水位高度LCD显示、低水位高水位报警以及自动加水等功能。本设计过程中主要采用了传感技术、单片机技术、光报警技术以及弱电控制强电的技术。本设计传感器使用了超声波模块,并且详细阐述了超声波测距测的原理,给出了系统构成框图。此系统具有易控制、工作可靠、测量精度高的优点,可实时监控液位。并采用52单片机系统控制整个电路的信号处理以及采用光电耦合和继电器来实现弱电控制强电来实现加水系统的自动控制。它能自动完成水位检测、光报警、上水停水的全部工作循环,保证液面高度始终处于较理想的范围内,它结构简单,制造成本低,灵敏度高,节约能源显著,是用于各种高层液体储存的理想设备。示意图:Altium Designer画的原理图和PCB图如下: 超声波水位控制器元件清单1) 9*15万用板 12) AT89C51单片机 13) 超声波探头 04) 40脚IC座 15) 4脚排针 06) 杜邦线4根 07) 继电器*2 08) LCD1602液晶 19) 103电位器 010) 16脚IC座 011) 16脚排针 112) 蜂鸣器 013) 8550三极管*3 014) 1k电阻*8 015) 10k电阻 016) 10uf电容 017) 30pf电容*2 018) 12M晶振 119) 3mmLED(红、绿各2个,黄1个) 020) 轻触按键*4 121) 自锁开关 122) DC电源插口 123) USB电源线(电池盒)24) 直流水泵*2(根据客户自选)单片机程序源码如下:
- /***************************************************************
- 名称:基于51单片机的超声波水位监测报警系统
- 单片机型号:AT89C51
- 单片机设置:时钟12T,晶体12MHZ
- 注:修改增加水泵控制和排水控制,即双继电器
- ***************************************************************/
- #include
- #include // 包含循环移位:_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);
- }
- }
- //按指定位置连续显示三个字符(三位数字)
- 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;
- }
- void main(void)
- {
- initIO(); //初始化IO端口
- delay500ms(); //启动延时,给器件进入正常工作状态留够时间
- initLCD1602(); //LCD初始化
- putLineCharsToLCD1602(lineOne, 8, 8, "D:000cm "); //显示distance (总)距离(检测探头到水库底部的距离)D
- putThreeCharToLCD1602(lineOne, 8 + 2, uiD); //显示三位数值
- putLineCharsToLCD1602(lineOne, 0, 8, "H:000cm "); //显示设定的最高报警水位H
- putThreeCharToLCD1602(lineOne, 0 + 2, uiH); //显示三位数值
- putLineCharsToLCD1602(lineTow, 0, 8, "L:000cm "); //显示设定的最低报警水位L
- putThreeCharToLCD1602(lineTow, 0 + 2, uiL); //显示三位数值
- putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm "); //显示当前CURRENT水位C
-
- initTimer0(); //初始化定时器0
- initTimer1();
-
- //阀门动作:初始先排水
- io_Control_Inlet = isio_Control_Inlet_OFF;
- io_Control_Outlet = isio_Control_Outlet_ON;
- g_flagSwitch = isNo;
-
- while(1)
- {
- io_US_TX = 1; //启动超声波模块信号
- delay10us();
- io_US_TX = 0;
-
- while(io_US_RX == 0); //等待计时开始
- TR0 = 1; //开启定时器0,计时开始
- IT1 = 1; //设置外中断INT1输入信号模式(1:Falling only仅下降沿有效 0:Low level低电平有效)
- EX1 = 1; //使能外中断INT1
-
- while(EX1 == 1 && g_flag == isNo)//等待中断或超时退出
- {
- uchar ucKeyValue = GetKey(); //在等待中检测按键
- if(ucKeyValue) execute_key_task(ucKeyValue); //如果有键按下则执行按键任务
- }
-
- if(CalculatedWaterLevel() == isNo) continue; //计算水位,如果超出范围返回isNo并重新循环
-
- TR0 = 0; //暂时关闭定时器0
- //清零定时器和计数变量以及标志
- TL0 = 0;
- TH0 = 0;
- g_flag = isNo;
- ucCount = 0;
- g_flag05s = isNo;
-
- TR0 = 1; //打开定时器0
- while(g_flag05s == isNo) //延时0.52秒,以防止此期间返回的超声波产生错误信息,并使显示变化放慢,保证视觉效果
- {
- uchar ucKeyValue = GetKey();
- if(ucKeyValue)
- {
- if(ucKeyValue == DATA_KEY_DEC)
- {
- g_flagBeepTimer = isNo;//用减小按键取消报警标志
- }
- execute_key_task(ucKeyValue); //如果有键按下则执行按键任务
- }
- }
- TR0 = 0; //暂时关闭定时器0
- //清零定时器和复位标志
- TL0 = 0;
- TH0 = 0;
- g_flag = isNo;
-
- //-----------------------------------
- //水箱清洗提示:
-
复制代码
作者: 小羡, 来源:面包板社区
链接: https://mbb.eet-china.com/blog/uid-me-4026945.html
版权声明:本文为博主原创,未经本人允许,禁止转载!
文章评论(0条评论)
登录后参与讨论