文章目錄
- 0 簡介
- 1 系統說明
- 2 背景意義
- 3 系統設計
-
- 3.1 總體方案
- 3.2 硬體設計
-
- 3.2.1 STC89C52
- 3.2.2 CLS150TD舵機
- 3.2.3 壓力傳感器
- 3.2.4 HX711 A/D子產品
- 3.2.5 供電及穩壓
- 3.2.6 TLN104/TLP104 紅外對管光電傳感器子產品介紹
- 3.2.7 其他硬體子產品
- 3.3 軟體實作
-
- 3.3.1 主程式邏輯
- 3.3.2 按鍵監聽子產品
- 3.3.3 溫度資料讀取
- 3.3.4 超音波檢測水位
- 3.3.5 LCD1 602 顯示子產品
- 4 實作效果
- 5 關鍵代碼
0 簡介
Hi,大家好,學長今天向大家介紹一個 單片機項目
基于stm32的寵物自動喂食系統
大家可用于 課程設計 或 畢業設計
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();
}
}
}
}
}