51单片机读取PCF8563时钟芯片
PCF8563时钟芯片介绍
PCF8563 有16 个8位寄存器:一个可自动增量的地址寄存器,一个内置32.768KHz的振荡器(带有一个内部集成的电容),一个分频器(用于给实时时钟RTC 提供源时钟),一个可编程时钟输出,一个定时器,一个报警器,一个掉电检测器和一个 400KHz I2C总线接口。 所有16个寄存器设计成可寻址的8位并行寄存器,但不是所有位都有用。前两个寄存器(内存地址00H,01H)用于控制寄存器和状态寄存器,内存地址02H~08H用于时钟计数器(秒~年计数器),地址 09H~0CH 用于报警寄存器(定义报警条件),地址 0DH 控制
CLKOUT管脚的输出频率,地址0EH 和0FH分别用于定时器控制寄存器和定时器寄存器。秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器,编码格式为 BCD,星期和星期报警寄存器不以BCD格式编码。
PCF8563的通信方式为IIC,管脚如下,其中主要用到的引脚为电源引脚、SCL和SDA引脚。
主要的寄存器如下,如果你只是需要进行时间的设置与读取的话,那么只需要看地址0x02到0x08的寄存器,它们分别存储的内容为秒,分,时,日,星期,月,年。
本文实现的功能很简单,主要就是对这些时间寄存器的内容进行读写,那么熟悉IIC的同学相信一定知道接下来要介绍的是什么了,那就是PCF8563的读写地址。众所周知,在IIC通信中,每一个设备都要有一个读写地址,PCF8563的读地址为0xA2,写地址为0xA3。知道读写地址以及我们要操作的芯片内部寄存器地址后就十分简单了。但是先不要着急,我们注意到PCF8563的0x00地址寄存器中,有一位STOP位,这个位是用于控制PCF8563的时钟启停的,0启动,1停止,因此如果我们不知道现在芯片时钟是开始还是停止状态,那么最好启动一下,向0x00地址发送0x00即可启动,停止是向0x00发送0x20。一般不需要多次发送开始命令,只要发送过一次开始命令,那么时钟就会一直运行,只要发送过一次停止命令,时钟就不会运行了。
这里额外说一下,PCF8563可以进行连续读写,也就是说,如果你要从0x02开始读,如果你不发送停止命令,那么它会按照地址顺序依次将数据都返回给你,当然,这里我们不使用这种连续读取的方法,我们还是用老办法,一个地址一个地址的读。感兴趣的同学可以自己尝试一下连续读。还有一点需要注意的是,虽然芯片的0x06寄存器存储的是星期信息,但是它不是自动计算的,你需要手动赋值,而且就算你赋值不对它也不会检查,其他位也是类似的,并且如果你赋值超过合法的范围可能会导致时钟出错,因此,需要注意这个地方。
代码
i2c.c
#include <STC8.H>
#include"intrins.h"// 板载芯片由于不需要杜邦线连接 因此信号传输速度较快 使用1us延时即可
// 使用外接PCF8563芯片时 不能使用这么短的IIC延时 应该增加到10us
sbit SCL=P6^1;
sbit SDA=P6^0;
//sbit SCL=P2^5;
//sbit SDA=P2^4;
void Delay10us() //@12.000MHz
{unsigned char i;_nop_();_nop_();i = 27;while (--i);
}void I2CStart(void)
{SCL=0;SDA=1;SCL=1;Delay10us();SDA=0;SCL=0;
}
void I2CStop()
{SCL=0;SDA=0;SCL=1;Delay10us();SDA=1;SCL=0;
}
// 在Write和Read操作中 8次对SCL时序进行操作后都立刻进行一次延时防止出现bug
void I2CSendByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++){SCL=0;if(Byte&(0x80>>i))SDA=1;elseSDA=0;Delay10us();SCL=1;Delay10us();}SCL=0;SDA=1;
}
unsigned char I2CReceiveByte()
{unsigned char Byte=0x00,i;for(i=0;i<8;i++){SCL=0;// 注意此处的延时!!!对于STC8A8K64S4A12这款芯片 需要在此处加上延时才能读取正确// 否则读取到的只有数据的第一位 原因未知Delay10us();SCL=1;Delay10us();if(SDA)Byte|=(0x80>>i);}SCL=0;SDA=1;return Byte;
}
void I2CSendAck(unsigned char ackbit)
{SCL=0;SDA=ackbit;SCL=1;Delay10us();SCL=0;SDA=1;
}
unsigned char I2CWaitAck()
{unsigned char ack;SCL=0;SCL=1;Delay10us();if(SDA)ack=0;elseack=1;SCL=0;SDA=1;return ack;
}
i2c.h
#ifndef __I2C_H__
#define __I2C_H__void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(unsigned char ackbit);#endif
PCF8563.c
#include"I2C.h"unsigned char PCF8563_ReadByte(unsigned char addr)
{unsigned char Byte=0x00;I2CStart();I2CSendByte(0xA2);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CStart();I2CSendByte(0xA3);I2CWaitAck();Byte=I2CReceiveByte();// 注意!!!这里一定要Ack 1I2CSendAck(1);I2CStop();return Byte;
}
void PCF8563_WriteByte(unsigned char addr,Byte)
{I2CStart();I2CSendByte(0xA2);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CSendByte(Byte);I2CWaitAck();I2CStop();
}
void PCF8563_Init()
{PCF8563_WriteByte(0x00,0x00);
}void PCF8563_SetTime(unsigned char*times)
{unsigned char i;for(i=0;i<8;i++){PCF8563_WriteByte(0x02+i,times[i]);}
}
main.c
#include <STC8.H>
#include"PCF8563.h"
#include"LCD12864.h"unsigned char cnt,year,month,day,hour,minute,second,D;
void Timer0Init(void) //5毫秒@12.000MHz
{AUXR |= 0x80; //定时器时钟1T模式TMOD &= 0xF0; //设置定时器模式TL0 = 0xA0; //设置定时初值TH0 = 0x15; //设置定时初值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时
}
/*
程序说明
程序实现了时间的设置读取与显示
当设置好时间后 即可进行读取 并在LCD12864上显示
第一行显示字符串Time 第二行显示年月日
第三行显示时分秒
注意 PCF8563_SetTime执行过一次后就要注释掉然后再次写入hex文件
否则每次开机都是从初始时间开始
使用外接PCF8563时 即使单片机断电 PCF的时钟依旧会继续执行 因为有外部电源
*/
void main()
{// 秒 分 时 日 星期 月 年// 分别对应的寄存器地址0x02 03 04 05 06 07 08unsigned char times[]={0x00,0x17,0x10,0x29,0x02,0x10,0x24};Timer0Init();ET0=1;EA=1;PCF8563_Init();LCD12864_Init();LCD12864_ShowStr(0,0,"Time");//PCF8563_SetTime(times);while(1){;}
}
// 如果要在中断中显示时间 则设置的LCD刷新时间不能太快
// 否则会显示错误 因为LCD本身的读写也需要时间
void Timer0Routine()interrupt 1
{cnt++;if(cnt<5)return;elsecnt=0;second=PCF8563_ReadByte(0x02);LCD12864_ShowH(2,4,second);minute=PCF8563_ReadByte(0x03);LCD12864_ShowH(2,2,minute);hour=PCF8563_ReadByte(0x04);LCD12864_ShowH(2,0,hour);day=PCF8563_ReadByte(0x05);LCD12864_ShowH(1,4,day);month=PCF8563_ReadByte(0x07);LCD12864_ShowH(1,2,month);year=PCF8563_ReadByte(0x08);LCD12864_ShowH(1,0,year);D=PCF8563_ReadByte(0x06);LCD12864_SetCursor(3,0);LCD12864_WriteData(D+'0');
}
重要提醒!!!,使用IIC读取PCF8563单个时间数据后,最后一步SendAck(1)输入的是1,而不是0!!!输入1代表我已经结束当前这次的读取了,不用再继续给我发送数据了,因为PCF8563是自动会返回连续寄存器内容的!没有这一句会导致下一次的IIC读取时序与PCF的时序不同而产生异常!!!这一注意事项其实在IIC的介绍中已经强调过!