專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

51單片機(jī)-溫度控制器設(shè)計(jì)詳解+電路+代碼

作者:佚名   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2013年12月14日   【字體:
這個(gè)51單片機(jī)電路里面,需要用到:
 

 
AT89C51   x1 主要控制單片機(jī)

 
AT24C02   x1 EEPROM-2K儲(chǔ)存器

 
DS180B2   x1 溫度傳感器

 
10uF x1 , 47uF x1 有極電容

 
104[0.1uF] x3 , 30pF x2 無極電容

 
12MHz x1 石英晶體振蕩器

 
1K[1千歐姆] x2 電阻

 
7cmx9cm x1 萬能洞洞電路板

 
繼電器若干[根據(jù)需要用到的繼電器數(shù)目安裝到電路上,主控芯片設(shè)計(jì)可以控制八位繼電器(整一個(gè)P1端口)]

 
總價(jià)好像12塊左右吧,建議淘寶一下,不同的商家出售器件的價(jià)格不同..

 

 

 
Q1:EEPROM的作用

 
A1:因?yàn)镋EPROM[電壓式可擦寫ROM]可以讓單片機(jī)進(jìn)行IIC讀寫外部?jī)?chǔ)存數(shù)據(jù),也方便以后換一個(gè)控制電路溫度值,單片機(jī)不可能每換一個(gè)控制溫

 
度值的時(shí)候就重新對(duì)主控芯片進(jìn)行編程..

 
Q2:為什么EEPROM要2K大小呢?

 
A2:太大的儲(chǔ)存空間沒有實(shí)質(zhì)上的意義,最多使用的數(shù)據(jù)只用26位數(shù)據(jù)位,而且更大的儲(chǔ)存空間的EEPROM造價(jià)也貴,AT24C02-DIP8[直插八個(gè)引

 
腳]需要0.8元左右,AT24C512-DIP8[數(shù)據(jù)存儲(chǔ)空間512KB]需要17元左右..

 
Q3:主控芯片可以用其它的51系列代替嗎?

 
A3:完全可以的,只需要在原代碼文件中修改一下頭文件就可以把程序移植到另一個(gè)型號(hào)的芯片,同理也可以把程序移植到AT89C2051中,

AT89C2051比AT89C51的一個(gè)好處就是C2051系列的芯片體積比C51系列的芯片小,可以把電路更小化,如果還要求電路更小化的話選用SOP封

裝[貼片型封裝]的電子元件是一個(gè)不錯(cuò)的選擇,不過太小的SOP元件焊接到電路上會(huì)另你很頭疼,電路布局一定程度上也會(huì)影響電路板的整體大

小..

 

 
EEPROM儲(chǔ)存數(shù)據(jù)的方式解析:


 

下面是EEPROM里面的數(shù)據(jù)儲(chǔ)存結(jié)構(gòu)[嘻嘻,圖畫得有點(diǎn)不好,技術(shù)問題請(qǐng)見諒啦..] 


 

首先從主控入口代碼處進(jìn)行分析:


 

 

#define EEPROM_StartAddr 0x4E

#define EEPROM_StartFlag 0xB0

 


 


 

 LED=1;
 

 

BitData=read_add(EEPROM_StartAddr);


 

if (BitData!=EEPROM_StartFlag) while(1); 


 

ListTotal=read_add(EEPROM_StartAddr+1);

for(i=0;i<ListTotal;i++)

{

CtrlList[i].AlarmTemp=read_add((EEPROM_StartAddr+2)+i*3);

CtrlList[i].CtrlPort =read_add((EEPROM_StartAddr+2)+i*3+1);

}  

 

 

LED=0;
 


 

代碼首先會(huì)從EEPROM里面讀取0x4E位里面的數(shù)據(jù)來和EEPROM起始數(shù)據(jù)位標(biāo)志進(jìn)行對(duì)比,判斷這個(gè)EEPROM的數(shù)據(jù)格式的正確性,由于沒有讓


 

主控芯片對(duì)每一個(gè)數(shù)據(jù)位進(jìn)行校驗(yàn),那么這就是一個(gè)簡(jiǎn)單的驗(yàn)證方法,要是對(duì)每一位數(shù)據(jù)都要進(jìn)行的驗(yàn)證的話得需要一個(gè)可靠,高效的算法,這樣雖


 

然安全,不過會(huì)對(duì)主控芯片初始化有一定的性能影響


 

判斷起始數(shù)據(jù)位失敗后,主控芯片會(huì)進(jìn)入到卡死狀態(tài)[見代碼 while(1); ,死循環(huán),處理器永遠(yuǎn)也跳不出這個(gè)循環(huán)到外面]


 

