跳到主要内容

使用 8051 和 DS12C887 的数字时钟电路

在本项目中,我将向您展示如何设计一个简单的数字时钟电路,使用 8051DS12C887 以及 DS1307 RTC 模块。

1. 介绍

数字时钟使用数字显示时间,并且在许多场景中都有应用,比如汽车、火车站、住宅、办公室等,以便提供准确的时间和日期。 在此类应用中,通常我们使用 RTC(实时时钟)IC 来精确显示时间和日期。

alt text

电路将时间显示在 LCD 上。对于此时钟,我们可以在任何时间点设置时间。 在本电路中,时钟可以工作在 24 小时制12 小时制,RTC 芯片通过 8051 控制器编程进行配置。

我将演示两种基于 8051 单片机的数字时钟电路:

  • 一种使用 RTC DS12C887

  • 另一种使用 RTC DS1307

2. 电路原理

这两个电路的主要原理是 8051 控制器持续从 RTC(实时时钟)IC 中读取数据,并按正确顺序处理这些数据以在 LCD 上显示时间。

3. 电路图 —— 使用 8051 和 DS12C887 的数字时钟电路

alt text

3.1 电路所需元件

  • 8051 单片机

  • 项目专用 PCB

  • 编程线缆

  • 直流电池或 12V、1A 适配器

  • DS12C887 RTC IC

  • 16x2 字母数字 LCD

  • 按钮开关 —— 4 个

  • 拨码开关 —— 3 个

  • 陶瓷电容 —— 33pF × 2

  • 12MHz 晶振

  • 电解电容 —— 10µF / 16V

  • 电阻(1/4 瓦)—— 10kΩ

  • 电位器 —— 10kΩ

  • 5V DC 电源电路

  • 单针连接导线

4. 电路设计

电路演示了如何将 RTC IC8051 控制器接口连接:

  • P0 口被用作 RTC 的数据端口

  • P2 口连接至 LCD 的数据引脚

  • 控制器的 P1.1、P1.2、P1.3 分别连接到 RS、RW、EN 引脚

  • P1.0 连接到 RTC 的 RESET 引脚

  • P1.4P1.5 连接到按钮,用于设置时间

  • P1.6 配置为 START 引脚,用于运行用户设定的时间

  • P3.3 连接到按钮,用于调用 set_time 函数

5. DS12C887 实时时钟

DS12C887 IC 在大多数应用中被广泛使用,用于提供精确的时间和日期。 该 IC 具有以下特点:

  • 提供 12 小时制24 小时制模式

  • 提供日历功能:日、月、年

  • 内部使用 锂电池,在断电时仍保持时间和日期更新

  • 拥有 128 字节 RAM

    • 其中 14 字节用于时间、日期和寄存器
    • 剩余 114 字节可用于通用数据存储

控制寄存器仅在外部供电时可访问。 此 IC 需要 ≥4.25V 电源,并在外部供电 200ms 后寄存器才可访问。

alt text

6. DS12C887 引脚说明

  • MOT:总线类型选择引脚

    • VCC → Motorola 总线模式
    • GND 或悬空 → Intel 总线模式
  • AD0–AD7(4–11 引脚):双向地址与数据线

  • GND(12 引脚):接地

  • CS(13 引脚):芯片选择引脚,低电平有效

  • AS(14 引脚):高电平脉冲用于地址与数据解复用

  • R/W(15 引脚):读写控制引脚

  • DS(17 引脚):数据选通信号

  • RESET(18 引脚):低脉冲复位所有中断标志,但不影响时间与日期

  • IRQ(19 引脚):低电平中断输出

  • SQW(23 引脚):输出方波信号

  • VCC(24 引脚):5V 电源

7. RTC 地址映射

DS12C887 RTC 拥有 128 字节 RAM,地址范围 00H – 7FH

  • 00H – 09H:用于存储 时钟、日历、闹钟数据
  • 0AH – 0DH:用于 状态与控制寄存器
  • 其余地址用于 通用数据存储
alt text

该 IC 包含 4 个主要寄存器:

  • 寄存器 A
  • 寄存器 B
  • 寄存器 C
  • 寄存器 D

这些寄存器需通过配置以获得准确的时间与日期,详细内容可参考 DS12C887 数据手册

代码

#include<reg51.h>
#include<absacc.h>
#define dataport P2
#define port P1
#define lcdport P3
sbit reset = port^0;
sbit rs =port^1;
sbit rw =port^2;
sbit e = port^3;
sbit dig_hr1=port^4;
sbit dig_min1=port^5;
sbit start=port^6;
int min1=0,hr1=0;
int min0=60,hr0=25;
unsigned char temp=60,hr,min,sec,num[60]={0x00,0x01,0x02,
0x03,0x04,0x05,0x06,0x07,0x08,0X09,0X10,0X11,0X12,0X13,
0X14,0X15,0X16,0X17,0X18,0X19,0X20,0X21,0X22,0X23,0X24,
0X25,0X26,0X27,0X28,0X29,0X30,0X31,0X32,0X33,0X34,0X35,
0X36,0X37,0X38,0X39,0X40,0X41,0X42,0X43,0X44,0X45,0X46,
0X47,0X48,0X49,0X50,0X51,0X52,0X53,0X54,0X55,0X56,0X57,
0X58,0X59};

