0 简介

Hi,大家好,学长今天向大家介绍一个 单片机项目

基于stm32的宠物自动喂食系统

大家可用于 课程设计 或 毕业设计

选题指导,项目分享:

https://gitee.com/yaa-dc/warehouse-1/blob/master/iot/README.md

1 系统说明

为了解决主人不在家而无法正常对宠物进行饮水喂食, 导致宠物身体不健康这一情况, 为此学长设计了宠物自动饮水喂食器。

宠物自动饮水喂食器主要由单片机最小系统、 水位检测系统、 温度检测系统、 饲料检测系统、 按键控制系统、 LCD1602 液晶显示系统组成。

单片机系统需要正常工作必须保证最小系统工作正常, 宠物自动饮水喂食器的饮水过程是通过超声波检测水位, 若检测到的水位低于设定水位就开启加水功能;

通过 DS18B20 温度传感器检测当前温度, 若检测到的温度低于设定温度则开启加温功能; 液晶屏显示当前水位及温度和设定水位及温度。 喂食过程是通过光电传感器检测饲料是否存在, 若检测到饲料不存在, 则开启加饲料功能。

2 背景意义

随着我国社会经济水平的快速发展以及城市化进程的不断加速, 很明显我国国民的生活水平不断得到提高, 而宠物业也因此得到飞速的发展。 现如今家庭宠物的饲养已经成为城市居民生活消遣的新方式, 而且饲养宠物的观念也与以往大不相同, 其中宠物的喂养就是人们最为关心的问题。 不过目前宠物主要还是依靠人工进行喂养。 而且随着人们生活水平不断的提高, 人们开始侧重于精神的追求, 很多年轻人和老年人都会收养自己的宠物, 但是年轻人大多时间都忙于工作, 老年人可能由于记忆或者身体不便等原因, 有时候可能顾及不到自己的宠物, 那么宠物很有可能因为缺水或者饥饿而导致不健康成长或者死亡。 如果是这样的话对于宠物的伤害是非常大的。 因此就急需一种能自动加水喂食的装置, 以保证宠物饮食正常。 人们可以在对宠物喂水和喂料上无需发费多余的时间即可很好的照顾自己的宠物, 具有极大的市场价值和经济效益。

3 系统设计

学长设计的是宠物自动饮水喂食器, 它主要要实现自动加水加温喂食功能。 即检测到的水位与温度低于我的设定值, 就开启加水加温喂食功能, 还能按键调整设定水位与温度, 并能显示设定与检测水位与温度值。 在电路设计中, 系统主要由主控器,测温模块, 水位检测模块, 食物检测模块, 显示模块, 按键模块和加温加水喂食模块组成。

在这里插入图片描述

3.1 总体方案

通过数字温度传感器 DS18B20、 HC-SR04 超声波传感器及 TLN104/TLP104 红外对管光电传感器对温度、 水位和食物存在进行检测, 将采集到的信号传送给STM32单片机进行处理, 若检测的温度或水位低于设定温度值或水位值, 或没有检测到食物信号, 单片机将控制加温加水喂食电路进行工作, 按键电路用于对温度及水位报警值进行设定, LCD1602 显示电路用于显示检测的温度水位及设定的温度和水位信息值, 系统方框图如下所示

在这里插入图片描述

3.2 硬件设计

3.2.1 STC89C52

单片机 STC89C52 是从传统 MCS51 基础上进行升级过来, 在内部资源分布上更加丰富, 比如时钟模块, 单片机管脚第 2 功能等。使用其他的芯片也是ok的。

在这里插入图片描述

3.2.2 CLS150TD舵机

舵机选用达盛舵机科技有限公司生产的CLS150TD型号的舵机,实物图及尺寸如图所示。该型号舵机运行温度在-15℃~70℃,工作电压范围为4.8v-6.8v,驱动方式为PWM波,脉冲范围为500~2500 μsec,控制角度为:360°。

