生活中,人工给花木浇水要花很多时间,土壤湿度也没有控制好。有时候因为主人长期外出,家里的花草树木会因为没人浇水而枯死。为了解决上述问题,本文利用单片机设计了一种自动和手动的浇水系统(如图1所示)。
图1自动浇水系统
一、功能描述
系统有自动和手动两种工作模式。两种工作模式通过手动自动切换按钮进行切换。系统开机自检,如果系统有故障会报警。如果系统工作正常,将进入自动状态,初始设定值为25%。当土壤湿度小于设定值时,水泵工作浇水,当高于设定值加偏移量(偏移量可根据实际情况确定,本文设为2)时,系统停止加水。在自动工作模式下,如果水可以因缺水而停止,或测量信号异常,系统会报警,水泵停止加水。在手动工作模式下,可以通过按加水键和停止键来手动加水和停止加水。该键控制LCD背光的开关。完成的电路如图2所示。
图2自动供水系统的电路板
二、硬件系统设计
(1)硬件系统的组成
系统选用的元器件目前市场上比较常见,系统的核心控制器件是STC公司的STC89C52RC单片机,应用广泛。除单片机外,还包括土壤水分传感器、AD转换器、LCD1602液晶显示器、按键、指示灯、蜂鸣器、继电器和水泵等。
(2)电路的工作原理
电路原理图如图3所示,包括电源电路、晶振及复位电路、检测电路、按键及显示电路、报警电路和输出控制电路。
1.电源电路
系统所需的5V电源直接由外部提供,可以由各种常见的手机充电器提供。USB电源端子和圆形端子用于连接外部电源。使用时,连接其中一个,另一个可以对外提供5V电源,方便系统电源的连接。
2.晶体振荡器和复位电路
电容C6、C7和晶振Y1组成振荡电路,为单片机提供所需的时钟。C8、R13和RST1键组成单片机复位电路,可以实现上电复位和手动复位。
3.探测电路
检测电路由土壤湿度传感器和AD转换器组成。选用了廉价易用的土壤湿度传感器模块和AD转换器PCF8591模块(见图4)。该模块具有四路输入和一路输出,可以满足系统扩展的需要。该电路将土壤水分转换成模拟量,经过AD转换后,转换成相应的单片机可以识别的数字量。
图4土壤湿度传感器和AD转换模块
4.按键和显示电路
按键显示电路由四个独立按键、LED指示灯和LCD1602组成,用于人机操作和系统各种工作状态的信息显示。
5.告警电路
报警电路由蜂鸣器和LED指示灯组成,实现了声光报警。15次后声音停止,LED指示灯一直闪烁。
6.输出控制电路
单片机的输出信号通过驱动电路驱动继电器,控制继电器的通断,用于驱动执行机构(本文中为5V DC水泵)的通断。LED指示灯显示输出状态。
三、软件系统设计
(1)系统流程图(见图5)
(2)系统软件设计
系统采用项目多文件和模块化编程,便于编程和程序移植。它由main.c、I2C.c、键盘组成。c,Lcd1602.c文件,对应的头文件和配置文件config.h Lcd1602.c主要负责液晶的底层显示驱动。I2C.c主要负责AD转换器的底层功能驱动。键盘。c负责关键处理,main.c负责系统初始化、自检、土壤水分检测、中值滤波和信号li
整个项目的软件按流程图编写即可,程序代码如下(注意报纸因为版面原因,无法全部刊登,这里可以看到完整的代码)。
参考程序
/***************************************
配置文件
**************************************/
#ifndef _CONFIG_H
#定义配置H
#包括《reg52.h》
#包括《intrins.h》
#在0上定义
#定义关闭一
#定义偏移2 //上下限偏差
typedef无符号字符uchar//8位无符号数
typedef无符号整数单元;//8位无符号数
sbit ak_key=p1^0;//背光控制
sbit up_key=p1^1;//增加_启动
sbit dn_key=p1^2;//减小_停止
sbit ma_key=p1^3;//手动_自动
sbit alarm_led=p1^4;//故障指示
sbit pump_led=p1^5;//抽水指示
sbit manual_led=p1^6;//手动指示
sbit auto_led=p1^7;//自动指示
sbit pump=p2^2;//水泵控制
sbit buzzer=p2^3;//蜂鸣器报警
#定义LCD1602_DB P0 //1602液晶数据口
sbit lcd1602_rs=p2^7;//1602液晶数据_指令选择
sbit lcd1602_rw=p2^6;//1602液晶读写选择
sbit lcd1602_e=p2^5;//1602液晶使能信号
sbit lcd1602_ak=p2^4;//1602液晶背光信号
sbit i2c_scl=p2^1;//AD转换器
sbit i2c_sda=p2^0;
#endif
/***************************************
主要的H。
***************************************/
#ifndef _MAIN_H
#定义_主要_H
enum eStaSystem //系统运行状态枚举
{
电子自动,电子手动,电子报警
};//设置,自动,手动,故障报警
#ifndef _MAIN_C
外部枚举系统;
#endif
无效延迟ms(无符号整数t);//延时函数
void SysInit();//系统初始化
void自检();//系统自检
void AutoWork();//系统自动运行
void显示LCD 1602();//测量、状态显示
空隙湿度();//土壤湿度检测
无符号字符GetADCValue(无符号字符CHN);//AD转换
void ConfigTimer0(无符号整数毫秒);//T0配置函数
#endif
/***************************************
主网站
***************************************/
#定义_主要_C
#包含"config.h "
#包含" main.h "
#包含"键盘. h "
#包含" I2C.h "
#包含"液晶显示器1602.h "
#定义N 3 //AD采样次数
#定义偏移2//上限偏移量
enum esta system sta system=E _ AUTO;//初始为自动状态
无符号字符T0RH=0;//定时器T0载值
无符号字符T0RL=0;
无符号字符TestVal//测量值
无符号字符计数;//蜂鸣器报警15次
//-
void main()
{
SysInit();
自检();
while(1)
{
关键动作();
AutoWork();
}
}
/****************************************
函数功能:系统初始化
入口参数:无
*****************************************/
void SysInit()
{
AK _ KEY=OFF//按键超正析象管(图片Orthicon)口初始化
UP _ KEY=OFF//
DN _ KEY=OFF
MA _ KEY=关;
ALARM _ LED=OFF//指示灯熄灭
泵浦LED=关;
手动_ LED=关闭
AUTO _ LED=关;
蜂鸣器=关;//关蜂鸣器
泵=关闭;//关水泵
LcdInit();//初始化液晶
LcdShowStr(0,0,-自动浇水-);//显示系统自检
LcdShowStr(1,1,"系统测试");//
配置定时器0(10);//配置T0定时10ms
EA=0;//关中断
}
/****************************************
函数功能:系统自动运行,故障报警
入口参数:无
*****************************************/
无效自动工作()
{
if(staSystem==E_AUTO)
{
湿度();
AUTO _ LED=开;
手动_ LED=关闭
if(TestVal》96||TestVal《5》
{
staSystem=E _ ALARM
cnt=30
EA=1;
}//故障
elseif(TestVal《SetVal
{
泵=开;
泵浦LED=开;
}
else if(TestVal)(设置值偏移))
{
泵=关闭;泵浦LED=关;
}
}
显示LCD 1602();//更新显示
}
/****************************************
函数功能:系统自检,故障报警
入口参数:无
*****************************************/
无效自检()
{
unsignedchar i,k,n;
无符号字符温度;
for(I=0;我《3;我)
{
报警指示灯=开;
泵浦LED=开;//抽水指示
手动_ LED=开;//手动指示
AUTO _ LED=开;//自动指示
戴莱姆斯(500);
ALARM _ LED=关闭
泵浦LED=关;//抽水指示
MANUAL _ LED=OFF//手动指示
AUTO _ LED=关;//自动指示
戴莱姆斯(500);
}
蜂鸣器=开;
戴莱姆斯(500);
蜂鸣器=关;
LcdClearScreen();
LcdShowStr(0,0,测试结果:);
for(I=0;我《4;i )//读取3次测量结果,过高故障报警
{
temp=getadc值(3);//直接读取0通道
if(temp)240 | | temp《10》k;
}
如果(k 2)//自检故障
{
EA=0;//关中断
while(1)
{
报警指示灯=开;
LcdShowStr(1,10,错误!");
戴莱姆斯(550);
LcdShowStr(1,10,");
ALARM _ LED=关闭
戴莱姆斯(250);
如果(n 《3》蜂鸣器=!蜂鸣器;
elseBUZZER=OFF
}
}
LcdClearScreen();
}
/****************************************
函数功能:测量、状态显示
入口参数:无
*****************************************/
空的显示1602()
{
LcdShowStr(0,0,PV:);
ShowNum(0,3,测试值);//显示实测值
LcdShowStr(0,6,“% RH”);
LcdShowStr(0,10,状态:);
LcdShowStr(1,0,SV:);
ShowNum(1,3,set val);//显示设定值
LcdShowStr(1,6,“% RH”);
if(sta system==E _ AUTO)LcdShowStr(1,10,自动);
else if(sta system==E _ MANUAL)LcdShowStr(1,10,“MANUAL”);
}
/**********************************************
函数功能:读取当前的物理输出核心转换值
入口参数:中国,公元通道号0-3
**********************************************/
无符号字符GetADCValue(无符号字符)
{
乌恰瓦尔;
I2C start();
如果(!I2C写的(0x 48《1》//寻址PCF8591 PCF8591如未应答,则停止操作并返回,00
{
I2C站();
返回0
}
I2C写(0x 40 |中国);//写入控制字节,选择转换通道
I2C start();
I2C写((0x 48《1)|0x 01);//寻址PCF8591 PCF8591指定后续为读操作
I2CReadACK();//先空读一个字节,提供采样转换时间
val=I2CReadNAK();//读取刚刚转换完的值
I2C站();
returnval
}
/****************************************
函数功能:按升序排列数组元素
入口参数:数组及数组长度
*****************************************/
void SortArray(unsigned char a[],unsignedchar a_len)
{
未签名字符I,临时
for(I=1;我《艾伦》我)
{
if(a[i-1]》a[i])
{
temp=a[I-1];
a[I-1]=a[I];
a[I]=temp;
I=0;
}
}
}
/****************************************
函数功能:中值滤波,线性转换后,获得土
壤湿度0-100%
入口参数:无
*****************************************/
空隙湿度()
{
无符号char i,tmp
无符号字符adBuf[N];
for(I=0;我《N;I)adBuf[I]=getadc值(1);//读取湿度
SortArray(adBuf,sizeof(adBuf));
tmp=adBuf[N/2];
tmp=getadc值(3);
TestVal=100-100 .* tmp/255;
}
/****************************************
函数功能:配置定时器0,定时时间
壤湿度0-100
入口参数:毫秒,定时时间(毫秒)
****************************************/
void ConfigTimer0(无符号整数毫秒)
{
无符号长终端监督程式(Terminal Monitor Program的缩写)
tmp=11059200/12;//定时器计数频率
tmp=(tmp * ms)/1000;//计算所需的计数值
tmp=65536-tmp;//计算定时器重载值
tmp=tmp 12//修正中断响应延时造成的误差
T0RH=(unsigned char)(tmp《8》);//定时器重载值拆分为高低字节
T0RL=(无符号字符)tmp
TMOD=0xf 0;//清零T0的控制位
TMOD |=0x 01;//配置T0为模式11
TH0=T0RH//加载T0重载值
TL0=T0RL
ET0=1;//使能T0中断
TR0=1;//启动T0
}
/****************************************
函数功能:延时函数
入口参数:t,延时约t毫秒
****************************************/
//- 延时-
无效延迟(单位t)
{
乌恰里
while(t -)
for(I=0;我《123;I);
}
/****************************************
函数功能:T0中断服务函数
入口参数:无
****************************************/
无效中断定时器0()中断一
{
静态无符号字符tmrms=0;
静态无符号字符TMR=0;
TH0=T0RH//定时器重新加载重载值
TL0=T0RL
tmrms
TMR=(TMR 1)0;
if(staSystem==E_ALARM) //故障液晶报警显示
{
if(TMR《50》LcdShowStr(1,11,)报警");
elseLcdShowStr(1,11 ");
if(tmrms =50)//定时0.5s
{
tmrms=0;
ALARM_LED=!警报_ LED
如果(CNT 0){ CNT-;蜂鸣器=!蜂鸣器;}
否则{蜂鸣器=关}
}
}
其他
{
ALARM _ LED=关闭
蜂鸣器=关;
}
}
/***************************************
液晶显示器1602.h
***************************************/
#ifndef _LCD1602_H
#define _LCD1602_H
#ifndef _LCD1602_C
#endif
void LcdWaitReady();//读取"忙"表示,等待液晶准备好
void LcdWriteCmd(无符号字符cmd);//给液晶发送命令煤矿管理局
void LcdWriteDat(无符号char dat);//写入数据函数
void LcdClearScreen();//液晶清屏
void LcdInit();//液晶初始化函数
void CursorPos(无符号字符行,无符号字符列);//光标定位
void ShowNum(无符号字符行,无符号字符列,无符号字符数);//显示3位数
void LcdShowStr(unsigned char row,unsignedchar col,unsigned char code * str);//显示字符串
#endif
/***************************************
液晶显示器1602.c
***************************************/
#define _LCD1602_C
#包含"config.h "
#包含"液晶显示器1602.h "
/********************************************
函数功能:判断液晶模块忙碌状态。忙,等待。
入口参数:无。
********************************************/
void LcdWaitReady() //读取"忙"表示,等待液晶准备好
{
无符号字符无线电台临时使用许可证
LCD1602 _ DB=0xFF
LCD 1602 _ RS=0;
LCD 1602 _ RW=1;
做//做。在…期间循环语句
{
LCD 1602 _ E=1;
sta=LCD1602 _ DB//读取状态字
LCD 1602 _ E=0;//读完了要关闭使能,防止液晶输出数据干扰总线
} while(sta0x 80);//位七等于一表示液晶正忙,重复检测直到其等于0 为止
}
/************************************************
函数功能:给液晶发送命令
入口参数:cmd
**************************************************/
void LcdWriteCmd(无符号字符命令)
{
LcdWaitReady();
LCD 1602 _ RS=0;
LCD 1602 _ RW=0;
LCD1602 _ DB=cmd
LCD 1602 _ E=1;
LCD 1602 _ E=0;
}
/************************************************
函数功能:给液晶发送数据
入口参数:dat
**************************************************/
void LcdWriteDat(无符号字符数据)//写入数据函数
{
LcdWaitReady();
LCD 1602 _ RS=1;
LCD 1602 _ RW=0;
LCD1602 _ DB=dat
LCD 1602 _ E=1;
LCD 1602 _ E=0;
}
/************************************************
函数功能:清屏
入口参数:无
**************************************************/
void LcdClearScreen()
{
LcdWriteCmd(0x 01);
}
/************************************************
函数功能:液晶初始化
入口参数:无
**************************************************/
void LcdInit() //液晶初始化函数
{
LcdWriteCmd(0x 38);//16*2 显示,5*7 点阵,8 位数据接口
LcdWriteCmd(0x0C);//显示器开,光标关闭
LcdWriteCmd(0x 06);//文字不动,地址自动加一
LcdWriteCmd(0x 01);//清屏
}
/************************************************
函数功能:定位光标
入口参数:行行位置,栏列位置
**************************************************/
无效光标(无符号字符行,无符号字符列)
{
row %=2;col %=40//防止越界
if(row)LcdWriteCmd(0xc 0 col);//第2行
elseLcdWriteCmd(0x 80列);//第一行
}
/************************************************
函数功能:显示3位数
入口参数:行行位置,栏列位置,编号显示数据
**************************************************/
void ShowNum(无符号字符行,无符号字符列,无符号字符数)
{
row %=2;col %=40//防止越界
LcdWriteCmd(0x80行*0x40列);//光标定位
如果(数字《10》
{
LcdWriteDat(" ");//百位
LcdWriteDat(" ");//十位
lcdwritedat(num"0”);//个位
}
埃尔塞夫(编号《100》)
{
LcdWriteDat(" ");//百位
lcdwritedat(num/10“0”);//十位数
lcdwritedat(num“0”);//有点
}
其他
{
LcdWriteDat(数字/100 ' 0 ');//数百
lcdwritedat(num/10“0”);//十位数
lcdwritedat(num“0”);//有点
}
}
/************************************************
函数:在给定位置显示一个字符串。
参数:行位置、列位置、字符串
**************************************************/
Void LcdShowStr(unsigned char row,unsignedchar col,unsigned char code *str) //显示字符
{
uchari=0;
row %=2;col %=40//阻止越境
LcdWriteCmd(0x80行*0x40列);//光标定位
for(;col《40str[i]!=0;I,col){ LcdWriteDat(str[I]);}
}
/***************************************
I2C.h
***************************************/
# I2C国际机场
#定义I2C_H
# I2C国际机场
#endif
void I2C start();//产生总线启动信号
void I2C stop();//生成一个总线停止信号
位I2CWrite(无符号字符dat);//I2C总线写操作,要写的字节dat dat,返回值是响应状态。
位I2CWrite(无符号字符dat);//I2C总线写操作,要写的字节dat dat,返回值是响应状态。
无符号字符I2CReadACK();//I2C总线读取并发送响应信号,返回值为读取的字节。
无符号字符I2CReadNAK();
#endif
/***************************************
I2C.c
***************************************/
#定义I2C_C
#包含" config.h "
#包含“I2C.h”
# define I2C delay(){ _ nop _();_ nop _();_ nop _();_ nop _();}
/********************************************
功能:产生总线启动信号。
入口参数:无。
********************************************/
void I2CStart()
{
I2C _ SDA=1;//首先确定SDA和SCL都高。
I2C _ SCL=1;
I2C delay();
I2C _ SDA=0;//首先拉低SDA
I2C delay();
I2C _ SCL=0;//再拉下SCL
}
/********************************************
功能:产生公交停车信号。
入口参数:无。
********************************************/
voidI2CStop()
{
I2C _ SCL=0;//首先,确保SDA和SCL都为低电平。
I2C _ SDA=0;
I2C delay();
I2C _ SCL=1;//先把SCL拉高
I2C delay();
I2C _ SDA=1;//再次拉高SDA
I2C delay();
}
/********************************************
函数:I2C总线写操作,返回值为响应状态。
入口参数:要写入的字节dat。
********************************************/
I2CWrite位(无符号字符数据)
{
位ack//用于临时存储响应位的值
无符号字符掩码;//用于检测字节中的位值的掩码变量
for(mask=0x 80;面膜!=0;面具=1)//从高到低进行。
{
If ((maskdat)==0) //该位的值输出到SDA
I2C _ SDA=0;
其他
I2C _ SDA=1;
I2C delay();
I2C _ SCL=1;//拉高SCL
I2C delay();
I2C _ SCL=0;//再次拉低SCL以完成一个位周期。
}
I2C _ SDA=1;//8位数据发送后,主机释放SDA检测从机的回应。
I2C delay();
I2C _ SCL=1;//拉高SCL
ack=I2C _ SDA;//此时读取SDA值,即从机的响应值。
I2C delay();
I2C _ SCL=0;//再次拉低SCL完成响应位,保持总线。
return(~ ack);//将响应值反转以符合通常的逻辑:0=不存在或忙或写失败,1=存在且空闲或写成功。
}
/********************************************
功能:I2C总线读取并发送不应答信号,
返回值是读取的字节。
入口参数:无。
********************************************/
无符号字符I2CReadNAK()
{
无符号字符掩码;
无符号char dat
I2C _ SDA=1;//确保主机首先释放SDA
for(mask=0x 80;面膜!=0;面具=1)//从高到低进行。
{
I2C delay();
I2C _ SCL=1;//拉高SCL
If(I2C_SDA==0) //读取SDA的值
dat=~ mask当//为0时,dat中的相应位被清0。
其他
dat |=mask当//为1时,dat中相应的位置为1。
I2C delay();
I2C _ SCL=0;//再次拉低SCL,以便从机发送下一位。
}
I2C _ SDA=1;//8位数据发送后,拉高SDA,发送不应答信号。
I2C延迟();
I2C _ SCL=1;//拉高SCL
I2C延迟();
I2C _ SCL=0;//再拉低SCL完成非应答位,并保持住总线
返回数字式录音带(digital audio tape)DOS文件名数据文件
}
/********************************************
函数功能:I2C总线读操作,并发送应答信号,
返回值为读到的字节。
入口参数:无。
********************************************/
无符号字符I2CReadACK()
{
无符号字符掩码;
无符号字符数据
I2C _ SDA=1;//首先确保主机释放国家药品监督管理局
for(掩码=0x 80面膜!=0;面具》=1)//从高位到低位依次进行
{
I2C延迟();
I2C _ SCL=1;//拉高SCL
if(I2C_SDA==0) //读取国家药品监督管理局的值
dat=~ mask//为0 时,dat中对应位清零
其他
dat |=mask//为一时,dat中对应位置一
I2C延迟();
I2C _ SCL=0;//再拉低SCL,以使从机发送出下一位
}
I2C _ SDA=0;//8 位数据发送完后,拉低SDA,发送应答信号
I2C延迟();
I2C _ SCL=1;//拉高SCL
I2C延迟();
I2C _ SCL=0;//再拉低SCL完成应答位,并保持住总线
返回数字式录音带(digital audio tape)DOS文件名数据文件
}/***************************************
键盘。h
***************************************/
#ifndef _KEY_BOARD_H
#define _KEY_BOARD_H
#ifndef _KEY_BOARD_C
走读生无符号char SetVal
#endif
void键操作();//按键处理,手动运行
#endif
/***************************************
键盘C
***************************************/
#define _KEY_BOARD_C
#包含"config.h "
#包含"键盘. h "
#包含"液晶显示器1602.h "
#包含" main.h "
无符号字符SetVal=25//设定初始值
/****************************************
函数功能:按键处理,手动运行
入口参数:无
*****************************************/
void KeyAction()
{
if(AK_KEY==0)//LCD1602背光控制
{
戴莱姆斯(10);
if(AK_KEY==0)
{
LCD1602_AK=!液晶显示器1602 _ AK
而(!AK _ KEY);
}
}
如果(MA_KEY==0)
{
戴莱姆斯(10);
如果(MA_KEY==0)
{
if(staSystem==E_AUTO)
{
staSystem=E _ MANUAL
AUTO _ LED=关;
手动_ LED=开;
}
elseif(staSystem==E_MANUAL)
{
staSystem=E _ AUTO
AUTO _ LED=开;
手动_ LED=关闭
}
而(!MA _ KEY);
}
}
if(staSystem==E_MANUAL)//手动工作状态
{
if(UP _ KEY==0){ PUMP=ON;泵浦LED=开;}//手动开
else if(DN _ KEY==0){ PUMP=OFF;泵浦LED=关;}//手动关}
}
elseif(staSystem==E_AUTO)//自动状态
{
if(UP_KEY==0)
{
戴莱姆斯(10);
if(UP_KEY==0)
{
if(设置val《100》设置val;//设置
而(!UP _ KEY);
}
}
if(DN_KEY==0)
{
戴莱姆斯(10);
if(DN_KEY==0)
{
if(设置值0)设置val-;//设置-
而(!DN _ KEY);
}
}
}
}责任编辑人:CC