模拟智能灌溉系统编程

一. 题目概述: 目的: 通过电位器 Rb2 输出电压信号,模拟湿度传感器输出信号,再通过AD 采集完成湿度测量功能; 通过 DS1302 芯片提供时间信息; 通过按键完成灌溉系统控制和湿度阈值调整功能,通过 LED 完成系统工作状态指示功能。 元器件:

一. 题目概述:

  1. 目的:
    通过电位器 Rb2 输出电压信号,模拟湿度传感器输出信号,再通过AD 采集完成湿度测量功能;
    通过 DS1302 芯片提供时间信息;
    通过按键完成灌溉系统控制和湿度阈值调整功能,通过 LED 完成系统工作状态指示功能。
  2. 元器件:
    系统硬件电路主要由单片机:控制电路、显示单元、ADC 采集单元、RTC 单元、EEPROM 存储单元、继电器控制电路及报警输出电路组成。
  3. 具体操作:
  4. 系统工作及初始化状态说明
    1.1 自动工作状态,根据湿度数据自动控制打开或关闭灌溉设备,以 L1 点亮指示;
    1.2 手动工作状态,通过按键控制打开或关闭灌溉设备,以 L2 点亮指示;
    1.3 系统上电后处于自动工作状态,系统初始湿度阈值为 50%,此时若湿度低于50%,灌溉设备自动打开,达到 50%后,灌溉设备自动关闭;
    1.4 灌溉设备打开或关闭通过继电器工作状态模拟。
  5. 数码管单元
    时间及湿度数据显示格式如图 2 所示:
    0 8 - 3 0 8 0 5
    时(8时) 分隔符 分(30分) 熄灭 湿度(5%)
    数码管DS1 数码管DS2

图 2. 显示格式(8 点 30 分,土壤湿度 5%)
3. 报警输出单元
系统工作于手动工作状态下时,若当前湿度低于湿度阈值,蜂鸣器发出提示音,并可通过按键 S6 关闭提醒功能。
4. 功能按键
2.1 按键 S7 设定为系统工作状态切换按键;
2.2 手动工作状态下按键 S6、S5、S4 功能设定如下:
按下 S6 关闭蜂鸣器提醒功能,再次按下 S6 打开蜂鸣器提醒功能,如此循环;
S5 功能设定为打开灌溉系统;
S4 功能设定为关闭灌溉系统。
2.3 自动工作状态下按键 S6、S5、S4 功能设定如下:
S6 功能设定为湿度阈值调整按键,按下 S6 后,进入湿度阈值调整界面(如图 3
所示),此时按下 S5 为湿度阈值加 1,按下 S4 湿度阈值减 1,再次按下 S6 后,系统将新的湿度阈值保存到 EEPROM 中,并退出湿度阈值设定界面。

    • 8 8 8 8 5 2
      湿度阈值设置提示符 熄灭 湿度阈值(52%)
      数码管DS1 数码管DS2

图 3. 湿度阈值设定界面
5. 实时时钟
“模拟智能灌溉系统”通过读取 DS1302 时钟芯片相关寄存器获得时间,DS1302芯片时、分、秒寄存器在程序中设定为系统进行初始化设定,时间为 08 时 30 分。
6. 湿度检测单元
以电位器 Rb2 输出电压信号模拟湿度传感器输出信号,且假定电压信号与湿度成正比例关系 H 湿度 = KVRb2(K 为常数),Rb2 电压输出为 5V 时对应湿度为 99%。
7. EEPROM 存储单元
系统通过 EEPROM 存储湿度阈值,自动工作状态下,可通过按键 S6、S5、S4 设置和保存阈值信息。

二.对问题的解读

  1. 所谓的电位器Rb2也就是滑动变阻器Rb2,目的在于让滑动变阻器模拟湿度传感器。
  2. RTC单元也就是时钟单元。
  3. AD就是数模转换器。
  4. I2c控制AD和E2PROM两部分,驱动大致一样,只是存储位置不一样。
    AD:0X90 E2PROM:0XA0
  5. 灌溉设备的开与关是以继电器的亮灭情况显示的。
  6. 在写时钟的驱动时,初始化即为写数据。
    三.问题的难点
    A. 最难的地方在于,控制系统的部分,也就是独立按键。这一部分,涉及到状态切换,继电器和蜂鸣器的状态等。极易让人混淆。
    B. 其次,就是更改官方给的驱动。

