欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5246|回復: 2
打印 上一主題 下一主題
收起左側

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

  [復制鏈接]
跳轉到指定樓層
樓主
土壤濕度檢測仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)


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


單片機源程序如下,代碼注釋很全面:
  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         //液晶數據口定義


  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芯片,因為單片機不能直接處理模擬信號(電壓),所以單片機測電壓的時候基本都是先經過一個模數轉換芯片,將模擬量
  25. 轉化成數字量,然后處理,ADC0832測量的電壓范圍是0-5V,它能夠將0—5V的電壓轉化成對應比例關系的0-255(8位是0-255)的數據,單片機直
  26. 接讀取ADC0832的數據獲取AD值數據,然后因為0-5V對應0-255數據,所以1V電對應的AD值就是51,就會有如下公式

  27.   電壓=AD值/51;

  28. 如果想把電壓數據精確到小數點后一位就是   電壓=AD值/5.1;
  29.                                           小數點后兩位就是   電壓=AD值/0.51;

  30. 不要問我為什么,純數學,小學生都會算。

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

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



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


  72. */
  73. void LCD1602_write(uchar order,dat)                                  //1602 一個字節  處理
  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);     // 功能設置 8位、5*7點陣
  96.         delay(1500);
  97.         LCD1602_write(0,0x0c);     //設置 光標   不顯示開關、不顯示光標、字符不閃爍
  98.         LCD1602_write(0,0x06);
  99.         LCD1602_write(0,0xd0);
  100.         delay(1500);
  101. }

  102. /*
  103. 數據顯示的時候一般的處理:

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

  109. 取余的用法也有很多種,大家只要知道出現這個的時候,一般都是進行數據提取的就行


  110. 然后
  111. 如果您是數碼管顯示數據,將提取的數據放到段碼數組里面送給IO即可,
  112. 如果是液晶顯示,需要將數據轉化成字符,因為液晶是字符屏,只能顯示字符數據,數據0對應的字符是0x30,數據1對應的字符是0x31,
  113. 所以將提取出的數據直接加上0x30送給液晶即可,或者加上'0' 也是一樣的



  114. */

  115. void show()        //顯示子函數
  116. {
  117.         if(state==0) //如果非設置狀態
  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);        //如果數據大于99顯示百位數
  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("%   ");                   //顯示%號

  131.                 LCD1602_write(0,0xC0);        //第二行,顯示當前模式
  132.                 LCD1602_writebyte("State:");
  133.                 LCD1602_write(0,0xC0+6);
  134.                 if(zt==1)
  135.                 {
  136.                         LCD1602_writebyte(" Auto     ");   //自動
  137.                 }else
  138.                 {
  139.                         LCD1602_writebyte("Manul     ");   //手動
  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)                        //當設置上限時,state=1,這時候會根據s1的狀態去顯示空白或者上限數據,而s1在定時器里是連續取反的
  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. 無論是什么單片機,只要是用內部存儲區域EEPROM基本使用都是這樣

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


  190. 另外----
  191. //51單片機存儲區域是8位的,也就是說能夠存下的最大數據是 255,而我們存的數據一旦大于256就會出現一些問題
  192. //所以,如果您的設計需要存儲的數據大于256,那就把數據拆開存   /256得到高位    %256得到低位,之所以是256,是因為0-255,256個數
  193. // 例如數據257           257/256=1        257%256=1    ,這就存進去兩個1,讀取的時候,將高位數據乘以256加低位數據,還原數據

  194. */

  195. void key()          //按鍵掃描
  196. {
  197.         if(!key_1)        //如果按鍵1按下
  198.         {
  199.                 delay(888);          //延時消抖
  200.                 if(!key_1)          //如果按鍵1按下
  201.                 {
  202.                         while(!key_1) show();         //松手檢測
  203.                         state=(state+1)%3;                 //設置變量自加,state的值不同設置的數據不同
  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的時候設置上限
  213.                         {
  214.                                 if(RH_H<100)RH_H++;
  215.                                 SectorErase(0x2000);         //保存上限值
  216.                                 byte_write(0x2000,RH_H);
  217.                         }else if(state==2)                  //state=2的時候設置下限
  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()          //報警函數
  255. {
  256.         if(zt==1)  //自動狀態
  257.         {
  258.                 if(RH>=RH_H)        //濕度大于上限
  259.                 {
  260.                         alarm_1=1;          //繼電器斷開
  261.                         led_1=0;          //上限指示燈點亮
  262.                 }else
  263.                 {
  264.                         led_1=1;         //否則上限指示燈熄滅
  265.                 }

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

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





  287. void main()        //主循環
  288. {        
  289.         float Ad_dat=0;
  290.         TMOD=0x01;        //配置定時器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數據
  303.                 cs++;
  304.                 if(cs==5)
  305.                 {
  306.                         cs=0;
  307.                         Ad_dat=(float)(sum/5);         //取一個平均值,用于濾波
  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();           //調用顯示函數
  315.                 key();           //調用按鍵掃描
  316.                 proc();           //調用報警子函數
  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)   //如果報警標志位是1,每10*50ms=500ms蜂鳴器狀態取反一次
  331.                 {
  332.                         beep=!beep;
  333.                 }else
  334.                 {
  335.                         beep=1;
  336.                 }        
  337.         }
  338.         if(ms>19)
  339.         {
  340.                 ms=0;
  341.         }        
  342. }
復制代碼

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

評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏8 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:143767 發表于 2021-3-22 12:47 | 只看該作者
好資料,最近我在用慧編程和光環板做這個,你這個很有參考價值
回復

使用道具 舉報

無效樓層,該帖已經被刪除
無效樓層,該帖已經被刪除
5#
ID:1059782 發表于 2022-12-30 16:19 | 只看該作者
您好,有proteus仿真文件可以發一下嗎
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表