單片機小項目——基於51單片機的溫度報警器


單片機小項目介紹

項目功能介紹

  • 編程語言:C語言。
  • 開發環境:keil。
  • 主要功能:1602屏顯示時間和溫度,當溫度超過預定值時蜂鳴器工作報警。
  • 此項目只是作為單片機初學者的一個小測驗。

硬件資源分配

  • 1602屏——P0,P2^7,P2^5,P2^6。
  • 串口——P2^0,P2^1。
  • 傳感器——DS18B20   P3^7;DS1302   P3^4,P3^5,P3^6。
  • 蜂鳴器——P1^6。

LCD1602屏配置

在h文件中聲明端口和函數:

#ifndef __LCD1602_H_
#define __LCD1602_H_
#include<reg52.h>

//重定義關鍵字
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

//定義端口
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;


//函數聲明
void Lcd1602_Delay1ms(uint c);  //延時函數
void LcdWriteCom(uchar com);  //寫入命令
void LcdWriteData(uchar dat);            //寫入數據
void LcdInit();                          //LCD初始化子程序


#endif

 在LCD1602.c文件中寫入時序和命令等函數代碼:

#include "LCD1602.h"

/***************************延時函數**************************/
void Lcd1602_Delay1ms(uint c)   //誤差 0us
{
    uchar a,b;
    for (; c>0; c--)
    {
         for (b=199;b>0;b--)
         {
              for(a=1;a>0;a--);
         }      
    }
        
}
/***************************底層函數**************************/
void LcdWriteCom(uchar com)      //寫入命令
{
    LCD1602_E = 0;     //使能
    LCD1602_RS = 0;       //選擇發送命令
    LCD1602_RW = 0;       //選擇寫入
    
    LCD1602_DATAPINS = com;     //放入命令
    Lcd1602_Delay1ms(1);        //等待數據穩定

    LCD1602_E = 1;              //寫入時序
    Lcd1602_Delay1ms(5);      //保持時間
    LCD1602_E = 0;
}

void LcdWriteData(uchar dat) //寫入數據 { LCD1602_E = 0; //使能清零 LCD1602_RS = 1; //選擇輸入數據 LCD1602_RW = 0; //選擇寫入 LCD1602_DATAPINS = dat; //寫入數據 Lcd1602_Delay1ms(1); LCD1602_E = 1; //寫入時序 Lcd1602_Delay1ms(5); //保持時間 LCD1602_E = 0; } void LcdInit() //LCD初始化子程序 { LcdWriteCom(0x38); //開顯示 LcdWriteCom(0x0c); //開顯示不顯示光標 LcdWriteCom(0x06); //寫一個指針加1 LcdWriteCom(0x01); //清屏 LcdWriteCom(0x80); //設置數據指針起點 }

 在main.c文件中運用:(這里先讓顯示屏顯示自定義的內容,稍后再做更改)

#include "reg52.h"
#include "LCD1602.h"

unsigned char Disp[]=" Pechin Science ";

void main()
{
    unsigned char i=0;
    LcdInit();
    for(i=0;i<16;i++)
    {
        LcdWriteData(Disp[i]);    
    }
    while(1)
    {
        
    }
}

 DS18B20溫度傳感器配置(並將其與LCD協同使用)

