目录
单独51芯片和LCD1602,编写LCD初始化和显示函数,测试无误后,可用于显示后续数据用于排查错误.
搭建灌溉&警报系统,定义对应引脚测试电机,LED及蜂鸣器是否能正常工作
A/D转换电路,用电位器模拟湿度变化,测得一个0~5的模拟值,采用ADC0832进行A/D转换,将转换的数字信号处理为百分数显示在LCD屏幕
测试电机及警报工作所需的条件,设置两个阈值,当返回的湿度信号小于最低阈值,电机启动模拟灌溉直至湿度到最高阈值,且小于最低阈值时警报工作,此时实现了基本的湿度检测和自动灌溉
不同模式设置,设置三个字符串数组存三种花卉的名称,及三个数组存对应的三组最高/低阈值,通过独立按键检测函数判断按下的键码,LCD将显示对应的名称,阈值,另设三个按键实现阈值+/-,高低阈值转换
Setting_Init()函数 ,用于初始化按键,以及没有按键按下时默认显示水仙花的数据
最后晶振电路和复位电路实际在仿真可以省略,因为仿真过程里时钟频率是人为设置的(11.0592MHZ),复位电路并不会起效果,并且纯硬件电路也不需要编写代码,但在实际电路中是需要安装的
一、功能介绍
二、仿真原理图
设计思路:
-
单独51芯片和LCD1602,编写LCD初始化和显示函数,测试无误后,可用于显示后续数据用于排查错误.
-
搭建灌溉&警报系统,定义对应引脚测试电机,LED及蜂鸣器是否能正常工作
-
A/D转换电路,用电位器模拟湿度变化,测得一个0~5的模拟值,采用ADC0832进行A/D转换,将转换的数字信号处理为百分数显示在LCD屏幕
-
测试电机及警报工作所需的条件,设置两个阈值,当返回的湿度信号小于最低阈值,电机启动模拟灌溉直至湿度到最高阈值,且小于最低阈值时警报工作,此时实现了基本的湿度检测和自动灌溉
-
不同模式设置,设置三个字符串数组存三种花卉的名称,及三个数组存对应的三组最高/低阈值,通过独立按键检测函数判断按下的键码,LCD将显示对应的名称,阈值,另设三个按键实现阈值+/-,高低阈值转换
-
搭建晶振电路和复位电路
三、函数设计
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工作原理:
· CS_ 片选使能,低电平芯片使能。
· CH0 模拟输入通道0,或作为IN+/-使用。
· CH1 模拟输入通道1,或作为IN+/-使用。
· GND 芯片参考零电位(地)。
· DI 数据信号输入,选择通道控制。
· DO 数据信号输出,转换数据输出。
· CLK 芯片时钟输入。
· Vcc/REF 电源输入及参考电压输入(复用)
其中DI,DO是双向的,一般不同时工作,所以将DI/DO连接咋同一个I/O口可节省IO口数量
时序图:
第一个时钟上升沿之前将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、设置函数库
首先显然需要一个返回按下按键键码的函数
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),复位电路并不会起效果,并且纯硬件电路也不需要编写代码,但在实际电路中是需要安装的
四、结果展示
见主页上传的演示视频