找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 5178|回復(fù): 2
收起左側(cè)

51單片機(jī)土壤濕度檢測(cè)源程序與原理圖PCB文件(注釋很詳細(xì))

  [復(fù)制鏈接]
ID:891249 發(fā)表于 2021-3-21 23:21 | 顯示全部樓層 |閱讀模式
土壤濕度檢測(cè)仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
1.png 2.png

Altium Designer畫的原理圖和PCB圖如下:(51hei附件中可下載工程文件,目前還不完整)
3.png

單片機(jī)源程序如下,代碼注釋很全面:
  1. #include<reg52.h>                                 //頭文件
  2. #include<intrins.h>
  3. #include"eeprom52.h"
  4. #define uchar unsigned char                 //宏定義
  5. #define uint unsigned int
  6. #define LCD1602_dat P0         //液晶數(shù)據(jù)口定義


  7. sbit LCD1602_rs=P2^5;//IO 定義
  8. sbit LCD1602_rw=P2^6;
  9. sbit LCD1602_e=P2^7;
  10. sbit beep=P1^3;                //蜂鳴器
  11. sbit led_1=P1^4;        //指示燈
  12. sbit led_2=P1^6;
  13. sbit key_1=P3^2;        //按鍵
  14. sbit key_2=P3^3;
  15. sbit key_3=P3^4;
  16. sbit alarm_1=P2^0;        //繼電器


  17. sbit ADC0832_CS=P1^2;
  18. sbit ADC0832_CLK=P1^0;
  19. sbit ADC0832_DIO=P1^1;         //adc0832引腳

  20. uint sum;
  21. uchar RH,RH_H=60,RH_L=20,state,ms,time_num,cs;
  22. bit beep1,zt,s1;


  23. /*
  24.     ADC0832是一款8位Ad芯片,因?yàn)閱纹瑱C(jī)不能直接處理模擬信號(hào)(電壓),所以單片機(jī)測(cè)電壓的時(shí)候基本都是先經(jīng)過一個(gè)模數(shù)轉(zhuǎn)換芯片,將模擬量
  25. 轉(zhuǎn)化成數(shù)字量,然后處理,ADC0832測(cè)量的電壓范圍是0-5V,它能夠?qū)?—5V的電壓轉(zhuǎn)化成對(duì)應(yīng)比例關(guān)系的0-255(8位是0-255)的數(shù)據(jù),單片機(jī)直
  26. 接讀取ADC0832的數(shù)據(jù)獲取AD值數(shù)據(jù),然后因?yàn)?-5V對(duì)應(yīng)0-255數(shù)據(jù),所以1V電對(duì)應(yīng)的AD值就是51,就會(huì)有如下公式

  27.   電壓=AD值/51;

  28. 如果想把電壓數(shù)據(jù)精確到小數(shù)點(diǎn)后一位就是   電壓=AD值/5.1;
  29.                                           小數(shù)點(diǎn)后兩位就是   電壓=AD值/0.51;

  30. 不要問我為什么,純數(shù)學(xué),小學(xué)生都會(huì)算。

  31. */
  32. unsigned int  A_D()
  33. {
  34.         unsigned char i;
  35.         unsigned char dat;
  36.         ADC0832_CS=1;   //一個(gè)轉(zhuǎn)換周期開始
  37.         ADC0832_CLK=0;  //為第一個(gè)脈沖作準(zhǔn)備
  38.         ADC0832_CS=0;  //CS置0,片選有效
  39.         ADC0832_DIO=1;    //DIO置1,規(guī)定的起始信號(hào)  
  40.         ADC0832_CLK=1;   //第一個(gè)脈沖
  41.         ADC0832_CLK=0;   //第一個(gè)脈沖的下降沿,此前DIO必須是高電平
  42.         ADC0832_DIO=1;   //DIO置1, 通道選擇信號(hào)  
  43.         ADC0832_CLK=1;   //第二個(gè)脈沖,第2、3個(gè)脈沖下沉之前,DI必須跟別輸入兩位數(shù)據(jù)用于選擇通道,這里選通道RH0
  44.         ADC0832_CLK=0;   //第二個(gè)脈沖下降沿
  45.         ADC0832_DIO=0;   //DI置0,選擇通道0
  46.         ADC0832_CLK=1;    //第三個(gè)脈沖
  47.         ADC0832_CLK=0;    //第三個(gè)脈沖下降沿
  48.         ADC0832_DIO=1;    //第三個(gè)脈沖下沉之后,輸入端DIO失去作用,應(yīng)置1
  49.         ADC0832_CLK=1;    //第四個(gè)脈沖
  50.         for(i=0;i<8;i++)  //高位在前
  51.         {
  52.                 ADC0832_CLK=1;         //第四個(gè)脈沖
  53.                 ADC0832_CLK=0;
  54.                 dat<<=1;       //將下面儲(chǔ)存的低位數(shù)據(jù)向右移
  55.                 dat|=(unsigned char)ADC0832_DIO;          //將輸出數(shù)據(jù)DIO通過或運(yùn)算儲(chǔ)存在dat最低位
  56.         }                                 
  57.         ADC0832_CS=1;          //片選無效
  58.         return dat;         //將讀書的數(shù)據(jù)返回     
  59. }

  60. void delay(uint T)                                          //延時(shí)函數(shù)
  61. {
  62.         while(T--);
  63. }



  64. /*
  65.     1602液晶,是常用的顯示器件,一共是16個(gè)管腳,其中有八個(gè)管腳是數(shù)據(jù)傳輸管腳,有三個(gè)管腳是數(shù)據(jù)命令使能端管腳,還有兩組電源管腳,
  66. 其中一組電源管腳是給整個(gè)液晶進(jìn)行供電的,還有一組電源是單純的背景光電源,還剩下的最后一個(gè)管腳是對(duì)比度調(diào)節(jié)管腳,一般接上一個(gè)3K電
  67. 阻再接地即可。
  68. 一般我們用的函數(shù),無非就是  LCD1602_write 和 LCD1602_writebyte
  69. LCD1602_write(x,y);   這個(gè)函數(shù)括號(hào)里面可以填寫兩個(gè)數(shù)據(jù),第一個(gè)數(shù)據(jù)只能是 0  1 ,是0就說明第二個(gè)數(shù)據(jù)對(duì)液晶來說就是命令,填1就說明
  70. 第二個(gè)數(shù)據(jù)對(duì)于液晶來說就是要顯示的數(shù)據(jù)。
  71. LCD1602_writebyte();  這個(gè)函數(shù)里面直接填上要顯示的字符串即可,自動(dòng)進(jìn)行顯示


  72. */
  73. void LCD1602_write(uchar order,dat)                                  //1602 一個(gè)字節(jié)  處理
  74. {
  75.     LCD1602_e=0;
  76.     LCD1602_rs=order;
  77.     LCD1602_dat=dat;
  78.     LCD1602_rw=0;
  79.     LCD1602_e=1;
  80.     delay(1);
  81.     LCD1602_e=0;                                                                                                                                                                                                     
  82. }

  83. void LCD1602_writebyte(uchar *prointer)                                   //1602 字符串    處理
  84. {
  85.     while(*prointer!='\0')
  86.     {
  87.         LCD1602_write(1,*prointer);
  88.         prointer++;
  89.     }
  90. }

  91. void LCD1602_cls()                                                                         //1602 初始化
  92. {
  93.         LCD1602_write(0,0x01);     //1602 清屏 指令
  94.         delay(1500);
  95.         LCD1602_write(0,0x38);     // 功能設(shè)置 8位、5*7點(diǎn)陣
  96.         delay(1500);
  97.         LCD1602_write(0,0x0c);     //設(shè)置 光標(biāo)   不顯示開關(guān)、不顯示光標(biāo)、字符不閃爍
  98.         LCD1602_write(0,0x06);
  99.         LCD1602_write(0,0xd0);
  100.         delay(1500);
  101. }

  102. /*
  103. 數(shù)據(jù)顯示的時(shí)候一般的處理:

  104.     首先,無論是數(shù)碼管顯示還是液晶顯示,進(jìn)行顯示的時(shí)候絕對(duì)都是一個(gè)一個(gè)進(jìn)行顯示的,那么,比如說一個(gè)數(shù)據(jù)123,一百二十三,
  105. 進(jìn)行顯示的時(shí)候,要先顯示1,然后是2,然后是3,那么怎么把數(shù)據(jù)提取出來??   
  106. 提取百位    123/100=1
  107. 提取十位    123/10=12      12%10=2     “%”是取余的意思,像這個(gè),就是12對(duì)10取余,換句話說,12除以10,然后取余數(shù),就是2
  108. 提取個(gè)位    123%10=3       解釋同上

  109. 取余的用法也有很多種,大家只要知道出現(xiàn)這個(gè)的時(shí)候,一般都是進(jìn)行數(shù)據(jù)提取的就行


  110. 然后
  111. 如果您是數(shù)碼管顯示數(shù)據(jù),將提取的數(shù)據(jù)放到段碼數(shù)組里面送給IO即可,
  112. 如果是液晶顯示,需要將數(shù)據(jù)轉(zhuǎn)化成字符,因?yàn)橐壕亲址,只能顯示字符數(shù)據(jù),數(shù)據(jù)0對(duì)應(yīng)的字符是0x30,數(shù)據(jù)1對(duì)應(yīng)的字符是0x31,
  113. 所以將提取出的數(shù)據(jù)直接加上0x30送給液晶即可,或者加上'0' 也是一樣的



  114. */

  115. void show()        //顯示子函數(shù)
  116. {
  117.         if(state==0) //如果非設(shè)置狀態(tài)
  118.         {
  119.                 LCD1602_write(0,0x80);                 //第一行
  120.                 LCD1602_writebyte("Humidity:");//顯示濕度
  121.                 LCD1602_write(0,0x80+9);
  122.                 if(RH>99)LCD1602_write(1,0x30+RH/100%10);        //如果數(shù)據(jù)大于99顯示百位數(shù)
  123.                 else LCD1602_writebyte(" ");                                //否則顯示空白
  124.                 LCD1602_write(0,0x80+10);
  125.                 if(RH>9)LCD1602_write(1,0x30+RH/10%10);
  126.                 else LCD1602_writebyte(" ");
  127.                 LCD1602_write(0,0x80+11);
  128.                 LCD1602_write(1,0x30+RH%10);
  129.                 LCD1602_write(0,0x80+12);
  130.                 LCD1602_writebyte("%   ");                   //顯示%號(hào)

  131.                 LCD1602_write(0,0xC0);        //第二行,顯示當(dāng)前模式
  132.                 LCD1602_writebyte("State:");
  133.                 LCD1602_write(0,0xC0+6);
  134.                 if(zt==1)
  135.                 {
  136.                         LCD1602_writebyte(" Auto     ");   //自動(dòng)
  137.                 }else
  138.                 {
  139.                         LCD1602_writebyte("Manul     ");   //手動(dòng)
  140.                 }
  141.                         
  142.         }else
  143.         {
  144.                 LCD1602_write(0,0x80);          //第一行顯示上限
  145.                 LCD1602_writebyte("RH_H:");
  146.                 LCD1602_write(0,0x80+5);
  147.                 if(state==1&&s1==1)                        //當(dāng)設(shè)置上限時(shí),state=1,這時(shí)候會(huì)根據(jù)s1的狀態(tài)去顯示空白或者上限數(shù)據(jù),而s1在定時(shí)器里是連續(xù)取反的
  148.                 {
  149.                         LCD1602_writebyte("   ");
  150.                 }else
  151.                 {
  152.                         if(RH_H>99)LCD1602_write(1,0x30+RH_H/100%10);
  153.                         else LCD1602_writebyte(" ");
  154.                         LCD1602_write(0,0x80+6);
  155.                         if(RH_H>9)LCD1602_write(1,0x30+RH_H/10%10);
  156.                         else LCD1602_writebyte(" ");
  157.                         LCD1602_write(0,0x80+7);
  158.                         LCD1602_write(1,0x30+RH_H%10);               
  159.                 }
  160.                 LCD1602_write(0,0x80+8);
  161.                 LCD1602_writebyte("%       ");

  162.                 LCD1602_write(0,0xC0);           //第二行顯示下限
  163.                 LCD1602_writebyte("RH_L:");
  164.                 LCD1602_write(0,0xC0+5);
  165.                 if(state==2&&s1==1)
  166.                 {
  167.                         LCD1602_writebyte("   ");
  168.                 }else
  169.                 {
  170.                         if(RH_L>99)LCD1602_write(1,0x30+RH_L/100%10);
  171.                         else LCD1602_writebyte(" ");
  172.                         LCD1602_write(0,0xC0+6);
  173.                         if(RH_L>9)LCD1602_write(1,0x30+RH_L/10%10);
  174.                         else LCD1602_writebyte(" ");
  175.                         LCD1602_write(0,0xC0+7);
  176.                         LCD1602_write(1,0x30+RH_L%10);
  177.                 }
  178.                 LCD1602_write(0,0xC0+8);
  179.                 LCD1602_writebyte("%       ");        
  180.         }
  181. }



  182. /*

  183. 無論是什么單片機(jī),只要是用內(nèi)部存儲(chǔ)區(qū)域EEPROM基本使用都是這樣

  184. 關(guān)于內(nèi)部存儲(chǔ)區(qū),EEPROM,不同的單片機(jī)使用流程基本一致,單片機(jī)內(nèi)部有很多存儲(chǔ)單元,或者說扇區(qū),每一個(gè)扇區(qū)下面有很多地址,
  185. 數(shù)據(jù)就是存儲(chǔ)在這些地址下面的。存儲(chǔ)函數(shù)的程序都是官方提供好的,這些程序,咱們只需要用三個(gè),一個(gè)是扇區(qū)擦除函數(shù),一個(gè)是
  186. 數(shù)據(jù)寫函數(shù),還有一個(gè)就是數(shù)據(jù)讀取函數(shù)。
  187. 扇區(qū)擦除函數(shù)------使用哪個(gè)扇區(qū),先對(duì)那個(gè)扇區(qū)進(jìn)行擦,函數(shù)里填寫要擦除扇區(qū)的首地址  例如  SectorErase(0x2000);就是說擦除首地址為0x2000的扇區(qū)數(shù)據(jù)  
  188. 數(shù)據(jù)存儲(chǔ)----------扇區(qū)擦除之后,就可以使用這個(gè)扇區(qū)下的地址進(jìn)行存儲(chǔ)數(shù)據(jù)     例如   byte_write(0x2000,123);  就是說將123存儲(chǔ)在0x2000地址下
  189. 數(shù)據(jù)讀取----------直接調(diào)用即可,例如     Dat=byte_read(0x2000);就是說將0x2000地址下的數(shù)據(jù)讀取出來給  Dat


  190. 另外----
  191. //51單片機(jī)存儲(chǔ)區(qū)域是8位的,也就是說能夠存下的最大數(shù)據(jù)是 255,而我們存的數(shù)據(jù)一旦大于256就會(huì)出現(xiàn)一些問題
  192. //所以,如果您的設(shè)計(jì)需要存儲(chǔ)的數(shù)據(jù)大于256,那就把數(shù)據(jù)拆開存   /256得到高位    %256得到低位,之所以是256,是因?yàn)?-255,256個(gè)數(shù)
  193. // 例如數(shù)據(jù)257           257/256=1        257%256=1    ,這就存進(jìn)去兩個(gè)1,讀取的時(shí)候,將高位數(shù)據(jù)乘以256加低位數(shù)據(jù),還原數(shù)據(jù)

  194. */

  195. void key()          //按鍵掃描
  196. {
  197.         if(!key_1)        //如果按鍵1按下
  198.         {
  199.                 delay(888);          //延時(shí)消抖
  200.                 if(!key_1)          //如果按鍵1按下
  201.                 {
  202.                         while(!key_1) show();         //松手檢測(cè)
  203.                         state=(state+1)%3;                 //設(shè)置變量自加,state的值不同設(shè)置的數(shù)據(jù)不同
  204.                 }
  205.         }

  206.         if(!key_2)
  207.         {
  208.                 delay(888);   //按鍵去抖
  209.                 if(!key_2)
  210.                 {
  211.                         while(!key_2)show();
  212.                         if(state==1)                 //state=1的時(shí)候設(shè)置上限
  213.                         {
  214.                                 if(RH_H<100)RH_H++;
  215.                                 SectorErase(0x2000);         //保存上限值
  216.                                 byte_write(0x2000,RH_H);
  217.                         }else if(state==2)                  //state=2的時(shí)候設(shè)置下限
  218.                         {
  219.                                 if(RH_L<RH_H-1)RH_L++;
  220.                                 SectorErase(0x2200);         //保存上限值
  221.                                 byte_write(0x2200,RH_L);
  222.                         }else
  223.                         {
  224.                                 zt=!zt;                        
  225.                         }        
  226.                 }
  227.         }

  228.         if(!key_3)
  229.         {
  230.                 delay(888);
  231.                 if(!key_3)
  232.                 {               
  233.                         while(!key_3)show();
  234.                         if(state==1)
  235.                         {
  236.                                 if(RH_H>RH_L+1)RH_H--;
  237.                                 SectorErase(0x2000);         //保存上限值
  238.                                 byte_write(0x2000,RH_H);
  239.                         }else if(state==2)
  240.                         {
  241.                                 if(RH_L>0)RH_L--;
  242.                                 SectorErase(0x2200);         //保存上限值
  243.                                 byte_write(0x2200,RH_L);
  244.                         }else
  245.                         {
  246.                                 if(zt==0)
  247.                                 {
  248.                                         alarm_1=!alarm_1;
  249.                                 }
  250.                         }
  251.                 }
  252.         }               
  253. }

  254. void proc()          //報(bào)警函數(shù)
  255. {
  256.         if(zt==1)  //自動(dòng)狀態(tài)
  257.         {
  258.                 if(RH>=RH_H)        //濕度大于上限
  259.                 {
  260.                         alarm_1=1;          //繼電器斷開
  261.                         led_1=0;          //上限指示燈點(diǎn)亮
  262.                 }else
  263.                 {
  264.                         led_1=1;         //否則上限指示燈熄滅
  265.                 }

  266.                 if(RH<=RH_L)         //如果小于下限
  267.                 {
  268.                         alarm_1=0;          //繼電器閉合
  269.                         led_2=0;           //下限指示燈點(diǎn)亮
  270.                 }else
  271.                 {
  272.                         led_2=1;           //否則下限指示燈熄滅
  273.                 }

  274.                 if(RH>=RH_H||RH<=RH_L)                //如果不在上下限區(qū)間內(nèi)
  275.                 {
  276.                         beep1=1;                   //報(bào)警標(biāo)志位置1
  277.                 }else
  278.                 {
  279.                         beep1=0;                 //否則置0
  280.                 }
  281.         }else                           //手動(dòng)狀態(tài)
  282.         {
  283.                 beep1=0;                //不報(bào)警
  284.                 led_1=led_2=1;         //指示燈不亮
  285.         }
  286. }





  287. void main()        //主循環(huán)
  288. {        
  289.         float Ad_dat=0;
  290.         TMOD=0x01;        //配置定時(shí)器0
  291.         TH0=0x3c;
  292.         TL0=0xb0;        //賦50ms初值
  293.         ET0=1;
  294.         TR0=1;
  295.         EA=1;                 //打開總中斷
  296.         LCD1602_cls();         //液晶初始化
  297.         RH_H=byte_read(0x2000);
  298.         RH_L=byte_read(0x2200);
  299.         if((RH_H>99)||(RH_L>99)||(RH_L>=RH_H))   {RH_H=30;  RH_L=20;}
  300.         while(1)
  301.         {
  302.                 sum+=A_D();        //累加5次AD數(shù)據(jù)
  303.                 cs++;
  304.                 if(cs==5)
  305.                 {
  306.                         cs=0;
  307.                         Ad_dat=(float)(sum/5);         //取一個(gè)平均值,用于濾波
  308.                         if(Ad_dat>250) Ad_dat=0;
  309.                         else if(Ad_dat<=70) Ad_dat=100;
  310.                         else Ad_dat=100-((Ad_dat-70)/1.8);         
  311.                         RH=(uint)(Ad_dat);
  312.                         sum=0;
  313.                 }
  314.                 show();           //調(diào)用顯示函數(shù)
  315.                 key();           //調(diào)用按鍵掃描
  316.                 proc();           //調(diào)用報(bào)警子函數(shù)
  317.         }
  318. }

  319. void UART_1() interrupt 1
  320. {
  321.         TH0=0x3c;
  322.         TL0=0xb0;
  323.         ms++;
  324.         if(ms%5==0)          //s1每5*50ms=250ms取反一次
  325.         {
  326.             s1=!s1;
  327.         }
  328.         if(ms%10==0)         
  329.         {
  330.                 if(beep1==1)   //如果報(bào)警標(biāo)志位是1,每10*50ms=500ms蜂鳴器狀態(tài)取反一次
  331.                 {
  332.                         beep=!beep;
  333.                 }else
  334.                 {
  335.                         beep=1;
  336.                 }        
  337.         }
  338.         if(ms>19)
  339.         {
  340.                 ms=0;
  341.         }        
  342. }
復(fù)制代碼

本人初學(xué),僅供參考,存在錯(cuò)誤和不足之處,請(qǐng)大家回帖多多指教,切勿照搬,文件下載:
代碼: 程序.7z (46.64 KB, 下載次數(shù): 119)
PCB:目前還不完整: 原理圖與PCB.7z (702.42 KB, 下載次數(shù): 107)
仿真文件不公開,以免有人抄,有要學(xué)習(xí)的按我的圖自己畫吧

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

ID:143767 發(fā)表于 2021-3-22 12:47 | 顯示全部樓層
好資料,最近我在用慧編程和光環(huán)板做這個(gè),你這個(gè)很有參考價(jià)值
回復(fù)

使用道具 舉報(bào)

ID:1059782 發(fā)表于 2022-12-30 16:19 | 顯示全部樓層
您好,有proteus仿真文件可以發(fā)一下嗎
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表