DS1302实时时钟具有能计算2100年之前的秒、分、时、日、日期、星期、月、年的能力,还有闰年调整的能力。
图1:DS1302引脚示意图
其中:
X1、X2:32.768KHz晶振接入引脚。
GND:地。
:复位引脚,低电平有效,操作时高电平。
I/O:数据输入/输出引脚,具有三态功能。
SCLK:串行时钟输入引脚。
Vcc1:工作电源引脚。
Vcc2:备用电源引脚。 接入电池断电时提供1302电源
图2:控制寄存器
图3:日历、时钟寄存器
图4:单字节读写时序
LCD时序图(写命令(RS=L):设置显示坐标,写数据(RS=H))
LCD1602关键性的指令设置
1.清屏指令
功能:<1> 清除液晶显示器,即将 DDRAM 的内容全部填入"空白"的 ASCII
码 20H;
<2> 光标归位,即将光标撤回液晶显示屏的左上方;
<3> 将地址计数器(AC)的值设为 0。
2.进入模式设置指令
功能 : 设定每次定入 1 位数据后光标的移位方向 , 并且设定每次写入的一个字符是否移动。参数设定的情况如下所示:
位名 设置
I/D 0=写入新数据后光标左移 1=写入新数据后光标右移
S 0=写入新数据后显示屏不移动 1=写入新数据后显示屏整体右
移 1 个字符
3:显示开关控制指令
功能:控制显示器开/关、光标显示/关闭以及光标是否闪烁。参数设定的情
况如下:
位名 设置
D 0=显示功能关 1=显示功能开
C 0=无光标 1=有光标
B 0=光标闪烁 1=光标不闪烁
4.功能设定指令
功能:设定数据总线位数、显示的行数及字型。参数设定的情况如下:
位名 设置
DL 0=数据总线为 4 位 1=数据总线为 8 位
N 0=显示 1 行 1=显示 2 行
F 0=5×7 点阵/每字符 1=5×10 点阵/每字符
5:显示字符地址
要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,例如第二行第一个字符的地址是 40H, 那么是否直接写入 40H 就可以将光标定位
在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7
恒定为高电平 1 所以实际写入的数据应该是 01000000B(40H)
+10000000B(80H)=11000000B(C0H)。在 1602 中我们就用前 16 个就行了。第二行也一样用前 16 个地址。
/*
实验效果 :1602显示时钟,按K3进入时钟设置,按K1选择设置的时分秒日月,按K2选择
选择设置加1。
*/
#include
#include
#define lcd1602data P0
typedef unsigned char u8;
typedef unsigned int u16;
sbit E=P2^7; //设置液晶端口 使能信号
sbit RW=P2^5; //读写选择 读:H/写:L
sbit RS=P2^6; //数据命令选择 数据:H/命令:L
sbit key1=P3^1;
sbit key2=P3^0;
sbit key3=P3^2;
sbit SCLK=P3^6; //设置时钟端口
sbit IO=P3^4;
sbit RST=P3^5;
u16 read[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //设置秒分时日月周年读寄存器
u16 write[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //设置秒分时日月周年写寄存器
u8 time[]={0x30,0x30,0x22,0x08,0x02,0x05,0x19}; //存放初始时间
u16 setmark,setplace; //setmark检测key3是否按下,setplace选择要调整的时间块。
void delay_ms(u16 n);
void initConfiguration();
void lcdDisplay();
void lcdwrite_com(u8 datas);
void lcdwrite_datas(u8 datas);
void lcdinit();
void writebyte(u8 address,u8 datas);
u8 readbyte(u8 address);
void ds1302init();
void read_time();
void main()
{
u16 i;
initConfiguration(); //初始化
lcdinit();
ds1302init();
while(1)
{
if(setmark==0) //时钟正常运作
{
read_time();
}
else //调整时间
{
if(key1==0)
{
delay_ms(1);
if(key1==0)
{
setplace++; //按下key1要调整的时间块加一
setplace%=7;
}
while(i<50&&key1==0) //等待key1松开
{
i++;
delay_ms(10);
}
i=0;
}
if(key2==0)
{
delay_ms(1);
if(key2==0) //按下key2对应的初始时间加一
{
time[setplace]++;
if((time[setplace]&0x0f)>9) //换成BCD码。
{
time[setplace]=time[setplace]+6;
}
if((time[setplace]>=0x60)&&(setplace<2)) //分秒只能到59
{
time[setplace]=0;
}
if((time[setplace]>=0x24)&&(setplace==2)) //小时只能到23
{
time[setplace]=0;
}
if((time[setplace]>=0x32)&&(setplace==3)) //日只能到31
{
time[setplace]=0;
}
if((time[setplace]>=0x13)&&(setplace==4)) //月只能到12
{
time[setplace]=0;
}
if((time[setplace]>=0x7)&&(setplace==5)) //周只能到7
{
time[setplace]=1;
}
}
while((i<50)&&(0==key2))
{
i++;
delay_ms(10);
}
i=0;
}
}
lcdDisplay();
}
}
void delay_ms(u16 n) //准确延时一毫秒,晶振12M
{
u16 a,b;
for(;n>0;n--)
{
for(a=199;a>0;a--)
{
for(b=1;b>0;b--)
{
;
}
}
}
}
void initConfiguration() //初始化外部中断0
{
EA=1;
IT0=1;
EX0=1;
}
void init0() interrupt 0 //按下key3时间保持不变
{
delay_ms(10);
if(key3==0)
{
setmark=~setmark;
setplace=0;
ds1302init();
}
}
void lcdDisplay() //显示函数
{
lcdwrite_com(0x80+0x00); //确定写数据的位置
lcdwrite_datas('2');
lcdwrite_datas('0');
lcdwrite_datas('0'+time[6]/16); //写入年份的高位
lcdwrite_datas('0'+(time[6]&0x0f)); //低位
lcdwrite_datas('-');
lcdwrite_datas('0'+time[4]/16);
lcdwrite_datas('0'+(time[4]&0x0f)); //里面的小括号很重要
lcdwrite_datas('-');
lcdwrite_datas('0'+time[3]/16);
lcdwrite_datas('0'+(time[3]&0x0f));
lcdwrite_com(0x8c);
lcdwrite_datas('0'+(time[5]&0x07));
lcdwrite_com(0xc0); //跳线帽Joe接vcc
lcdwrite_datas('0'+time[2]/16);
lcdwrite_datas('0'+(time[2]&0x0f));
lcdwrite_datas('-');
lcdwrite_datas('0'+time[1]/16);
lcdwrite_datas('0'+(time[1]&0x0f));
lcdwrite_datas('-');
lcdwrite_datas('0'+time[0]/16);
lcdwrite_datas('0'+(time[0]&0x0f));
}
void lcdwrite_com(u8 datas) //lcd液晶写入指令操作 模拟时序
{
E=0;
RW=0;
RS=0;
lcd1602data=datas;
delay_ms(1);
E=1;
//delay_ms(1);
E=0;
}
void lcdwrite_datas(u8 datas) //lcd液晶写入数据操作
{
E=0;
RW=0;
RS=1;
lcd1602data=datas;
delay_ms(1); //延时不能太长否则调整不灵敏
E=1;
//delay_ms(1);
E=0;
}
void lcdinit() //LCD液晶初始化
{
lcdwrite_com(0x38); //8位总线,显示两行,5x7点阵
lcdwrite_com(0x0c); //开显示功能,无关标
lcdwrite_com(0x06); //光标右移,即数据依次右移
lcdwrite_com(0x01); //清屏
lcdwrite_com(0x80); //第一个数据位置
}
void writebyte(u8 address,u8 datas) //在寄存器里写时间数据,模拟时序
{
u8 i;
RST=0;
_nop_();
SCLK=0;
_nop_();
RST=1;
_nop_();
for(i=0;i<8;i++)
{
IO=address&0x01;
address>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
for(i=0;i<8;i++)
{
IO=datas&0x01;
datas>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;
}
u8 readbyte(u8 address) //读寄存器里的时间数据
{
u8 i,datas,dat;
RST=0;
//_nop_();
SCLK=0;
//_nop_();
RST=1;
//_nop_();
for(i=0;i<8;i++)
{
IO=address&0x01;
address>>=1;
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
//_nop_();
for(i=0;i<8;i++)
{
dat=IO;
datas=(datas>>1)|(dat<<7);
SCLK=1;
_nop_();
SCLK=0;
_nop_();
}
RST=0;
//_nop_();
SCLK=1;
//_nop_();
IO=0;
//_nop_();
IO=1;
//_nop_();
return datas;
}
void ds1302init() //在寄存器里写入初始时间
{
u8 i;
writebyte(0x8e,0x00); //关闭写保护寄存器
for(i=0;i<7;i++)
{
writebyte(write[i],time[i]);
}
writebyte(0x8e,0x80); //打开写保护寄存器
}
void read_time() //读取时钟时间 time数组存储
{
u8 i;
for(i=0;i<7;i++)
{
time[i]=readbyte(read[i]);
}
}
以上是我在学习过程中的一点总结,用的是普中的51单片机·。