四.问题的解决
A. 控制系统:

void keyscan()
{
	if(P30==0)
	{
		delayms(5);
			if(P30==0)
			{
				//手动状态
				if(status==0)
				{
					status=1;
					P2=0x80;
					P0=0xfd;
				}
				//自动状态
				else if(status==1)
				{
					status=0;
					P2=0x80;
					P0=0xfe;
				}
				
			}
			while(!P30);
	}	

	
	
	
	else  	if(P31==0)
	{
		delayms(5);
			if(P31==0)
			{
				
				if(status==0)
				{
					if(S6==0){S6=1;}
					else if(S6==1){S6=0;EEPROM_write(0x10,fazhi);}//退出时将阀值记录
				}
				
				else if(status==1)
				{
					kai=~kai; //手动状态时,蜂鸣器位移响应
				}
				
			}
			
			while(!P31);
	}
	
	
	
	
		else 	if(P32==0)
	{
		delayms(5);
			if(P32==0)
			{
				
				if(status==0){jia=1;}//自动状态  加1
				else if(status==1){jidian=1;}//手动状态 打开继电器

			}
			while(!P32);
	}
	
	
		else 	if(P33==0)
		{
			delayms(5);
			if(P33==0)
			{
				
				if(status==0){jian=1;}//自动状态  减1
				else if(status==1){jidian=0;}//手动状态 关闭继电器
					
			}
			while(!P33);
		}
	
}

还有主函数的部分:
		//自动状态
		if(status==0)
		{
			if(shidu<fazhi)
			{
				P2=0xa0;
				P0=0x10;//打开继电器
			}
			else 
			{
				P2=0xa0;
				P0=0x00;//关闭继电器
			}
			
		//6 自动状态下按键S6的切换
		if(S6==1)
		{
			
			if(jia==1)//检测加是否被按下
			{
				jia=0;
				fazhi++;
			}
			
			if(jian==1)//检测减是否被按下
			{
				jian=0;
				fazhi--;
			}
			
			yi=10;er=10;san=11;si=11;wu=11;liu=11;qi=fazhi/10;ba=fazhi%10;
		}
		else if(S6==0)
		{
			DS_get();
			yi=shijian[2]/10;er=shijian[2]%10;san=10;
			si=shijian[1]/10;wu=shijian[1]%10;liu=11;
			qi=shidu/10;ba=shidu%10;
			
		}
	}
	
	
	//手动状态
	else if(status==1)
	{
		if((shidu<fazhi)&&(kai==0))
		{
			if(jidian==1)
			{
				P2=0xa0;P0=0x10;//打开继电器
			}
			else if(jidian==0)
			{
				P2=0xa0;P0=0x00;//关闭继电器
			}
			
			//报警功能
			if((shidu<fazhi)&&(kai==1))
			{									//开蜂鸣器
				if(jidian==1)//接着判断继电器是否要打开
				{
					P2=0xa0;P0=0x50;
				}
				else if(jidian==0)
				{
					P2=0xa0;P0=0x40;//打开报警器
				}
				
				else if(shidu>fazhi)
				{
					//关闭继电器与否
					if(jidian==1){P2=0xa0;P0=0x10;}
					else if(jidian==0){P2=0xa0;P0=0x00;}
				}
				
				yi=shijian[2]/10;er=shijian[2]%10;san=10;
				si=shijian[1]/10;wu=shijian[1]%10;liu=11;
				qi=shidu/10;ba=shidu%10;
		}
		
	}
}

B. 驱动
i. I2c通信改动部分

uchar  AD_read(uchar add)
{
	uchar temp;
	
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();

IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_Stop();

return temp;

}

uchar EEPROM_read(uchar add)
{
	uchar temp;
	
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();

IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_Stop();

return temp;

}

void  EEPROM_write(uchar add,uchar dat)
{
	IIC_Start();//启动开始接受信号
	IIC_SendByte(0xa0);//选择器件
	IIC_WaitAck();//响应
	IIC_SendByte(add);//写地址
	IIC_WaitAck();//响应
	IIC_SendByte(dat);//写数据
	IIC_WaitAck();//响应
	IIC_Stop();//停止接受信号
	

}

