18b20時序要求比較嚴(yán)格。
所遇問題:測出溫度值不準(zhǔn)確,不停的從255于實際溫度值跳變。
解決方法:在讀取溫度值時,關(guān)掉中斷。不過燈有一些閃。
該程序所測溫度的精度不能達(dá)到0.5,需要稍微改動。
不知道1602穩(wěn)定了沒,明天再試一次。
反應(yīng)好像還不夠靈敏。
#include "reg52.h" #include<intrins.h> #define uchar unsigned char //溫度傳感器與單片機(jī)接口為 P0.1 sbit DQ=P1^5; // sbit E=P0^7; sbit RW=P0^6; sbit RS=P0^5; sbit LED=P3^5; //sbit lcd_flag=P2^7; //定義是lcd否忙標(biāo)志 sbit P1_7 = P3 ^ 7; // 控制LED11 sbit P1_6 = P3 ^ 6; // 控制LED10 sbit P1_2 = P3 ^ 4; // uchar timer=0x00; //字節(jié)變量timer用于計時,每計數(shù)器記滿100次,就采樣一次溫度 uchar chars[]={"GYRGB.Liuzhou^_^"}; // 液晶顯示第一行 uchar chars2[]={"Welcome!"}; //液晶顯示第二行 uchar scale_0; //燈光灰度 /////////////////////////////// //各個功能子函數(shù) /////////////////////////////// void delay(void); //18b20溫度轉(zhuǎn)換后所需的延時 void delay1(void); //動態(tài)數(shù)碼管顯示所需的較短的延時 unsigned char Read18B20(void); //讀18b20子函數(shù) void Write18B20(uchar ch); //寫18b20子函數(shù) void Delay15(uchar n); //讀寫所需的最基本單位時間的延時 void Init18B20(void); //初始化18b20函數(shù) ///////////////////////////// //新增的液晶顯示程序 ///////////////////////////// void WriteLCDcom(uchar ch); void WriteLCDdat(uchar ch); void main(void) { uchar i,tl,th; //變量i用于循環(huán)計數(shù),tl和th用于獲得二進(jìn)制溫度值 uchar temp; //變量temp用于存放有效的溫度值 uchar bith,bitt,biti,bitz; //將要顯示在數(shù)碼管上的百位、十位、個位和十分位的碼值 unsigned int n; RCAP2H =0xFE; // 賦T2的預(yù)置值,溢出1次是1/2000秒鐘 RCAP2L =0x0C; ET2=1; // 打開定時器2中斷 EA=1; //總中斷允許 TR2=1; // 啟動定時器2 WriteLCDcom(0x01); //清屏 WriteLCDcom(0x38); WriteLCDcom(0x0c); WriteLCDcom(0x06); WriteLCDcom(0x80); for(i=0;i<16;i++) WriteLCDdat(chars); WriteLCDcom(0xc0); for(i=0;i<8;i++) WriteLCDdat(chars2); WriteLCDcom(0xce); WriteLCDdat(0xdf); WriteLCDdat(0x43); //////////////////////////////// //12M晶振,16位計數(shù)器 //溢出時間 65536us約等于65.5ms //溢出100次采樣溫度一次,采樣周期6.5秒 //////////////////////////////// while(1) { LED=1; Init18B20(); //復(fù)位18b20,每次復(fù)位18b20都是默認(rèn)的12位轉(zhuǎn)換精度 Write18B20(0xcc);//向18b20寫入跳過激光rom操作 _nop_(); //稍等片刻 Write18B20(0x44);//命令18b20開始溫度的測量以及模數(shù)轉(zhuǎn)換 delay(); //18b20轉(zhuǎn)換時間較長應(yīng)該等待稍長時間 Init18B20(); //每一次對18b20的讀寫都要先復(fù)位 TR2=0; //18b20對時序要求比較嚴(yán)格,因此在讀取溫度時要關(guān)中斷,否則測的溫度值會出錯 Write18B20(0xcc);//照例跳過ROM的操作 _nop_(); //等 Write18B20(0xbe); //讀18b20的溫度數(shù)據(jù),可以連續(xù)讀5個字節(jié) _nop_(); //等 tl=Read18B20(); //讀第一個字節(jié),里面是12位有效數(shù)字的低八位 th=Read18B20(); //讀第二個字節(jié),是擴(kuò)展的符號位和有效值的高四位 TR2=1; Init18B20(); temp=(th<<4)+(tl>>4); //實際上,temp=(th<<4)+(tl>>4)這個式子得到的是只包含了數(shù)整值的溫度值 //th向左移4位,拋棄了擴(kuò)展的符號位;tl向右移4位,拋棄了4位小數(shù)位 bith=temp/100; //得到百位數(shù)字 bitt=(temp%100)/10; //得到十位數(shù)字 biti=temp%10; //得到個位數(shù)字,由于要顯示小數(shù)點(diǎn),所以要減去一個0x80 bitz=0; //只是作為好看的位數(shù),實際我們在計算temp的時候已經(jīng)將小數(shù)位舍去了 /*****************************************/ // 液晶的顯示 /*****************************************/ WriteLCDcom(0xc8); WriteLCDdat(bith+0x30); WriteLCDdat(bitt+0x30); WriteLCDdat(biti+0x30); WriteLCDdat(0x2e); WriteLCDdat(bitz+0x30); LED=1; for(n=0;n<50000;n++); // 每過一會兒就自動加一個檔次的亮度 if(temp==26) scale_0=0; else if(temp==27) scale_0=1; else if(temp==28) scale_0=2; else if(temp==29) scale_0=3; else if(temp==30) scale_0=4; else if(temp==31) scale_0=5; else if(temp==32) scale_0=6; else if(temp==33) scale_0=7; else if(temp==34) scale_0=8; else scale_0=9; } } /******************************************************************************************** * 函數(shù)名稱:Timer2_Server() * 功 能:定時器2溢出中斷服務(wù)程序。1/2000 秒中斷1次。 * 入口參數(shù):無 * 出口參數(shù):無 *********************************************************************************************/ void Timer2_Server(void) interrupt 5 { static uchar tt; // tt用來保存當(dāng)前時間在1秒中的比例位置 TF2=0; tt++; if(tt==10) // 每1/200秒整開始輸出低電平 { tt=0; if(scale_0!=0) // 加入該句的目的是避免滅燈時發(fā)生閃爍 { P1_7=0; P1_6=0; P1_2=0; } } if(scale_0==tt) // 按照當(dāng)前占空比切換輸出高電平 { P1_7=1; P1_6=1; P1_2=1; } } void delay(void) //長延時,18b20在執(zhí)行溫度轉(zhuǎn)換操作的時候需要耗費(fèi)較長時間 { //在這段時間里18b20需要測溫,做模數(shù)轉(zhuǎn)換,并將轉(zhuǎn)換的二進(jìn)制數(shù)值存儲到自帶的臨時寄存區(qū)里去 uchar i,j; for(i=0;i<200;i++) //具體需要多長時間芯片手冊上有介紹,這個要繼續(xù)深入了解 /*************************??******/ for(j=0;j<100;j++) ; } unsigned char Read18B20(void) //最基本的讀18b20的函數(shù),并向主函數(shù)返回讀到的那個字節(jié) { unsigned char ch; //相當(dāng)于串行緩存器 unsigned char q ; //循環(huán)計數(shù)器 for(q=0;q<8;q++) //循環(huán)8次,讀出串行的8位數(shù)據(jù),先讀到的是數(shù)據(jù)的最低位 //因此要從ch的最高位存起,然后依次將ch向右移,就像火車進(jìn)站那樣 { // 7 6 5 4 3 2 1 0 (ch) // MSB-6-5-4-3-2-1-LSB ----> (數(shù)據(jù)) ch=ch>>1; //先移位再賦值,出現(xiàn)賦值8次但是只移位7次的效果 DQ=0; //單線總線的要求,要讀器件,就要產(chǎn)生一個上升沿,然后釋放總線,現(xiàn)在要回到低電平 _nop_(); // 稍微停頓,讓器件探測到電平已經(jīng)變低了 DQ=1; //拉高總線產(chǎn)生上升沿,同時,在某種意義上,對單片機(jī)的端口寫1,也就是讓端口處于待讀的狀態(tài),一舉兩得 _nop_();_nop_(); //4個空操作函數(shù),等待,給18b20響充分的時間響應(yīng),具體需要多久的時間要看器件手冊 _nop_();_nop_(); // 讀響應(yīng)是多久,需要繼續(xù)深入了解 /********************??*****/ //而且這個-nop-()函數(shù)到底會延時多長時間,要深入了解 /****************??***/ if(DQ==1) //開始讀端口,如果為1,則將ch最高位寫為1 { ch=ch|0x80;} //ch|0x80就是 XXXX XXXX | 1000 0000 每一位相或的結(jié)果就是得到 1XXX XXXX,火車開始進(jìn)站了 else //如果讀到的數(shù)是0,那么就把ch的最高位置為0 { ch=ch&0x7f;} // ch & 0x7f 就是 XXXX XXXX & 0111 1111 ,結(jié)果自然就是 0XXX XXXX,數(shù)字最低位就進(jìn)站好了 Delay15(3); //延時少少,延時多長要繼續(xù)深入了解 /****************??*****/ DQ=1; //讀完之后再次置端口為1,好為下一次讀做準(zhǔn)備,其實很關(guān)鍵的一步 } return (ch); //將讀到的數(shù)據(jù)返回給主函數(shù),這就我們想要的結(jié)果了 } void Write18B20(uchar ch) //寫18b20的函數(shù) { uchar i; //一個循環(huán)計數(shù)變量 for(i=0;i<8;i++) //循環(huán)8次,每次一位二進(jìn)制 { DQ=0; //從讀和寫函數(shù)的比較可以得知,產(chǎn)生讀/寫的條件都是要先產(chǎn)生低電平,只是低電平的維持時間長短不一 Delay15(1); //寫操作需要的低電平持續(xù)時間比讀操作要長的多 DQ=ch &0x01; //向總線上寫ch的最低位,跟讀的操作類似,只是這時“車站”是18b20,而“列車”是ch Delay15(3); // 7 6 5 4 3 2 1 0 (18b20) // MSB-6-5-4-3-2-1-LSB ----> (ch) // ch & 0x01 就是 xxxx xxxx & 0000 0001,結(jié)果是0000 000x //為什么可以用一個位變量 DQ= 0000 000x,這個問題需要繼續(xù)深入了解 /*************??****/ DQ=1; //寫完一位后,將總線抬高,為下一次拉低做準(zhǔn)備 ch=ch >>1; //將ch第二低位推到最低位,等待發(fā)射出去 _nop_(); //等待一段時間 } } void Init18B20(void) //初始化18b20 { DQ=0; //初始化操作同樣是由低電平開始,但是這個初始化低電平要持續(xù)得最久 Delay15(30);//至少延時480us,到底是多少,要找到-nop-()函數(shù)源碼,反匯編之后才知道/*******************??****/ DQ=1; //抬高總線,一舉兩得,可以準(zhǔn)備接受18b20的存在低電平 Delay15(10);//至少延時100us } void Delay15(uchar n) //貌似滿精確的延時程序 { //具體要在 intrins.h頭文件中找到它,那么首先就要找到intrins.h /****************??*****/ do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); n--; }while(n); } /* bit LCDbusy() { bit flag; RS=0; //寄存器為LOW RW=1; //是否讀寫為high E=1; //使能端為high if(lcd_flag==1) flag=1; E=0; return flag; } */ void WriteLCDcom(uchar ch) { // while(LCDbusy()); RS=0; RW=0; E=0; P2=ch; delay(); E=1; E=0; } void WriteLCDdat(uchar ch) { // while(LCDbusy()); RS=1; RW=0; E=0; P2=ch; delay(); E=1; E=0; } 不明白為什么加了檢測1602是否忙的程序進(jìn)去,為什么P2口就沒有數(shù)據(jù)了呢?液晶就不能顯示了呢?