在这里插入图片描述
工作原理:通过给舵机的信号线(橙黄色)输入周期为20ms的PWM波,通高电平时间:0.5-2.5ms可以使舵机旋转0~360°。如图所示。

在这里插入图片描述

3.2.3 压力传感器

压力传感器选用电阻应变式压力传感器,以电阻应变计为转换元件,由弹性敏感元件、电阻应变计、补偿电阻和外壳组成,弹性敏感元件受到所测量的力而产生变形,并使附着其上的电阻应变计一起变形。电阻应变计再将变形转换为电阻值的变化,从而可以测量压力的变化,再通过A/D转换模块,即可得到被测物体的重量。所需接线的引脚为:EXC+、EXC-、SIC+、SIC-。实物图、引脚如图所示。

在这里插入图片描述
在这里插入图片描述

3.2.4 HX711 A/D模块

A/D模块选用HX711芯片,主要实现将压力传感器检测到的电压信号转换成STM32可读的数字信号。所需连接的引脚:AVDD引脚、GND引脚、INA+引脚、INA-引脚、DT引脚、SCK引脚、VCC引脚。HX711模块实物图如图所示。

在这里插入图片描述

3.2.5 供电及稳压

供电使用3S锂电池作为整体装置的供电,3S锂电池可以提供11.1v的电压。3S锂电池实物如图所示。

在这里插入图片描述

3.2.6 TLN104/TLP104 红外对管光电传感器模块介绍

该传感器模块对环境光线适应能力强, 其具有一对红外线发射与接收管, 发射管发射出一定频率的红外线, 当检测方向遇到障碍物时, 红外线反射回来被接收管接收,经过比较器电路处理之后, 绿色指示灯会亮起, 同时信号输出接口输出数字信号(一个低电平信号) , 可通过电位器旋钮调节检测距离, 有效距离范围 2~30cm, 工作电压为 3. 3V-5V。 实物图如

在这里插入图片描述

3.2.7 其他硬件模块

DS18B20和LCD本片文章学长不在复述

3.3 软件实现

3.3.1 主程序逻辑

首先先给单片机上电, 系统软件设计需先对 LCD1602 与 T0、 T1 先初始化, 然后进行温度与水位读取转换, 其次按键扫描, 再完成对宠物自动饮水喂食器的温度和水位检测以及判断是否含有食物。 如果当检测的温度或者水位低于设定值 29 ℃ 或 15cm或者没有检测到食物, 将使喂食器自动实现加温加水以及喂食功能, 以此不断运行。主函数的功能就是对程序设计中使用的变量及模块初始化并将各功能子模块进行调用。

在这里插入图片描述

3.3.2 按键监听模块

硬件电路中使用 4 个独立按键用来作为温度和水位阀值的设定装置, 由于采用独立式接法, 因此只需要判断对应的输入电平即可确认按键是否按下以及是哪个按键按下。 凡是在按键处理的过程中都需要对其消抖处理, 因为按键在按下和松开的过程中会产生抖动, 如果不进行消抖处理, 那么当按键按下后就判断其状态, 这个很有可能是按键抖动所产生的误操作。 由于硬件电路中并没有做消抖处理, 所以需要通过软件来进行消抖。

在这里插入图片描述

3.3.3 温度数据读取

硬件电路中使用了单总线接口的数字温度传感器 DS18B20 作为喂食器的温度检测装置, 由于我们使用的单片机没有单总线接口, 必须通过管脚来模拟时序通信。 根据温度传感器的初始化及读写时序图可以模拟, 温度传感器在读写数据的时候是先读写低位, 然后再是高位。 所以需要一个循环函数及移位操作来读写 8 次。 然后通过发送温度转换及读取命令就可以读取到温度数据, 读取到的温度数据还需要对其进行处理才能转换为实际的温度。 温度转换读取流程图如图

在这里插入图片描述

3.3.4 超声波检测水位

硬件电路中使用了 HC-SR04 超声波传感器模块来检测水位的变化, 由于使用超声波来测距需要对时间进行计算, 所以需要使用到单片机内的定时器功能, 通过定时器计算超声波返回的高电平持续时间就可以计算距离, 水位距离检测流程图如图

