外接晶振為12MHz時,51單片機相關周期的具體值為:
振蕩周期=1/12us;
狀態周期=1/6us;
機器周期=1us;
指令周期=1~4us;
51單片機定時/計數器的工作由兩個特殊功能寄存器控制。TMOD用于設置其工作方式;TCON用于控制其啟動和中斷申請。
1、工作方式寄存器TMOD
工作方式寄存器TMOD用于設置定時/計數器的工作方式,低四位用于T0,高四位用于T1。其格式如下:
GATE是門控位, GATE=0時,用于控制定時器的啟動是否受外部中斷源信號的影響。只要用軟件使TCON中的TR0或TR1為1,就可以啟動定時/計數器工作;GATA=1時,要用軟件使TR0或TR1為1,同時外部中斷引腳INT0/1也為高電平時,才能啟動定時/計數器工作。即此時定時器的啟動條件,加上了INT0/1引腳為高電平這一條件。
C/T :定時/計數模式選擇位。C/T =0為定時模式;C/T =1為計數模式。
M1M0:工作方式設置位。定時/計數器有四種工作方式。
控制寄存器TCON
TCON的高4位用于控
制定時/計數器的啟動和中斷申請。其格式如下:
TF1(TCON.7):T1溢出中斷請求標志位。T1計數溢出時由硬件自動置TF1為1。CPU響應中斷后TF1由硬件自動清0。T1工作時,CPU可隨時查詢TF1的狀態。所以,TF1可用作查詢測試的標志。TF1也可以用軟件置1或清0,同硬件置1或清0的效果一樣。
TR1(TCON.6):T1運行控制位。TR1置1時,T1開始工作;TR1置0時,T1停止工作。TR1由軟件置1或清0。所以,用軟件可控制定時/計數器的啟動與停止。
TF0(TCON.5):T0溢出中斷請求標志位,其功能與TF1類同。
TR0(TCON.4):T0運行控制位,其功能與TR1類同。
定時/計數器的工作方式
1、方式1
方式1的計數位數是16位,由TL0作為低8位,TH0
作為高8位,組成了16位加1計數器 。計數個數與計數初值的關系為:X=2(16次方)-N
2、方式2
為自動重裝初值的8位計數方式。 計數個數與計數初值的關系為:X=28-N
工作方式2特別適合于用作較精確的脈沖信號發生器。所以串口通信處用此方式。
計數器初值的計算
機器周期也就是CPU完成一個基本操作所需要的時間。
機器周期=1/單片機的時鐘頻率。
51單片機內部時鐘頻率是外部時鐘的12分頻。也就是說當外部晶振的頻率輸入到單片機里面的時候要進行12分頻。比如說你用的是12MHZ的晶振,那么單片機內部的時鐘頻率就是12/12MHZ,當你使用12MHZ的外部晶振的時候。機器周期=1/1M=1us。
而我們定時1ms的初值是多少呢,1ms/1us=1000。也就是要計數1000個數,初值=65535-1000+1(因為實際上計數器計數到66636才溢出)=64536=FC18H
串口通信
比特率是每秒鐘傳輸二進制代碼的位數,單位是:位/秒(bps)。如每秒鐘傳送240個字符,而每個字符格式包含10位(1個起始位、1個停止位、8個數據位),這時的比特率為:
10位×240個/秒 = 2400 bps
SCON 是一個特殊功能寄存器,用以設定串行口的工作方式、接收/發送控制以及設置狀態標志:
SM0和SM1為工作方式選擇位,可選擇四種工作方式:
SM2,多機通信控制位,主要用于方式2和方式3。當接收機的SM2=1時可以利用收到的RB8來控制是否激活RI(RB8=0時不激活RI,收到的信息丟棄;RB8=1時收到的數據進入SBUF,并激活RI,進而在中斷服務中將數據從SBUF讀走)。當SM2=0時,不論收到的RB8為0和1,均可以使收到的數據進入SBUF,并激活RI(即此時RB8不具有控制RI激活的功能)。通過控制SM2,可以實現多機通信。
在方式0時,SM2必須是0。在方式1時,如果SM2=1,則只有接收到有效停止位時,RI才置1。
REN,允許串行接收位。由軟件置REN=1,則啟動串行口接收數據;若軟件置REN=0,則禁止接收
TI,發送中斷標志位。在方式0時,當串行發送第8位數據結束時,或在其它方式,串行發送停止位的開始時,由內部硬件使TI置1,向CPU發中斷申請。在中斷服務程序中,必須用軟件將其清0,取消此中斷申請。
RI,接收中斷標志位。在方式0時,當串行接收第8位數據結束時,或在其它方式,串行接收停止位的中間時,由內部硬件使RI置1,向CPU發中斷申請。也必須在中斷服務程序中,用軟件將其清0,取消此中斷申請。
PCON中只有一位SMOD與串行口工作有關 :SMOD(PCON.7) 波特率倍增位。在串行口方式1、方式2、方式3時,波特率與SMOD有關,當SMOD=1時,波特率提高一倍。復位時,SMOD=0。
80C51串行口的工作方式
這里只介紹方式1:
方式1是10位數據的異步通信口。TXD為數據發送引腳,RXD為數據接收引腳,傳送一幀數據的格式如圖所示。其中1位起始位,8位數據位,1位停止位。
用軟件置REN為1時,接收器以所選擇波特率的16倍速率采樣RXD引腳電平,檢測到RXD引腳輸入電平發生負跳變時,則說明起始位有效,將其移入輸入移位寄存器,并開始接收這一幀信息的其余位。接收過程中,數據從輸入移位寄存器右邊移入,起始位移至輸入移位寄存器最左邊時,控制電路進行最后一次移位。當RI=0,且SM2=0(或接收到的停止位為1)時,將接收到的9位數據的前8位數據裝入接收SBUF,第9位(停止位)進入RB8,并置RI=1,向CPU請求中斷。
方式1的波特率 =(2SMOD/32)·(T1溢出率)
T1 溢出率 = fosc /{12×[256 -(TH1)]}
DS18B20溫度傳感器原理
DS18B20溫度轉換規則
DS18B20的核心功能是它可以直接讀出數字的溫度數值。溫度傳感器的精度為用戶可編程的9,10,11或12位,分別以0.5℃,0.25℃,0.125℃和0.0625℃增量遞增。在上電狀態下默認的精度為12位。
這是12位轉化后得到的12位數據,存儲在DS18B20的兩個8位的RAM中,高字節的前5位是符號位,如果測得的溫度大于0,這5位為‘0’,只要將測到的數值乘以0.0625即可得到實際溫度;如果溫度小于0,這5位為‘1’,測到的數值需要先減1再取反再乘以0.0625即可得到實際溫度。
(1).數據線拉到低電平“0”。
(2).延時480微妙(該時間的時間范圍可以從480到960微妙)。
(3).數據線拉到高電平“1”。
(4).延時等待80微妙。如果初始化成功則在15到60微妙時間內產生一個由DS18B20所返回的低電平“0”.根據該狀態可以來確定它的存在,但是應注意不能無限的進行等待,不然會使程序進入死循環,所以要進行超時判斷。
(5).若CPU讀到了數據線上的低電平“0”后,還要做延時,其延時的時間從發出的高電平算起(第(3)步的時間算起)最少要480微妙。
讀時序
(1).將數據線拉低“0”。
(2).延時1微妙。
(3).將數據線拉高“1”,釋放總線準備讀數據。
(4).延時10微妙。
(5).讀數據線的狀態得到1個狀態位,并進行數據處理。
(6).延時45微妙。
(7).重復1~7步驟,直到讀完一個字節。
寫時序
(1).數據線先置低電平“0”
(2).延時15微妙。
(3).按從低位到高位的順序發送數據(一次只發送一位)。
(4).延時60微妙。
(5).將數據線拉到高電平。
(6).重復1~5步驟,直到發送完整的字節。
(7).最后將數據線拉高。
#include<reg51.h>
#define LCD1602_DATAPINS P0typedef unsigned int uint;
typedef unsigned char uchar;sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
sbit DSPORT=P3^7;uchar CNCHAR[6] = "攝氏度";void LcdInit();
void LcdWriteData(uchar dat);
void LcdWriteCom(uchar com);
void LcdDisplay(int);
void UsartConfiguration();
uchar init();
void writebyte(uchar datas);
void change_temper();
void read_tempercom();
uint read_temper();
uchar readbyte();
void DelayMs(unsigned int x);void main(void)
{UsartConfiguration();LcdInit(); //初始化LCD1602LcdWriteCom(0x88); //寫地址 80表示初始地址LcdWriteData('C'); while(1){LcdDisplay(read_temper());
// Delay1ms(1000);//1s鐘刷一次}
}void DelayMs(unsigned int x) //0.14ms誤差 0us
{unsigned char i;while(x--){for (i = 0; i<13; i++){}}
}void LcdDisplay(int temp) //lcd顯示
{unsigned char i, datas[] = {0, 0, 0, 0, 0}; //定義數組float tp; if(temp< 0) //當溫度值為負數{LcdWriteCom(0x80); //寫地址 80表示初始地址SBUF='-';//將接收到的數據放入到發送寄存器while(!TI); //等待發送數據完成TI=0; //清除發送完成標志位LcdWriteData('-'); //顯示負//因為讀取的溫度是實際溫度的補碼,所以減1,再取反求出原碼temp=temp-1;temp=~temp;tp=temp;temp=tp*0.0625*100+0.5; //留兩個小數點就*100,+0.5是四舍五入,因為C語言浮點數轉換為整型的時候把小數點//后面的數自動去掉,不管是否大于0.5,而+0.5之后大于0.5的就是進1了,小于0.5的就//算由?.5,還是在小數點后面。}else{ LcdWriteCom(0x80); //寫地址 80表示初始地址LcdWriteData('+'); //顯示正SBUF='+';//將接收到的數據放入到發送寄存器while(!TI); //等待發送數據完成TI=0; //清除發送完成標志位tp=temp;//因為數據處理有小數點所以將溫度賦給一個浮點型變量//如果溫度是正的那么,那么正數的原碼就是補碼它本身temp=tp*0.0625*100+0.5; //留兩個小數點就*100,+0.5是四舍五入,因為C語言浮點數轉換為整型的時候把小數點//后面的數自動去掉,不管是否大于0.5,而+0.5之后大于0.5的就是進1了,小于0.5的就//算加上0.5,還是在小數點后面。}datas[0] = temp / 10000;datas[1] = temp % 10000 / 1000;datas[2] = temp % 1000 / 100;datas[3] = temp % 100 / 10;datas[4] = temp % 10;LcdWriteCom(0x82); //寫地址 80表示初始地址LcdWriteData('0'+datas[0]); //百位 SBUF = '0'+datas[0];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;LcdWriteCom(0x83); //寫地址 80表示初始地址LcdWriteData('0'+datas[1]); //十位SBUF = '0'+datas[1];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;LcdWriteCom(0x84); //寫地址 80表示初始地址LcdWriteData('0'+datas[2]); //個位 SBUF = '0'+datas[2];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;LcdWriteCom(0x85); //寫地址 80表示初始地址LcdWriteData('.'); //顯示 ‘.’SBUF = '.';//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;LcdWriteCom(0x86); //寫地址 80表示初始地址LcdWriteData('0'+datas[3]); //顯示小數點 SBUF = '0'+datas[3];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;LcdWriteCom(0x87); //寫地址 80表示初始地址LcdWriteData('0'+datas[4]); //顯示小數點 SBUF = '0'+datas[4];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;for(i=0; i<6; i++){SBUF = CNCHAR[i];//將接收到的數據放入到發送寄存器while (!TI); //等待發送數據完成TI = 0;}}void UsartConfiguration()
{SCON=0X50; //設置為工作方式1TMOD=0X20; //設置計數器工作方式2PCON=0X80; //波特率加倍TH1=0XF3; //計數器初始值設置,注意波特率是4800的TL1=0XF3;
// ES=1; //打開接收中斷
// EA=1; //打開總中斷TR1=1; //打開計數器
}/****************
溫度傳感器部分
****************/
uchar init()
{uchar i=0;DSPORT=0;i = 70; //將總線拉低480us~960uswhile(i--);//延時642usDSPORT=1; //然后拉高總線,如果DS18B20做出反應會將在15us~60us后總線拉低i=0;while(DSPORT) //等待DS18B20拉低總線{DelayMs(1);i++; if(i>5){return 0; //初始化失敗}}return 1;
}void writebyte(uchar datas)
{uchar i,j=1;for(i=0;i<8;i++){DSPORT=0; //每寫入一位數據之前先把總線拉低1us//j++; //此處延時1us,好像又影響不大DSPORT=datas&0x01;j=6;while(j--); //延時68us,持續時間最少60usDSPORT=1; //然后釋放總線,至少1us給總線恢復時間才能接著寫入第二個數值datas>>=1;}
}uchar readbyte()
{uchar dat=0,temp;uint i ,j;for(i=0;i<8;i++){DSPORT=0; //先將總線拉低1usj++;DSPORT=1; //然后釋放總線j++; //此處延時有變化,等待6usj++;temp=DSPORT; //讀取數據,從最低位開始讀取/*將byte左移一位,然后與上右移7位后的bi,注意移動之后移掉那位補0。*/dat=(dat>>1)|(temp<<7);j = 4; //讀取完之后等待48us再接著讀取下一個數while(j--);; //好像沒影響}return dat; //不是temp
}void change_temper()
{init();DelayMs(1); //無影響writebyte(0xcc); //跳過ROM操作命令 writebyte(0x44); //溫度轉換命令
}void read_tempercom()
{init();//DelayMs(1);writebyte(0xcc);writebyte(0xbe); //發送讀取溫度命令
}uint read_temper()
{uchar tml,tmh;uint t=0;change_temper(); //先寫入轉換命令read_tempercom(); //然后等待轉換完后發送讀取溫度命令tml=readbyte(); //讀取溫度值共16位,先讀低字節tmh=readbyte(); //再讀高字節t=tmh;t<<=8; //移8位t=tml|t;return t;
}
/********************LCD液晶部分
********************/void LcdWriteCom(uchar com) //寫入命令
{LCD1602_E = 0; //使能LCD1602_RS = 0; //選擇發送命令LCD1602_RW = 0; //選擇寫入LCD1602_DATAPINS = com; //放入命令DelayMs(1); //等待數據穩定LCD1602_E = 1; //寫入時序DelayMs(5); //保持時間LCD1602_E = 0;
}void LcdWriteData(uchar dat) //寫入數據
{LCD1602_E = 0; //使能清零LCD1602_RS = 1; //選擇輸入數據LCD1602_RW = 0; //選擇寫入LCD1602_DATAPINS = dat; //寫入數據DelayMs(1);LCD1602_E = 1; //寫入時序DelayMs(5); //保持時間LCD1602_E = 0;
}void LcdInit() //LCD初始化子程序
{LcdWriteCom(0x38); //開顯示LcdWriteCom(0x0c); //開顯示不顯示光標LcdWriteCom(0x06); //寫一個指針加1LcdWriteCom(0x01); //清屏LcdWriteCom(0x80); //設置數據指針起點
}
LCD顯示部分可以參考我一篇文章
51單片機DS1302時鐘LCD1602顯示
以上是我在學習過程中的一點總結,用的是普中的51單片機·。
總結
以上是生活随笔為你收集整理的51单片机LCD显示温度与串口接受温度的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。