在DS18B20.c文件中寫入相關函數:

  1 #include "DS18B20.h"
  2 
  3 
  4 /***************************延時函數**************************/
  5 void Delay1ms(unsigned int y)
  6 {
  7     unsigned int x;
  8     for( ; y>0; y--)
  9     {
 10         for(x=110; x>0; x--);
 11     }
 12 }
 13 
 14 
 15 /***************************底層函數**************************/
 16 
 17 unsigned char Ds18b20Init()                //初始化函數
 18 {
 19     unsigned char i;
 20     DSPORT = 0;                             //將總線拉低480us~960us
 21     i = 70;    
 22     while(i--);                            //延時642us
 23     DSPORT = 1;                            //然后拉高總線,如果DS18B20做出反應會將在15us~60us后總線拉低
 24     i = 0;
 25     while(DSPORT)                        //等待DS18B20拉低總線
 26     {
 27         Delay1ms(1);
 28         i++;
 29         if(i>5)                                //等待>5MS
 30         {
 31             return 0;                        //初始化失敗
 32         }
 33     
 34     }
 35     return 1;                                //初始化成功
 36 }
 37 
 38 
 39 void Ds18b20WriteByte(unsigned char dat)            //寫入一個字節
 40 {
 41     unsigned int i, j;
 42 
 43     for(j=0; j<8; j++)
 44     {
 45         DSPORT = 0;                                           //每寫入一位數據之前先把總線拉低1us
 46         i++;
 47         DSPORT = dat & 0x01;                          //然后寫入一個數據,從最低位開始
 48         i=6;
 49         while(i--);                                             //延時68us,持續時間最少60us
 50         DSPORT = 1;                                                //然后釋放總線,至少1us給總線恢復時間才能接着寫入第二個數值
 51         dat >>= 1;
 52     }
 53 }
 54 
 55 
 56 unsigned char Ds18b20ReadByte()                    //讀取一個字節
 57 {
 58     unsigned char byte, bi;
 59     unsigned int i, j;    
 60     for(j=8; j>0; j--)
 61     {
 62         DSPORT = 0;                                    //先將總線拉低1us
 63         i++;
 64         DSPORT = 1;                                    //然后釋放總線
 65         i++;
 66         i++;                                                //延時6us等待數據穩定
 67         bi = DSPORT;                              //讀取數據,從最低位開始讀取
 68         /*將byte左移一位,然后與上右移7位后的bi,注意移動之后移掉那位補0。*/
 69         byte = (byte >> 1) | (bi << 7);                          
 70         i = 4;                                            //讀取完之后等待48us再接着讀取下一個數
 71         while(i--);
 72     }                
 73     return byte;
 74 }
 75 
 76 /***************************高層函數**************************/
 77 
 78 void  Ds18b20ChangTemp()            //DS18B20轉換溫度
 79 {
 80     Ds18b20Init();
 81     Delay1ms(1);
 82     Ds18b20WriteByte(0xcc);            //跳過ROM操作命令         
 83     Ds18b20WriteByte(0x44);        //溫度轉換命令
 84     //Delay1ms(100);                        //等待轉換成功,而如果你是一直刷着的話,就不用這個延時了
 85    
 86 }
 87 
 88 
 89 void  Ds18b20ReadTempCom()        //發送讀取溫度命令
 90 {    
 91 
 92     Ds18b20Init();
 93     Delay1ms(1);
 94     Ds18b20WriteByte(0xcc);           //跳過ROM操作命令
 95     Ds18b20WriteByte(0xbe);             //發送讀取溫度命令
 96 }
 97 
 98 int Ds18b20ReadTemp()                    //讀取溫度
 99 {
100     unsigned int temp = 0;
101     unsigned char tmh, tml;    
102     Ds18b20ChangTemp();                     //先寫入轉換命令
103     Ds18b20ReadTempCom();                //然后等待轉換完后發送讀取溫度命令
104     tml = Ds18b20ReadByte();        //讀取溫度值共16位,先讀低字節
105     tmh = Ds18b20ReadByte();        //再讀高字節
106     temp = tmh;
107     temp <<= 8;
108     temp |= tml;
109     return temp;
110 }
111 
112 
113 void dataprosTemp(unsigned int temp)      //溫度讀取處理轉換函
114 {
115         float tp; 
116         DisplayTemp[0] = '+';            //因為測量溫度為正,所以使開頭為+;
117         tp=temp;                                        //因為數據處理有小數點所以將溫度賦給一個浮點型變量
118         //如果溫度是正的那么,那么正數的原碼就是補碼它本身
119         temp=tp*0.0625*100+0.5;    
120         //留兩個小數點就*100,+0.5是四舍五入,因為C語言浮點數轉換為整型的時候把小數點
121         //后面的數自動去掉,不管是否大於0.5,而+0.5之后大於0.5的就是進1了,小於0.5的就
122         //算加上0.5,還是在小數點后面。
123     
124 
125     DisplayTemp[1] = temp / 1000;
126     DisplayTemp[2] = temp % 1000 / 100;
127     DisplayTemp[3] = '.';
128     DisplayTemp[4] = temp % 100 / 10;
129     DisplayTemp[5] = temp % 10;
130 }

  在DS18B20.h文件中聲明:

 1 #ifndef __DS18B20_H_
 2 #define __DS18B20_H_
 3 #include<reg52.h>
 4 
 5 
 6 //定義使用的IO口
 7 sbit DSPORT=P3^7;
 8 
 9 
