【Proteus仿真】51单片机+PCF8563+LCD1602时间显示

【Proteus仿真】51单片机+PCF8563+LCD1602时间显示


  • Proteus仿真效果演示
    在这里插入图片描述

看似简单的一个IIC通讯模块,硬是折腾了2天才出这样的仿真效果,在此调试期间,总是有那么几位数据显示不正常,调试的时候找不到北,有些数据看似一条正确的道路方向,这往往就是一个陷阱,纠缠于此,就很难有所突破,很难找到正确的路。推倒重来,网上搜索可参照的能用的案例很少,都是零星碎片的知识,没有完整的成功案例,有时需要摸着石头过河,有时候需要另辟蹊径。

  • 采用定时器0一秒钟刷新一次数据。

主程序示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

#include <reg52.h>
#include "LCD1602.h"
#include "IIC.h"

#define uchar unsigned char
#define uint unsigned int

uchar flags = 1; //1秒定时标志
static uchar cnt = 0; //用来记录中断次数,设置的中断定时为1ms,中断200次即一秒钟
unsigned char \*weeklist[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

unsigned char write_buff[8],read_buff[8];
//-----------延时用于稳定-------------
void delay\_kk(uint k)
{
while(k--);
}
//-----------时间预设定值---------
void time\_init()
{
iic\_send\_add\_byte(0x02,0x30); //0秒
iic\_send\_add\_byte(0x03,0x26); //0分钟
iic\_send\_add\_byte(0x04,0x11); //0小时
iic\_send\_add\_byte(0x05,0x04); //3号
iic\_send\_add\_byte(0x06,0x02); //week
iic\_send\_add\_byte(0x07,0x01); //1月 20世纪的
iic\_send\_add\_byte(0x08,0x22); //2022年
}

//----------------PCF8563初始化-----------------
void pcf\_init()
{
iic\_send\_add\_byte(0x00,0x00); //启动时钟
}

//----------------BCD转10进制-----------
uchar bcd\_dec(uchar bat)
{
// uchar temp1,temp2,tol;
// temp1=bat&0x0f;
// temp2=(bat&0xf0)>>4;
// tol=temp2\*10+temp1;
return ( (bat/16\*10) + (bat%16) );
//return tol;
}
//------------LCD1602显示:年,月,日,小时,分钟,秒,
void display\_time(uchar sec,min,hour,day,mon,year,unsigned char \*week)
{
LCD\_Write\_String(1,0,"20");
LCD1602\_WriteData(year/10 +'0');
LCD1602\_WriteData(year%10 +'0');
LCD1602\_WriteData('-');
LCD1602\_WriteData(mon/10 + 0x30);
LCD1602\_WriteData(mon%10 + 0x30);
LCD1602\_WriteData('-');
LCD1602\_WriteData(day/10 + 0x30);
LCD1602\_WriteData(day%10 + 0x30);
LCD\_Write\_String(13,0,week);

LCD1602\_WriteCom(0xc0 + 4);//第二行第四列
LCD1602\_WriteData(hour/10 + '0');
LCD1602\_WriteData(hour%10 + '0');
LCD1602\_WriteData(':');
LCD1602\_WriteData(min/10 + 0x30);
LCD1602\_WriteData(min%10 + 0x30);
LCD1602\_WriteData(':');
LCD1602\_WriteData(sec/10 + 0x30);
LCD1602\_WriteData(sec%10 + 0x30);
}

void TimeInit()
{
EA = 1; //打开中断总开关
ET0 = 1; //打开T0中断开关
TMOD = 0x01; //设置定时器工作在模式1
TH0=(65536-5000)/256; //高四位寄存器的值;//5000us=5ms
TL0=(65536-5000)%256; //低四位寄存器的值;
TR0 = 1; //允许定时器T0工作
}
void main()
{
uchar sec,min,hour,day,mon,year,week,sec1,min1,hour1,day1,mon1,year1,\*week1;
//PCF8563读出的变量和代转换成的十进制变量
TimeInit();
LCD1602\_Init();
// iic\_send\_add\_byte(0x00,0x20); // 关闭时钟
// delay\_kk(1000);
// time\_init(); //时钟芯片初始时间设置
// pcf\_init();
delay\_kk(1000);
while(1)
{
if(flags){
ReadData1(0x02,0x07,read_buff) ;/\*多字节\*/
sec = 0x7f&read_buff[0];//秒
min = 0x7f&read_buff[1] ;
hour = 0x3f&read_buff[2];
day = 0x3f&read_buff[3];//日期
week = 0x07&read_buff[4];//周不需要BCD转码
mon = 0x1f&read_buff[5];//月份
year = 0xff&read_buff[6] ;
sec1=bcd\_dec(sec);//将读取的BCD码秒转换成十进制秒以便运算
min1=bcd\_dec(min);
hour1=bcd\_dec(hour);
day1=bcd\_dec(day);
mon1=bcd\_dec(mon);
year1=bcd\_dec(year);
week1 = weeklist[week];
// switch(week-1){
// case 6: week1="Sat";break;
// case 5: week1="Fri";break;
// case 4: week1="Thu";break;
// case 3: week1="Wes";break;
// case 2: week1="Tue";break;
// case 1: week1="Mon";break;
// case 0: week1="Sun";break;
// }
flags=0;
}
display\_time(sec1,min1,hour1,day1,mon1,year1,week1); //LCD1602显示时间
}
}
void InterruptTimer0() interrupt 1
{

TH0=(65536-5000)/256; //高四位寄存器的值;//5000us=5ms
TL0=(65536-5000)%256; //低四位寄存器的值;
cnt++; //中断次数计数值加1
if(cnt>=200) //中断次数达到200次即为1秒
{
cnt = 0; //清零,重新记录中断次数
flags = 1; //设置1秒定时标志位1
}
}


BCD码转10进制

1
2
3
4
5
6
7
8
9
10
//----------------BCD转10进制-----------
unsigned char bcd\_dec(unsigned char bat)
{
unsigned char temp1,temp2,tol;
temp1=bat&0x0f;
temp2=(bat&0xf0)>>4;
tol=temp2\*10+temp1;
return tol;
}

或者

1
2
3
4
5
	unsigned char bcd\_dec(unsigned char bat)
{
return ( (bat/16\*10) + (bat%16) );
}

星期数据

  • 通过switch语句条件也可以
1
2
3
4
5
6
7
8
9
10
			switch(week-1){
case 0: week1="Sun";break;
case 6: week1="Sat";break;
case 5: week1="Fri";break;
case 4: week1="Thu";break;
case 3: week1="Wed";break;
case 2: week1="Tue";break;
case 1: week1="Mon";break;
default :week1="---";break;

我使用的是

1
2
3
4
unsigned char \*weeklist[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
unsigned char\*week1;
week1 = weeklist[week];

多字节连续读数据函数(关键函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void ReadData1(unsigned char address,unsigned char count,unsigned char \* buff) /\*多字节\*/
{
unsigned char i;
iic\_start();
iic\_send\_byte(0xa2); /\*写命令\*/
iic\_ack();
iic\_send\_byte(address); /\*写地址\*/
iic\_ack();
iic\_start();
iic\_send\_byte(0xa3); /\*读命令\*/
iic\_ack();
for(i=0;i<count;i++)
{
buff[i]=Readbyte();
if(i<count-1)
WriteACK(0);
}
WriteACK(1);
iic\_stop();
}

读1byte数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned char Readbyte()
{
unsigned char i,bytedata=0;
SDA=1;
\_nop\_();
for(i=0; i<8; i++)
{
SCL=1;
delay();
bytedata<<=1;
if(SDA==1)bytedata=bytedata|0x01;
delay();
SCL=0;
delay();
}
return (bytedata);
}

程序源码和仿真资源

1
2
3
链接:https://pan.baidu.com/s/1nEQlL9xQAVBH9vpFbOw11Q 
提取码:t3yi