【Proteus仿真】LCD1602+DS1307按键设置简易时钟

【Proteus仿真】LCD1602+DS1307按键设置简易时钟


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

功能说明

通过按键设置当前时间,按键调整时间只实现了对时分秒的设置,年月日以及星期是程序代码写入的,按键不能进行调整,功能需自行完善。

*

示例代码

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/\* Main.c file generated by New Project wizard
\*
\* Created: 周四 6月 23 2022
\* Processor: AT89C51
\* Compiler: Keil for 8051
\*/

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

电路图说明:
将单片机P2.7和P1.0分别与RTC(实时时钟)的时钟信号SCL端
和数据信号SDA端相连, RTC的sout引脚与单片机P3.2相连,RTC的
地址是11010000(0xD0)独立键盘k0~K3接P1.1~P1.3
程序功能:
使用时钟芯片ds1307制作一个实时时钟
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
#include <reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
//实时时钟两信号线
sbit scl=P2^7;
sbit sda=P1^0;
//独立键盘扫描位
sbit k0=P1^1;
sbit k1=P1^2;
sbit k2=P1^3;
//液晶显示器控制位
sbit lcd_rs=P3^4;
sbit lcd_e=P3^5;
void init();//初始化IIC总线,使总线处于备用状态,
//初始化lcd并显示上面table1和table2的值,初始化定时器0,初始化变量时分秒


/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*IIC总线控制函数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void IIC\_start();//IIC通信开始信号
void IIC\_stop();//IIC通信停止信号
void IIC\_un\_ack();//单片机发送非应答位
//void IIC\_ack();//单片机发送应答位
void IIC\_check\_ack();//单片机检查应答位
void IIC\_write\_byte(uchar);//发送一字节数据
uchar IIC\_read\_byte();//接收一字节数据
void nop();//5us空操作延迟
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
/\*\*\*\*\*\*\*\*\*\*ds1307读写函数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void ds1307\_write\_date(uchar ,uchar ,uchar);//向指定器件写一字节数据
//参数1器件地址,参数2要存的数据存放位置8位地址
//参数3要写的数据

uchar ds1307\_read\_date(uchar ,uchar );//从指定器件读一字节数据,
//参数1被通信器件地址,参数2要读的数据存放位置8位地址
//函数返回读取的数据。
void delay(uint);//基数1ms延迟程序
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/



/\*\*\*\*\*\*\*\*\*\*\*\*\*\*LCD操作函数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

uchar hour,minute,second,k0_num;//时分秒变量,菜单键k0第几次按下计数,ds1307跟新标志
//时钟初显示
uchar code table1[]="2022-06-23 Thu";
uchar code table2[]=" 16:31:15 ";//2022年6月23日16:31:15



void lcd\_write\_com(uchar);//向液晶显示器写指令设置其显示的格式
void lcd\_write\_date(uchar);//向液晶显示器写显示的数据

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*时钟函数\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void lcd\_write\_second(uchar);//向lcd写秒
void lcd\_write\_minute(uchar);//向lcd写分
void lcd\_write\_hour(uchar);//向lcd写时
void run\_time();//时钟走动显示
void keyscan();//菜单键扫描
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

/\*\*\*\*\*\*\*\*\*十进值与BCD码之间互换\*\*\*\*\*\*\*\*\*\*/
uchar b_second,b_minute,b_hour;
uchar dectobcd(uchar);//十进值转bcd码
uchar bcdtodec(uchar);//bcd码转十进值
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

void main()
{
init();
while(1)
{
keyscan();
}
}

void nop()
{
\_nop\_();\_nop\_();\_nop\_();
\_nop\_();\_nop\_();\_nop\_();
}

