单片机小项目介绍

项目功能介绍


  • 编程语言: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() 函数后面的注释忘记加分号。修改后出现新的问题。

单片机小项目——基于51单片机的温度报警器_数据处理


  • 第二个问题:修改第一个问题后出现如下问题,经翻译:出现多重定义。经过检查发现,是因为将数组 WRITE_RTC_ADDR[7] 和 READ_RTC_ADDR[7] 以及 TIME[7] 未在DS1302.c文件中定义,而是直接在DS1302.h文件中进行声明,所以出现错误。经过发现,出现的许多警告也是因为这种原因。修改后就没问题了。

单片机小项目——基于51单片机的温度报警器_#include_02


  • 第三个问题:尝试在显示屏上同时显示时间和温度,且时间在第一行,温度在第二行。因为显示屏一行只显示16个字符,尝试将相关数组大小定义成16,并将多余位置定义成空格。但是尝试失败,因为显示屏内部的地址实际一行不是16个,只是本单片机只显示16个,内部的地址只运用了前面的16个,后面的没有使用,但是存在。所以最后实验结果是只显示出时间而没显示出温度。
  • 接第三个问题:LCD屏内部地址是连续的,第一行末尾地址与第二行首地址也是连续的,但是一行不止16个地址,如下图所示。首地址是00,但是因为DB7位必须是1,所以设置地址要在原基础上加0x80。为解决第三个问题,在main.c函数中显示完时间后,添加 LcdWriteCom(0xc0); 改变写入LCD的数据地址。最终结果显示正确。

单片机小项目——基于51单片机的温度报警器_2d_03单片机小项目——基于51单片机的温度报警器_数据处理_04

蜂鸣器配置:

在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中的时间,未按下时则不读取。因为某些原因这里就不再进行展示了。若后期想到什么好方法会进行更新。