温室盆栽灌溉系统设计(51)

目录

一、功能介绍

 二、仿真原理图

设计思路:

单独51芯片和LCD1602,编写LCD初始化和显示函数,测试无误后,可用于显示后续数据用于排查错误.

搭建灌溉&警报系统,定义对应引脚测试电机,LED及蜂鸣器是否能正常工作

A/D转换电路,用电位器模拟湿度变化,测得一个0~5的模拟值,采用ADC0832进行A/D转换,将转换的数字信号处理为百分数显示在LCD屏幕

测试电机及警报工作所需的条件,设置两个阈值,当返回的湿度信号小于最低阈值,电机启动模拟灌溉直至湿度到最高阈值,且小于最低阈值时警报工作,此时实现了基本的湿度检测和自动灌溉

 不同模式设置,设置三个字符串数组存三种花卉的名称,及三个数组存对应的三组最高/低阈值,通过独立按键检测函数判断按下的键码,LCD将显示对应的名称,阈值,另设三个按键实现阈值+/-,高低阈值转换

搭建晶振电路和复位电路

三、函数设计 

1、延时函数

1.1、实现功能

1.2、代码实现

2、独立按键函数

2.1、实现功能

2.2、设计原理

2.3、代码实现

3、LCD1602功能函数

4、Warning函数

4.1、实现功能

4.2、设计原理

4.3、代码实现

5、 A/D模数转换函数

5.1、实现功能

5.2、设计原理

ADC0832工作原理:

5.3、代码实现

 6、设置函数库

Key()函数

Setting函数 

 Show_Set函数,显示当前设置(名称,阈值,测得湿度)

Setting_Init()函数 ,用于初始化按键,以及没有按键按下时默认显示水仙花的数据

main()函数设计

 主函数设计比较简单

最后晶振电路和复位电路实际在仿真可以省略,因为仿真过程里时钟频率是人为设置的(11.0592MHZ),复位电路并不会起效果,并且纯硬件电路也不需要编写代码,但在实际电路中是需要安装的 

四、结果展示


 

一、功能介绍

d42499d68f8949f1a92271f8133cf32d.jpeg

 二、仿真原理图

2a99a229aee245428be68c28b739e5fb.png

设计思路:

  1. 单独51芯片和LCD1602,编写LCD初始化和显示函数,测试无误后,可用于显示后续数据用于排查错误.

  2. 搭建灌溉&警报系统,定义对应引脚测试电机,LED及蜂鸣器是否能正常工作

  3. A/D转换电路,用电位器模拟湿度变化,测得一个0~5的模拟值,采用ADC0832进行A/D转换,将转换的数字信号处理为百分数显示在LCD屏幕

  4. 测试电机及警报工作所需的条件,设置两个阈值,当返回的湿度信号小于最低阈值,电机启动模拟灌溉直至湿度到最高阈值,且小于最低阈值时警报工作,此时实现了基本的湿度检测和自动灌溉

  5.  不同模式设置,设置三个字符串数组存三种花卉的名称,及三个数组存对应的三组最高/低阈值,通过独立按键检测函数判断按下的键码,LCD将显示对应的名称,阈值,另设三个按键实现阈值+/-,高低阈值转换

  6. 搭建晶振电路和复位电路

三、函数设计 

1、延时函数

1.1、实现功能

实现毫秒单位延时

1.2、代码实现

#include<intrins.h>

void Delay(unsigned int xms)		//@11.0592MHz
{
	unsigned char i, j;
    while(xms)
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
	
}

2、独立按键函数

2.1、实现功能

获取按键值函数,用于检测按下的按键键码

2.2、设计原理

首先需要声明按键引脚

sbit SET_SHUIXIAN = P1^0;   //定义6个按键引脚
sbit SET_MUDAN = P1^1;
sbit SET_MEIGUI = P1^2;
sbit SET_ADD = P1^3;
sbit SET_MINUS = P1^4;
sbit MOD = P1^5;

当引脚为低电平时接地表示对应按键按下

2.3、代码实现