在这里插入图片描述

3.3.5 LCD1 602 显示模块

在对 LCD 液晶初始化的时候我们需要了解其具有步骤, 根据数据手册可以知道,要对 LCD1602 液晶初始化, 首先需要一段时间的延时, 然后根据个人要求选择具体的显示模式, 可以选择显示光标或者光标闪烁等, 最后需要对 LCD 进行一次清屏, 将内部 RAM 的数据全部清除, 等待下一次的显示。 初始化结束以后根据时序编写写命令函数和写数据函数, 最后就是使用这 2 个函数来进行数据显示, 流程图如图

在这里插入图片描述

4 实现效果

将电源接口接外电源 5V 供电, 按下上电按钮, 电源指示灯亮起, 系统通电正常工作。 LCD1602 显示设定的水位 15. 0 与温度值 29, 如图
在这里插入图片描述

当用 HC-RC04 超声波传感器检测到的水位低于设定水位 15mm 时, 就驱动加水电路, 即加水绿灯亮起, 液晶屏显示检测到的水位值; 当用 DS18B20 检测到的温度低于设定温度 29℃时, 驱动加温电路, 即加温红灯亮起, 液晶屏显示检测到的温度值。

在这里插入图片描述
当用 TLN104/TLP104 红外对管光电传感器在 5cm 内检测不到食物存在时, 就驱动喂食电路, 即喂食黄灯亮起

在这里插入图片描述

通过按键可设置水位上下限范围为 0cm—20cm; 温度上下限范围 0℃~+125℃,根据用户需求可通过按键去调整设定水位与温度值

在这里插入图片描述

5 关键代码

