本次設計的主要思路是利用51系列單片機,數字溫度傳感器DS18B20和1602LCD液晶顯示,構成實現溫度檢測與顯示的單片機控制系統,即數字溫度計。通過對單片機編寫相應的程序,達到能夠實時檢測周圍溫度的目的。 通過對本課題的設計能夠熟悉數字溫度計的工作原理及過程,了解各功能器件(單片機、DS18B20、LCD)的基本原理與應用,掌握各部分電路的硬件連線與程序編寫,最終完成對數字溫度計的總體設計。其具體的要求如下: 1、根據設計要求,選用AT89C51單片機為核心器件; 2、溫度檢測器件采用DS18B20數字式溫度傳感器,利用單總線式連接方式與單片機的串行接口P3.3引腳相連; 3、顯示電路采用1602LCD液晶顯示溫度值,此類液晶模塊不僅可以顯示數字、字符,還可以顯示各種圖形符號以及少量自定義符號,人機界面友好,使用操作也更加靈活、方便,使其日益成為各種儀器儀表等設備的首選。 系統的開發過程 本設計主要介紹了用單片機和數字溫度傳感器DS18B20相結合的方法來實現溫度的采集,以單片機AT89C51芯片為核心,溫度傳感器DS18B20和1602LCD液晶顯示,構成了一個多功能單片機數字溫度計。其主要研究內容包括兩方面,一是對系統硬件部分的設計,包括溫度采集電路和顯示電路;二是對系統軟件部分的設計,應用C語言實現溫度的采集與顯示。通過利用數字溫度傳感器DS18B20進行設計,能夠滿足實時檢測溫度的要求,同時通過1602LCD的顯示功能,可以實現不間斷的溫度顯示。其總體設計框圖一如下:
圖一:總體設計框圖 第一節AT89C51簡介 AT89C51是美國ATMEL公司生產的低功耗,高性能CMOS8位單片機,片內含4kbytes的可編程的Flash只讀程序存儲器,兼容標準8051指令系統及引腳,并集成了 Flash 程序存儲器,既可在線編程(ISP),也可用傳統方法進行編程,因此,低價位AT89C51單片機可應用于許多高性價比的場合,可靈活應用于各種控制領域,對于簡單的測溫系統已經足夠。單片機AT89C51具有低電壓供電和體積小等特點,四個端口只需要兩個口就能滿足電路系統的設計需要,很適合便攜手持式產品的設計使用系統可用二節電池供電。芯片AT89C51的引腳排列如圖二所示:
圖二:AT89C51單片機引腳圖 第二節晶振電路的設計 單片機晶振電路的設計如圖三所示。XTAL1(X1)為反向振蕩放大器的輸入及內部時鐘工作電路的輸入。按照理論上AT89C51使用的是12MHz的晶振,但實測使用11.0592MHz。所以設計者通常用的是11.0592MHz。
圖三:單片機晶振電路 第三節溫度采集電路的設計 DALLAS 最新單線數字溫度傳感器DS18B20是一種新型的“一線器件”,其體積更小、更適用于多種場合、且適用電壓更寬、更經濟。DALLAS 半導體公司的數字化溫度傳感器DS18B20是世界上第一片支持“一線總線”接口的溫度傳感器。溫度測量范圍為-55~+125 攝氏度,可編程為9~12 位轉換精度,測溫分辨率可達0.0625攝氏度,分辨率設定參數以及用戶設定的報警溫度存儲在EEPROM 中,掉電后依然保存。被測溫度用符號擴展的16位數字量方式串行輸出;其工作電源既可以在遠端引入,也可以采用寄生電源方式產生;多個DS18B20可以并聯到3 根或2 根線上,CPU只需一根端口線就能與諸多DS18B20 通信,占用微處理器的端口較少,可節省大量的引線和邏輯電路。因此用它來組成一個測溫系統,具有線路簡單,在一根通信線,可以掛很多這樣的數字溫度計,十分方便。本設計的溫度采集電路如圖四所示。
圖四:溫度采集電路圖 第五節溫度顯示電路的設計 顯示器常用作單片機最簡單的輸出設備,用以顯示單片機的運行結果和運行狀態等。常用的顯示器主要有LED和LCD,它們都具有耗電少、成本低、線路簡單、壽命長等優點,廣泛應用于單片機顯示數字量的場合。設計中采用LCD顯示器。液晶顯示器(LCD)具有功耗低、體積小、質量輕、功耗小的特點。點陣字符型液晶顯示器把LCD控制器、點陣驅動器、字符存儲器集成在一塊印刷電路板上,構成便于應用的液晶模塊。此類液晶模塊不僅可以顯示數字、字符,還可以顯示各種圖形符號以及少量自定義符號,并且可以實現屏幕的上下左右滾動、文字的閃爍等功能,人機界面友好,使用操作也更加靈活、方便,使其日益成為各種儀器儀表等設備的首選。圖五為本設計的顯示電路圖。
圖五:顯示電路圖 第六節應用軟件介紹 本設計主要用Proteus仿真軟件和Keil編譯軟件。 本設計主要用Proteus仿真軟件和Keil編譯軟件 4.1.1 Proteus的介紹 Proteus軟件是英國Labcenter electronics公司出版的EDA工具軟件。它不僅具有其它EDA工具軟件的仿真功能,還能仿真單片機及外圍器件,它是目前最好的仿真單片機及外圍器件的工具,雖然目前國內推廣剛起步,但已受到單片機愛好者、從事單片機教學教師、致力于單片機開發應用的科技工作者的青睞。Proteus是世界上著名的EDA工具(仿真軟件),從原理布圖、代碼調試到單片機與外圍電路協同仿真,一鍵切換發到PCB設計,真正實現了從 概念到產品的完整設計。是目前世界上唯一將電路仿真軟件、PCB設計軟件和虛擬模型仿真軟件三合一的設計平臺,其處理器模型支持8051、HC11、PIC10/12/16/18/24/30/DsPIC33、AVR、ARM、8086HE MSP430等,2010年即將增加Cortex和DSP系列處理器,并持續增加其他系列處理器模型。在編譯方面,它也支持IAR、Keil和MPLAB等多種編譯器。 Keil C51是美國Keil Software公司出品的51系列兼容單片機C語言軟件開發系統,與匯編相比,C語言在功能上、結構性、可讀性、可維護性上有明顯的優勢,因而易學易用。Keil提供了包括C編譯器、宏匯編、連接器、庫管理和一個功能強大的仿真調試器等在內的完整開發方案,通過一個集成開發環境(uVision)將這些部分組合在一起。運行Keil軟件需要WIN98、NT、WIN2000、WINXP等操作系統。如果你使用C語言編程,那么Keil幾乎就是你的不二之選,即使不使用C語言而僅用匯編語言編程,其方便易用的集成環境、強大的軟件仿真調試工具也會令你事半功倍。 系統測試情況 進入測試,開關不閉合,系統默認顯示1602LCD顯示當前采集的溫度,當溫度變化時,系統實時采集DS18B20的溫度并顯示出來,當采集的溫度超過系統所設置的上限或者下限的時候,系統自動報警,開關閉合,顯示報警溫度的上限值和下限值。綜合仿真圖如圖六所示:
系統的優點與不足 優點:軟件可以實時檢測溫度值,并顯示,當溫度超出預設范圍時及時報警,并且還可以檢測并顯示零下溫度。 缺點:只是做到了仿真程序,沒有具體的硬件實現,系統運行時預設溫度不能改動。
附錄代碼
- #include<reg51.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- sbit DQ=P3^3;//DS18B20數據線
- sbit BEEP=P3^7;//報警器
- sbit LCD_RS=P2^0;
- sbit LCD_RW=P2^1;
- sbit LCD_EN=P2^2;
- sbit K1=P1^7;
- uchar code Temp_Disp_Title[]={" Current Temp : "};
- uchar Current_Temp_Display_Buffer[]={"TEMP: "};
- uchar code Alarm_Temp[]={"ALARM TEMP Hi Lo"};
- uchar Alarm_HI_LO_STR[]={"Hi: Lo: "};
- uchar code df_Table[]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};//溫度小數位對照表
- char Alarm_Temp_HL[2]={100,0};
- uchar CurrentT=0;//當前讀取的溫度整數部分
- uchar Temp_Value[]={0x00,0x00};//從DS18B20讀取的溫度值
- uchar Display_Digit[]={0,0,0,0};//待顯示的各溫度數位
- bit HI_Alarm=0,LO_Alarm=0;//高溫低溫報警標志
- bit DS18B20_IS_OK=1;//傳感器正常標志
- uint Time0_Count=0;//定時器延時累加
- //延時
- void DelayMS(uint x){
- uchar i;
- while(x--)for(i=0;i<120;i++);
- }
- //讀LCD狀態
- uchar Read_LCD_State(){
- uchar state;
- LCD_RS=0;LCD_RW=1;LCD_EN=1;DelayMS(1);state=P0;LCD_EN=0;DelayMS(1);
- return state;
- }
- //忙等待
- void LCD_Busy_Wait(){
- while((Read_LCD_State()&0x80)==0x80);
- DelayMS(5);
- }
- //寫LCD指令
- void Write_LCD_Command(uchar cmd){
- LCD_Busy_Wait();
- LCD_RS=0;LCD_RW=0;LCD_EN=0;P0=cmd;LCD_EN=1;DelayMS(1);LCD_EN=0;
- }
- //向LCD寫數據
- void Write_LCD_Data(uchar dat){
- LCD_Busy_Wait();
- LCD_RS=1;LCD_RW=0;LCD_EN=0;P0=dat;LCD_EN=1;DelayMS(1);LCD_EN=0;
- }
- //延時
- void DelayXus(int x){
- uchar i;
- while(x--)for(i=0;i<200;i++);
- }
- //延時
- void Delay(uint num){
- while(--num);
- }
- //初始化DS18B20
- uchar Init_DS18B20(){
- uchar status;
- DQ=1;Delay(8);
- DQ=0;Delay(90);
- DQ=1;Delay(8);
- status=DQ;
- Delay(100);
- DQ=1;
- return status;//初始化成功返回0
- }
- //讀一字節
- uchar ReadOneByte(){
- uchar i,dat=0;
- DQ=1;_nop_();
- for(i=0;i<8;i++){
- DQ=0;dat>>=1;DQ=1;_nop_();_nop_();
- if(DQ)dat|=0x80;Delay(30);DQ=1;
- }
- return dat;
- }
- //寫一個字節
- void WriteOneByte(uchar dat){
- uchar i;
- for(i=0;i<8;i++){
- DQ=0;DQ=dat&0x01;Delay(5);DQ=1;dat>>=1;
- }
- }
- //讀取溫度值
- void Read_Temperature(){
- if(Init_DS18B20()==1)//DS18B20故障
- DS18B20_IS_OK=0;
- else{
- WriteOneByte(0xcc);//跳過序列號
- WriteOneByte(0x44);//啟動溫度轉換
- Init_DS18B20();
- WriteOneByte(0xcc);//跳過序列號
- WriteOneByte(0xbe);//讀取溫度寄存器
- Temp_Value[0]=ReadOneByte();//讀取低8位
- Temp_Value[1]=ReadOneByte();//溫度高8位
- Alarm_Temp_HL[0]=ReadOneByte();//報警TH
- Alarm_Temp_HL[1]=ReadOneByte();//報警TL
- DS18B20_IS_OK=1;
- }
- }
- //設置DS18B20溫度報警值
- void Set_Alarm_Temp_Value(){
- Init_DS18B20();
- WriteOneByte(0xcc);//跳過序列號
- WriteOneByte(0x4e);//將設定的溫度報警值寫入DS18B20
- WriteOneByte(Alarm_Temp_HL[0]);//寫TH
- WriteOneByte(Alarm_Temp_HL[1]);//寫TL
- WriteOneByte(0x7f);//12位精度
- Init_DS18B20();
- WriteOneByte(0xcc);//跳過序列號
- WriteOneByte(0x48);//將設定的溫度報警值寫入DS18B20
- }
- //設置液晶顯示位置
- void Set_LCD_POS(uchar p){
- Write_LCD_Command(p|0x80);
- }
- //在LCD上顯示當前溫度
- void Display_Temperature(){
- uchar i;
- uchar t=150;//延時值
- uchar ng=0;//負數標志
- char Signed_Current_Temp;//如果為負數則取反加1,并設置負數標識
- if((Temp_Value[1]&0xf8)==0xf8){
- Temp_Value[1]=~Temp_Value[1];
- Temp_Value[0]=~Temp_Value[0]+1;
- if(Temp_Value[0]==0x00)Temp_Value[1]++;
- ng=1;//設負數標識
- }
- //查表得到溫度小數部分
- Display_Digit[0]=df_Table[Temp_Value[0]&0x0f];
- //獲取溫度整數部分(無符號)
- CurrentT=((Temp_Value[0]&0xf0)>>4)|((Temp_Value[1]&0x07)<<4);
- //有符號的當前溫度值,注意此處定義為char,其值可為-128~+127
- Signed_Current_Temp=ng?-CurrentT:CurrentT;
- //高低溫報警標志設置(與定義為char類型的Alarm_Temp_HL比較,這樣可區分正負比較)
- HI_Alarm=Signed_Current_Temp>=Alarm_Temp_HL[0]?1:0;
- LO_Alarm=Signed_Current_Temp<=Alarm_Temp_HL[1]?1:0;
- //將整數部分分解為三位待顯示數字
- Display_Digit[3]=CurrentT/100;
- Display_Digit[2]=CurrentT%100/10;
- Display_Digit[1]=CurrentT%10;
- //刷新LCD顯示緩沖
- Current_Temp_Display_Buffer[11]=Display_Digit[0]+'0';
- Current_Temp_Display_Buffer[10]='.';
- Current_Temp_Display_Buffer[9]=Display_Digit[1]+'0';
- Current_Temp_Display_Buffer[8]=Display_Digit[2]+'0';
- Current_Temp_Display_Buffer[7]=Display_Digit[3]+'0';
- //高位為0時不顯示
- if(Display_Digit[3]==0)Current_Temp_Display_Buffer[7]=' ';
- //高位為0且次高位為0時,次高位不顯示
- if(Display_Digit[2]==0&&Display_Digit[3]==0)
- Current_Temp_Display_Buffer[8]=' ';
- //負數符號顯示在恰當位置
- if(ng)
- {
- if(Current_Temp_Display_Buffer[8]==' ')
- Current_Temp_Display_Buffer[8]='-';
- else if(Current_Temp_Display_Buffer[7]==' ')
- Current_Temp_Display_Buffer[7]='-';
- else Current_Temp_Display_Buffer[6]='-';
- }
- //在第一行顯示標題
- Set_LCD_POS(0x00);
- for(i=0;i<16;i++)Write_LCD_Data(Temp_Disp_Title[ i]);
- //在第二行顯示當前溫度
- Set_LCD_POS(0x40);
- for(i=0;i<16;i++)Write_LCD_Data(Current_Temp_Display_Buffer[ i]);
- //顯示溫度符號
- Set_LCD_POS(0x4d);Write_LCD_Data(0x00);
- Set_LCD_POS(0x4e);Write_LCD_Data('C');
- }
- //定時器中斷,控制警報聲音
- void T0_INT()interrupt 1{
- TH0=-1000/256;
- TL0=-1000%256;
- BEEP=!BEEP;
- if(++Time0_Count==400){
- Time0_Count=0;
- TR0=0;
- }
- }
- //顯示報警溫度
- void Disp_Alarm_Temperature(){
- uchar i,ng;
- //顯示Alarm_Temp_HL數組中的報警溫度值
- //由于Alarm_Temp_HL類型為char,故可以直接進行正負比較
- //高溫報警值
- ng=0;
- if(Alarm_Temp_HL[0]<0)//如果為負數則取反加1
- {
- Alarm_Temp_HL[0]=~Alarm_Temp_HL[0]+1;
- ng=1;
- }
- //分解高溫各數位到待顯示串中
- Alarm_HI_LO_STR[4]=Alarm_Temp_HL[0]/100+'0';
- Alarm_HI_LO_STR[5]=Alarm_Temp_HL[0]/10%10+'0';
- Alarm_HI_LO_STR[6]=Alarm_Temp_HL[0]%10+'0';
- //屏蔽高位不顯示
- if(Alarm_HI_LO_STR[4]=='0')Alarm_HI_LO_STR[4]=' ';
- if(Alarm_HI_LO_STR[4]==' '&&Alarm_HI_LO_STR[5]=='0')
- Alarm_HI_LO_STR[5]=' ';
- //"-"符號顯示
- if(ng){
- if(Alarm_HI_LO_STR[5]==' ')Alarm_HI_LO_STR[5]='-';
- else if(Alarm_HI_LO_STR[4]==' ')Alarm_HI_LO_STR[4]='-';
- else Alarm_HI_LO_STR[3]='-';
- }
- //低溫報警值
- ng=0;
- if(Alarm_Temp_HL[1]<0)//如果為負數則取反加1
- {
- Alarm_Temp_HL[1]=~Alarm_Temp_HL[1]+1;
- ng=1;
- }
- //分解高溫各數位到待顯示串中
- Alarm_HI_LO_STR[12]=Alarm_Temp_HL[1]/100+'0';
- Alarm_HI_LO_STR[13]=Alarm_Temp_HL[1]/10%10+'0';
- Alarm_HI_LO_STR[14]=Alarm_Temp_HL[1]%10+'0';
- //屏蔽高位不顯示
- if(Alarm_HI_LO_STR[12]=='0')Alarm_HI_LO_STR[12]=' ';
- if(Alarm_HI_LO_STR[12]==' '&&Alarm_HI_LO_STR[13]=='0')
- Alarm_HI_LO_STR[13]=' ';
- //"-"符號顯示
- if(ng){
- if(Alarm_HI_LO_STR[13]==' ')Alarm_HI_LO_STR[13]='-';
- else if(Alarm_HI_LO_STR[12]==' ')Alarm_HI_LO_STR[12]='-';
- else Alarm_HI_LO_STR[11]='-';
- }
- //顯示高低溫報警溫度值
- Set_LCD_POS(0x00);//顯示標題
- for(i=0;i<16;i++)Write_LCD_Data(Alarm_Temp[ i]);
- Set_LCD_POS(0x40);//顯示高低溫
- for(i=0;i<16;i++)Write_LCD_Data(Alarm_HI_LO_STR[ i]);
- }
- void LCD_Initialise(){
- Write_LCD_Command(0x38);DelayXus(5);
- Write_LCD_Command(0x01);DelayXus(5);
- Write_LCD_Command(0x06);DelayXus(5);
- Write_LCD_Command(0x0c);DelayXus(5);
- }
- void main(){
- LCD_Initialise();
- IE=0x82;
- TMOD=0x01;
- TH0=-1000/256;
- TL0=-1000%256;
- TR0=0;
- K1=1;
- Set_Alarm_Temp_Value();
- Read_Temperature();
- Delay(50000);
- Delay(50000);
- while(1){
- if(K1==0)
- {
- //顯示報警溫度上下限
- Read_Temperature();
- Disp_Alarm_Temperature();
- DelayXus(100);
- }else
- {
- //正常顯示當前溫度,越界時報警
- Read_Temperature();
- if(DS18B20_IS_OK){
- if(HI_Alarm==1||LO_Alarm==1)TR0=1;
- else TR0=0;
- Display_Temperature();
- }
- DelayXus(100);
- }
- }
- }
復制代碼
|