ii. 时钟

uchar shijian[]={0,30,8,0,0,0,0};
				unsigned char Read_Ds1302 ( unsigned char address )
				{
				 	unsigned char i,temp=0x00,dat1,dat2;//更改
				 	RST=0;
					_nop_();
				 	SCK=0;
					_nop_();
				 	RST=1;
					_nop_();
				 	Write_Ds1302_Byte(address);
				 	for (i=0;i<8;i++) 	
				 	{		
						SCK=0;
						temp>>=1;	
				 		if(SDA)
				 		temp|=0x80;	
				 		SCK=1;
					} 
				 	RST=0;
					_nop_();
				 	RST=0;
					SCK=0;
					_nop_();
					SCK=1;
					_nop_();
					SDA=0;
					_nop_();
					SDA=1;
					_nop_();
					
					dat1=temp/16;
					dat2=temp%16;
					temp=dat1*10+dat2;
				//	temp=0.39*temp;
					return (temp);			
				}
				//初始化
				void DS_init(void)
				{
				 uchar i,add;
					add=0x80;//80是写,81则是读
					Write_Ds1302(0x8e,0x00);//开始对1302进行操作
					for(i=0;i<7;i++)
					{
						Write_Ds1302(add,shijian[i]);
						add=add+2;
					}
					Write_Ds1302(0x8e,0x80);//80则是关闭
				}
				//读取
				void DS_get()
				{
				  uchar i,add;
					add=0x81;//81是读
					for(i=0;i<7;i++)
					{
						shijian[i]=Read_Ds1302(add);
						add=add+2;
					}
					Write_Ds1302(0x8e,0x80);
				}

五.经验总结
 驱动的更改没有太大的变化,只需要将更改地方理解并记住即可。做了几次真题,驱动也就更改那么几下,大致不会改变。所以,需要平时反复的理解并加以记忆,方可将这一部分写好。需要说明的是:驱动就只有3个部分i2c总线,DS1302和DS18B20。而i2c总线只控制两个部分:ADC转换器和EEPROM(带电可擦可编程读写器),在这一届赛题中,这一部分已经定型,可以当做模板使用。
 独立按键的部分,思维卡在了如何调换模式上,自己想了两个办法,一个是逻辑关系不对,另一个是自认为逻辑正确,但实际没有实现。这个的核心在于:设立一个标志位,举个栗子:工作状态的转换,用status这样一个标志位,用0表示自动状态,用1表示手动状态。即可区分开来。那么蜂鸣器和继电器也是同样的道理。但到这里并没有完全解决这个问题,蜂鸣器和继电器的开和关的判定也是一个问题。在子函数掉用过后,主函数中尤其要注意这个问题。继电器的判断还是比较容易的,但蜂鸣器就没有那么简单了。办法和之前的一样,设置一个标志位kai,根据题意进行改动。他在子函数中,需要反复实现转换时,就要用到位移~可以实现了。这样就用不到for循环了,简便易懂。
 怎么对阀值实现加减呢?我原来的想法是:在调用IIC的程序时增加一个变量num,便可以实现。但现实是不可以。那么正确的办法是:需要定义一个变量加法:jia, 还有阀值:fazhi。在S5键盘按下后,主函数进行判断,之后立即归0 ,为的是下一次加的时候,不会多加。最后直接将阀值加1。减法也是如此。
 驱动程序怎么改动?
以时钟芯片为例:在驱动的基础上,最重要的是初始化,也相当于读的操作。当然根据程序的要求,不能合二为一,需要分开写,因为两者还是有点区别的。首先,要注意的是:0x80是写数据,0x81是读数据,之后的操作是一样的,6个字节靠for循环一个一个进去,进行操作,最后就是停止这些操作。还有一点小问题,就是延时的问题,驱动中给的是52的延时,但是51的比52的快了8-12倍,所以,需要将延时,加上7个机器周期。
 读问题时,注意元器件的转化。
 以后再做赛题时,先看有何功能,按照这样的次序写程序: 写十五分钟的模板
改驱动 看任务书写操作并逐步调试。这样的大思路三步走,基本可以写完几乎全部的程序了。

知秋君
上一篇 2024-08-26 15:12
下一篇 2024-08-26 14:48

相关推荐