共计 13161 个字符,预计需要花费 33 分钟才能阅读完成。
我的项目性能介绍
- 编程语言: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 文件中写入相干函数:
#include "DS18B20.h" | |
/*************************** 延时函数 **************************/ | |
void Delay1ms(unsigned int y) | |
{ | |
unsigned int x; | |
for(; y>0; y--) | |
{for(x=110; x>0; x--); | |
} | |
} | |
/*************************** 底层函数 **************************/ | |
unsigned char Ds18b20Init() // 初始化函数 | |
{ | |
unsigned char i; | |
DSPORT = 0; // 将总线拉低 480us~960us | |
i = 70; | |
while(i--); // 延时 642us | |
DSPORT = 1; // 而后拉高总线,如果 DS18B20 做出反馈会将在 15us~60us 后总线拉低 | |
i = 0; | |
while(DSPORT) // 期待 DS18B20 拉低总线 | |
{Delay1ms(1); | |
i++; | |
if(i>5) // 期待 >5MS | |
{return 0; // 初始化失败} | |
} | |
return 1; // 初始化胜利 | |
} | |
void Ds18b20WriteByte(unsigned char dat) // 写入一个字节 | |
{ | |
unsigned int i, j; | |
for(j=0; j<8; j++) | |
{ | |
DSPORT = 0; // 每写入一位数据之前先把总线拉低 1us | |
i++; | |
DSPORT = dat & 0x01; // 而后写入一个数据,从最低位开始 | |
i=6; | |
while(i--); // 延时 68us,持续时间起码 60us | |
DSPORT = 1; // 而后开释总线,至多 1us 给总线复原工夫能力接着写入第二个数值 | |
dat >>= 1; | |
} | |
} | |
unsigned char Ds18b20ReadByte() // 读取一个字节 | |
{ | |
unsigned char byte, bi; | |
unsigned int i, j; | |
for(j=8; j>0; j--) | |
{ | |
DSPORT = 0; // 先将总线拉低 1us | |
i++; | |
DSPORT = 1; // 而后开释总线 | |
i++; | |
i++; // 延时 6us 期待数据稳固 | |
bi = DSPORT; // 读取数据,从最低位开始读取 | |
/* 将 byte 左移一位,而后与上右移 7 位后的 bi,留神挪动之后移掉那位补 0。*/ | |
byte = (byte >> 1) | (bi << 7); | |
i = 4; // 读取完之后期待 48us 再接着读取下一个数 | |
while(i--); | |
} | |
return byte; | |
} | |
/*************************** 高层函数 **************************/ | |
void Ds18b20ChangTemp() //DS18B20 转换温度 | |
{Ds18b20Init(); | |
Delay1ms(1); | |
Ds18b20WriteByte(0xcc); // 跳过 ROM 操作命令 | |
Ds18b20WriteByte(0x44); // 温度转换命令 | |
//Delay1ms(100); // 期待转换胜利,而如果你是始终刷着的话,就不必这个延时了 | |
} | |
void Ds18b20ReadTempCom() // 发送读取温度命令 | |
{Ds18b20Init(); | |
Delay1ms(1); | |
Ds18b20WriteByte(0xcc); // 跳过 ROM 操作命令 | |
Ds18b20WriteByte(0xbe); // 发送读取温度命令 | |
} | |
int Ds18b20ReadTemp() // 读取温度 | |
{ | |
unsigned int temp = 0; | |
unsigned char tmh, tml; | |
Ds18b20ChangTemp(); // 先写入转换命令 | |
Ds18b20ReadTempCom(); // 而后期待转换完后发送读取温度命令 | |
tml = Ds18b20ReadByte(); // 读取温度值共 16 位,先读低字节 | |
tmh = Ds18b20ReadByte(); // 再读高字节 | |
temp = tmh; | |
temp <<= 8; | |
temp |= tml; | |
return temp; | |
} | |
void dataprosTemp(unsigned int temp) // 温度读取解决转换函 | |
{ | |
float tp; | |
DisplayTemp[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,还是在小数点前面。DisplayTemp[1] = temp / 1000; | |
DisplayTemp[2] = temp % 1000 / 100; | |
DisplayTemp[3] = '.'; | |
DisplayTemp[4] = temp % 100 / 10; | |
DisplayTemp[5] = temp % 10; | |
} |
在 DS18B20.h 文件中申明:
#ifndef __DS18B20_H_ | |
#define __DS18B20_H_ | |
#include<reg52.h> | |
// 定义应用的 IO 口 | |
sbit DSPORT=P3^7; | |
// 申明全局函数 | |
void Delay1ms(unsigned int); | |
unsigned char Ds18b20Init(); | |
void Ds18b20WriteByte(unsigned char com); | |
unsigned char Ds18b20ReadByte(); | |
void Ds18b20ChangTemp(); | |
void Ds18b20ReadTempCom(); | |
int Ds18b20ReadTemp(); | |
void dataprosTemp(unsigned int temp); | |
// 申明全局变量 | |
extern unsigned char DisplayTemp[8]; | |
#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 文件中)
void LcdWriteData(unsigned char dat) // 写入数据 | |
{ | |
unsigned char datt; | |
if(dat != '.' && dat != '+') | |
{switch(dat) | |
{ | |
case 0:datt='0';break; | |
case 1:datt='1';break; | |
case 2:datt='2';break; | |
case 3:datt='3';break; | |
case 4:datt='4';break; | |
case 5:datt='5';break; | |
case 6:datt='6';break; | |
case 7:datt='7';break; | |
case 8:datt='8';break; | |
case 9:datt='9';break; | |
} | |
} | |
else | |
{datt=dat;} | |
LCD1602_E = 0; // 使能容许 | |
LCD1602_RS = 1; // 抉择输出数据 | |
LCD1602_RW = 0; // 抉择写入 | |
LCD1602_DATAPINS = datt; // 写入数据 | |
Lcd1602_Delay1ms(1); | |
LCD1602_E = 1; // 写入时序 | |
Lcd1602_Delay1ms(5); // 放弃工夫 | |
LCD1602_E = 0; | |
} |
- 第三个问题:LCD 显示屏无奈显示小数点。尝试应用 if 语句判断传进 void LcdWriteData(unsigned char dat) 函数的值是否是小数点,将上述的 switch 函数增加 if–else 语句。最终能显示显示小数点。问题解决。(此代码更改的地位在 LCD1602.c 文件中)
第四个问题:无奈显示正号(因为本次我的项目测量温度为正值,所以没有思考负号的状况,但负号也与此相似)。办法同上述小数点问题雷同,增加 if 语句进行判断,最终能显示出正号,问题解决。
DS1302 时钟模块配置
在 DS1302.c 文件中写入对应函数:
#include "DS1302.h" | |
//---DS1302 时钟初始化 2016 年 5 月 7 日星期六 12 点 00 分 00 秒。---// | |
//--- 存储程序是秒分时日月周年, 存储格局是用 BCD 码 ---// | |
unsigned char TIME[7] = {0, 0, 0x12, 0x07, 0x05, 0x06, 0x16}; | |
//---DS1302 写入和读取时分秒的地址命令 ---// | |
//--- 秒分时日月周年 最低位读写位;-------// | |
unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; | |
unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; | |
void Ds1302Write(unsigned char addr, unsigned char dat) // 向 DS1302 命令(地址 + 数据){ | |
unsigned char n; | |
RST = 0; | |
_nop_(); | |
SCLK = 0;// 先将 SCLK 置低电平。_nop_(); | |
RST = 1; // 而后将 RST(CE) 置高电平。_nop_(); | |
for (n=0; n<8; n++)// 开始传送八位地址命令 | |
{ | |
DSIO = addr & 0x01;// 数据从低位开始传送 | |
addr >>= 1; | |
SCLK = 1;// 数据在回升沿时,DS1302 读取数据 | |
_nop_(); | |
SCLK = 0; | |
_nop_();} | |
for (n=0; n<8; n++)// 写入 8 位数据 | |
{ | |
DSIO = dat & 0x01; | |
dat >>= 1; | |
SCLK = 1;// 数据在回升沿时,DS1302 读取数据 | |
_nop_(); | |
SCLK = 0; | |
_nop_();} | |
RST = 0;// 传送数据完结 | |
_nop_();} | |
unsigned char Ds1302Read(unsigned char addr) // 读取一个地址的数据 | |
{ | |
unsigned char n,dat,dat1; | |
RST = 0; | |
_nop_(); | |
SCLK = 0;// 先将 SCLK 置低电平。_nop_(); | |
RST = 1;// 而后将 RST(CE) 置高电平。_nop_(); | |
for(n=0; n<8; n++)// 开始传送八位地址命令 | |
{ | |
DSIO = addr & 0x01;// 数据从低位开始传送 | |
addr >>= 1; | |
SCLK = 1;// 数据在回升沿时,DS1302 读取数据 | |
_nop_(); | |
SCLK = 0;//DS1302 降落沿时,搁置数据 | |
_nop_();} | |
_nop_(); | |
for(n=0; n<8; n++)// 读取 8 位数据 | |
{ | |
dat1 = DSIO;// 从最低位开始接管 | |
dat = (dat>>1) | (dat1<<7); | |
SCLK = 1; | |
_nop_(); | |
SCLK = 0;//DS1302 降落沿时,搁置数据 | |
_nop_();} | |
RST = 0; | |
_nop_(); // 以下为 DS1302 复位的稳固工夫, 必须的。SCLK = 1; | |
_nop_(); | |
DSIO = 0; | |
_nop_(); | |
DSIO = 1; | |
_nop_(); | |
return dat; | |
} | |
void Ds1302Init() // 初始化 DS1302. | |
{ | |
unsigned char n; | |
Ds1302Write(0x8E,0X00); // 禁止写爱护,就是敞开写爱护性能 | |
for (n=0; n<7; n++)// 写入 7 个字节的时钟信号:分秒时日月周年 | |
{Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]); | |
} | |
Ds1302Write(0x8E,0x80); // 关上写爱护性能 | |
} | |
void Ds1302ReadTime() // 读取时钟信息 | |
{ | |
unsigned char n; | |
for (n=0; n<7; n++)// 读取 7 个字节的时钟信号:分秒时日月周年 | |
{TIME[n] = Ds1302Read(READ_RTC_ADDR[n]); | |
} | |
} | |
void DS1302datapros() // 工夫读取解决转换函数 | |
{Ds1302ReadTime(); | |
Ds1302DisplayTime[0] = TIME[2]/16; // 时 | |
Ds1302DisplayTime[1] = TIME[2]&0x0f; | |
Ds1302DisplayTime[2] = '-' ; // 横杠 | |
Ds1302DisplayTime[3] = TIME[1]/16; // 分 | |
Ds1302DisplayTime[4] = TIME[1]&0x0f; | |
Ds1302DisplayTime[5] = '-' ; // 横杠 | |
Ds1302DisplayTime[6] = TIME[0]/16; // 秒 | |
Ds1302DisplayTime[7] = TIME[0]&0x0f; | |
} |
在 DS1302.h 文件中申明函数:
#ifndef __DS1302_H_ | |
#define __DS1302_H_ | |
#include<reg52.h> | |
#include<intrins.h> | |
//--- 定义 ds1302 应用的 IO 口 ---// | |
sbit DSIO=P3^4; | |
sbit RST=P3^5; | |
sbit SCLK=P3^6; | |
//--- 定义全局函数 ---// | |
void Ds1302Write(unsigned char addr, unsigned char dat); | |
unsigned char Ds1302Read(unsigned char addr); | |
void Ds1302Init(); | |
void Ds1302ReadTime(); | |
void DS1302datapros(); | |
// 退出全局变量 | |
extern unsigned char Ds1302DisplayTime[8]; | |
#endif |
在 main.c 函数中使用:
#include "reg52.h" | |
#include "LCD1602.h" | |
#include "DS18B20.h" | |
#include "DS1302.h" | |
unsigned char DS18B20Display[8]; | |
unsigned char Ds1302DisplayTime[8]; | |
void Delay1ms(unsigned int y) | |
{ | |
unsigned int x; | |
for(; y>0; y--) | |
{for(x=110; x>0; x--); | |
} | |
} | |
void main() | |
{ | |
unsigned int i; | |
Ds1302Init(); | |
while(1) | |
{LcdInit(); | |
DS1302datapros(); // 工夫数据处理 | |
DS18B20datapros(Ds18b20ReadTemp()); // 温度数据处理函数 | |
for(i=0;i<8;i++) | |
{LcdWriteData(Ds1302DisplayTime[i]); // 显示工夫 | |
} | |
LcdWriteCom(0xc0); // 写入 LCD 地址命令,扭转写入数据地址。for(i=0;i<6;i++) | |
{LcdWriteData(DS18B20Display[i]); // 显示温度 | |
} | |
Delay1ms(100); | |
} | |
} |
遇到到问题及解决办法:
- 第一个问题:代码运行后显示如下谬误。经发现是 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 函数中增加蜂鸣器相干代码:
#include "reg52.h" | |
#include "FengMingQi.h" | |
unsigned int i; | |
void FengMing(unsigned int temp) | |
{ | |
unsigned int j=100; | |
if(temp>=30) | |
{for(i=0;i<50;i++) | |
{ | |
beep=~beep; | |
while(j--); | |
j=100; | |
} | |
} | |
} |
在 FengMingQi.h 函数中申明函数和串口:
#ifndef __FengMingQi_H_ | |
#define __FengMingQi_H_ | |
#include<reg52.h> | |
#include<intrins.h> | |
sbit beep = P1^6; | |
void FengMing(unsigned int temp); | |
#endif |
在 main.c 中调用蜂鸣器函数:
#include "reg52.h" | |
#include "LCD1602.h" | |
#include "DS18B20.h" | |
#include "DS1302.h" | |
#include "FengMingQi.h" | |
unsigned char DS18B20Display[8]; | |
unsigned char Ds1302DisplayTime[8]; | |
unsigned int temp; | |
void Delay1ms(unsigned int y) | |
{ | |
unsigned int x; | |
for(; y>0; y--) | |
{for(x=110; x>0; x--); | |
} | |
} | |
void main() | |
{ | |
unsigned int i; | |
Ds1302Init(); | |
while(1) | |
{LcdInit(); | |
DS1302datapros(); // 工夫数据处理 | |
temp=DS18B20datapros(Ds18b20ReadTemp()); // 温度数据处理函数 | |
for(i=0;i<8;i++) | |
{LcdWriteData(Ds1302DisplayTime[i]); // 显示工夫 | |
} | |
LcdWriteCom(0xc0); // 写入 LCD 地址命令,扭转写入数据地址。for(i=0;i<6;i++) | |
{LcdWriteData(DS18B20Display[i]); // 显示温度 | |
} | |
FengMing(temp); // 蜂鸣器的函数 | |
Delay1ms(100); | |
} | |
} |
呈现的问题及解决办法:
- 问题一:呈现重定义谬误,在 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 文件中增加相干代码:
#include "EEPROM.h" | |
// 延时函数 10us | |
void Delay10us() | |
{ | |
unsigned char a,b; | |
for(b=1;b>0;b--) | |
for(a=2;a>0;a--); | |
} | |
/************************** 底层函数 **********************/ | |
// 起始信号:在 SCL 时钟信号在高电平期间 SDA 信号产生一个降落沿,起始之后 SDA 和 SCL 都为 0 | |
void I2cStart() | |
{ | |
SDA=1; | |
Delay10us(); | |
SCL=1; | |
Delay10us();// 建设工夫是 SDA 放弃工夫 >4.7us | |
SDA=0; | |
Delay10us();// 放弃工夫是 >4us | |
SCL=0; | |
Delay10us();} | |
// 终止信号:在 SCL 时钟信号高电平期间 SDA 信号产生一个回升沿, 完结之后放弃 SDA 和 SCL 都为 1,示意总线闲暇 | |
void I2cStop() | |
{ | |
SDA=0; | |
Delay10us(); | |
SCL=1; | |
Delay10us();// 建设工夫大于 4.7us | |
SDA=1; | |
Delay10us();} | |
/********************************** 高层函数 *****************************************/ | |
// 通过 I2C 发送一个字节。在 SCL 时钟信号高电平期间,放弃发送信号 SDA 保持稳定, 发送胜利返回 1,发送失败返回 0 | |
unsigned char I2cSendByte(unsigned char dat) | |
{ | |
unsigned char a=0,b=0;// 最大 255,一个机器周期为 1us,最大延时 255us。for(a=0;a<8;a++)// 要发送 8 位,从最高位开始 | |
{ | |
SDA=dat>>7; // 起始信号之后 SCL=0,所以能够间接扭转 SDA 信号 | |
dat=dat<<1; | |
Delay10us(); | |
SCL=1; | |
Delay10us();// 建设工夫 >4.7us | |
SCL=0; | |
Delay10us();// 工夫大于 4us} | |
SDA=1; | |
Delay10us(); | |
SCL=1; | |
while(SDA)// 期待应答,也就是期待从设施把 SDA 拉低 | |
{ | |
b++; | |
if(b>200) // 如果超过 2000us 没有应答发送失败,或者为非应答,示意接管完结 | |
{ | |
SCL=0; | |
Delay10us(); | |
return 0; | |
} | |
} | |
SCL=0; | |
Delay10us(); | |
return 1; | |
} | |
// 应用 I2c 读取一个字节 | |
unsigned char I2cReadByte() | |
{ | |
unsigned char a=0,dat=0; | |
SDA=1; // 起始和发送一个字节之后 SCL 都是 0 | |
Delay10us(); | |
for(a=0;a<8;a++)// 接管 8 个字节 | |
{ | |
SCL=1; | |
Delay10us(); | |
dat<<=1; | |
dat|=SDA; | |
Delay10us(); | |
SCL=0; | |
Delay10us();} | |
return dat; | |
} | |
// 往 24c02 的一个地址写入一个数据 | |
void At24c02Write(unsigned char addr,unsigned char dat) | |
{I2cStart(); | |
I2cSendByte(0xa0);// 发送写器件地址 | |
I2cSendByte(addr);// 发送要写入内存地址 | |
I2cSendByte(dat); // 发送数据 | |
I2cStop();} | |
// 读取 24c02 的一个地址的一个数据 | |
unsigned char At24c02Read(unsigned char addr) | |
{ | |
unsigned char num; | |
I2cStart(); | |
I2cSendByte(0xa0); // 发送写器件地址 | |
I2cSendByte(addr); // 发送要读取的地址 | |
I2cStart(); | |
I2cSendByte(0xa1); // 发送读器件地址 | |
num=I2cReadByte(); // 读取数据 | |
I2cStop(); | |
return num; | |
} |
在 EEPROM.h 文件中申明函数:
#ifndef __EEPROM_H_ | |
#define __EEPROM_H_ | |
#include <reg52.h> | |
sbit SCL=P2^1; | |
sbit SDA=P2^0; | |
void I2cStart(); | |
void I2cStop(); | |
unsigned char I2cSendByte(unsigned char dat); | |
unsigned char I2cReadByte(); | |
void At24c02Write(unsigned char addr,unsigned char dat); | |
unsigned char At24c02Read(unsigned char addr); | |
#endif |
main.c 函数中调用:
#include "reg52.h" | |
#include "LCD1602.h" | |
#include "DS18B20.h" | |
#include "DS1302.h" | |
#include "FengMingQi.h" | |
#include "EEPROM.h" | |
unsigned char DS18B20Display[8]; | |
unsigned char Ds1302DisplayTime[8]; | |
unsigned int temp; | |
void Delay1ms(unsigned int y) | |
{ | |
unsigned int x; | |
for(; y>0; y--) | |
{for(x=110; x>0; x--); | |
} | |
} | |
void main() | |
{ | |
unsigned int i; | |
TIME[0]=At24c02Read(1); // 读取 EEPROM 地址 1 内的工夫数据保留在 TIME 中 | |
TIME[1]=At24c02Read(2); | |
TIME[2]=At24c02Read(3); | |
Ds1302Init(); | |
while(1) | |
{LcdInit(); | |
DS1302datapros(); // 工夫数据处理 | |
At24c02Write(1,TIME[0]); // 在 EEPROM 地址 1 内写入工夫数据 | |
At24c02Write(2,TIME[1]); // 在 EEPROM 地址 2 内写入工夫数据 | |
At24c02Write(3,TIME[2]); // 在 EEPROM 地址 3 内写入工夫数据 | |
temp=DS18B20datapros(Ds18b20ReadTemp()); // 温度数据处理函数 | |
for(i=0;i<8;i++) | |
{LcdWriteData(Ds1302DisplayTime[i]); // 显示工夫 | |
} | |
LcdWriteCom(0xc0); // 写入 LCD 地址命令,扭转写入数据地址。for(i=0;i<6;i++) | |
{LcdWriteData(DS18B20Display[i]); // 显示温度 | |
} | |
FengMing(temp); // 蜂鸣器的函数 | |
Delay1ms(100); // 延时一段时间后再进行扫描温度。} | |
} |
呈现的问题及解决办法:
- 问题一:代码无误后烧录到单片机上,在一开始的几次开关电源中,工夫失常显示且实现保留性能,然而多开关几次后时分秒显示谬误,然而经测验,EEPROM 的保留功任然能实现,即关电后再次开机任然显示上次的工夫。初步检测是在 main 函数中一开始 读取 EEPROM 地址 1 内的工夫数据保留在 TIME 中时 呈现问题,这里应该须要一个 if 语句判断是否 EEPROM 中是否保留有工夫,然而这个判断用的变量也须要有保留性能,即在断电后该变量值不失落,所以目前并未想到什么好的办法进行判断。这里能够利用按键或红外零碎,即当按键按下时读取 EEPROM 中的工夫,未按下时则不读取。因为某些起因这里就不再进行展现了。若前期想到什么好办法会进行更新。
正文完