單片機小項目介紹
項目功能介紹
- 編程語言: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中的時間,未按下時則不讀取。因為某些原因這里就不再進行展示了。若后期想到什么好方法會進行更新。