10 //聲明全局函數
11 void Delay1ms(unsigned int);
12 unsigned char Ds18b20Init();
13 void Ds18b20WriteByte(unsigned char com);
14 unsigned char Ds18b20ReadByte();
15 void  Ds18b20ChangTemp();
16 void  Ds18b20ReadTempCom();
17 int Ds18b20ReadTemp();
18 void dataprosTemp(unsigned int temp);
19 
20 
21 //聲明全局變量
22 extern unsigned char DisplayTemp[8];
23 
24 
25 #endif

 在main.c函數中運用:

 1 #include "reg52.h"
 2 #include "LCD1602.h"
 3 #include "DS18B20.h"
 4    
 5 
 6 
 7 void main()
 8 {
 9     unsigned int i;
10 11 
12     while(1)
13     {
14         LcdInit();
15 16         dataprosTemp(Ds18b20ReadTemp());     //數據處理函數
17         for(i=0;i<6;i++)
18         {
19             LcdWriteData(DisplayTemp[i]);    
20         }
21         Delay1ms(100);
22     }
23 }

 遇到的問題及解決方法:

  • 第一個問題:原本打算將函數  void datapros(unsigned int temp)   寫在DS18B20.c文件中然后再在DS18B20.h文件中聲明,但是該函數使用到數組  unsigned int DisplayData[8]; 且此數組在main.c文件中有所使用,但是在DS18B20.h文件中聲明時,出現未知錯誤,無警告無錯誤但就是不能運行,所以將該函數直接寫在main.c文件中。問題得到解決。
  • 接上一個問題:數組unsigned int DisplayData[8]; 在DS18B20.h文件中無法定義情況。並不是無法定義,是因為缺少關鍵詞extern。
  • 第二個問題:因為要求顯示溫度,而溫度是數字,但是LCD1602輸入的數據必須是字符,直接傳入數字LCD屏幕顯示混亂。首次嘗試將溫度數字定義成字符類型,但是LCD屏顯示亂碼。初步估計輸入類型必須是 ‘A’  這種類型才能正常顯示。再次嘗試,使用折中的辦法,在  void LcdWriteData(unsigned int dat)  寫入數據函數中添加下列代碼,將傳遞過來的溫度數字通過switch的方法轉換成 ‘A’  這種類型字符,再將該字符傳遞給LCD1602,顯示屏能正常顯示。問題解決。(此代碼更改的位置在LCD1602.c文件中)
 1 void LcdWriteData(unsigned char dat)                 //寫入數據
 2 {
 3     unsigned char datt;
 4     if(dat != '.' && dat != '+')
 5     {
 6         switch(dat)
 7         {
 8         case 0:datt='0';break;
 9         case 1:datt='1';break;
10         case 2:datt='2';break;
11         case 3:datt='3';break;
12         case 4:datt='4';break;
13         case 5:datt='5';break;
14         case 6:datt='6';break;
15         case 7:datt='7';break;
16         case 8:datt='8';break;
17         case 9:datt='9';break;
18         }
19     }
20     else
21     {
22         datt=dat;
23     }
24     
25 
26     LCD1602_E = 0;                                        //使能允許
27     LCD1602_RS = 1;                                        //選擇輸入數據
28     LCD1602_RW = 0;                                        //選擇寫入
29 
30     LCD1602_DATAPINS = datt;                     //寫入數據
31     Lcd1602_Delay1ms(1);
32 
33     LCD1602_E = 1;                                       //寫入時序
34     Lcd1602_Delay1ms(5);                        //保持時間
35     LCD1602_E = 0;
36 }
  • 第三個問題:LCD顯示屏無法顯示小數點。嘗試使用if語句判斷傳進  void LcdWriteData(unsigned char dat)  函數的值是否是小數點,將上述的switch函數添加if--else語句。最終能顯示顯示小數點。問題解決。(此代碼更改的位置在LCD1602.c文件中)
  • 第四個問題:無法顯示正號(因為本次項目測量溫度為正值,所以沒有考慮負號的情況,但負號也與此類似)。方法同上述小數點問題相同,添加if語句進行判斷,最終能顯示出正號,問題解決。

