水塔水位探头(51单片机超声波水位控制器设计 (C源码+PCB+原理图与实物制作))

Posted

篇首语:我允许别人比我强,但我不允许我没有别人努力。本文由小常识网(cha138.com)小编为大家整理,主要介绍了水塔水位探头(51单片机超声波水位控制器设计 (C源码+PCB+原理图与实物制作))相关的知识,希望对你有一定的参考价值。

水塔水位探头(51单片机超声波水位控制器设计 (C源码+PCB+原理图与实物制作))

最近几天总是下雨,搞的人心情也很差,昨天看到有粉丝留言说,导师让他们组做一个关于超声波控制液体水位的项目,但是无从下手,希望我帮帮他,可以有偿。他是我的铁粉,下面我就把我前段时间帮人家设计的一个超声波水位自动控制系统 分享出来,希望可以帮到他们。

51单片机超声波水位控制器设计

设计说明:

本设计中液晶显示有4个字母,分别为

H------容器的最高水位设定值(不能高于实际高度) L------容器的最低水位设定值

D-----容器实际高度(可以设置)

C-----容器内液体的高度(在实际演示中,障碍物离探头越近,液晶C显示越大,因为障碍物好比液面,离探头近了说明水位高了)

特别提醒:如果容器实际高度D你设置为1米,那么C液体的高度最高能测到98cm,因为探头的盲区在2cm左右。如果D设为2米,那么最高能测到1.98m.

按键功能分别为:设置键 增加键 减小键 复位键

三个指示灯的分别功能为:红色----超过设定的最高水位H 黄色-----低于设定的最低水位L

绿色----最高H和最低L中间

本文采用AT89C52单片机系统实现了水塔水位的自动控制,设计出一种低成本、高实用价值的水塔水位控制器。该系统具有水位检测、水位高度LCD显示、低水位高水位报警以及自动加水等功能。

本设计过程中主要采用了传感技术、单片机技术、光报警技术以及弱电控制强电的技术。本设计传感器使用了超声波模块,并且详细阐述了超声波测距测的原理,给出了系统构成框图。此系统具有易控制、工作可靠、测量精度高的优点,可实时监控液位。并采用52单片机系统控制整个电路的信号处理以及采用光电耦合和继电器来实现弱电控制强电来实现加水系统的自动控制。它能自动完成水位检测、光报警、上水停水的全部工作循环,保证液面高度始终处于较理想的范围内,它结构简单,制造成本低,灵敏度高,节约能源显著,是用于各种高层液体储存的理想设备。

为了大家更好地理解,请如下看示意图

制作出来的实物图如下:

AD的设计图如下:

超声波水位控制器元件清单

1) 9*15万用板 1

2) AT89C51单片机 1

3) 超声波探头 0

4) 40脚IC座 1

5) 4脚排针 0

6) 杜邦线4根 0

7) 继电器*2 0

8) LCD1602液晶 1

9) 103电位器 0

10) 16脚IC座 0

11) 16脚排针 1

12) 蜂鸣器 0

13) 8550三极管*3 0

14) 1k电阻*8 0

15) 10k电阻 0

16) 10uf电容 0

17) 30pf电容*2 0

18) 12M晶振 1

19) 3mmLED(红、绿各2个,黄1个) 0

20) 轻触按键*4 1

21) 自锁开关 1

22) DC电源插口 1

23) USB电源线(电池盒)

24) 直流水泵*2(根据客户自选)

单片机程序源码如下:

/***************************************************************

名称:基于51单片机的超声波水位监测报警系统

单片机型号:AT89C51

单片机设置:时钟12T,晶体12MHZ

作者:从零开始学单片机

注:修改增加水泵控制和排水控制,即双继电器

***************************************************************/

#include <reg51.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;

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


鉴于篇幅限制,只能写部分代码

最后,如果有什么意见或者建议欢迎您留言给我,让我们共同学习一起进步,

如果需要 完整代码或设计文件,请在下方留言或者私信我,看到后会第一时间回复。

谢谢!

感谢你的阅读,希望您有所收获,喜欢请点赞评论加关注!

相关参考

水塔水位探头安装方法(明朝文物被盗福州警方出手了)

保护和传承文化遗产,是全社会的共同责任。5月27日,福州市公安局仓山分局专门举办发还仪式,将追回的4件被盗明清文物返还给受害人。案件侦破经过事情回溯到今年2月23日,当时,福州市公安局仓山分局刑侦大队接到报案...

水塔水位开关(液位控制器在反渗透设备中的应用)

在反渗透设备中我们要用到液位控制器,那液位控制器在反渗透设备中的作用有哪些呢?液位控制器是指通过机械式或者电子式的方法来进行高低液位的控制,可以控制电磁阀、水泵等,从而来实现半自动化或者全自动化的控制...

水塔水位控制电路图(水池水位无线自动控制系统)

...离水位开关信号的传输和远程控制,主要是针对远距离的水塔、山顶蓄水池及江河水位监测而设计的全自动无线电测控系统。DTD110HW水位无线控制系统既可以监视水位的变化,同时还可以自动控制水泵的启停。水位DTD110HW远距离...

水塔水位控制器坏了怎么修(无线液位控制器一键解决液位控制问题)

  无线液位控制器一键解决液位控制问题:  远距离的水位检测和控制是许多项目工程施工过程都会遇到的问题,如何才能解决好这个问题,给广大的用户造成了困扰。西安鼎兴作为一家拥有社会责任感和使用命感的企业,...

水塔水位控制plc编程图(恒压供水系统及其设计选型)

常用供水工艺:水箱/水塔供水:重力供水。特点:供水压力比例恒定,有储水;缺点:要建水塔或水箱,面积大,不美观等。气压供水:管道加压。特点:灵活,建设快,无污染,改变压力罐,压力来改变供水压力;缺点:需...

水塔自动抽水控制器(6公里远程无线智能水泵控制器的主要功能)

  6公里远程无线智能水泵控制器产品主要功能:  1、实现6公里内水塔内水位的全自动检测和水泵的全自动控制启停:低水位时水泵全自动泵水,高水位时水泵自动停机。  2、具有水泵防锈处理技术;当水泵长时间处于停...

水池液位控制器(水塔水位、水池液位无线数据采集与远程监控系统解决方案)

目录一、系统设计概述1二、技术要求1三、设计标准2四、系统设计思想21、分层分布式结构22、快速稳定的通讯传输形式23、灵活的组网模式34、模块化、智能化的设计理念35、扩展性强36、兼容性好3五、传输方案选择4六、设备选...

水塔水位自动控制plc梯形图(从零开始学习PLC程序设计的经典应用)

经验设计方法要求设计者具有较丰富的实践经验,掌握较多的典型应用程序的基本环节。根据被控对象对控制系统的具体要求,凭经验选择基本环节,并把它们有机地组合起来。其设计过程是逐步完善的,一般不易获得最佳方案...

水塔水位控制器解读(数显水位控制器的应用范围)

数显水位控制器是—套可实现水位液位连续检测控制的设备,整套产品由安装在现场的液位传感器.中间信号传输电缆、液位控制显示主机构成。显示控制主机采用先进工艺技术生产的芯片元器件制造而成。可以实时显示当前液位...

水塔水位控制器价格(水位控制器产品用途及结构原理)

水位控制器是指通过机械式或电子式的方法来进行高低水位的控制,可以控制电磁阀、水泵等,成为水位自动控制器,从而来实现半自动化或者全自动化。广泛应用于工业锅炉、民用建筑用水池、水塔、水箱,以及石油化工、造...