#include <at89x52.h> 
#include <intrins.h>
#include "LCD1602display.h"
#define TX P1_3 //定义串行通信的数据发送
#define RX P1_4 //定义串行通信的数据接收
#define uchar unsigned char
#define uint unsigned int
sbit P4_0=0xc0; //定义 P4 口
//按键定义
sbit DS18B20_up =P2^1;
sbit DS18B20_down =P2^0;
sbit Watet_up =P3^6;
sbit Watet_down =P3^5;
//切换定义
sbit wendu=P3^4;
sbit DS=P1^5; //定义 DS18B20 接口
sbit Light=P3^7; //定义光电传感器接口
//定义加水加温加料驱动接口
sbit hot=P2^2; //红灯
sbit water=P2^3;//绿灯
sbit food=P2^4; //黄灯
void Delay400Ms(void);//延时 400 毫秒函数
// unsigned char code Range[] ="H: ";
unsigned char code ASCII[13] = "0123456789.-M";
unsigned char code table[]="H: ";//unsigned char code table[]="H:000.0cm";
//unsigned char code table1[]="!!! Out of range";
//unsigned char code ASCII[13] = "0123456789.";
unsigned char code table1[]=" temp: ";
unsigned char disbuff[4]={0,0,0,0};//用于分别存放距离的值 0.1mm、 mm、 cm 和 m 的unsigned char disbuff1[4]={0,0,0,0};//用于分别存放距离的值 0.1mm、 mm、 cm 和 m 的unsigned char disbuff2[4]={0,0,0,0};//用于分别存放距离的值 0.1mm、 mm、 cm 和 m 的//unsigned char disbuff1[2]={0,0};
//unsigned char disbuff2[2]={0,0};
void Count(void);//距离计算函数
void key() ;  
unsigned int time=0;//用于存放定时器时间值
unsigned int S=0;//用于存放距离的值
unsigned int H=0,V1=150,V2=29.0,V2_T;
bit flag =0; //量程溢出标志位
int rang=0;
//unsigned int i=0,H1=29,L1=30,fan;
/*************延时子函数 *******************************************/
void delayb(uint count)
{ uint i;
while(count)
{
i=200;
while(i>0)
i--;
count--;
}
}
/*************DS18B20 初始化*******************************************/
void dsreset(void)
{
uint i;
DS=0;
i=103; //延时最短 480us(计算机运行的时间)
while(i>0)i--;
DS=1; //等待 16-60us, 收到低电平一个约 60-240us 则复位成功
i=4;
while(i>0)i--;
}
/*************读一位*************************************************/
bit tmpreadbit(void)
{
uint i;
bit dat;
DS=0; //将总线拉低, 要在 1us 之后释放总线
i++; //小延时一下
DS=1; //释放总线后的 15us 内 DS18B20 会发送内部数据位
i++;i++;
dat=DS;
i=8; //一个字节共 8 位
while(i>0)i--;
return (dat); //将一个字节数据返回
}
/*************读一个字节*************************************************/
uchar tmpread(void)
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tmpreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面, 这样刚好//一个字节在 DAT}
return(dat); //将一个字节数据返回
}
/*************写一个字节*************************************************/
void tmpwritebyte(uchar dat)
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) // 写 1 部分
{
DS=0;
i++;i++;
DS=1;
i=8;
 while(i>0)i--;
}
else
{
DS=0; //写 0 部分
i=8;
 while(i>0)i--;
DS=1;
i++;i++;
}
}
}
/*********** 发送 温 度转 换 命令
***********************************************/
void tmpchange(void)
{
dsreset(); //初始化 DS18B20
delayb(1); //延时
tmpwritebyte(0xcc); // 跳过序列号命令
tmpwritebyte(0x44); //发送温度转换命令
}
int tmp()
{
int temp;
uchar a,b;
dsreset();
delayb(1);
tmpwritebyte(0xcc);
tmpwritebyte(0xbe); //发送读取数据命令
a=tmpread(); //连续读两个字节数据
b=tmpread();
temp=b;
temp<<=8;
temp=temp|a;  //两字节合成一个整型变量。
return temp; //返回温度值
}
/******* 读 取 温 度 传 感 器 的序 列 号
***************************************************/
void readrom()  //只有一片 DS18B20, 不需要读序列号, 本程序中没有用到此函数
{
uchar sn1,sn2;
dsreset();
delayb(1);
tmpwritebyte(0x33);
sn1=tmpread();
sn2=tmpread();
}
void key()
{ 
if(Water_up==0)
{
 //Delay1ms(40);
if(Watet_up==0)
{//V1=12;
 //Delay1ms(40);
 V1++;
 //V1=V1*10;
 if(V1==200)V1=0;
while(!Watet_up) ;
}
}
if(Water_down==0) //如果检测到低电平, 说明按键按下
{
//Delay1ms(40);
if(Watet_down==0) //再次确认按键是否按下, 没有按下则退出
{ //Delay1ms(40);
 V1--; //V2=V2*10;
 if(V2==100)V1=150;
while(!Watet_down); //等待按键释放
}
}
if(DS18B20_up==0)
{//V1=12;
 //Delay1ms(40);
 V2++;
 //V1=V1*10;
 if(V2==15)V2=15;
while(!DS18B20_up) ;
}
if(DS18B20_down==0)
{//Delay1ms(40);
if(DS18B20_down==0)
{ //Delay1ms(40);
 V2--; //V2=V2*10;
 if(V2==0)V2=15;
while(!DS18B20_down) ;
}
}
}