DS1302時鍾模塊配置

在DS1302.c文件中寫入對應函數:

  1 #include "DS1302.h"
  2 
  3 //---DS1302時鍾初始化2016年5月7日星期六12點00分00秒。---//
  4 //---存儲順序是秒分時日月周年,存儲格式是用BCD碼---//
  5 unsigned char TIME[7] = {0, 0, 0x12, 0x07, 0x05, 0x06, 0x16};    
  6 
  7 //---DS1302寫入和讀取時分秒的地址命令---//
  8 //---秒分時日月周年 最低位讀寫位;-------//
  9 unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
 10 unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
 11 
 12 
 13 
 14 
 15 void Ds1302Write(unsigned char addr, unsigned char dat)    //向DS1302命令(地址+數據)
 16 {
 17     unsigned char n;
 18     RST = 0;
 19     _nop_();
 20 
 21     SCLK = 0;//先將SCLK置低電平。
 22     _nop_();
 23     RST = 1; //然后將RST(CE)置高電平。
 24     _nop_();
 25 
 26     for (n=0; n<8; n++)//開始傳送八位地址命令
 27     {
 28         DSIO = addr & 0x01;//數據從低位開始傳送
 29         addr >>= 1;
 30         SCLK = 1;//數據在上升沿時,DS1302讀取數據
 31         _nop_();
 32         SCLK = 0;
 33         _nop_();
 34     }
 35     for (n=0; n<8; n++)//寫入8位數據
 36     {
 37         DSIO = dat & 0x01;
 38         dat >>= 1;
 39         SCLK = 1;//數據在上升沿時,DS1302讀取數據
 40         _nop_();
 41         SCLK = 0;
 42         _nop_();    
 43     }    
 44          
 45     RST = 0;//傳送數據結束
 46     _nop_();
 47 }
 48 
 49 
 50 unsigned char Ds1302Read(unsigned char addr)                    //讀取一個地址的數據
 51 {
 52     unsigned char n,dat,dat1;
 53     RST = 0;
 54     _nop_();
 55 
 56     SCLK = 0;//先將SCLK置低電平。
 57     _nop_();
 58     RST = 1;//然后將RST(CE)置高電平。
 59     _nop_();
 60 
 61     for(n=0; n<8; n++)//開始傳送八位地址命令
 62     {
 63         DSIO = addr & 0x01;//數據從低位開始傳送
 64         addr >>= 1;
 65         SCLK = 1;//數據在上升沿時,DS1302讀取數據
 66         _nop_();
 67         SCLK = 0;//DS1302下降沿時,放置數據
 68         _nop_();
 69     }
 70     _nop_();
 71     for(n=0; n<8; n++)//讀取8位數據
 72     {
 73         dat1 = DSIO;//從最低位開始接收
 74         dat = (dat>>1) | (dat1<<7);
 75         SCLK = 1;
 76         _nop_();
 77         SCLK = 0;//DS1302下降沿時,放置數據
 78         _nop_();
 79     }
 80 
 81     RST = 0;
 82     _nop_();    //以下為DS1302復位的穩定時間,必須的。
 83     SCLK = 1;
 84     _nop_();
 85     DSIO = 0;
 86     _nop_();
 87     DSIO = 1;
 88     _nop_();
 89     return dat;    
 90 }
 91 
 92 
 93 void Ds1302Init()                        //初始化DS1302.
 94 {
 95     unsigned char n;
 96     Ds1302Write(0x8E,0X00);         //禁止寫保護,就是關閉寫保護功能
 97     for (n=0; n<7; n++)//寫入7個字節的時鍾信號:分秒時日月周年
 98     {
 99         Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);    
100     }
101     Ds1302Write(0x8E,0x80);         //打開寫保護功能
102 }
103 
104 
105 void Ds1302ReadTime()                    //讀取時鍾信息
106 {
107     unsigned char n;
108     for (n=0; n<7; n++)//讀取7個字節的時鍾信號:分秒時日月周年
109     {
110         TIME[n] = Ds1302Read(READ_RTC_ADDR[n]);
111     }
112         
113 }
114 
115 
116 
117 void DS1302datapros()                          //時間讀取處理轉換函數
118 {
119     Ds1302ReadTime();
120     Ds1302DisplayTime[0] = TIME[2]/16;                //
121     Ds1302DisplayTime[1] = TIME[2]&0x0f;                 
122     Ds1302DisplayTime[2] = '-'    ;                        //橫杠
123     Ds1302DisplayTime[3] = TIME[1]/16;                //
124     Ds1302DisplayTime[4] = TIME[1]&0x0f;    
125     Ds1302DisplayTime[5] = '-' ;                            //橫杠
126     Ds1302DisplayTime[6] = TIME[0]/16;                //
127     Ds1302DisplayTime[7] = TIME[0]&0x0f;
128 }

 

 在DS1302.h文件中聲明函數:

 1 #ifndef __DS1302_H_
 2 #define __DS1302_H_
 3 
 4 #include<reg52.h>
 5 #include<intrins.h>
 6 
 7 //---定義ds1302使用的IO口---//
 8 sbit DSIO=P3^4;
 9 sbit RST=P3^5;
