智能宠物小屋
资料链接:https://download.csdn.net/download/mbs520/20365056
QQ交流群1:981140834
QQ交流群2:473982062
QQ交流群3:718245727
QQ交流群4:598455837
一、项目设计要求
以单片机为控制核心设计一个智能宠物小屋控制系统,设计基本要求如下:
1)具有自动喂食喂水、自动通风换气、自动清洁等功能;
2) 完成智能宠物小屋控制系统硬件电路设计;
3) 完成智能宠物小屋控制系统软件设计。
二、硬件框架搭建
(一)硬件框架
(二)设计功能
(1)实时时间、温度显示以及调控温度;
控制时钟芯片将当前时间显示在液晶屏上,设加、减、功能切换、参数切换四个按键,用于调节时间、调节功能闹钟及手动开启或关闭某功能设置;温度采集电路完成温度值的检测及数值的转换和线性化处理,处理后发送给单片机,由单片机控制温度值显示;
将采集的温度与标准温度值进行比对,若比设定值大的话就开启风扇至设定温度停止;
(2)定时喂食喂水。能够定时地为宠物提供食物和水;
通过时钟电路设定喂食时间,带扇叶的步进电机正转打开喂食口,让食物从料仓落到食盘,然后电机反转关闭喂食口;
通过时钟电路设定喂水时间,由单片机控制在水仓的水泵抽水通过塑料软管流入宠物水盆,水盆内有水位传感器测水位,达到标准水位停止抽水。
(3)定时清洁宠物生活区域。根据电机正反转循环清扫笼子底部;
设定清洁时间,控制带有清洁电刷的步进电机正反转清洁,利用红外检测宠物是否在屋内,若在屋内在下个清洁时间清洁,反之即时开启清洁;
三、硬件材料选型
(一)单片机选型
使用STC89C52单片机,其内部有2 bytes 片内RAM数据存储器。由于要随时要对程序进行修改和新增一些程序的功能,与AT89S52相比,STC89C52无需编程器和安装驱动就可以下载,而且片内带2 K bytes EEPROM 存储空间,因此选择STC89C52单片机作为主控系统。
(二)显示模块
采用LCD液晶屏显示,不但耗能少,而且可显示汉字与英文。在显示内容方面也变得更加灵活,修改程序编写就可完成,无需对硬件进行修改,也更便于扩展其他电路。此外,该模块硬件连接也比较简单易操作。与方案一较之,方案二耗能更少、显示更灵活、电路的扩展更方便,而且软硬件设计简单,因此选用LCD液晶屏为显示模块。
(三)水位传感器
Water Sensor水位传感器体积小巧、简单易用,可靠性高,易清洁,安装方便,价格低廉。输出信号为模拟信号,连接电路后可基本测出水位情况,满足水位检测要求。相比浮球式水位传感器,Water Sensor水位传感器简单易用。
(四)红外传感器
本文选用的红外感应模块为红外对管模块,主要是通过两个红外元件进行发射与接受进行感应。当发射载波频率与接收头固定频率处于一致的情况下,就可以接收红外感应。发射管先发送红外信号,红外信号会随着传送距离的加大慢慢减弱,当信号接触到检测体时,就形成红外反射。当反射的信号弱时,光敏二极管 L2 接收的红外光较弱,比较器LM393 的3脚电压大于2脚电压,接收检测引脚处于高电平状态,此时宠物较远;当反射信号较强,接收检测引脚处于低电平状态,此时宠物较近。
(五)时钟芯片模块
使用DS1302芯片实现时钟控制,DS1302 芯片是一款专门用于时钟控制的芯片,工作电压在2.0V-5.5V之间,可为实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,也可选择使用 24 或12 小时计时法,还可对每月的天数和闰年进行调整[7]。另外,它可以同步串行的方式完成与单片机之间的通信,因此,选用DS1302芯片作为时钟芯片。
(六)温度采集模块
采用数字式传感器DS18B20,具有测量范围广,测量精度高的特点。DS18B20只需要一条数据线就可以直接把数字温度值输出,无需外接AD电路,有利于电路简单化。因此,本系统选用DS18B20为温度传感器。
(七)电机驱动模块
使用H桥电路的L298N驱动器作为电机驱动模块。H桥电路可控制电机转速和方向,而且稳定性高。一片L298N驱动芯片可同时控制两个直流电机,过程并不复杂,通过主控芯片的I/O输入对输入端的逻辑电平状态进行改变,就可完成电机的正转反转控制,同时还具有过热自断和反馈检测的特性。另外,当电机出现卡死的情况时,芯片会启动过电流功能保护设备,所以选用L298N作为电机驱动模块。
(八)风扇,清理电机,水泵
直接选最便宜的直流电机
(九)其它
电阻、电容、晶振、二极管、三极管、蜂鸣器等…
四、元件清单
Description | Comment | Designator | Quantity |
电容 | 30pF | C1、C2 | 2 |
电容 | 10μF | C3 | 1 |
晶振 | 12M | X1 | 1 |
电机驱动 | L298N | LN1,LN2 | 2 |
温度传感器 | DS18B20 | IC1 | 1 |
红外传感器 | HW | IC2 | 1 |
单片机 | STC89C52 | IC3 | 1 |
时钟芯片 | DS1302 | IC4 | 1 |
电阻 | 10K | R1~R5 | 5 |
电阻 | 4.7K | R6 | 1 |
排阻 | 1K | Rp1 | 1 |
电位器 | 10K | WR1,WR2 | 1 |
蜂鸣器 | 有源 | BEEP | 1 |
液晶显示器 | LCD1602 | J4 | 1 |
按键 | 轻触 | Key1,Key2,Key3, Key4, Key5 | 5 |
发光二极管 | 红色 | LED1 | 1 |
风扇、水泵、电机 | 3-6V直流 | M1,M2,M3,M4 | 4 |
三极管 | 2N3906 | Q1,Q2,Q3,Q4 | 4 |
开关 | 拨动 | SW1 | 1 |
纽扣电池 | 3v | BAT1 | 1 |
晶振 | 32.768KHz | X2 | 1 |
二极管 | IN 4148 | D1,D2,D3 | 3 |
水位传感器 | 模拟 | T1 | 1 |
排针 | 4脚、多脚 | Header | 1 |
洞洞板 | 玻纤 | 9*15cm | 1 |
五、原理图设计
看不清,需要下载资料,用DXP或者AD软件打开
六、电路主板焊接
(一)先画布局图再进行焊接
(二)焊接好,并按照原理图连接好外围电路
七、程序框架设计
(一)主程序框架
其他具体框架参考论文
八、程序设计
篇幅有限、只贴出部分程序
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include "ds1302.h"
#include "temp.h"
#include "lcd.h"
#include "eeprom.h"
#include <string.h>
sbit K1 = P1^0; //按键
sbit K2 = P1^1;
sbit K3 = P1^2;
sbit K4 = P1^3;
sbit LCD_LED = P2^4; //lcd背光
sbit BEEP = P1^4; //蜂鸣器
sbit IR = P1^5; //红外
sbit WIT_0 = P1^6; //水位检测
sbit WIT_1 = P1^7;
sbit WATER_EN = P2^0; //水泵
sbit FAN_EN = P2^1; //风扇
sbit CLR_EN_A = P2^2; //清理电机
sbit CLR_EN_B = P2^3; //清理电机
sbit FEED_EN_A = P3^2; //喂食
sbit FEED_EN_B = P3^3; //喂食
typedef unsigned int u16;
typedef unsigned char u8;
//风扇启动时间
//8小时一次
u8 fan_time8[3] = {0x01,0x09,0x17};
//5小时一次
u8 fan_time5[5] = {0x00,0x05,0x10,0x15,0x20};
//3小时一次
u8 fan_time3[8] = {0x02,0x05,0x08,0x11,0x14,0x17,0x20,0x23};
//时间结构体
typedef struct
{
u8 hour;
u8 min;
u8 sec;
}type_time;
//系统信息结构体
typedef struct
{
type_time feed_time[5];//定时5个喂食时间
u8 feed_time_sw[5]; //5个喂食闹钟开关 0关 1开
type_time clr_time[5];//定时5个清理时间
u8 clr_time_sw[5]; //5个清理闹钟开关 0关 1开
}type_Sysinfo;
const u8 MENU_MAX = 6;//5个界面
u8 menu_val=1;//当前显示的界面页数
float curr_temp;//当前温度
volatile u8 state=0; //系统工作状态
//bit0 :进水状态
//bit1 :风扇状态
//bit2 :清理状态 正传
//bit3 :清理状态 反转
//bit4: 喂食状态 正传
//bit5: 喂食状态 反转
type_time curr_time;//当前时间
u8 fan_time = 5; //风扇每隔多长时间吹一次 hour
u8 fan_len_time = 5;//风扇转一次多少分钟 min
unsigned int ti=0; //修改第几个时间参数 、修改第几个闹钟参数
//日期
typedef struct
{
u8 year;
u8 month;
u8 day;
u8 hour;
u8 min;
u8 sec;
}type_date;
type_Sysinfo sysinfo;
void delay(unsigned int t );
void delay_ms(unsigned int t );
void dis_sys_start(void );
void dis_menu1_date(u8 sel);
void dis_menu2_mdf_date(void);
void dis_menu3_mdf_fdtime(void );
void dis_menu4_mdf_clrtime(void );
void dis_menu5_state(void );
void ctrl_motor(void);
void dis_menu6_motor_ctrl(void );
void key_scan(void );
u8 get_key_val(void);
void save_sysinfo(void );
void read_sysinfo(void );
/*******************************************************************************
* 函 数 名 : Timer0Init
* 函数功能 : 定时器0初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//开启定时器
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main(void)
{
u16 ucount=0;
//只执行一次
if(byte_read(0x2200) != 0x56)//如果是第一次执行
{
memset(&sysinfo,0,sizeof(sysinfo));
save_sysinfo();//清除数据
SectorErase(0x2200);
byte_write(0x2200,0x56);
}
//关闭所有电机
WATER_EN = 1; //水泵
FAN_EN = 1; //风扇
CLR_EN_A = 1; //清理电机
CLR_EN_B = 1; //清理电机
FEED_EN_A = 1; //喂食
FEED_EN_B = 1; //喂食
LcdInit(); //lcd初始化
//Ds1302Init(); //时钟初始化
Ds18b20Init(); //温度传感器初始化
Timer0Init(); //定时器初始化
read_sysinfo();//读取系统信息
LcdClean(); //LCD清屏
dis_sys_start();//显示初始界面
LcdClean(); //LCD清屏
while(1)
{
switch(menu_val) //界面选择
{
case 1: //时间显示界面
if(ucount %80 == 0)dis_menu1_date(1);
ctrl_motor();//电机控制
break;
case 2: //时间修改模式
dis_menu2_mdf_date();
break;
case 3: //设置喂食闹钟界面
dis_menu3_mdf_fdtime();
break;
case 4: //设置清理闹钟界面
dis_menu4_mdf_clrtime();
break;
case 5:
if(ucount %100 == 0)dis_menu5_state(); //显示系统状态
break;
case 6:
if(ucount %100 == 0)dis_menu6_motor_ctrl();//电机控制
break;
}
key_scan();//按键烧苗
ucount++;
delay_ms(10);
}
}
/*******************************************************************************
* 函 数 名 : void Timer0() interrupt 1
* 函数功能 : 定时器0中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0() interrupt 1
{
static u16 i,speed;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(state & 0x01)speed=5;
else speed = 10;
if(state != 0)//有电机需要转动
{
if(i%speed == 0)//开启
{
if(state & 0x01)//水泵
{
WATER_EN = 0; //水泵
}
else if(state & 0x02)
{
FAN_EN = 0; //风扇
}
else if(state & 0x04)
{
CLR_EN_A = 0; //清理电机 正传
CLR_EN_B = 1; //清理电机
}
else if(state & 0x08)
{
CLR_EN_A = 1; //清理电机 反传
CLR_EN_B = 0; //清理电机
}
else if(state & 0x10)
{
FEED_EN_A = 1; //喂食电机 正传
FEED_EN_B = 0; //喂食电机
}
else if(state & 0x20)
{
FEED_EN_A = 0; //喂食电机 反传
FEED_EN_B = 1; //喂食电机
}
}
else //关闭
{
WATER_EN = 1; //水泵 6
FAN_EN = 1; //风扇 10
CLR_EN_A = 1; //清理电机 10
CLR_EN_B = 1; //清理电机 10
FEED_EN_B = 1;
FEED_EN_A = 1;
}
}
}
/*********延时函数***********/
void delay_ms(unsigned int t) //毫秒延时
{
unsigned int a,b;
for(a=0;a<t;a++)
for(b=0;b<120;b++);
}
void dis_sys_start(void)
{
LcdWriteCom(0x80);
LcdWritestr("Welcome!");
LcdWriteCom(0xc0);
LcdWritestr("Pet Feed System");
delay_ms(2000);
}
/*******************************************************************************
* 函 数 名 : dis_menu1_date
* 函数功能 : 显示时间日期温度
*******************************************************************************/
void dis_menu1_date(u8 sel)
{
float tp;
int temp;
if(menu_val == 1)
{
Ds1302ReadTime();//读取时间
}
//显示日期
LcdWriteCom(0x80);
LcdWritestr("20");
LcdWriteData(TIME[6]/16+0x30); //年
LcdWriteData(TIME[6]%16+0x30);
LcdWriteData('-');
LcdWriteData(TIME[4]/16+0x30); //月
LcdWriteData(TIME[4]%16+0x30);
LcdWriteData('-');
LcdWriteData(TIME[3]/16+0x30); //日
LcdWriteData(TIME[3]%16+0x30);
LcdWritestr(" ");
switch(TIME[5]) //显示星期
{
case 0:LcdWritestr("Mon"); break;
case 1:LcdWritestr("Tue"); break;
case 2:LcdWritestr("Wed"); break;
case 3:LcdWritestr("Thu"); break;
case 4:LcdWritestr("Fri"); break;
case 5:LcdWritestr("Sat"); break;
case 6:LcdWritestr("Sun"); break;
}
LcdWriteCom(0xC0);
LcdWriteData(' ');
LcdWriteData(TIME[2]/16+0x30); //时
LcdWriteData(TIME[2]%16+0x30);
LcdWriteData(':');
LcdWriteData(TIME[1]/16+0x30); //分
LcdWriteData(TIME[1]%16+0x30);
LcdWriteData(':');
LcdWriteData(TIME[0]/16+0x30); //秒
LcdWriteData(TIME[0]%16+0x30);
LcdWritestr(" ");
curr_time.hour = TIME[2];
curr_time.min = TIME[1];
curr_time.sec = TIME[0];
if(sel)
{
EA = 0;//关中断
temp = Ds18b20ReadTemp();//获取温度
EA = 1;//开中断
//显示温度
if(temp< 0)
{
LcdWriteCom(0xca);
LcdWriteData('-');
temp=temp-1;
temp=~temp;
tp=temp;
temp=tp*0.0625*100+0.5;
}
else
{
LcdWriteCom(0xca);
LcdWriteData('+');
tp=temp;
temp=tp*0.0625*100+0.5;
}
LcdWriteData(temp % 10000 / 1000 + 0x30);
LcdWriteData(temp % 1000 / 100 + 0x30);
LcdWriteData('.');
LcdWriteData(temp % 100 / 10 + 0x30);
LcdWriteData(temp % 10 + 0x30);
curr_temp = (float) temp/100.0;
}
}
/*************修改时间************/
void dis_menu2_mdf_date(void)
{
static u16 ucount=0;
ucount++;
if(ucount ==30)
{
dis_menu1_date(0);//显示静止时间
if(ti == 7) //闪烁OK
{
LcdWriteCom(0xca);
LcdWritestr(" ->OK ");
}
}
else if(ucount == 60)//闪烁
{
ucount = 0;
switch(ti)
{
case 0:LcdWriteCom(0xc7);
LcdWritestr(" ");break;
case 1:LcdWriteCom(0xc4);
LcdWritestr(" ");break;
case 2:LcdWriteCom(0xc1);
LcdWritestr(" ");break;
case 3:LcdWriteCom(0x88);
LcdWritestr(" ");break;
case 4:LcdWriteCom(0x85);
LcdWritestr(" ");break;
case 5: LcdWriteCom(0x8c);
LcdWritestr(" ");break;
case 6:LcdWriteCom(0x80);
LcdWritestr(" ");break;
case 7:LcdWriteCom(0xca);
LcdWritestr(" ");break;
}
}
}
/*******************************************************************************
* 函 数 名 : dis_menu3_mdf_fdtime
* 函数功能 : 修改喂食时间界面
*******************************************************************************/
u8 feed_cnt=0; //当前修改的第几个时间
void dis_menu3_mdf_fdtime(void )
{
static u16 ucount = 0;
ucount++;
if(ucount == 30)
{
LcdWriteCom(0x80); //显示
LcdWritestr("feed time ");
LcdWriteCom(0x8a);
LcdWriteData(feed_cnt+0x30);
LcdWritestr(" ");
LcdWriteCom(0xca);
if(sysinfo.feed_time_sw[feed_cnt] == 0)LcdWritestr(" OFF ");
else LcdWritestr(" ON ");
LcdWriteCom(0xc1);
LcdWriteData(sysinfo.feed_time[feed_cnt].hour/16+0x30);
LcdWriteData(sysinfo.feed_time[feed_cnt].hour%16+0x30);
LcdWriteData(':');
LcdWriteData(sysinfo.feed_time[feed_cnt].min/16+0x30);
LcdWriteData(sysinfo.feed_time[feed_cnt].min%16+0x30);
LcdWriteData(':');
LcdWriteData(sysinfo.feed_time[feed_cnt].sec/16+0x30);
LcdWriteData(sysinfo.feed_time[feed_cnt].sec%16+0x30);
}
else if(ucount == 60 )
{
ucount =0;
switch(ti)
{
case 0:LcdWriteCom(0x8a);
LcdWritestr(" ");break;
case 1:LcdWriteCom(0xc7);
LcdWritestr(" ");break;
case 2:LcdWriteCom(0xc4);
LcdWritestr(" ");break;
case 3:LcdWriteCom(0xc1);
LcdWritestr(" ");break;
case 4:LcdWriteCom(0xcc);
LcdWritestr(" ");break;
}
}
}
/*******************************************************************************
* 函 数 名 : dis_menu3_mdf_fdtime
* 函数功能 : 修改清理时间界面
*******************************************************************************/
void dis_menu4_mdf_clrtime(void )
{
static u16 ucount = 0;
ucount++;
if(ucount == 30)
{
LcdWriteCom(0x80); //显示
LcdWritestr("clear time ");
LcdWriteCom(0x8b);
LcdWriteData(feed_cnt+0x30);
LcdWritestr(" ");
LcdWriteCom(0xca);
if(sysinfo.clr_time_sw[feed_cnt] == 0)LcdWritestr(" OFF ");
else LcdWritestr(" ON ");
LcdWriteCom(0xc1);
LcdWriteData(sysinfo.clr_time[feed_cnt].hour/16+0x30);
LcdWriteData(sysinfo.clr_time[feed_cnt].hour%16+0x30);
LcdWriteData(':');
LcdWriteData(sysinfo.clr_time[feed_cnt].min/16+0x30);
LcdWriteData(sysinfo.clr_time[feed_cnt].min%16+0x30);
LcdWriteData(':');
LcdWriteData(sysinfo.clr_time[feed_cnt].sec/16+0x30);
LcdWriteData(sysinfo.clr_time[feed_cnt].sec%16+0x30);
}
else if(ucount == 60 )
{
ucount =0;
switch(ti)
{
case 0:LcdWriteCom(0x8b);
LcdWritestr(" ");break;
case 1:LcdWriteCom(0xc7);
LcdWritestr(" ");break;
case 2:LcdWriteCom(0xc4);
LcdWritestr(" ");break;
case 3:LcdWriteCom(0xc1);
LcdWritestr(" ");break;
case 4:LcdWriteCom(0xcc);
LcdWritestr(" ");break;
}
}
}
/*******************************************************************************
* 函 数 名 : dis_menu4_state
* 函数功能 : 显示系统状态
*******************************************************************************/
void dis_menu5_state(void )
{
LcdWriteCom(0x80); //显示
LcdWritestr("Pet: ");
if(IR == 0)
LcdWritestr("whthin ");
else
LcdWritestr("absent ");
LcdWriteCom(0xc0);
LcdWritestr("water line: ");
if(WIT_0==1 && WIT_1==1)
LcdWritestr("0cm");
else if(WIT_0==0 && WIT_1==1)
LcdWritestr("1cm");
else if(WIT_0==0 && WIT_1==0)
LcdWritestr("3cm");
}
/*******************************************************************************
* 函 数 名 : dis_menu6_motor_ctrl
* 函数功能 : 电机控制
*******************************************************************************/
void dis_menu6_motor_ctrl(void )
{
LcdWriteCom(0x80);
LcdWritestr("motor:");
switch(state)
{
case 0x01:
LcdWritestr("water ");
break;
case 0x02:
LcdWritestr("fan ");
break;
case 0x04:
LcdWritestr("clear +");
break;
case 0x08:
LcdWritestr("clear -");
break;
case 0x10:
LcdWritestr("feed +");
break;
case 0x20:
LcdWritestr("feed -");
break;
default:
LcdWritestr("none ");
}
}
/*******************************************************************************
* 函 数 名 : ctrl_motor
* 函数功能 : 外设电机控制
*******************************************************************************/
void ctrl_motor(void)
{
u8 i;
static u8 clr_z_flag=0,clr_f_flag=0,code_flag=0,water_flag=0;
static u8 feed_z_flag=0,feed_f_flag=0,feed_t_flag=0;
static u8 ucount=0;
//static u8 fan_flag=0;
static u16 clr_cnt=0,water_cnt=0;//fan_cnt=0,
static u16 feed_cnt=0,code_cnt=0;
ucount++;
九、下载验证
根据实现的功能对宠物智能小屋主要功能进行验证。
宠物智能小屋系统上电后,系统显示初始化界面,
初始化后液晶第一行显示当前的日期与星期,第二行显示实时时间与温度。
可通过按键设定喂食、清洁闹钟时间
红外检测与水位检测验证。宠物检测到在小屋内显示“within”,未在小屋内显示“absent”。水位检测则在第二行显示water line: 具体水位厘米数。本系统也可以通过手动按键开启、关闭喂水功能。下图5.6为宠物未在小屋内,水位检测到为0cm,下图5.7为宠物在小屋内,水位检测到为3cm。
喂食定时时间到后,电机正转打开喂食口,喂食完成后,电机反转关闭喂食口。本系统也可以通过手动按键对电机进行正转、反转功能。