/********距离计算程序***************/
void Conut(void)
 {
 time=TH0*256+TL0; //转成 16 进制
 TH0=0;
 TL0=0;
 S=time*0.58;//先算出一共的时间是多少微秒。
S=S*0.17;//此时计算到的结果为毫米, 并且是精确到毫米的后两位了, 有两个小
数点
H=210-S;
 //V=(3.14*3.3*3.3)*H;
 if((H<V1)&&(H>0)) //触发低水位警戒线, 进水
  water=0;
 else water=1;
//LCD 显示
 {  key() ;
disbuff[0]=H%10;
 disbuff[1]=H/10%10;
disbuff[2]=H/100%10;
 disbuff[3]=H/1000;
 DisplayListChar(0, 1, table);
 DisplayOneChar(2, 1, ASCII[disbuff[3]]);
 DisplayOneChar(3, 1, ASCII[disbuff[2]]); 
 DisplayOneChar(4, 1, ASCII[disbuff[1]]);
DisplayOneChar(5, 1, ASCII[10]);
 DisplayOneChar(6, 1, ASCII[disbuff[0]]);
 disbuff1[0]=V1%10;
 disbuff1[1]=V1/10%10;
 disbuff1[2]=V1/100%10;
 disbuff1[3]=V1/1000;
 disbuff2[0]=V2%10;
 disbuff2[1]=V2/10%10;
 disbuff2[2]=V2/100%10;
disbuff2[3]=V2/1000;
//  DisplayListChar(0, 0,Range); 
 DisplayOneChar(2, 0, ASCII[disbuff1[3]]);
 DisplayOneChar(3, 0, ASCII[disbuff1[2]]); 
 DisplayOneChar(4, 0, ASCII[disbuff1[1]]);
DisplayOneChar(5, 0, ASCII[10]);
DisplayOneChar(6, 0, ASCII[disbuff1[0]]);
DisplayOneChar(8, 0, ASCII[disbuff2[3]]);
 DisplayOneChar(9, 0, ASCII[disbuff2[2]]); 
 DisplayOneChar(10, 0, ASCII[disbuff2[1]]);
//DisplayOneChar(11, 0, ASCII[10]);
 DisplayOneChar(11, 0, ASCII[disbuff2[0]]);
 }
 }

/*************主程序********************/
void main(void)
{
unsigned long a;
 //unsigned long a;
 uint t;
 float tt,f_temp;
Delay1ms(10); //启动等待, 等 LCM 讲入工作状态
 LCMInit(); //LCM 初始化
 //Delay1ms(5);//延时片刻
//  DisplayListChar(0, 0, Range);
 DisplayListChar(0, 0, table);
// DisplayListChar(0, 1, table1);
TMOD=0x61;//设 T0 为方式 1, GATE=0; T1 为方式 2, C/T=1(计数功能)
EA=1; //中断总允许位
TH0=0;
TL0=0;
ET0=1; //T0 中断允许
 TR0=0; //T0 不工作
TH1=0XE7;
TL1=0XE7;
 TR1=1;ET1=1;
 while(1)
 { //if(wendu==0)   
{
tmpchange(); //温度转换
 Delay1ms(10);
 tt=tmp()/16;  //得到十进制温度值
//f_temp=tt*10; 
  t=tt;
  disbuff[0]=t/100;//显示百位值
   t=t%100;
   disbuff[1]=t/10; //显示温度十位值
   //t=t%10;
   disbuff[2]=t%10;  //显示温度个位值
  //  disbuff[3]=t%10;  //显示小数点后一位
if(t<V2)
 hot=0;
else hot=1;
// if(t>V2){hot=0;}
// else if (0<t<V2){hot=1;}
// else if(t<0){;}
// else {hot=1;}
DisplayListChar(0, 1, table);
 DisplayOneChar(9, 1, ASCII[disbuff[1]]);
DisplayOneChar(10, 1, ASCII[disbuff[2]]);
  //while(!wendu) ;
 Delay1ms(50);//延时片刻
}
//else
{
 if(Light==0)
food=1;
 else food=0;
  key() ;
 Delay1ms(10);
  RX=1;// 为了让读引脚电平稳定, 最好先置 1 再读取。
 StartModule();
for(a=5000;a>0;a--)//a 的值也会限制检测距离, a 的值越大, 检测时间间隔越长
{ 
key() ;
 if(RX==1) //判断 RX 是否等于 1 了, 如果等于 1 就启动定时器计数
  {
Timer_Count();
  }
 }
 } 
}
}

选题指导,项目分享:

https://gitee.com/yaa-dc/warehouse-1/blob/master/iot/README.md

Logo

鸿蒙生态一站式服务平台。

更多推荐