判斷起始數(shù)據(jù)位成功后,下一步就從EEPROM的0x4F位讀取出需要控制溫度的項(xiàng)目總數(shù),然后再按照這個(gè)項(xiàng)目總數(shù)來進(jìn)行數(shù)據(jù)位偏移來讀取將要


 

控制的溫度數(shù)值和警報(bào)控制端口,注意兩個(gè)控制數(shù)據(jù)結(jié)構(gòu)中間需要用一個(gè)NULL[即0x00]來隔開來,防止數(shù)據(jù)結(jié)構(gòu)被打亂


 

從EEPROM里面讀取完成數(shù)據(jù)后,主控芯片工作指示燈發(fā)光,開始進(jìn)入溫度控制


 

主要代碼:


 

 

while(1)

{

TempData=get_temp();

BitData=(TempData%1000/100)*10;//十位溫度。

              BitData=(TempData%1000%100/10)+BitData;//個(gè)位溫度。

for(i=0;i<ListTotal;i++)

         {

 CtrlElec=(CtrlList[i].AlarmTemp>=BitData)?0:1;

 CtrlListPort=CtrlList[i].CtrlPort;

 CtrlPort(CtrlListPort,CtrlElec);     //符合條件輸出底電,不符合輸出高電

 delayb(100);

         }

delayb(200);      

}

 


 

這個(gè)可不像SetTimer()一樣,用SetTimer()指定了的函數(shù)不需要加上while 循環(huán),僅且把上面的代碼當(dāng)作線程來看待,讓這個(gè)線程運(yùn)行的代碼段永遠(yuǎn)


 

都是這個(gè),假如線程的代碼一旦執(zhí)行完畢[也就是說跳出了while 死循環(huán)],那么它就會(huì)關(guān)閉掉自己和釋放屬于自己的TLS[線程本地儲(chǔ)存]


 

先從DS18B02里面讀取出來溫度值,然后再對(duì)溫度進(jìn)行轉(zhuǎn)換,把轉(zhuǎn)換好的溫度再和從EEPROM里面讀取出來的數(shù)據(jù)結(jié)構(gòu)進(jìn)行比較


 

一個(gè)溫度控制判斷周期約一秒[以12MHz來計(jì)算的話,應(yīng)該是略大于一秒而不會(huì)小于一秒]..


 


 


 

下面是溫度控制器的電路:


 


 

5V供電電路:

 


 


 

主控芯片及外圍器件電路:

 


 


 

電路中用繼電器來控制外部電路的開閉,G[公共端] B[常閉端] K[常開端] 就是讓繼電器控制外部電路的開關(guān),5V電壓和P1^0端口是用來控制繼電器


 

的G端和B,K端通路.原理如下:


 

單片機(jī)通過引腳輸出 1 0來實(shí)現(xiàn)控制外圍電路,以P1^0[P1端口的第1位引腳]為例,當(dāng)P1^0輸出1的時(shí)候,引腳會(huì)產(chǎn)生一個(gè)5V的高電平.當(dāng)P1^0輸出0


 

的時(shí)候,引腳會(huì)產(chǎn)生一個(gè)0V的低電平


 

 

 


 


 

注:繼電器原理圖來自百度百科-> http://baike.baidu.com/view/39560.htm


 

繼電器原理:當(dāng)控制電路兩端有電流通過時(shí),電和金屬發(fā)生電磁感應(yīng),使得銜鐵被磁力吸引而控制工作電路的開關(guān)

 
繼電器和P1^0引腳之間的關(guān)系電路圖如下:
 

 

 
 

 
當(dāng)P1^0輸出1[即5V]時(shí),控制電路兩端沒有電流通過,因?yàn)闆]有電壓差的緣故,所以繼電器還是把開關(guān)B G 通路,但是當(dāng)P1^0輸出0的時(shí)候,左邊的電

 
壓為5V,右邊的電壓位0V,于是電流就從左向右流過,這個(gè)時(shí)候繼電器會(huì)把開關(guān)K G 變?yōu)橥?/div>

 
同理,主控芯片的工作指示燈也是這個(gè)原理,你可以去看看上面的EEPROM讀取數(shù)據(jù)代碼和主控芯片及外圍器件電路結(jié)合起來一起看看,相信你也

會(huì)把這個(gè)原理給弄明白

 

 
八路溫度控制器實(shí)物圖如下[八路只使用了其中一路,程序也實(shí)地運(yùn)行過,由于室內(nèi)溫度調(diào)節(jié)問題,繼電器沒有反應(yīng),因?yàn)榻oEEPROM設(shè)置30度報(bào)警

 
溫度,而且還有零下報(bào)警代碼沒有寫和報(bào)警溫度小數(shù)位判斷沒有寫]:

 

 


 

 