10 sbit SCLK=P3^6;
11 
12 
13 //---定義全局函數---//
14 void Ds1302Write(unsigned char addr, unsigned char dat);
15 unsigned char Ds1302Read(unsigned char addr);
16 void Ds1302Init();
17 void Ds1302ReadTime();
18 void DS1302datapros();
19 
20 
21 
22 //加入全局變量
23 extern unsigned char Ds1302DisplayTime[8];
24 
25 
26 
27 #endif

 

 在main.c函數中運用:

 1 #include "reg52.h"
 2 #include "LCD1602.h"
 3 #include "DS18B20.h"
 4 #include "DS1302.h"    
 5 
 6 
 7 
 8 unsigned char DS18B20Display[8];
 9 unsigned char Ds1302DisplayTime[8];
10 
11 void Delay1ms(unsigned int y)
12 {
13     unsigned int x;
14     for( ; y>0; y--)
15     {
16         for(x=110; x>0; x--);
17     }
18 }
19 
20 void main()
21 {
22     unsigned int i;
23     Ds1302Init();
24 
25     while(1)
26     {
27         LcdInit();
28         DS1302datapros();                                            //時間數據處理
29         DS18B20datapros(Ds18b20ReadTemp());     //溫度數據處理函數
30         for(i=0;i<8;i++)
31         {
32             LcdWriteData(Ds1302DisplayTime[i]);      //顯示時間
33         }
34         LcdWriteCom(0xc0);                      //寫入LCD地址命令,改變寫入數據地址。
35         for(i=0;i<6;i++)
36         {
37             LcdWriteData(DS18B20Display[i]);            //顯示溫度
38         }
39         Delay1ms(100);
40     }
41 }