void init()
{
uchar i;
//将数据线和时钟线拉高,处于备用状态即非忙
sda=1;
nop();
scl=1;
nop();

//初始化写入LCD的时分秒
second=16;
minute=23;
hour=16;
//初始化ds1307
ds1307\_write\_date(0xd0,0x07,0x10);//初始化1307的控制字,使其SOUT引脚输出1Hz的方波。
//初始化1307的时间
ds1307\_write\_date(0xd0,6,0x14);
ds1307\_write\_date(0xd0,5,0x06);
ds1307\_write\_date(0xd0,4,0x09);
ds1307\_write\_date(0xd0,3,0x00);
ds1307\_write\_date(0xd0,2,0x23);
ds1307\_write\_date(0xd0,1,0x59);
ds1307\_write\_date(0xd0,0,0x55);

//初始化液晶显示器
lcd\_write\_com(0x38);//设置16\*2行,5\*7点阵,8位数据接口
lcd\_write\_com(0x06);//写字符指针和光标自动向右移动
lcd\_write\_com(0x0c);//开显示,不显示光标,光标不闪烁
lcd\_write\_com(0x01);//显示清屏
for(i=0;i<15;i++)
{
lcd\_write\_date(table1[i]);
}
lcd\_write\_com(0x80+0x40);
for(i=0;i<10;i++)
{
lcd\_write\_date(table2[i]);
}

//开启外部中断0
EA=1;
EX0=1;
IT0=1;
}
void IIC\_start()
{
//在时钟信号有效scl=1时,数据线负跳变
sda=1;
nop();
scl=1;
nop();
sda=0;
nop();
scl=0;//拉低为下个时钟数据作准备
nop();
}
void IIC\_stop()
{
//在时钟信号有效scl=1时,数据线正跳变,释放总线,使总线处于备用状态即非忙
sda=0;
nop();
scl=1;
nop();
sda=1;
nop();
nop();
nop();
}
void IIC\_un\_ack()
{
//时钟信号有效时,sda给1,给出非应答
sda=1;
nop();
scl=1;
nop();
scl=0;//拉低为下个时钟数据作准备
nop();
}
/\*
void IIC\_ack()
{
//时钟信号有效时,sda给0,给出应答
sda=0;
nop();
scl=1;
nop();
scl=0;//拉低为下个时钟数据作准备
nop();
}
\*/
void IIC\_check\_ack()
{
//uchar i;
//时钟信号有效时,检测数据信号sda是否为0,给0说明有应答信号
scl=1;
//while((sda==1)&&(i<255)) i++;
nop();
scl=0;//拉低为下个时钟数据作准备
nop();
}
uchar IIC\_read\_byte( )
{
uchar i,temp;//temp用于存放从数据线读到的数据

for(i=0;i<8;i++)//从数据线读取一个字节(8位)
{
scl=1;
nop();
temp=(temp<<1)|sda;//temp左移,腾出第0位,然后将数据线上的一位数用或
//运算存放到temp的第0位
scl=0;//拉低为下个时钟数据作准备
nop();
}
return temp;
}

void IIC\_write\_byte(uchar byte)
{
uchar i;
for(i=0;i<8;i++)//向数据线写一个字节(8位)
{
byte=byte<<1;//左移位,将byte第7位移到PSW中的进位标志位CY中
sda=CY;
nop();
scl=1;
nop();
scl=0;//拉低为下个时钟数据作准备
nop();
}
sda=1;//释放总线,准备接收被控设备应答信号
nop();
}

void ds1307\_write\_date(uchar d_address,uchar r_address,uchar date)
{

IIC\_start();
IIC\_write\_byte(d_address & (~0x01));//写器件地址,方向是写,所以或上0x00
IIC\_check\_ack();//等待应答
IIC\_write\_byte(r_address);//写存储器数据存放的8位地址
IIC\_check\_ack();//等待应答
IIC\_write\_byte(date);//写一字节数据
IIC\_check\_ack();//等待应答
IIC\_stop();
}

uchar ds1307\_read\_date(uchar d_address,uchar r_address)
{
uchar r;
IIC\_start();
IIC\_write\_byte(d_address & (~0x01));//写器件地址,方向是写
IIC\_check\_ack();//等待应答
IIC\_write\_byte(r_address);//写存储器数据存放的8位地址
IIC\_check\_ack();//等待应答

//换方向
IIC\_start();
IIC\_write\_byte(d_address|0x01);//写器件地址,方向是读,所以或上1
IIC\_check\_ack();
r=IIC\_read\_byte();//读取一个字节数据
IIC\_un\_ack();
IIC\_stop();
return r;//将读到的数据返回
}
void delay(uint z)//晶振频率12MHz时,z=1时延迟1ms
{
uint a,b;
for(a=z;a>0;a--)
for(b=110;b>0;b--);
}



void lcd\_write\_com(uchar com)//lcd写指令函数定义
{
lcd_rs=0;
P0=com;
delay(1);
lcd_e=1;
lcd_e=0;
delay(2);
}
void lcd\_write\_date(uchar date)//lcd写数据函数定义
{
lcd_rs=1;
P0=date;
delay(1);
lcd_e=1;
lcd_e=0;
delay(2);
}


void lcd\_write\_second(uchar sec)//向lcd写秒
{
uchar h,l;
h=sec/10;
l=sec%10;
lcd\_write\_com(0x80+0x40+10);
lcd\_write\_date(0x30+h);
lcd\_write\_date(0x30+l);
}
void lcd\_write\_minute(uchar min)//向lcd写分
{
uchar h,l;
h=min/10;
l=min%10;
lcd\_write\_com(0x80+0x40+7);
lcd\_write\_date(0x30+h);
lcd\_write\_date(0x30+l);
}
void lcd\_write\_hour(uchar hou)//向lcd写时
{
uchar h,l;
h=hou/10;
l=hou%10;
lcd\_write\_com(0x80+0x40+4);
lcd\_write\_date(0x30+h);
lcd\_write\_date(0x30+l);
}



