历史上的今天
今天是:2024年12月21日(星期六)
2021年12月21日 | 51单片机超声波水位控制器设计
2021-12-21 来源:eefocus
说明:
本设计中液晶显示有4个字母,分别为
H------容器的最高水位设定值(不能高于实际高度)
L------容器的最低水位设定值
D-----容器实际高度(可以设置)
C-----容器内液体的高度(在实际演示中,障碍物离探头越近,液晶C显示越大,因为障碍物好比液面,离探头近了说明水位高了)
特别提醒:如果容器实际高度D你设置为1米,那么C液体的高度最高能测到98cm,因为探头的盲区在2cm左右。如果D设为2米,那么最高能测到1.98m.
按键功能分别为:设置键 增加键 减小键 复位键
三个指示灯的分别功能为:红色----超过设定的最高水位H 黄色-----低于设定的最低水位L
绿色----最高H和最低L中间
本文采用AT89C52单片机系统实现了水塔水位的自动控制,设计出一种低成本、高实用价值的水塔水位控制器。该系统具有水位检测、水位高度LCD显示、低水位高水位报警以及自动加水等功能。
本设计过程中主要采用了传感技术、单片机技术、光报警技术以及弱电控制强电的技术。本设计传感器使用了超声波模块,并且详细阐述了超声波测距测的原理,给出了系统构成框图。此系统具有易控制、工作可靠、测量精度高的优点,可实时监控液位。并采用52单片机系统控制整个电路的信号处理以及采用光电耦合和继电器来实现弱电控制强电来实现加水系统的自动控制。它能自动完成水位检测、光报警、上水停水的全部工作循环,保证液面高度始终处于较理想的范围内,它结构简单,制造成本低,灵敏度高,节约能源显著,是用于各种高层液体储存的理想设备。
为了大家更好的理解,请如下看示意图

制作出来的实物图如下:



AD的设计图如下:


超声波水位控制器元件清单
9*15万用板 1
AT89C51单片机 1
超声波探头 0
40脚IC座 1
4脚排针 0
杜邦线4根 0
继电器*2 0
LCD1602液晶 1
103电位器 0
16脚IC座 0
16脚排针 1
蜂鸣器 0
8550三极管*3 0
1k电阻*8 0
10k电阻 0
10uf电容 0
30pf电容*2 0
12M晶振 1
3mmLED(红、绿各2个,黄1个) 0
轻触按键*4 1
自锁开关 1
DC电源插口 1
USB电源线(电池盒)
直流水泵*2(根据客户自选)
单片机程序源码如下:
/***************************************************************
名称:基于51单片机的超声波水位监测报警系统
单片机型号:AT89C51
单片机设置:时钟12T,晶体12MHZ
作者:从零开始学单片机
注:修改增加水泵控制和排水控制,即双继电器
***************************************************************/
#include #include #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); 1 2 3 } // 延时100us void delay100us(void) //@12MHz { uchar i; _nop_(); i = 47; while (--i); 1 2 3 } // 延时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); 1 2 3 4 5 6 } // 延时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;
史海拾趣
|
将5.0BSP移值到6.0 上时,运行到串口驱动时,就运行不下去了, DNW提示信息: WARNING: CReg2440Uart::CReg2440Uart failed to obtain processor frequency - using default value (66750000). DEBUG: Serial2 SYSINTR : -766318612 WARNING: C ...… 查看全部问答> |
|
请教如何获取monthcalendar(日历控件)被选中的日期信息,急急急急急急急! 如题,小弟在做一个日程管理的小应用程序, 请问如何获取在日历控件上选定的日期信息, 比如选定某日, 在程序中要提取该日的日期和星期几的信息。 谢谢各位,很急,请大家帮帮忙。… 查看全部问答> |
|
去年夏天,家里用了20多年的老冰箱(老人家买的,买了新冰箱后不够用,所以一直用着)坏了照明灯泡,用起来很不方便。当时把灯泡拿到电子市场去配,竟然找不到这种灯头的,只有作罢。 今天晚上在家没事,就 ...… 查看全部问答> |
|
之前能仿真的。能下载程序的。 后来设置了一下 就只有不行了 一直是 AXD Uable to halt ARM core 我也不知道哪里出了问题。.   ...… 查看全部问答> |
|
#include #include void main(void) { WDTCTL=WDT_MDLY_32; IE1|=WDTIE; P1DIR|=BIT6; while(1); } #pragma vector=WDT_VECTOR __interrupt void watchdor(void) { P1O ...… 查看全部问答> |
|
我也来加入四层板的学习,在上班抽空时间总算布局完了,大概布了下,还不是很整齐,在这里得感谢@okhxyyo 的耐心帮忙和指导~ 希望能大家一起共同进步和学习… 查看全部问答> |




