|
土壤濕度檢測(cè)仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
Altium Designer畫的原理圖和PCB圖如下:(51hei附件中可下載工程文件,目前還不完整)
單片機(jī)源程序如下,代碼注釋很全面:
- #include<reg52.h> //頭文件
- #include<intrins.h>
- #include"eeprom52.h"
- #define uchar unsigned char //宏定義
- #define uint unsigned int
- #define LCD1602_dat P0 //液晶數(shù)據(jù)口定義
- sbit LCD1602_rs=P2^5;//IO 定義
- sbit LCD1602_rw=P2^6;
- sbit LCD1602_e=P2^7;
- sbit beep=P1^3; //蜂鳴器
- sbit led_1=P1^4; //指示燈
- sbit led_2=P1^6;
- sbit key_1=P3^2; //按鍵
- sbit key_2=P3^3;
- sbit key_3=P3^4;
- sbit alarm_1=P2^0; //繼電器
- sbit ADC0832_CS=P1^2;
- sbit ADC0832_CLK=P1^0;
- sbit ADC0832_DIO=P1^1; //adc0832引腳
- uint sum;
- uchar RH,RH_H=60,RH_L=20,state,ms,time_num,cs;
- bit beep1,zt,s1;
- /*
- ADC0832是一款8位Ad芯片,因?yàn)閱纹瑱C(jī)不能直接處理模擬信號(hào)(電壓),所以單片機(jī)測(cè)電壓的時(shí)候基本都是先經(jīng)過一個(gè)模數(shù)轉(zhuǎn)換芯片,將模擬量
- 轉(zhuǎn)化成數(shù)字量,然后處理,ADC0832測(cè)量的電壓范圍是0-5V,它能夠?qū)?—5V的電壓轉(zhuǎn)化成對(duì)應(yīng)比例關(guān)系的0-255(8位是0-255)的數(shù)據(jù),單片機(jī)直
- 接讀取ADC0832的數(shù)據(jù)獲取AD值數(shù)據(jù),然后因?yàn)?-5V對(duì)應(yīng)0-255數(shù)據(jù),所以1V電對(duì)應(yīng)的AD值就是51,就會(huì)有如下公式
- 電壓=AD值/51;
- 如果想把電壓數(shù)據(jù)精確到小數(shù)點(diǎn)后一位就是 電壓=AD值/5.1;
- 小數(shù)點(diǎn)后兩位就是 電壓=AD值/0.51;
- 不要問我為什么,純數(shù)學(xué),小學(xué)生都會(huì)算。
-
- */
- unsigned int A_D()
- {
- unsigned char i;
- unsigned char dat;
- ADC0832_CS=1; //一個(gè)轉(zhuǎn)換周期開始
- ADC0832_CLK=0; //為第一個(gè)脈沖作準(zhǔn)備
- ADC0832_CS=0; //CS置0,片選有效
- ADC0832_DIO=1; //DIO置1,規(guī)定的起始信號(hào)
- ADC0832_CLK=1; //第一個(gè)脈沖
- ADC0832_CLK=0; //第一個(gè)脈沖的下降沿,此前DIO必須是高電平
- ADC0832_DIO=1; //DIO置1, 通道選擇信號(hào)
- ADC0832_CLK=1; //第二個(gè)脈沖,第2、3個(gè)脈沖下沉之前,DI必須跟別輸入兩位數(shù)據(jù)用于選擇通道,這里選通道RH0
- ADC0832_CLK=0; //第二個(gè)脈沖下降沿
- ADC0832_DIO=0; //DI置0,選擇通道0
- ADC0832_CLK=1; //第三個(gè)脈沖
- ADC0832_CLK=0; //第三個(gè)脈沖下降沿
- ADC0832_DIO=1; //第三個(gè)脈沖下沉之后,輸入端DIO失去作用,應(yīng)置1
- ADC0832_CLK=1; //第四個(gè)脈沖
- for(i=0;i<8;i++) //高位在前
- {
- ADC0832_CLK=1; //第四個(gè)脈沖
- ADC0832_CLK=0;
- dat<<=1; //將下面儲(chǔ)存的低位數(shù)據(jù)向右移
- dat|=(unsigned char)ADC0832_DIO; //將輸出數(shù)據(jù)DIO通過或運(yùn)算儲(chǔ)存在dat最低位
- }
- ADC0832_CS=1; //片選無效
- return dat; //將讀書的數(shù)據(jù)返回
- }
- void delay(uint T) //延時(shí)函數(shù)
- {
- while(T--);
- }
- /*
- 1602液晶,是常用的顯示器件,一共是16個(gè)管腳,其中有八個(gè)管腳是數(shù)據(jù)傳輸管腳,有三個(gè)管腳是數(shù)據(jù)命令使能端管腳,還有兩組電源管腳,
- 其中一組電源管腳是給整個(gè)液晶進(jìn)行供電的,還有一組電源是單純的背景光電源,還剩下的最后一個(gè)管腳是對(duì)比度調(diào)節(jié)管腳,一般接上一個(gè)3K電
- 阻再接地即可。
- 一般我們用的函數(shù),無非就是 LCD1602_write 和 LCD1602_writebyte
- LCD1602_write(x,y); 這個(gè)函數(shù)括號(hào)里面可以填寫兩個(gè)數(shù)據(jù),第一個(gè)數(shù)據(jù)只能是 0 1 ,是0就說明第二個(gè)數(shù)據(jù)對(duì)液晶來說就是命令,填1就說明
- 第二個(gè)數(shù)據(jù)對(duì)于液晶來說就是要顯示的數(shù)據(jù)。
- LCD1602_writebyte(); 這個(gè)函數(shù)里面直接填上要顯示的字符串即可,自動(dòng)進(jìn)行顯示
-
-
- */
- void LCD1602_write(uchar order,dat) //1602 一個(gè)字節(jié) 處理
- {
- LCD1602_e=0;
- LCD1602_rs=order;
- LCD1602_dat=dat;
- LCD1602_rw=0;
- LCD1602_e=1;
- delay(1);
- LCD1602_e=0;
- }
- void LCD1602_writebyte(uchar *prointer) //1602 字符串 處理
- {
- while(*prointer!='\0')
- {
- LCD1602_write(1,*prointer);
- prointer++;
- }
- }
- void LCD1602_cls() //1602 初始化
- {
- LCD1602_write(0,0x01); //1602 清屏 指令
- delay(1500);
- LCD1602_write(0,0x38); // 功能設(shè)置 8位、5*7點(diǎn)陣
- delay(1500);
- LCD1602_write(0,0x0c); //設(shè)置 光標(biāo) 不顯示開關(guān)、不顯示光標(biāo)、字符不閃爍
- LCD1602_write(0,0x06);
- LCD1602_write(0,0xd0);
- delay(1500);
- }
-
- /*
- 數(shù)據(jù)顯示的時(shí)候一般的處理:
- 首先,無論是數(shù)碼管顯示還是液晶顯示,進(jìn)行顯示的時(shí)候絕對(duì)都是一個(gè)一個(gè)進(jìn)行顯示的,那么,比如說一個(gè)數(shù)據(jù)123,一百二十三,
- 進(jìn)行顯示的時(shí)候,要先顯示1,然后是2,然后是3,那么怎么把數(shù)據(jù)提取出來??
- 提取百位 123/100=1
- 提取十位 123/10=12 12%10=2 “%”是取余的意思,像這個(gè),就是12對(duì)10取余,換句話說,12除以10,然后取余數(shù),就是2
- 提取個(gè)位 123%10=3 解釋同上
- 取余的用法也有很多種,大家只要知道出現(xiàn)這個(gè)的時(shí)候,一般都是進(jìn)行數(shù)據(jù)提取的就行
- 然后
- 如果您是數(shù)碼管顯示數(shù)據(jù),將提取的數(shù)據(jù)放到段碼數(shù)組里面送給IO即可,
- 如果是液晶顯示,需要將數(shù)據(jù)轉(zhuǎn)化成字符,因?yàn)橐壕亲址,只能顯示字符數(shù)據(jù),數(shù)據(jù)0對(duì)應(yīng)的字符是0x30,數(shù)據(jù)1對(duì)應(yīng)的字符是0x31,
- 所以將提取出的數(shù)據(jù)直接加上0x30送給液晶即可,或者加上'0' 也是一樣的
-
- */
- void show() //顯示子函數(shù)
- {
- if(state==0) //如果非設(shè)置狀態(tài)
- {
- LCD1602_write(0,0x80); //第一行
- LCD1602_writebyte("Humidity:");//顯示濕度
- LCD1602_write(0,0x80+9);
- if(RH>99)LCD1602_write(1,0x30+RH/100%10); //如果數(shù)據(jù)大于99顯示百位數(shù)
- else LCD1602_writebyte(" "); //否則顯示空白
- LCD1602_write(0,0x80+10);
- if(RH>9)LCD1602_write(1,0x30+RH/10%10);
- else LCD1602_writebyte(" ");
- LCD1602_write(0,0x80+11);
- LCD1602_write(1,0x30+RH%10);
- LCD1602_write(0,0x80+12);
- LCD1602_writebyte("% "); //顯示%號(hào)
- LCD1602_write(0,0xC0); //第二行,顯示當(dāng)前模式
- LCD1602_writebyte("State:");
- LCD1602_write(0,0xC0+6);
- if(zt==1)
- {
- LCD1602_writebyte(" Auto "); //自動(dòng)
- }else
- {
- LCD1602_writebyte("Manul "); //手動(dòng)
- }
-
- }else
- {
- LCD1602_write(0,0x80); //第一行顯示上限
- LCD1602_writebyte("RH_H:");
- LCD1602_write(0,0x80+5);
- if(state==1&&s1==1) //當(dāng)設(shè)置上限時(shí),state=1,這時(shí)候會(huì)根據(jù)s1的狀態(tài)去顯示空白或者上限數(shù)據(jù),而s1在定時(shí)器里是連續(xù)取反的
- {
- LCD1602_writebyte(" ");
- }else
- {
- if(RH_H>99)LCD1602_write(1,0x30+RH_H/100%10);
- else LCD1602_writebyte(" ");
- LCD1602_write(0,0x80+6);
- if(RH_H>9)LCD1602_write(1,0x30+RH_H/10%10);
- else LCD1602_writebyte(" ");
- LCD1602_write(0,0x80+7);
- LCD1602_write(1,0x30+RH_H%10);
- }
- LCD1602_write(0,0x80+8);
- LCD1602_writebyte("% ");
- LCD1602_write(0,0xC0); //第二行顯示下限
- LCD1602_writebyte("RH_L:");
- LCD1602_write(0,0xC0+5);
- if(state==2&&s1==1)
- {
- LCD1602_writebyte(" ");
- }else
- {
- if(RH_L>99)LCD1602_write(1,0x30+RH_L/100%10);
- else LCD1602_writebyte(" ");
- LCD1602_write(0,0xC0+6);
- if(RH_L>9)LCD1602_write(1,0x30+RH_L/10%10);
- else LCD1602_writebyte(" ");
- LCD1602_write(0,0xC0+7);
- LCD1602_write(1,0x30+RH_L%10);
- }
- LCD1602_write(0,0xC0+8);
- LCD1602_writebyte("% ");
- }
- }
- /*
- 無論是什么單片機(jī),只要是用內(nèi)部存儲(chǔ)區(qū)域EEPROM基本使用都是這樣
- 關(guān)于內(nèi)部存儲(chǔ)區(qū),EEPROM,不同的單片機(jī)使用流程基本一致,單片機(jī)內(nèi)部有很多存儲(chǔ)單元,或者說扇區(qū),每一個(gè)扇區(qū)下面有很多地址,
- 數(shù)據(jù)就是存儲(chǔ)在這些地址下面的。存儲(chǔ)函數(shù)的程序都是官方提供好的,這些程序,咱們只需要用三個(gè),一個(gè)是扇區(qū)擦除函數(shù),一個(gè)是
- 數(shù)據(jù)寫函數(shù),還有一個(gè)就是數(shù)據(jù)讀取函數(shù)。
- 扇區(qū)擦除函數(shù)------使用哪個(gè)扇區(qū),先對(duì)那個(gè)扇區(qū)進(jìn)行擦,函數(shù)里填寫要擦除扇區(qū)的首地址 例如 SectorErase(0x2000);就是說擦除首地址為0x2000的扇區(qū)數(shù)據(jù)
- 數(shù)據(jù)存儲(chǔ)----------扇區(qū)擦除之后,就可以使用這個(gè)扇區(qū)下的地址進(jìn)行存儲(chǔ)數(shù)據(jù) 例如 byte_write(0x2000,123); 就是說將123存儲(chǔ)在0x2000地址下
- 數(shù)據(jù)讀取----------直接調(diào)用即可,例如 Dat=byte_read(0x2000);就是說將0x2000地址下的數(shù)據(jù)讀取出來給 Dat
- 另外----
- //51單片機(jī)存儲(chǔ)區(qū)域是8位的,也就是說能夠存下的最大數(shù)據(jù)是 255,而我們存的數(shù)據(jù)一旦大于256就會(huì)出現(xiàn)一些問題
- //所以,如果您的設(shè)計(jì)需要存儲(chǔ)的數(shù)據(jù)大于256,那就把數(shù)據(jù)拆開存 /256得到高位 %256得到低位,之所以是256,是因?yàn)?-255,256個(gè)數(shù)
- // 例如數(shù)據(jù)257 257/256=1 257%256=1 ,這就存進(jìn)去兩個(gè)1,讀取的時(shí)候,將高位數(shù)據(jù)乘以256加低位數(shù)據(jù),還原數(shù)據(jù)
- */
-
- void key() //按鍵掃描
- {
- if(!key_1) //如果按鍵1按下
- {
- delay(888); //延時(shí)消抖
- if(!key_1) //如果按鍵1按下
- {
- while(!key_1) show(); //松手檢測(cè)
- state=(state+1)%3; //設(shè)置變量自加,state的值不同設(shè)置的數(shù)據(jù)不同
- }
- }
- if(!key_2)
- {
- delay(888); //按鍵去抖
- if(!key_2)
- {
- while(!key_2)show();
- if(state==1) //state=1的時(shí)候設(shè)置上限
- {
- if(RH_H<100)RH_H++;
- SectorErase(0x2000); //保存上限值
- byte_write(0x2000,RH_H);
- }else if(state==2) //state=2的時(shí)候設(shè)置下限
- {
- if(RH_L<RH_H-1)RH_L++;
- SectorErase(0x2200); //保存上限值
- byte_write(0x2200,RH_L);
- }else
- {
- zt=!zt;
- }
- }
- }
- if(!key_3)
- {
- delay(888);
- if(!key_3)
- {
- while(!key_3)show();
- if(state==1)
- {
- if(RH_H>RH_L+1)RH_H--;
- SectorErase(0x2000); //保存上限值
- byte_write(0x2000,RH_H);
- }else if(state==2)
- {
- if(RH_L>0)RH_L--;
- SectorErase(0x2200); //保存上限值
- byte_write(0x2200,RH_L);
- }else
- {
- if(zt==0)
- {
- alarm_1=!alarm_1;
- }
- }
- }
- }
- }
- void proc() //報(bào)警函數(shù)
- {
- if(zt==1) //自動(dòng)狀態(tài)
- {
- if(RH>=RH_H) //濕度大于上限
- {
- alarm_1=1; //繼電器斷開
- led_1=0; //上限指示燈點(diǎn)亮
- }else
- {
- led_1=1; //否則上限指示燈熄滅
- }
- if(RH<=RH_L) //如果小于下限
- {
- alarm_1=0; //繼電器閉合
- led_2=0; //下限指示燈點(diǎn)亮
- }else
- {
- led_2=1; //否則下限指示燈熄滅
- }
- if(RH>=RH_H||RH<=RH_L) //如果不在上下限區(qū)間內(nèi)
- {
- beep1=1; //報(bào)警標(biāo)志位置1
- }else
- {
- beep1=0; //否則置0
- }
- }else //手動(dòng)狀態(tài)
- {
- beep1=0; //不報(bào)警
- led_1=led_2=1; //指示燈不亮
- }
- }
- void main() //主循環(huán)
- {
- float Ad_dat=0;
- TMOD=0x01; //配置定時(shí)器0
- TH0=0x3c;
- TL0=0xb0; //賦50ms初值
- ET0=1;
- TR0=1;
- EA=1; //打開總中斷
- LCD1602_cls(); //液晶初始化
- RH_H=byte_read(0x2000);
- RH_L=byte_read(0x2200);
- if((RH_H>99)||(RH_L>99)||(RH_L>=RH_H)) {RH_H=30; RH_L=20;}
- while(1)
- {
- sum+=A_D(); //累加5次AD數(shù)據(jù)
- cs++;
- if(cs==5)
- {
- cs=0;
- Ad_dat=(float)(sum/5); //取一個(gè)平均值,用于濾波
- if(Ad_dat>250) Ad_dat=0;
- else if(Ad_dat<=70) Ad_dat=100;
- else Ad_dat=100-((Ad_dat-70)/1.8);
- RH=(uint)(Ad_dat);
- sum=0;
- }
- show(); //調(diào)用顯示函數(shù)
- key(); //調(diào)用按鍵掃描
- proc(); //調(diào)用報(bào)警子函數(shù)
- }
- }
- void UART_1() interrupt 1
- {
- TH0=0x3c;
- TL0=0xb0;
- ms++;
- if(ms%5==0) //s1每5*50ms=250ms取反一次
- {
- s1=!s1;
- }
- if(ms%10==0)
- {
- if(beep1==1) //如果報(bào)警標(biāo)志位是1,每10*50ms=500ms蜂鳴器狀態(tài)取反一次
- {
- beep=!beep;
- }else
- {
- beep=1;
- }
- }
- if(ms>19)
- {
- ms=0;
- }
- }
復(fù)制代碼
本人初學(xué),僅供參考,存在錯(cuò)誤和不足之處,請(qǐng)大家回帖多多指教,切勿照搬,文件下載:
代碼:
程序.7z
(46.64 KB, 下載次數(shù): 119)
2021-3-21 23:19 上傳
點(diǎn)擊文件名下載附件
PCB:目前還不完整:
原理圖與PCB.7z
(702.42 KB, 下載次數(shù): 107)
2021-3-21 23:19 上傳
點(diǎn)擊文件名下載附件
仿真文件不公開,以免有人抄,有要學(xué)習(xí)的按我的圖自己畫吧
|
評(píng)分
-
查看全部評(píng)分
|