遇到到問題及解決方法:

  • 第一個問題:代碼運行后顯示如下錯誤。經發現是 DS1302datapros()  函數后面的注釋忘記加分號。修改后出現新的問題。

  •  第二個問題:修改第一個問題后出現如下問題,經翻譯:出現多重定義。經過檢查發現,是因為將數組 WRITE_RTC_ADDR[7] 和 READ_RTC_ADDR[7] 以及 TIME[7] 未在DS1302.c文件中定義,而是直接在DS1302.h文件中進行聲明,所以出現錯誤。經過發現,出現的許多警告也是因為這種原因。修改后就沒問題了。

  •  第三個問題:嘗試在顯示屏上同時顯示時間和溫度,且時間在第一行,溫度在第二行。因為顯示屏一行只顯示16個字符,嘗試將相關數組大小定義成16,並將多余位置定義成空格。但是嘗試失敗,因為顯示屏內部的地址實際一行不是16個,只是本單片機只顯示16個,內部的地址只運用了前面的16個,后面的沒有使用,但是存在。所以最后實驗結果是只顯示出時間而沒顯示出溫度。
  • 接第三個問題:LCD屏內部地址是連續的,第一行末尾地址與第二行首地址也是連續的,但是一行不止16個地址,如下圖所示。首地址是00,但是因為DB7位必須是1,所以設置地址要在原基礎上加0x80。為解決第三個問題,在main.c函數中顯示完時間后,添加  LcdWriteCom(0xc0);   改變寫入LCD的數據地址。最終結果顯示正確。

     

 蜂鳴器配置:

在FengMingQi.c函數中添加蜂鳴器相關代碼:

 1 #include "reg52.h"
 2 #include "FengMingQi.h"
 3 
 4 
 5 unsigned int i;
 6 
 7 void FengMing(unsigned int temp)
 8 {
 9     unsigned int j=100;
10     if(temp>=30)
11     {
12          for(i=0;i<50;i++)
13         {
14             beep=~beep;
15             while(j--);
16             j=100;
17         }
18     }
19 
20 }

 

 

在FengMingQi.h函數中聲明函數和串口:

 1 #ifndef __FengMingQi_H_
 2 #define __FengMingQi_H_
 3 
 4 #include<reg52.h>
 5 #include<intrins.h>
 6 
 7 
 8 sbit beep = P1^6;
 9 void FengMing(unsigned int temp);
10 
11 
12 
13 
14 #endif

 

在main.c中調用蜂鳴器函數:

 1 #include "reg52.h"
 2 #include "LCD1602.h"
 3 #include "DS18B20.h"
 4 #include "DS1302.h"    
 5 #include "FengMingQi.h"
 6 
 7 
 8 unsigned char DS18B20Display[8];
 9 unsigned char Ds1302DisplayTime[8];
10 unsigned int temp;
11 
12 void Delay1ms(unsigned int y)
13 {
14     unsigned int x;
15     for( ; y>0; y--)
16     {
17         for(x=110; x>0; x--);
18     }
19 }
20 
21 void main()
22 {
23     unsigned int i;
24     Ds1302Init();
25 
26     while(1)
27     {
28         LcdInit();
29         DS1302datapros();                                            //時間數據處理
30         temp=DS18B20datapros(Ds18b20ReadTemp());     //溫度數據處理函數
31         for(i=0;i<8;i++)
32         {
33             LcdWriteData(Ds1302DisplayTime[i]);      //顯示時間
34         }
35         LcdWriteCom(0xc0);                      //寫入LCD地址命令,改變寫入數據地址。
36         for(i=0;i<6;i++)
37         {
38             LcdWriteData(DS18B20Display[i]);            //顯示溫度
39         }
40         FengMing(temp);            //蜂鳴器的函數
41         Delay1ms(100);
42     }
43 }