void delay(unsigned int msec )
{
int i ,j ;
for(i=0;i<msec;i++)
for(j=0; j<1275; j++);
}

void lcd_cmd(unsigned char item)
{
dataport = item;
rs= 0;
rw=0;
e=1;
delay(1);
e=0;
return;
}

// function to send data
void lcd_data(unsigned char item)
{
dataport = item;
rs= 1;
rw=0;
e=1;
delay(1);
e=0;
return;
}

void lcd_string(unsigned char *str)
{
int i=0;
while(str[i]!='\0')
{
lcd_data(str[i]);
i++;
delay(1);
}
return;
}

lcd_int(int time_val)
{
int int_amt;
int_amt=time_val/10;
lcd_data(int_amt+48);
int_amt=time_val%10;
lcd_data(int_amt+48);
}


void lcd()
{
lcd_cmd(0x38);
delay(5);
lcd_cmd(0x0C);
delay(5);
lcd_cmd(0x80);
delay(5);
}

void set_rtc_time()
{
XBYTE[10]=0x20;
XBYTE[11]=0x82;
XBYTE[0]=0x00;
XBYTE[2]=min;
XBYTE[4]=hr;
XBYTE[7]=0x01;
XBYTE[8]=0x01;
XBYTE[9]=0x10;
XBYTE[1]=0xFF;
XBYTE[3]=0xFF;
XBYTE[5]=0xFF;
XBYTE[11]=0x22;
}

void set_hr1()
{
hr1++;
if(hr1>23)
hr1=0;
lcd_cmd(0xc3);
lcd_int(hr1);
lcd_data(':');
hr0=hr1;
}

void set_min1()
{
min1++;
if(min1>59)
min1=0;
lcd_cmd(0xc6);
lcd_int(min1);
min0=min1;
}

void set_time() interrupt 2
{
lcd_cmd(0x01);
if(start==0)
{
lcd_string("SET TIMING");
lcd_cmd(0xc3);
lcd_int(hr1);
lcd_data(':');
lcd_int(min1);
while(start==0)
{
delay(10);
if(dig_hr1==0)
set_hr1();
if(dig_min1==0)
set_min1();
}
}
lcd_cmd(0x01);
hr=num[hr1];
min=num[min1];
set_rtc_time();
lcd_cmd(0x80);
lcd_string("TIME:");
hr0=25;
min0=60;
}

bcdconv(unsigned char mybyte)
{
unsigned char x,y;
x= mybyte & 0x0F;
x=x | 0x30;
y= mybyte & 0xF0;
y=y>>4;
y=y | 0x30;
lcd_data(y);
lcd_data(x);
}

void read_rtc_display()
{

XBYTE[11]=0x02;
hr=XBYTE[4];
lcd_cmd(0x85);
//if(hr!=hr0)
{
bcdconv(hr);
lcd_data(':');
hr0=hr;
}

min=XBYTE[2];

//if(min!=min0)
{
bcdconv(min);
lcd_data(':');
//min0=min;
}
sec=XBYTE[0];

//if(sec!=temp)
{
bcdconv(sec);
//temp=sec;
}
}

void main()
{
reset=1;
lcd();
XBYTE[10]=0x20;
XBYTE[1]=0xFF;
XBYTE[3]=0xFF;
XBYTE[5]=0xFF;
XBYTE[11]=0x02;
lcd_cmd(0x01);
IE=0x84;
lcd_cmd(0x80);
lcd_string("TIME:");
while(1)
{
read_rtc_display();
}
}

8. 使用 8051 和 DS1307 的数字时钟电路

alt text

8.1 所需元件

  • 8051 单片机

  • 8051 开发板(可选)

  • 8051 编程器

  • DS1307 RTC 模块

  • 11.0592MHz 晶振(用于 8051)

  • 32.768KHz 晶振(用于 DS1307)

  • 电容:33pF × 2、10µF

  • 电阻:

    • 1kΩ × 2
    • 10kΩ × 2
    • 8 × 1kΩ 上拉电阻
    • 10kΩ 电位器
  • 3V 锂电池

  • 按钮开关

  • 16x2 LCD 显示屏

alt text

代码

#include<reg51.h>
#include<string.h>
#define lcd P3
sbit rs=P2^0;
sbit e=P2^1;

