18b20時(shí)序要求比較嚴(yán)格。
所遇問題:測出溫度值不準(zhǔn)確,不停的從255于實(shí)際溫度值跳變。
解決方法:在讀取溫度值時(shí),關(guān)掉中斷。不過燈有一些閃。
該程序所測溫度的精度不能達(dá)到0.5,需要稍微改動(dòng)。
不知道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用于計(jì)時(shí),每計(jì)數(shù)器記滿100次,就采樣一次溫度 uchar chars[]={"GYRGB.Liuzhou^_^"}; // 液晶顯示第一行 uchar chars2[]={"Welcome!"}; //液晶顯示第二行 uchar scale_0; //燈光灰度 /////////////////////////////// //各個(gè)功能子函數(shù) /////////////////////////////// void delay(void); //18b20溫度轉(zhuǎn)換后所需的延時(shí) void delay1(void); //動(dòng)態(tài)數(shù)碼管顯示所需的較短的延時(shí) unsigned char Read18B20(void); //讀18b20子函數(shù) void Write18B20(uchar ch); //寫18b20子函數(shù) void Delay15(uchar n); //讀寫所需的最基本單位時(shí)間的延時(shí) void Init18B20(void); //初始化18b20函數(shù) ///////////////////////////// //新增的液晶顯示程序 ///////////////////////////// void WriteLCDcom(uchar ch); void WriteLCDdat(uchar ch); void main(void) { uchar i,tl,th; //變量i用于循環(huán)計(jì)數(shù),tl和th用于獲得二進(jìn)制溫度值 uchar temp; //變量temp用于存放有效的溫度值 uchar bith,bitt,biti,bitz; //將要顯示在數(shù)碼管上的百位、十位、個(gè)位和十分位的碼值 unsigned int n; RCAP2H =0xFE; // 賦T2的預(yù)置值,溢出1次是1/2000秒鐘 RCAP2L =0x0C; ET2=1; // 打開定時(shí)器2中斷 EA=1; //總中斷允許 TR2=1; // 啟動(dòng)定時(shí)器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位計(jì)數(shù)器 //溢出時(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)換時(shí)間較長應(yīng)該等待稍長時(shí)間 Init18B20(); //每一次對(duì)18b20的讀寫都要先復(fù)位 TR2=0; //18b20對(duì)時(shí)序要求比較嚴(yán)格,因此在讀取溫度時(shí)要關(guān)中斷,否則測的溫度值會(huì)出錯(cuò) Write18B20(0xcc);//照例跳過ROM的操作 _nop_(); //等 Write18B20(0xbe); //讀18b20的溫度數(shù)據(jù),可以連續(xù)讀5個(gè)字節(jié) _nop_(); //等 tl=Read18B20(); //讀第一個(gè)字節(jié),里面是12位有效數(shù)字的低八位 th=Read18B20(); //讀第二個(gè)字節(jié),是擴(kuò)展的符號(hào)位和有效值的高四位 TR2=1; Init18B20(); temp=(th<<4)+(tl>>4); //實(shí)際上,temp=(th<<4)+(tl>>4)這個(gè)式子得到的是只包含了數(shù)整值的溫度值 //th向左移4位,拋棄了擴(kuò)展的符號(hào)位;tl向右移4位,拋棄了4位小數(shù)位 bith=temp/100; //得到百位數(shù)字 bitt=(temp%100)/10; //得到十位數(shù)字 biti=temp%10; //得到個(gè)位數(shù)字,由于要顯示小數(shù)點(diǎn),所以要減去一個(gè)0x80 bitz=0; //只是作為好看的位數(shù),實(shí)際我們?cè)谟?jì)算temp的時(shí)候已經(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++); // 每過一會(huì)兒就自動(dòng)加一個(gè)檔次的亮度 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() * 功 能:定時(shí)器2溢出中斷服務(wù)程序。1/2000 秒中斷1次。 * 入口參數(shù):無 * 出口參數(shù):無 *********************************************************************************************/ void Timer2_Server(void) interrupt 5 { static uchar tt; // tt用來保存當(dāng)前時(shí)間在1秒中的比例位置 TF2=0; tt++; if(tt==10) // 每1/200秒整開始輸出低電平 { tt=0; if(scale_0!=0) // 加入該句的目的是避免滅燈時(shí)發(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) //長延時(shí),18b20在執(zhí)行溫度轉(zhuǎn)換操作的時(shí)候需要耗費(fèi)較長時(shí)間 { //在這段時(shí)間里18b20需要測溫,做模數(shù)轉(zhuǎn)換,并將轉(zhuǎn)換的二進(jìn)制數(shù)值存儲(chǔ)到自帶的臨時(shí)寄存區(qū)里去 uchar i,j; for(i=0;i<200;i++) //具體需要多長時(shí)間芯片手冊(cè)上有介紹,這個(gè)要繼續(xù)深入了解 /*************************??******/ for(j=0;j<100;j++) ; } unsigned char Read18B20(void) //最基本的讀18b20的函數(shù),并向主函數(shù)返回讀到的那個(gè)字節(jié) { unsigned char ch; //相當(dāng)于串行緩存器 unsigned char q ; //循環(huán)計(jì)數(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)生一個(gè)上升沿,然后釋放總線,現(xiàn)在要回到低電平 _nop_(); // 稍微停頓,讓器件探測到電平已經(jīng)變低了 DQ=1; //拉高總線產(chǎn)生上升沿,同時(shí),在某種意義上,對(duì)單片機(jī)的端口寫1,也就是讓端口處于待讀的狀態(tài),一舉兩得 _nop_();_nop_(); //4個(gè)空操作函數(shù),等待,給18b20響充分的時(shí)間響應(yīng),具體需要多久的時(shí)間要看器件手冊(cè) _nop_();_nop_(); // 讀響應(yīng)是多久,需要繼續(xù)深入了解 /********************??*****/ //而且這個(gè)-nop-()函數(shù)到底會(huì)延時(shí)多長時(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); //延時(shí)少少,延時(shí)多長要繼續(xù)深入了解 /****************??*****/ DQ=1; //讀完之后再次置端口為1,好為下一次讀做準(zhǔn)備,其實(shí)很關(guān)鍵的一步 } return (ch); //將讀到的數(shù)據(jù)返回給主函數(shù),這就我們想要的結(jié)果了 } void Write18B20(uchar ch) //寫18b20的函數(shù) { uchar i; //一個(gè)循環(huán)計(jì)數(shù)變量 for(i=0;i<8;i++) //循環(huán)8次,每次一位二進(jìn)制 { DQ=0; //從讀和寫函數(shù)的比較可以得知,產(chǎn)生讀/寫的條件都是要先產(chǎn)生低電平,只是低電平的維持時(shí)間長短不一 Delay15(1); //寫操作需要的低電平持續(xù)時(shí)間比讀操作要長的多 DQ=ch &0x01; //向總線上寫ch的最低位,跟讀的操作類似,只是這時(shí)“車站”是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 //為什么可以用一個(gè)位變量 DQ= 0000 000x,這個(gè)問題需要繼續(xù)深入了解 /*************??****/ DQ=1; //寫完一位后,將總線抬高,為下一次拉低做準(zhǔn)備 ch=ch >>1; //將ch第二低位推到最低位,等待發(fā)射出去 _nop_(); //等待一段時(shí)間 } } void Init18B20(void) //初始化18b20 { DQ=0; //初始化操作同樣是由低電平開始,但是這個(gè)初始化低電平要持續(xù)得最久 Delay15(30);//至少延時(shí)480us,到底是多少,要找到-nop-()函數(shù)源碼,反匯編之后才知道/*******************??****/ DQ=1; //抬高總線,一舉兩得,可以準(zhǔn)備接受18b20的存在低電平 Delay15(10);//至少延時(shí)100us } void Delay15(uchar n) //貌似滿精確的延時(shí)程序 { //具體要在 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ù)了呢?液晶就不能顯示了呢?