unsigned char Key()
{
	unsigned char KeyNumber = 0;
	//按键引脚为低电平表示按键按下
	if(SET_SHUIXIAN == 0){Delay(20);while(!SET_SHUIXIAN);Delay(20);KeyNumber = 1;}  
	if(SET_MUDAN == 0){Delay(20);while(!SET_MUDAN);Delay(20);KeyNumber = 2;}
	if(SET_MEIGUI == 0){Delay(20);while(!SET_MEIGUI);Delay(20);KeyNumber = 3;}
	if(SET_ADD == 0){Delay(20);while(!SET_ADD);Delay(20);KeyNumber = 4;}
	if(SET_MINUS == 0){Delay(20);while(!SET_MINUS);Delay(20);KeyNumber = 5;}
	if(MOD == 0){Delay(20);while(!MOD);Delay(20);KeyNumber = 6;}	
	return KeyNumber;
}

3、LCD1602功能函数

LCD1602功能函数有初始化、显示字符、显示无符号/有符号数字、显示字符串等等

函数声明模块

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

本次用到的LCD1602显示函数有LCD_Init、LCD_ShowString、LCD_ShowNum

代码实现段

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

4、Warning函数

4.1、实现功能

警报&灌溉函数,根据测得湿度控制发动机和警报系统工作

4.2、设计原理

定义三个引脚

sbit PMQ = P2^3;   //蜂鸣器引脚定义
sbit LED_RED = P2^4;  //LED引脚定义
sbit WATER = P2^5;	//发动机引脚定义

设置三个参数湿度humidity,最高阈值HIGH,最低阈值LOW

湿度小于阈值,开启发动机,LED和蜂鸣器警报

湿度大于等于阈值,发动机停止,LED和蜂鸣器不警报 

湿度在阈值之间,发动机工作直至到达最高阈值,LED和蜂鸣器不警报

再使用前需初始化三个引脚,即给低电平,因为电路开启默认所有引脚为高电平

4.3、代码实现

 

	if(humidity<LOW)  //湿度小于阈值,开启发动机,LED和蜂鸣器警报
	{
		WATER = 1;
		LED_RED = 1;
		PMQ = 1;
	}
	else if(humidity>=HIGH)  //湿度大于等于阈值,发动机停止,LED和蜂鸣器不警报
	{
		WATER = 0;
		LED_RED = 0;
		PMQ = 0;
	}
	else			//湿度在阈值之间,发动机工作直至到达最高阈值,LED和蜂鸣器不警报
	{
		LED_RED = 0;
		PMQ = 0;
	}

5、 A/D模数转换函数

5.1、实现功能

ADC0832模数转换函数,将输入的模拟电压值(0-5V)转换为数字量

5.2、设计原理

输入模拟量来自电位器电压值(0~5)

ADC0832工作原理:

ca5655634db34e3cb3e7341d276febd5.png

· CS_ 片选使能,低电平芯片使能。

· CH0 模拟输入通道0,或作为IN+/-使用。

· CH1 模拟输入通道1,或作为IN+/-使用。

· GND 芯片参考零电位(地)。

· DI 数据信号输入,选择通道控制。

· DO 数据信号输出,转换数据输出。

· CLK 芯片时钟输入。

· Vcc/REF 电源输入及参考电压输入(复用) 

其中DI,DO是双向的,一般不同时工作,所以将DI/DO连接咋同一个I/O口可节省IO口数量

 时序图:

32fc75c0b2894f6e9cdb61ff6a796279.png

第一个时钟上升沿之前将CS'置0,表示芯片解除禁用,在此之前CLK,DI/DO电平任意

第一个时钟上升沿下沉之前将DI置为高电平,表示开启AD转换

第二、三时钟上升沿为选择工作方式,分别在时钟上升沿到来之前将DI置为0或1

若两位DI依次为1,、0,则工作方式为CH0单通道;

若两位数据为0、0,工作方式为CH0作为正输入端IN+,CH1作为负输入端IN-进行输入

若两位数据为0、1,将CH0作为负输入端IN-,CH1 作为正输入端IN+进行输入

到第三个脉冲的下降之后DI端的输入电平就失去输入作用,此后DO/DI端则开始利用数据输出DO进行转换数据的读取.

从第4个脉冲下降沿开始由DO端输出转换数据最高位Data7,随后每一个脉冲的下降沿DO端输出下一位数据。直到第11个脉冲时发出最低位数据Data0,一个字节的数据输出完成,此阶段为MSB先出;自第12个脉冲开始从Data0开始每一个时钟依次从低位到高位重新读取该字节数据,用于校验,当两次读取的数据相同时,该数据就作为一次AD转换的结果输出.