sbit c=P0^0;
sbit d=P0^1;

void delay (int);
void cmd (unsigned char);
void display (unsigned char);
void string (char *);
void init (void);

void i2c_start (void);
void i2c_stop (void);
void i2c_write (unsigned char);
unsigned char i2c_read (void);
void i2c_ack (void);
void i2c_noack (void);
void write (unsigned char, unsigned char, unsigned char);
unsigned char read (unsigned char, unsigned char);
void set_time (unsigned char, unsigned char, unsigned char);


int no[10]={48,49,50,51,52,53,54,55,56,57};
unsigned int temp1[3]=0;

void delay (int d)
{
unsigned char i=0;
for(;d>0;d--)
{
for(i=250;i>0;i--);
for(i=248;i>0;i--);
}
}

void cmd (unsigned char c)
{
lcd=c;
rs=0;
e=1;
delay(5);
e=0;
}
void display (unsigned char c)
{
lcd=c;
rs=1;
e=1;
delay(5);
e=0;
}
void string (char *p)
{
while(*p)
{
display(*p++);
}
}
void init (void)
{
cmd(0x38);
cmd(0x0c);
cmd(0x01);
cmd(0x80);
}


void i2c_start (void)
{
c=1;
d=1;
delay(1);
d=0;
delay(1);
}

void i2c_stop (void)
{
c=0;
delay(1);
d=0;
delay(1);
c=1;
delay(1);
d=1;
delay(1);
}
void i2c_write (unsigned char a)
{
char j;
for(j=0;j<8;j++)
{
c=0;
d=(a&0x80>>j)?1:0;
c=1;
}
}
unsigned char i2c_read (void)
{
char j;
unsigned char temp=0;
for(j=0;j<8;j++)
{
c=0;
if(d)
temp=temp|(0x80>>j);
c=1;
}
return temp;
}
void i2c_ack (void)
{
c=0;
delay(1);
d=1;
delay(1);
c=1;
delay(1);
while(d==1);
c=0;
delay(1);
}
void i2c_noack (void)
{
c=0;
delay(1);
d=1;
delay(1);
c=1;
delay(1);

}

void write (unsigned char sa, unsigned char w_addr, unsigned char dat)
{
i2c_start();
i2c_write(sa);
i2c_ack();
i2c_write(w_addr);
i2c_ack();
i2c_write(dat);
i2c_ack();
i2c_stop();
delay(10);
}
unsigned char read (unsigned char sa, unsigned char w_addr)
{
unsigned char buf=0;
i2c_start();
i2c_write(sa);
i2c_ack();
i2c_write(w_addr);
i2c_ack();
i2c_start();
i2c_write(sa|0x01);
i2c_ack();
buf=i2c_read();
i2c_noack();
i2c_stop();
return buf;
}
void set_time (unsigned char hour, unsigned char min, unsigned char sec)
{
unsigned int temp[3];
temp[0]=(((unsigned char)(sec/10))<<4)|((unsigned char)(sec%10));
temp[1]=(((unsigned char)(min/10))<<4)|((unsigned char)(min%10));
temp[2]=(((unsigned char)(hour/10))<<4)|((unsigned char)(hour%10));
write(0xd0,0x00,temp[0]);
write(0xd0,0x01,temp[1]);
write(0xd0,0x02,temp[2]);
}


void main()
{
unsigned char temp=0;
init();
string(" Digital Clock ");
cmd(0xc0);
string(" using 8051 ");

delay(3000);
cmd(0x01);
set_time(12,59,51);
delay(500);
while(1)
{
cmd(0x80);
temp=read(0xd0,0x00);
temp1[0]=((temp&0x7F)>>4)*10 + (temp&0x0F);
temp=read(0xd0,0x01);
temp1[1]=(temp>>4)*10 + (temp&0x0F);
temp=read(0xd0,0x02);
temp1[2]=(temp>>4)*10 + (temp&0x0F);

display(no[(temp1[2]/10)%10]);
display(no[temp1[2]%10]);
display(':');
display(no[(temp1[1]/10)%10]);
display(no[temp1[1]%10]);
display(':');
display(no[(temp1[0]/10)%10]);
display(no[temp1[0]%10]);

}
while(1);
}

9. 如何操作

  1. 首先将程序烧录到 8051 单片机

  2. 根据电路图连接元件

  3. 接通主电源

  4. 打开 LCD,可以看到当前时间显示

  5. 若需要设置时间:

    • START 引脚置低
    • 按下 P3.3 上的按钮
    • LCD 显示 “Set Time”
    • 使用连接到 P1.4 的按钮设置小时
    • 使用另一按钮设置分钟
  6. START 引脚置高,时钟开始运行

10. 电路应用

  • 可用于办公室、住宅、酒店、汽车等场景
  • 本项目可通过小幅修改支持 闹钟功能