void inter0()interrupt 0
{
run\_time();
}

void run\_time()//时钟走动函数定义
{
//从1307中读出时间
b_second=ds1307\_read\_date(0xd0,0);//返回函数r值秒
b_minute=ds1307\_read\_date(0xd0,1);
b_hour=ds1307\_read\_date(0xd0,2);
//将读出的时间转换成十进值
second=bcdtodec(b_second);
minute=bcdtodec(b_minute);
hour=bcdtodec(b_hour);
//更新lcd显示时间
lcd\_write\_second(second);
lcd\_write\_minute(minute);
lcd\_write\_hour(hour);
}

uchar dectobcd(uchar dec)//十进值到BCD码
{
uchar x,y;
x=dec/10;//得到bcd的十位数
y=dec%10;//得到bcd的个位数
y=(x<<4)|y;
return y;
}
uchar bcdtodec(uchar bcd)//bcd码转十进值
{
uchar x,y;
y=bcd/16;//得到十进制的十位数
x=bcd%16;//得到十进制的个位数
y=y\*10+x;
return y;
}

void keyscan()//菜单键按下扫描函数定义
{
k0=1;
k1=1;
k2=1;
//判断菜单键k0是否按下
if(k0==0)
{
delay(5);
if(k0==0)
{
while(!k0);//松手检测
k0_num++;//记录菜单键k0是第几次按下,而判断是调节秒、分还是时。
if(k0_num==1)//指示调节秒
{
EX0=0;//关闭外部中断0,使时间走动停止,准备调时间
ds1307\_write\_date(0xd0,0,0x80|b_second);
lcd\_write\_com(0x80+0x40+11);//数据指针和光标移动秒出
lcd\_write\_com(0x0f);//使光标显示,并光标处字符格符闪烁,用来提示用户此时可以调节
//什么?是秒,是分还是时。
}
if(k0_num==2)//指示调节分
{
lcd\_write\_com(0x80+0x40+8);//数据指针和光标移动分出
}
if(k0_num==3)//指示调节时
{
lcd\_write\_com(0x80+0x40+5);//数据指针和光标移动时出
}
if(k0_num==4)//退出时间调节菜单
{
k0_num=0;//菜单键k0按下清零
lcd\_write\_com(0x0c);//恢复到时钟走动状态,即光标不显示,不闪烁
//将所调的时间转换成bcd码
b_second=dectobcd(second);
b_minute=dectobcd(minute);
b_hour=dectobcd(hour);
//将所调的时间向时钟写入
ds1307\_write\_date(0xd0,2,b_hour);
ds1307\_write\_date(0xd0,1,b_minute);
ds1307\_write\_date(0xd0,0,b_second);

EX0=1;//开外部中断0,时间开始继续走动
}

}
}
//判断是否按下过时间调节菜单键k0,而处于需要调节时间状态
if(k0_num)
{
//判断是否是上调键k1按下
if(k1==0)
{
delay(5);
if(k1==0)
{
while(!k1);//松手检测
if(k0_num==1)//上调秒
{
second++;
if(second==60)
second=0;
lcd\_write\_second(second);//向lcd写入被修改的时间
lcd\_write\_com(0x80+0x40+11);//光标位置重定位到所要调的时间处

}
if(k0_num==2)//上调分
{
minute++;
if(minute==60)
minute=0;
lcd\_write\_minute(minute);
lcd\_write\_com(0x80+0x40+8);
}
if(k0_num==3)//上调时
{
hour++;
if(hour==24)
hour=0;
lcd\_write\_hour(hour);
lcd\_write\_com(0x80+0x40+5);
}
}
}
//判断是否是下调键k2按下
if(k2==0)
{
delay(5);
if(k2==0)
{
while(!k2);
if(k0_num==1)//下调节秒
{
second--;
if(second==255)
second=59;
lcd\_write\_second(second);
lcd\_write\_com(0x80+0x40+11);
}
if(k0_num==2)//下调节分
{
minute--;
if(minute==255)
minute=59;
lcd\_write\_minute(minute);
lcd\_write\_com(0x80+0x40+8);
}
if(k0_num==3)//下调节时
{
hour--;
if(hour==255)
hour=23;
lcd\_write\_hour(hour);
lcd\_write\_com(0x80+0x40+5);
}
}
}
}
}


仿真资源和程序源码

本实验基于Proteus8.12平台

1
2
3
链接:https://pan.baidu.com/s/15j7vId51clOJlcNxIMkcww 
提取码:8z3w


做任何决定,最好能不委屈自己,也不为难别人。