5.3、代码实现

定义引脚与头文件

#include<intrins.h>

sbit ADC_CLK = P3^1; //定义ADC0832引脚
sbit ADC_CS = P3^0;
sbit ADC_DI = P3^2;
sbit ADC_DO = P3^2;

 #include<intrins.h>中包括接下来需要用到的延时函数_nop_().

ADC0832初始化,未开始工作前,CS置1芯片禁用,CLK置0,DI/O任意

	ADC_CS = 1;
	ADC_CLK = 0;
	ADC_DI = 1;

前三个时钟代码实现

	ADC_CS = 0;    //片选使能
	ADC_CLK = 0;
	ADC_DI = 1;
	_nop_();
	ADC_CLK = 1;   //第一个时钟脉冲,启动AD转换
	_nop_();
	ADC_CLK = 0;
	//选通道 DI 输入1  0  选择单通道,通道0
	ADC_DI = 1;	   //第二个脉冲输入1表明单通道输入
	_nop_();
	ADC_CLK = 1;	
	_nop_();
	ADC_CLK = 0;
	ADC_DI = 0;	   //第三个脉冲输入0表明选择通道0
	_nop_();
	ADC_CLK = 1;	
	_nop_();
	ADC_CLK = 0;
	
	ADC_DO = 1;  //数据线拉高准备接收数据

 定义两个变量ad_result1 ,ad_result2 用于接收数据

LSB先出,设置8次循环,每次循环,模拟时钟上升沿和下降沿,并将ad_result1左移一位,当输出DO为1时ad_result1与0x01即(0000 0001)进行或运算,DO为0则不进行,8次循环后一个字节数据接收完成

MSB先出,同样设置8次循环,但在时序图中发现第12个时钟上升沿期间,Data0位已经在数据位,因此循环中先进行数据读取再模拟时钟上升沿和下降沿,数据读取不同点在于从最低位开始读取,因此将ad_result2右移一位,读取时与0x80进行或运算

代码实现

	for(i=0;i<8;i++)
	{
		ADC_CLK = 1;
		_nop_();
		ADC_CLK = 0;
		ad_result1 = ad_result1<<1; //左移一位 MSB先出
		if(ADC_DO==1)
			ad_result1 = ad_result1 | 0x01;   //数据为1时保存最后一位为1,0不变
	}
	
	for(i=0;i<8;i++)
	{
		ad_result2 = ad_result2>>1; //右移一位 LSB先出
		if(ADC_DO==1)
			ad_result1 = ad_result1 | 0x80;   //数据为1时保存第一位为1,0不变
		ADC_CLK = 1;
		_nop_();
		ADC_CLK = 0;
	}
	ADC_CLK = 1;
	ADC_CS = 1;    //一次操作结束,禁用
	ADC_DI = 1;

输出结果

	if(ad_result1==ad_result2)
		return ad_result1;
	else
		return 0;

这一阶段结束,可以设置湿度阈值来控制电机和警报 

 6、设置函数库

e95cf5d34d4d4f7ebdea5a77ca1ba55a.png

 首先显然需要一个返回按下按键键码的函数

Key()函数

引脚连接按键接地,当其中一个按键按下时,连接的引脚相当于直接接地,为低电平,因此低电平为判断条件,再将每个按键按下赋予键码和对应意义如图所示,代码在上文

整个Setting.c需要用到的库文件

#include"Setting.h"
#include"Warning.h"
#include"LCD1602.h"
#include"Delay.h"
#include<string.h>
#include <REGX51.H>

定义数组存储三种花卉的名称、阈值

unsigned char shuixian[2] = {20,70};  //初始化设置阈值
unsigned char mudan[2] = {30,60};
unsigned char meigui[2] = {25,75};
char name1[]="ShuiXianHua";        //定义花名字符串数组
char name2[]="MuDanHua";
char name3[]="MeiGuiHua";

其他变量定义

unsigned char low = 20;   //存储当前的阈值
unsigned char high = 70;
char *str;      //用于指向阈值数组地址
unsigned char Mod = 0;   //+/-的对象选择 0表示加减最低阈值。1表示加减最高阈值
char name[13] = '\0';     //存储当前花名