實(shí)物圖和電路原理圖基本一致,這個(gè)大家不用擔(dān)心

 

 
代碼和大家分享,可以再這個(gè)基礎(chǔ)上再加以改進(jìn),也歡迎你們?cè)谑褂么a的時(shí)候找到BUG然后修改..

 

下面是main.c 文件:
#include <reg51.h>
#define uchar unsigned char 
#define uint unsigned int

#define EEPROM_StartAddr 0x4E
#define EEPROM_StartFlag 0xB0
/*
#define EEPROM_EndFlag  0xC0
#define EEPROM_EndAddr  0x4E+2+24 //8個(gè)數(shù)據(jù)總數(shù)x3個(gè)數(shù)據(jù)位
//EEPROM_EndAddr=0x4E+2+8x03 每個(gè)數(shù)據(jù)占3字節(jié)(兩個(gè)主要數(shù)據(jù)和一個(gè)NULL),0x49系獲得數(shù)據(jù)項(xiàng)目總數(shù)
*/
typedef int CTRL_TOTAL;//列表總項(xiàng)目

typedef struct EEPROM_DataList//EEPROM數(shù)據(jù)結(jié)構(gòu)
{
  int  AlarmTemp;//報(bào)警溫度
  int  CtrlPort; //控制端口
} CTRL_DATALIST;

uint    get_temp();
void    tmpchange(void);
void    delayb(uint count);
void    init(); 
void    write_add(uchar address,uchar date);
uchar   read_add(uchar address);

void CtrlPort(int PortCode,int Ctrl);

sbit Beep=P3^4;    // 測(cè)試用,可略
sbit LED=P2^7;

sbit CtrlPort1=P1^0;
sbit CtrlPort2=P1^1;
sbit CtrlPort3=P1^2;
sbit CtrlPort4=P1^3;
sbit CtrlPort5=P1^4;
sbit CtrlPort6=P1^5;
sbit CtrlPort7=P1^6;
sbit CtrlPort8=P1^7;

CTRL_TOTAL     ListTotal;
CTRL_DATALIST  CtrlList[8];
void delayb(uint count) ;
void main()

  int BitData;       // EEPROM位數(shù)據(jù)緩存 
  int i;    // 計(jì)數(shù)循環(huán)變量
  int TempData;  // 當(dāng)前溫度值
  int CtrlListPort; // 將要控制的引腳
  int CtrlElec;  // 將要控制的引腳輸出電平

  LED=1;
  init();
  BitData=read_add(EEPROM_StartAddr);

  if (BitData!=EEPROM_StartFlag){while(1);} // 卡住它,不要讓它重新運(yùn)行
  // 關(guān)于EEPROM獲取數(shù)據(jù)信息可以從同文件夾中圖片有指導(dǎo)
  ListTotal=read_add(EEPROM_StartAddr+1);
  for(i=0;i<ListTotal;i++)
  {
   CtrlList[i].AlarmTemp=read_add((EEPROM_StartAddr+2)+i*3);
   CtrlList[i].CtrlPort =read_add((EEPROM_StartAddr+2)+i*3+1);
  } 
  tmpchange();
 
  for(i=0;i<=7;i++) CtrlPort(i,1);   //輸出高電位
  LED=0;          //工作指示燈開啟

  while(1)
  {
   TempData=get_temp();
   BitData=(TempData%1000/100)*10;  //十位溫度。
       BitData=(TempData%1000%100/10)+BitData;//個(gè)位溫度。
   for(i=0;i<ListTotal;i++)
      {
     CtrlElec=(CtrlList[i].AlarmTemp>=BitData)?0:1;
     CtrlListPort=CtrlList[i].CtrlPort;
     CtrlPort(CtrlListPort,CtrlElec); //符合條件輸出底電,不符合輸出高電
     delayb(100);
      }
   delayb(200);                  
  }
}

void CtrlPort(int PortCode,int Ctrl)//控制P1.0-P1-7
{
     switch(PortCode)
  {
      case 1: CtrlPort1=Ctrl;
       case 2: CtrlPort2=Ctrl;
      case 3: CtrlPort3=Ctrl;
       case 4: CtrlPort4=Ctrl;
      case 5: CtrlPort5=Ctrl;
      case 6: CtrlPort6=Ctrl;
      case 7: CtrlPort7=Ctrl;
      case 8: CtrlPort8=Ctrl;
     }
}

 

相關(guān)文章