出現的問題及解決方法:

  • 問題一:出現重定義錯誤,在FengMingQi.c文件中定義了端口P1^6;然后又在FengMingQi.h文件中定義了一次,所以出現錯誤。
  • 問題二:在代碼運行無誤時,將文件燒錄到單片機上,時間和溫度都能正常顯示,但是在溫度超過30后蜂鳴器未響應(這里因為室內溫度20幾度,用手捂住DS18B20溫度傳感器才勉強能達到30度,所以設置上限30度),但是當溫度顯示超過31度時,蜂鳴器響應。經分析發現,在蜂鳴器  void FengMing(unsigned int temp)  函數中定義的是unsigned int 類型,是整型,而在下面的 if 語句中使用的是temp>30,所以在31度時才會報警。修改后能正常實現功能。

EEPROM配置:

在EEPROM.c文件中添加相關代碼:

  1 #include "EEPROM.h"
  2 
  3 
  4 //延時函數10us
  5 
  6 void Delay10us()
  7 {
  8     unsigned char a,b;
  9     for(b=1;b>0;b--)
 10         for(a=2;a>0;a--);
 11 
 12 }
 13 
 14 
 15 /**************************底層函數**********************/
 16 
 17 
 18  //起始信號:在SCL時鍾信號在高電平期間SDA信號產生一個下降沿,起始之后SDA和SCL都為0
 19 void I2cStart()      
 20 {
 21     SDA=1;
 22     Delay10us();
 23     SCL=1;
 24     Delay10us();//建立時間是SDA保持時間>4.7us
 25     SDA=0;
 26     Delay10us();//保持時間是>4us
 27     SCL=0;            
 28     Delay10us();        
 29 }
 30 
 31 //終止信號:在SCL時鍾信號高電平期間SDA信號產生一個上升沿,結束之后保持SDA和SCL都為1,表示總線空閑
 32 void I2cStop()
 33 {
 34     SDA=0;
 35     Delay10us();
 36     SCL=1;
 37     Delay10us();//建立時間大於4.7us
 38     SDA=1;
 39     Delay10us();        
 40 }
 41 
 42 
 43 /**********************************高層函數*****************************************/
 44 
 45 //通過I2C發送一個字節。在SCL時鍾信號高電平期間,保持發送信號SDA保持穩定,發送成功返回1,發送失敗返回0
 46 unsigned char I2cSendByte(unsigned char dat)
 47 {
 48     unsigned char a=0,b=0;//最大255,一個機器周期為1us,最大延時255us。        
 49     for(a=0;a<8;a++)//要發送8位,從最高位開始
 50     {
 51         SDA=dat>>7;     //起始信號之后SCL=0,所以可以直接改變SDA信號
 52         dat=dat<<1;
 53         Delay10us();
 54         SCL=1;
 55         Delay10us();//建立時間>4.7us
 56         SCL=0;
 57         Delay10us();//時間大於4us        
 58     }
 59     SDA=1;
 60     Delay10us();
 61     SCL=1;
 62     while(SDA)//等待應答,也就是等待從設備把SDA拉低
 63     {
 64         b++;
 65         if(b>200)     //如果超過2000us沒有應答發送失敗,或者為非應答,表示接收結束
 66         {
 67             SCL=0;
 68             Delay10us();
 69             return 0;
 70         }
 71     }
 72     SCL=0;
 73     Delay10us();
 74      return 1;        
 75 }
 76 
 77 
 78 //使用I2c讀取一個字節
 79 unsigned char I2cReadByte()
 80 {
 81     unsigned char a=0,dat=0;
 82     SDA=1;            //起始和發送一個字節之后SCL都是0
 83     Delay10us();
 84     for(a=0;a<8;a++)//接收8個字節
 85     {
 86         SCL=1;
 87         Delay10us();
 88         dat<<=1;
 89         dat|=SDA;
 90         Delay10us();
 91         SCL=0;
 92         Delay10us();
 93     }
 94     return dat;        
 95 }
 96 
 97 
 98 //往24c02的一個地址寫入一個數據
 99 void At24c02Write(unsigned char addr,unsigned char dat)