Setting函数 

/**
  * @brief 按键设置函数,用于不同按键对应功能实现包括更改花名,阈值,增大/减小阈值
  * @param  Keynum,humidity
  * @retval 无
  */
void Setting(unsigned char Keynum,humidity)

当按键1按下时,str指针指向水仙花的阈值数组地址,要显示的最低最高阈值分别为水仙花的最低阈值和最高阈值,名称显示“ShuiXianHua” 以此类推;

	if(Keynum == 1)            //1键按下,存储水仙花的阈值&名称
	{	
		str = shuixian;       //指针指向水仙花阈值数组
		low = shuixian[0];
		high = shuixian[1];
		strcpy(name,name1);
	}

当按键4按下时如果此时MOD=0,则str指向的当前数组里最低阈值减1,若MOD=1,当前最高阈值减1;按键5按下时同理,为+1

	if(Keynum == 4)			//4键按下,模式0当前最低阈值+1,模式1最高阈值+1
	{	
		if(Mod == 0)
		{
			(*str) = (*str)+1;   //
			low = (*str);
		}
		else if(Mod == 1)
		{
			*(str+1) = *(str+1)+1;
			high = *(str+1);
		}
	}

按键6按下为MOD在0、1之间转换,工作在加减最低阈值和加减最高阈值两种模式 

	if(Keynum == 6)			//6键按下,转换模式
	{	
		if(Mod == 0)
			Mod = 1;
		else if(Mod == 1)
			Mod = 0;
	}

 Show_Set函数,显示当前设置(名称,阈值,测得湿度)

由于存储名称和阈值的数组都为全局变量,而湿度需要在主函数中处理,所以Show_Set函数只有一个参数湿度humidity,为处理过湿度数据的百分数形式

	//显示当前花名,湿度,阈值
	LCD_ShowString(1,1,name);
	LCD_ShowNum(2,14,humidity,2);
	LCD_ShowString(2,16,"%");
	LCD_ShowString(2,1,"S:");
	LCD_ShowNum(2,3,low,2);
	LCD_ShowString(2,5,"%  N:");
	LCD_ShowNum(2,10,high,2);	
	LCD_ShowString(2,12,"%");
	Warning(humidity,high,low);

Setting_Init()函数 ,用于初始化按键,以及没有按键按下时默认显示水仙花的数据

	SET_SHUIXIAN = 1;  //初始化按键引脚,设为高电平
	SET_MUDAN = 1;
	SET_MEIGUI = 1;
	SET_ADD = 1;
	SET_MINUS = 1;
	MOD = 1;
	LCD_ShowString(1,1,name1); //默认初始显示水仙花
	LCD_ShowString(2,1,"S:20");
	LCD_ShowString(2,5,"%  N:70");
	LCD_ShowString(2,12,"%");

main()函数设计

 主函数设计比较简单

首先定义两个变量用于接收键码和A/D转换后的输出结果

其次是各模块初始化

	LCD_Init();   //LCD1602初始化
	Warning_Init();	    //警报&发动机初始化	
	ADC0832_Init();		//ADC0832初始化
	Setting_Init();		//Setting函数初始化

处理A/D转换结果便于显示

		ad_result = ADC0832_Conv();  //接收AD转换得到的数字量
		ad = 1.0*ad_result*100/255;		//转换为百分比

 接收键码并在判断有按键按下的时刻传递键码和湿度值给Setting函数修改设置

		KeyNum = Key(); //接收按下的键码
		if(KeyNum)		//有按键按下时
		{
			Setting(KeyNum,ad);   //设置为对应当前键码的花名,湿度阈值
		}

 LCD显示当前数据

		Show_Set(ad);	//LCD显示当前花名,阈值,测得湿度

由于键码按下只有一瞬间,所以显示数据函数需要放在判断条件外,在while循环里不断显示 

最后晶振电路和复位电路实际在仿真可以省略,因为仿真过程里时钟频率是人为设置的(11.0592MHZ),复位电路并不会起效果,并且纯硬件电路也不需要编写代码,但在实际电路中是需要安装的 

四、结果展示

见主页上传的演示视频

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值