单片机的模拟智能灌溉系统
最近在寫簡歷,于是我開始翻翻我的電腦里面有什么存貨可以拿來曬曬,沒成想,還真有不少啊,于是我就先拿出一部分當年寫的單片機程序來曬曬了!
首先是我當年的得意之作,模擬智能灌溉系統:
先上原理圖
這是用proteus畫的模擬圖,當然還有實物圖,不過都被我放在自己的工作室了(話說明明是實驗室好不好)目前是在外面進一步學習,我就不把實物曬出來了。
程序的好壞在于它的流程圖是否好,于是我就把我當時畫的流程圖拿了出來!
那么該項目到底是什么呢?
就是當年比賽的一個預習題,不過平臺不一樣搞得我當時比較狼狽就拿了個三等獎回來,哎,希望我嵌入式小組的學弟學妹們能夠加油,幫我拿個一等獎回來!了了我心中的遺憾吧!
功能簡述:
要求“模擬智能灌溉系統”能夠實現土壤濕度測量、土壤濕度和時間顯示、濕度閾值設定及存儲等基本功能。通過電位器Rb2輸出電壓信號,模擬濕度傳感器輸出信號,再通過AD采集完成濕度測量功能;通過DS1302芯片提供時間信息;通過按鍵完成灌溉系統控制和濕度閾值調整功能,通過LED完成系統工作狀態指示功能。系統硬件電路主要由單片機控制電路、顯示單元、ADC采集單元、RTC單元、EEPROM存儲單元、繼電器控制電路及報警輸出電路組成,系統框圖如圖1所示:
1. 系統工作及初始化狀態說明:
1.1、自動工作狀態,根據濕度數據自動控制打開或關閉灌溉設備,以L1點亮指示;
1.2、手動工作狀態,通過按鍵控制打開或關閉灌溉設備,以L2點亮指示;
1.3、系統上電后處于自動工作狀態,系統初始濕度閾值為50%,此時若濕度低于50%,灌溉設備自動打開,達到50%后,灌溉設備自動關閉;
1.4、灌溉設備打開或關閉通過繼電器工作狀態模擬。
2. 數碼管單元:
“模擬智能灌溉系統”通過讀取DS1302時鐘芯片相關寄存器獲得時間,DS1302芯片時、分、秒寄存器在程序中設定為系統進行初始化設定,時間為08時30分。
3. 報警輸出單元:
系統工作于手動工作狀態下時,若當前濕度低于濕度閾值,蜂鳴器發出提示音,并可通過按鍵S6關閉提醒功能。
4. 功能按鍵:
4.1、按鍵S7設定為系統工作狀態切換按鍵;
4.2、手動工作狀態下按鍵S6、S5、S4功能設定如下:
按下S6關閉蜂鳴器提醒功能,再次按下S6打開蜂鳴器提醒功能,如此循環;
S5功能設定為打開灌溉系統;S4功能設定為關閉灌溉系統。
4.3、自動工作狀態下按鍵S6、S5、S4功能設定如下:
S6功能設定為濕度閾值調整按鍵,按下S6后,進入濕度閾值調整界面(如圖3所示),此時按下S5為濕度閾值加1,按下S4濕度閾值減1,再次按下S6后,系統將新的濕度閾值保存到EEPROM中,并退出濕度閾值設定界面。
5. 實時時鐘:
“模擬智能灌溉系統”通過讀取DS1302時鐘芯片相關寄存器獲得時間,DS1302芯片時、分、秒寄存器在程序中設定為系統進行初始化設定,時間為08時30分。
6. 濕度檢測單元:
以電位器Rb2輸出電壓信號模擬濕度傳感器輸出信號,且假定電壓信號與濕度成正比例關系H濕度= KVRb2(K為常數),Rb2電壓輸出為5V時對應濕度為99%。
7. EEPROM存儲單元:
系統通過EEPROM存儲濕度閾值,自動工作狀態下,可通過按鍵S6、S5、S4設置和保存閾值信息。
下面將是我寫的一些代碼了,大三上寫的代碼,可能不是那么好吧!勿見怪啊,當時都沒有代碼規范的思想,寫得比較凌亂,我也因為對它不再想修改了,就不改格式了,因而對看本博客的同學們表示歉意了!
(1)主函數main.c #include<reg52.h>#include <intrins.h> #include<I2C.h> #define PCF8591 0x90 //PCF8591 地址 #include<ds1302.h> //else IO unsigned char AD_CHANNEL; unsigned long xdata LedOut[8]; unsigned int D[32]; sbit LS138A=P2^2; sbit LS138B=P2^3; sbit LS138C=P2^4; sbit L1=P2^5; sbit L2=P2^6; sbit beed=P1^0; sbit RELAY=P1^1; sbit k4=P1^2; sbit k5=P1^3; sbit k6=P1^4; sbit k7=P1^5; //此表為 LED 的字模, 共陰數碼管 0-9 - unsigned char code table[]= {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; unsigned char delay1[8]={10,10,0,0,0,0,0,0};unsigned char HUO,flag; unsigned char key,mode,th; /** 延遲函數 **/ void delay() {unsigned char a=100; while(a--); } /************ at24c02功能模塊 ***/ void At24c02Write(unsigned char addr,unsigned char dat) {I2C_Start();I2C_SendByte(0xa0, 1);//發送寫器件地址I2C_SendByte(addr, 1);//發送要寫入內存地址I2C_SendByte(dat, 0); //發送數據 I2C_Stop(); }unsigned char At24c02Read(unsigned char addr) {unsigned char num;I2C_Start();I2C_SendByte(0xa0, 1); //發送寫器件地址I2C_SendByte(addr, 1); //發送要讀取的地址 I2C_Start();I2C_SendByte(0xa1, 1); //發送讀器件地址num=I2C_ReadByte(); //讀取數據 I2C_Stop();return num; } /******************************************************************* ADC發送字節[命令]數據函數 *******************************************************************/ bit ISendByte(unsigned char sla,unsigned char c) {Start_I2c(); //啟動總線SendByte(sla); //發送器件地址if(ack==0)return(0);SendByte(c); //發送數據if(ack==0)return(0);Stop_I2c(); //結束總線return(1); } /******************************************************************* ADC讀字節數據函數 *******************************************************************/ unsigned char IRcvByte(unsigned char sla) { unsigned char c;Start_I2c(); //啟動總線SendByte(sla+1); //發送器件地址if(ack==0)return(0);c=RcvByte(); //讀取數據0Ack_I2c(1); //發送非就答位Stop_I2c(); //結束總線return(c); } unsigned char keysan(void) { if(!k7){while(!k7);return 7;}if(!k6){while(!k6);return 6;}if(!k5){while(!k5);return 5;} if(!k4){while(!k4);return 4;} return 0; } /**初始設置函數**/ void seting() { unsigned char isFirstRun=0;beed=1;L1=0;L2=1;RELAY=1; mode=1;isFirstRun=!At24c02Read(2);//通過EEPROM的'2'地址出是否有值來判斷是否為第一次運行if(isFirstRun)//若第一次運行 {At24c02Write(2,1);//往'2'地址寫入信息供以后開機時判斷At24c02Write(1,50);//寫入默認閾值50 }else {HUO=At24c02Read(1);//從EEPROM的'1'地址處讀出閾值 }delay1[6]=HUO/10;delay1[7]=HUO%10;InitTIMER0(); //初始化定時器0 Set_RTC(); } /**** 判斷函數 ***/ void chouse(unsigned char key) { switch(key) { case 4:if(mode==1){ if(j==1){ HUO--;delay1[6]=HUO/10;delay1[7]=HUO%10;delay();} }else { RELAY=1;}break; case 5:if(mode==1){if(j==1){ HUO++; if(HUO>99)HUO=99;delay1[6]=HUO/10;delay1[7]=HUO%10;delay();}}else {RELAY=0;}break;case 6:if(mode==1){ if(flag==1)//在設置模式,保存閾值并退出設置模式 {At24c02Write(1,HUO);//在EEPROM的'1'地址處寫入閾值flag=0;}else//不在設置模式,將進入設置模式 {flag=1;}}else {beed=(beed==1)?0:1;} break;case 7:if(mode==1){mode=0;L1=0;L2=1;}else {mode=1;L1=1;L2=0;} break;default:break;}if(th<HUO){if(mode==0)//手動模式 {if(beed==1)//如果打開了報警提示beed=0;//報警 }else//自動模式 {RELAY=0;//打開灌溉beed=1;}}else //不小于閾值 {if(mode==0)//手動模式 {if(beed==1)//如果打開了報警提示beed=0;//關閉報警 }else//自動模式 {RELAY=1;//關閉灌溉 }} if(ReadRTC_Flag){ReadRTC_Flag=0;Read_RTC();l_tmpdisplay[0]=l_tmpdate[2]/16;//數據的轉換,因我們采用數碼管0~9的顯示,將數據分開l_tmpdisplay[1]=l_tmpdate[2]&0x0f;l_tmpdisplay[2]=10; //加入"-"l_tmpdisplay[3]=l_tmpdate[1]/16;l_tmpdisplay[4]=l_tmpdate[1]&0x0f;l_tmpdisplay[5]=10;}ISendByte(PCF8591,0x44); D[0]=IRcvByte(PCF8591); //ADC0 模數轉換1th=D[0]/2.55;if(th>99) th=99;l_tmpdisplay[6]=th/10;l_tmpdisplay[7]=th%10; } /**主函數***/ void main() { seting();while(1){ key=keysan(); chouse(key);} }void InitTIMER0(void) {TMOD|=0x01;//定時器設置 16位TH0=0xef;//初始化值TL0=0xf0;ET0=1;TR0=1;EA=1; }/******************************************************************/ /* 定時器中斷函數 */ /******************************************************************/ void tim(void) interrupt 1 using 1//中斷,用于數碼管掃描 {static unsigned char i,num;TH0=0xf5;TL0=0xe0;if(flag==1){//在設置模式P0=table[delay1[i]];}else{// P0=table[l_tmpdisplay[i]]; //查表法得到要顯示數字的數碼段 }switch(i) { case 0:LS138A=0; LS138B=0; LS138C=0; break; case 1:LS138A=1; LS138B=0; LS138C=0; break; case 2:LS138A=0; LS138B=1; LS138C=0; break; case 3:LS138A=1; LS138B=1; LS138C=0; break; case 4:LS138A=0; LS138B=0; LS138C=1; break;case 5:LS138A=1; LS138B=0; LS138C=1; break;case 6:LS138A=0; LS138B=1; LS138C=1; break;case 7:LS138A=1; LS138B=1; LS138C=1; break;}i++;if(i==8){i=0;num++;if(10==num) //隔段時間讀取1302的數據。時間間隔可以調整 {ReadRTC_Flag=1; //使用標志位判斷num=0;}}} (2)I2C總線函數 #include<reg52.h> #include <intrins.h> #include <I2C.h> #define NOP() _nop_() /* 定義空指令 */ #define _Nop() _nop_() /*定義空指令*/ sbit SCL=P2^1; //I2C 時鐘 sbit SDA=P2^0; //I2C 數據 bit ack; /*應答標志位*/ /*******************************************************************起動總線函數 函數原型: void Start_I2c(); 功能: 啟動I2C總線,即發送I2C起始條件. ********************************************************************/ void Start_I2c() {SDA=1; /*發送起始條件的數據信號*/_Nop();SCL=1;_Nop(); /*起始條件建立時間大于4.7us,延時*/_Nop();_Nop();_Nop();_Nop(); SDA=0; /*發送起始信號*/_Nop(); /* 起始條件鎖定時間大于4μs*/_Nop();_Nop();_Nop();_Nop(); SCL=0; /*鉗住I2C總線,準備發送或接收數據 */_Nop();_Nop(); } /*******************************************************************結束總線函數 函數原型: void Stop_I2c(); 功能: 結束I2C總線,即發送I2C結束條件. ********************************************************************/ void Stop_I2c() {SDA=0; /*發送結束條件的數據信號*/_Nop(); /*發送結束條件的時鐘信號*/SCL=1; /*結束條件建立時間大于4μs*/_Nop();_Nop();_Nop();_Nop();_Nop();SDA=1; /*發送I2C總線結束信號*/_Nop();_Nop();_Nop();_Nop(); } /*******************************************************************字節數據發送函數 函數原型: void SendByte(UCHAR c); 功能: 將數據c發送出去,可以是地址,也可以是數據,發完后等待應答,并對此狀態位進行操作.(不應答或非應答都使ack=0) 發送數據正常,ack=1; ack=0表示被控器無應答或損壞。 ********************************************************************/ void SendByte(unsigned char c) {unsigned char BitCnt;for(BitCnt=0;BitCnt<8;BitCnt++) /*要傳送的數據長度為8位*/{if((c<<BitCnt)&0x80)SDA=1; /*判斷發送位*/else SDA=0; _Nop();SCL=1; /*置時鐘線為高,通知被控器開始接收數據位*/_Nop(); _Nop(); /*保證時鐘高電平周期大于4μs*/_Nop();_Nop();_Nop(); SCL=0; }_Nop();_Nop();SDA=1; /*8位發送完后釋放數據線,準備接收應答位*/_Nop();_Nop(); SCL=1;_Nop();_Nop();_Nop();if(SDA==1)ack=0; else ack=1; /*判斷是否接收到應答信號*/SCL=0;_Nop();_Nop(); } /*******************************************************************字節數據接收函數 函數原型: UCHAR RcvByte(); 功能: 用來接收從器件傳來的數據,并判斷總線錯誤(不發應答信號),發完后請用應答函數應答從機。 ********************************************************************/ unsigned char RcvByte() {unsigned char retc;unsigned char BitCnt;retc=0; SDA=1; /*置數據線為輸入方式*/for(BitCnt=0;BitCnt<8;BitCnt++){_Nop(); SCL=0; /*置時鐘線為低,準備接收數據位*/_Nop();_Nop(); /*時鐘低電平周期大于4.7μs*/_Nop();_Nop();_Nop();SCL=1; /*置時鐘線為高使數據線上數據有效*/_Nop();_Nop();retc=retc<<1;if(SDA==1)retc=retc+1; /*讀數據位,接收的數據位放入retc中 */_Nop();_Nop(); }SCL=0; _Nop();_Nop();return(retc); } /********************************************************************應答子函數 函數原型: void Ack_I2c(bit a); 功能: 主控器進行應答信號(可以是應答或非應答信號,由位參數a決定) ********************************************************************/ void Ack_I2c(bit a) {if(a==0)SDA=0; /*在此發出應答或非應答信號 */else SDA=1;_Nop();_Nop();_Nop(); SCL=1;_Nop();_Nop(); /*時鐘低電平周期大于4μs*/_Nop();_Nop();_Nop(); SCL=0; /*清時鐘線,鉗住I2C總線以便繼續接收*/_Nop();_Nop(); } void I2C_Delay10us() { unsigned char a,b;for(b=1; b>0; b--){for(a=2; a>0; a--);} } /******************************************************************************* * 函 數 名 : I2C_Start() * 函數功能 : 起始信號:在I2C_SCL時鐘信號在高電平期間I2C_SDA信號產生一個下降沿 * 輸 入 : 無 * 輸 出 : 無 * 備 注 : 起始之后I2C_SDA和I2C_SCL都為0 ****************************************************************************/ void I2C_Start() {SDA = 1;I2C_Delay10us();SCL = 1;I2C_Delay10us();//建立時間是I2C_SDA保持時間>4.7usSDA = 0;I2C_Delay10us();//保持時間是>4usSCL = 0; I2C_Delay10us(); } /****************************************************************************** * 函 數 名 : I2C_Stop() * 函數功能 : 終止信號:在I2C_SCL時鐘信號高電平期間I2C_SDA信號產生一個上升沿 * 輸 入 : 無 * 輸 出 : 無 * 備 注 : 結束之后保持I2C_SDA和I2C_SCL都為1;表示總線空閑 ******************************************************************************/ void I2C_Stop() {SDA = 0;I2C_Delay10us();SCL = 1;I2C_Delay10us();//建立時間大于4.7us SDA = 1;I2C_Delay10us(); } /******************************************************************************* * 函 數 名 : I2cSendByte(uchar num) * 函數功能 : 通過I2C發送一個字節。在I2C_SCL時鐘信號高電平期間, * * 保持發送信號I2C_SDA保持穩定 * 輸 入 : num ,ack * 輸 出 : 0或1。發送成功返回1,發送失敗返回0 * 備 注 : 發送完一個字節I2C_SCL=0, 需要應答則應答設置為1,否則為0 ******************************************************************************/ unsigned char I2C_SendByte(unsigned char dat, unsigned char ack) {unsigned char a = 0,b = 0;//最大255,一個機器周期為1us,最大延時255us。for(a=0; a<8; a++)//要發送8位,從最高位開始 {SDA = dat >> 7; //起始信號之后I2C_SCL=0,所以可以直接改變I2C_SDA信號dat = dat << 1;I2C_Delay10us();SCL = 1;I2C_Delay10us();//建立時間>4.7usSCL = 0;I2C_Delay10us();//時間大于4us }SDA = 1;I2C_Delay10us();SCL = 1;while(SDA && (ack == 1))//等待應答,也就是等待從設備把I2C_SDA拉低 {b++;if(b > 200) //如果超過200us沒有應答發送失敗,或者為非應答,表示接收結束 {SCL = 0;I2C_Delay10us();return 0;}}SCL = 0;I2C_Delay10us();return 1; } /******************************************************************************* * 函 數 名 : I2cReadByte() * 函數功能 : 使用I2c讀取一個字節 * 輸 入 : 無 * 輸 出 : dat * 備 注 : 接收完一個字節I2C_SCL=0 *****************************************************************************/ unsigned char I2C_ReadByte() { unsigned char a = 0,dat = 0;SDA = 1; //起始和發送一個字節之后I2C_SCL都是0 I2C_Delay10us();for(a=0; a<8; a++)//接收8個字節 {SCL = 1;I2C_Delay10us();dat <<= 1;dat |= SDA;I2C_Delay10us();SCL = 0;I2C_Delay10us();}return dat; } 其H文件: extern bit ack; //起動總線函數 extern void Start_I2c(); //結束總線函數 extern void Stop_I2c(); //應答子函數 extern void Ack_I2c(bit a); //字節數據發送函數 extern void SendByte(unsigned char c); //有子地址發送多字節數據函數 extern bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no) ; //無子地址發送多字節數據函數 extern bit ISendStrExt(unsigned char sla,unsigned char *s,unsigned char no); //無子地址讀字節數據函數 extern unsigned char RcvByte(); extern void I2C_Start(); extern void I2C_Stop(); extern unsigned char I2C_SendByte(unsigned char dat, unsigned char ack); extern unsigned char I2C_ReadByte(); (3)DS1302時鐘函數: #include<reg52.h> //包含頭文件,一般情況不需要改動,頭文件包含特殊功能寄存器的定義 #include <intrins.h>sbit SCK=P3^6; //時鐘 sbit SDA=P3^4; //數據 sbit RST = P3^5;// DS1302復位 bit ReadRTC_Flag;//定義讀DS1302標志 unsigned char l_tmpdate[7]={0,30,8,15,5,3,8};//秒分時日月周年08-05-15 12:00:00 unsigned char l_tmpdisplay[8];code unsigned char write_rtc_address[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //秒分時日月周年 最低位讀寫位 code unsigned char read_rtc_address[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //code unsigned char table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; //共陰數碼管 0-9 '-' '熄滅‘表 /******************************************************************/ /* 寫一個字節 */ /******************************************************************/ void Write_Ds1302_Byte(unsigned char temp) {unsigned char i;for (i=0;i<8;i++) //循環8次 寫入數據 { SCK=0;SDA=temp&0x01; //每次傳輸低字節 temp>>=1; //右移一位SCK=1;} } /******************************************************************/ /* 寫入DS1302 */ /******************************************************************/ void Write_Ds1302( unsigned char address,unsigned char dat ) {RST=0;_nop_();SCK=0;_nop_();RST=1; _nop_(); //啟動Write_Ds1302_Byte(address); //發送地址Write_Ds1302_Byte(dat); //發送數據RST=0; //恢復 } /******************************************************************/ /* 讀出DS1302數據 */ /******************************************************************/ unsigned char Read_Ds1302 ( unsigned char address ) {unsigned char i,temp=0x00;RST=0;_nop_();_nop_();SCK=0;_nop_();_nop_();RST=1;_nop_();_nop_();Write_Ds1302_Byte(address);for (i=0;i<8;i++) //循環8次 讀取數據 { if(SDA)temp|=0x80; //每次傳輸低字節SCK=0;temp>>=1; //右移一位 _nop_();_nop_();_nop_();SCK=1;} RST=0;_nop_(); //以下為DS1302復位的穩定時間 _nop_();RST=0;SCK=0;_nop_();_nop_();_nop_();_nop_();SCK=1;_nop_();_nop_();SDA=0;_nop_();_nop_();SDA=1;_nop_();_nop_();return (temp); //返回 } /******************************************************************/ /* 讀時鐘數據 */ /******************************************************************/ void Read_RTC(void) //讀取 日歷 {unsigned char i,*p;p=read_rtc_address; //地址傳遞for(i=0;i<7;i++) //分7次讀取 秒分時日月周年 {l_tmpdate[i]=Read_Ds1302(*p);p++;} } /******************************************************************/ /* 設定時鐘數據 */ /******************************************************************/ void Set_RTC(void) //設定 日歷 {unsigned char i,*p,tmp;for(i=0;i<7;i++){ //BCD處理tmp=l_tmpdate[i]/10;l_tmpdate[i]=l_tmpdate[i]%10;l_tmpdate[i]=l_tmpdate[i]+tmp*16;} Write_Ds1302(0x8E,0X00);p=write_rtc_address; //傳地址 for(i=0;i<7;i++) //7次寫入 秒分時日月周年 {Write_Ds1302(*p,l_tmpdate[i]);p++; }Write_Ds1302(0x8E,0x80); } 其H文件: extern void Write_Ds1302_byte(unsigned char temp); extern void Write_Ds1302( unsigned char address,unsigned char dat ); extern unsigned char Read_Ds1302 ( unsigned char address ); extern void Read_RTC(void);//read RTC extern void Set_RTC(void); //set RTC extern void InitTIMER0(void);//inital timer0 extern bit ReadRTC_Flag;//定義讀DS1302標志 extern unsigned char l_tmpdisplay[8]; extern unsigned char l_tmpdate[7]; extern bit ReadRTC_Flag;//定義讀DS1302標志轉載于:https://www.cnblogs.com/samuelwnb/p/3977636.html
總結
以上是生活随笔為你收集整理的单片机的模拟智能灌溉系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript对象中的this属性
- 下一篇: 小米手机调用系统截图挂掉问题