100 {
101     I2cStart();
102     I2cSendByte(0xa0);//發送寫器件地址
103     I2cSendByte(addr);//發送要寫入內存地址
104     I2cSendByte(dat);    //發送數據
105     I2cStop();
106 }
107 
108 
109 
110 //讀取24c02的一個地址的一個數據
111 unsigned char At24c02Read(unsigned char addr)
112 {
113     unsigned char num;
114     I2cStart();
115     I2cSendByte(0xa0); //發送寫器件地址
116     I2cSendByte(addr); //發送要讀取的地址
117     I2cStart();
118     I2cSendByte(0xa1); //發送讀器件地址
119     num=I2cReadByte(); //讀取數據
120     I2cStop();
121     return num;    
122 }

 在EEPROM.h文件中聲明函數:

 1 #ifndef __EEPROM_H_
 2 #define __EEPROM_H_
 3 
 4 #include <reg52.h>
 5 
 6 sbit SCL=P2^1;
 7 sbit SDA=P2^0;
 8 
 9 void I2cStart();
10 void I2cStop();
11 unsigned char I2cSendByte(unsigned char dat);
12 unsigned char I2cReadByte();
13 void At24c02Write(unsigned char addr,unsigned char dat);
14 unsigned char At24c02Read(unsigned char addr);
15 
16 
17 
18 
19 
20 #endif

 main.c函數中調用:

 1 #include "reg52.h"
 2 #include "LCD1602.h"
 3 #include "DS18B20.h"
 4 #include "DS1302.h"    
 5 #include "FengMingQi.h"
 6 #include "EEPROM.h"
 7 
 8 unsigned char DS18B20Display[8];
 9 unsigned char Ds1302DisplayTime[8];
10 unsigned int temp;
11 
12 
13 
14 
15 void Delay1ms(unsigned int y)
16 {
17     unsigned int x;
18     for( ; y>0; y--)
19     {
20         for(x=110; x>0; x--);
21     }
22 }
23 
24 void main()
25 {
26     unsigned int i;
27     
28     TIME[0]=At24c02Read(1);      //讀取EEPROM地址1內的時間數據保存在TIME中
29     TIME[1]=At24c02Read(2);
30     TIME[2]=At24c02Read(3);
31 
32     Ds1302Init();
33     while(1)
34     {
35         LcdInit();
36         
37         DS1302datapros();                //時間數據處理
38         At24c02Write(1,TIME[0]);   //在EEPROM地址1內寫入時間數據
39         At24c02Write(2,TIME[1]);   //在EEPROM地址2內寫入時間數據
40         At24c02Write(3,TIME[2]);   //在EEPROM地址3內寫入時間數據
41         
42         
43         temp=DS18B20datapros(Ds18b20ReadTemp());     //溫度數據處理函數
44 
45         for(i=0;i<8;i++)
46         {
47             LcdWriteData(Ds1302DisplayTime[i]);      //顯示時間
48         }
49         
50         LcdWriteCom(0xc0);                      //寫入LCD地址命令,改變寫入數據地址。
51         
52         for(i=0;i<6;i++)
53         {
54             LcdWriteData(DS18B20Display[i]);            //顯示溫度
55         }
56         
57         FengMing(temp);            //蜂鳴器的函數
58         
59         
60         
61         Delay1ms(100);            //延時一段時間后再進行掃描溫度。
62         
63         
64     }
65 }

 出現的問題及解決方法:

  • 問題一:代碼無誤后燒錄到單片機上,在一開始的幾次開關電源中,時間正常顯示且實現保存功能,但是多開關幾次后時分秒顯示錯誤,但是經測驗,EEPROM的保存功任然能實現,即關電后再次開機任然顯示上次的時間。初步檢測是在main函數中一開始  讀取EEPROM地址1內的時間數據保存在TIME中時  出現問題,這里應該需要一個if 語句判斷是否EEPROM中是否保存有時間,但是這個判斷用的變量也需要有保存功能,即在斷電后該變量值不丟失,所以目前並未想到什么好的方法進行判斷。這里可以應用按鍵或紅外系統,即當按鍵按下時讀取EEPROM中的時間,未按下時則不讀取。因為某些原因這里就不再進行展示了。若后期想到什么好方法會進行更新。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM