PCF8653是PHILIPS公司(或类似品牌,因具体制造商可能随时间变化)推出的一款工业级多功能时钟/日历芯片,它具有多种功能特性,广泛应用于各种需要实时时钟和日历功能的电子设备中。以下是对PCF8653的详细介绍:
一、主要功能
- 实时时钟与日历:提供秒、分、时、日、月、年的实时时间显示,支持BCD码编码格式。
- 报警功能:可以设置多个报警条件,如时间到达特定值时触发报警。
- 定时器功能:内置定时器,可用于实现定时任务或倒计时功能。
- 时钟输出功能:提供可编程的时钟输出,可以根据需要设置输出频率。
- 中断输出功能:当特定事件发生时(如时间到达、报警触发等),能够产生中断信号。
二、接口与通信
- I2C总线接口:PCF8653通过I2C总线接口与外部设备进行通信,支持标准的I2C读写操作。
- 地址设置:具有固定的I2C从设备地址,用于在I2C总线上识别和区分不同的设备。
三、技术规格
- 工作电压:通常具有较宽的工作电压范围,如1.0V至5.5V,具体范围可能因不同型号而有所差异。
- 工作电流:低功耗设计,典型工作电流较低,有助于延长电池寿命。
- 总线速度:支持较高的总线速度,如400Kbits/s,以实现快速的数据传输。
四、应用场景
- 嵌入式系统:作为嵌入式系统中的实时时钟模块,提供准确的时间基准。
- 智能家居:在智能家居设备中用于定时控制、闹钟设置等功能。
- 工业控制:在工业控制系统中,用于记录事件时间、定时执行任务等。
五、注意事项
- 在使用PCF8653时,需要遵循I2C总线的通信协议和时序要求。
- 根据具体的应用需求,合理设置芯片的各项参数和功能。
- 在连接外部电路时,注意电源和地线的正确连接,以及信号线的抗干扰措施。
六、总结
PCF8653是一款功能丰富、性能稳定的工业级多功能时钟/日历芯片,通过I2C总线接口与外部设备通信,广泛应用于各种需要实时时钟和日历功能的电子设备中。在使用时,需要遵循相关的技术规格和注意事项,以确保其正常工作并发挥最佳性能。
七、代码
stm32源码.c文件如下:
/*
============================================================
MODEL NAME : PCF8563.c
MODEL FUNCTION : Define system globe variables and functions
RELATION MODEL : Others models in project
============================================================
*/
#include "PCF8563.c.h"
/***********************************************************
Define implements of functions in PCF8563.c model
************************** START **************************/
uint16_t t; // 延时变量
uint8_t date[10] = {'0','1','2','3','4','5','6','7','8','9'}; /* 或与FCCLK相同 */
/****************************************************************************
* 名称:ShortDelay()
* 功能:短延时函数。
* 入口参数:n 延时参数
* 出口参数:无
****************************************************************************/
void ShortDelay(uint8_t n)
{
while(n--);
}
/****************************************************************************
* 名称:LongDelay()
* 功能:长延时函数。
* 入口参数:n 延时参数
* 出口参数:无
****************************************************************************/
void LongDelay(uint16_t n)
{
while(n--);
}
/*******************************************************************
* 函数名称: StartI2C()
* 功能描述: 启动I2C总线,即发送I2C起始条件.
* 入口参数:无
* 出口参数:无
********************************************************************/
void StartI2C(void)
{
PCF8563_I2C_SDA_H; ShortDelay(I2CDLY);
PCF8563_I2C_SCL_H; ShortDelay(I2CDLY);
PCF8563_I2C_SDA_L; ShortDelay(I2CDLY);
PCF8563_I2C_SCL_L; ShortDelay(I2CDLY);
}
/*******************************************************************
* 函数名称: StopI2C()
* 功能描述: 结束I2C总线,即发送I2C结束条件.
* 入口参数:无
* 出口参数:无
********************************************************************/
void StopI2C(void)
{
PCF8563_I2C_SDA_L; ShortDelay(I2CDLY);
PCF8563_I2C_SCL_H; ShortDelay(I2CDLY);
PCF8563_I2C_SDA_H; ShortDelay(I2CDLY);
}
/*******************************************************************
* 函数名称: InitI2C()
* 功能描述: 初始化I2C.
* 入口参数:无
* 出口参数:无
********************************************************************/
void InitI2C(void)
{
GPIO_InitStructure.GPIO_Pin = PCF8563_I2C_SCL_PIN | PCF8563_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(PCF8563_I2C_PORT, &GPIO_InitStructure);
GPIO_SetBits(PCF8563_I2C_PORT,PCF8563_I2C_SCL_PIN);
PCF8563_I2C_SCL_L;
StopI2C();
}
/*******************************************************************
* 函数名称: SendByte(uint8 dat)
* 功能描述: 将数据dat发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0 假)
发送数据正常ack=1; ack=0表示被控器无应答或损坏。
* 入口参数:dat 要传送的数据
* 出口参数:ack 应答信号
********************************************************************/
uint8_t SendByte(uint8_t dat)
{
uint8_t i,ack;
for(i=0;i<8;i++) //要传送的数据长度为8位
{
if((dat<<i)&0x80) //判断发送位
PCF8563_I2C_SDA_H;
else
PCF8563_I2C_SDA_L;
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_H; //置时钟线为高,通知被控器开始接收数据位
ShortDelay(I2CDLY); //保证时钟高电平周期大于4μs
ShortDelay(I2CDLY);
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_L;
}
ShortDelay(I2CDLY);
PCF8563_I2C_SDA_H; //8位发送完后释放数据线,准备接收应答位
ShortDelay(I2CDLY);
PCF8563_I2C_SDAModeIn();
PCF8563_I2C_SCL_H;
ShortDelay(I2CDLY);
if(PCF8563_I2C_SDA_READ()) //判断是否接收到应答信号
ack=0;
else
ack=1;
PCF8563_I2C_SCL_L;
PCF8563_I2C_SDAModeOut();
ShortDelay(I2CDLY);
return(ack);
}
/********************************************************************
*应答子函数
*原型: void Ack_I2c(uint8 a);
*功能:主控器进行应答信号,(可以是应答或非应答信号)
********************************************************************/
void Ack_I2c(uint8_t a)
{
//在此发出应答或非应答信号
if(a==0)
PCF8563_I2C_SDA_L;
else
PCF8563_I2C_SDA_H;
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_H;
ShortDelay(I2CDLY); //时钟低电平周期大于4μs
ShortDelay(I2CDLY);
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_L; //清时钟线,钳住I2C总线以便继续接收
ShortDelay(I2CDLY);
}
//向有子地址器件发送1个字节数据函数
uint8_t ISendByte(uint8_t sla,uint8_t suba,uint8_t dat)
{
StartI2C();
if(!SendByte(sla))
return(0);
if(!SendByte(suba))
return(0);
if(!SendByte(dat))
return(0);
StopI2C();
return(1);
}
/*******************************************************************
*向有子地址器件发送多字节数据函数
*函数原型: uint8 ISendStr(uint8 sla,uint8 suba,ucahr *s,uint8 no);
*功能: 从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
如果返回1表示操作成功,否则操作有误。
*注意: 使用前必须已结束总线。
********************************************************************/
uint8_t ISendStr(uint8_t sla,uint8_t suba,uint8_t *s,uint8_t no)
{
uint8_t i;
StartI2C(); //启动总线
if(!SendByte(sla)) //发送器件地址
return(0);
if(!SendByte(suba)) //发送器件子地址
return(0);
for(i=0;i<no;i++)
{
if(!SendByte(*s++)) //发送数据
return(0);
}
StopI2C(); //结束总线
return(1);
}
/*******************************************************************
*字节数据接收函数
*函数原型: uint8 RcvByte();
*功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数。
********************************************************************/
uint8_t RcvByte()
{
uint8_t retc,i;
retc=0;
PCF8563_I2C_SDA_H; //置数据线为输入方式+I/O方向
PCF8563_I2C_SDAModeIn();
for(i=0;i<8;i++)
{
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_L; //置时钟线为低,准备接收数据位
ShortDelay(I2CDLY); //时钟低电平周期大于4.7μs
ShortDelay(I2CDLY);
ShortDelay(I2CDLY);
PCF8563_I2C_SCL_H; //置时钟线为高使数据线上数据有效
ShortDelay(I2CDLY);
retc=retc<<1;
if(PCF8563_I2C_SDA_READ())
retc=retc+1; //读数据位,接收的数据位放入retc中
ShortDelay(I2CDLY);
}
PCF8563_I2C_SDAModeOut();
PCF8563_I2C_SCL_L;
ShortDelay(I2CDLY);
return(retc);
}
/*******************************************************************
*向有子地址器件读取1个字节数据函数
*函数原型: uint8 ISendByte(uint8 sla,uint8 suba,uint8 rdat);
*功能: 从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
地址sla,子地址suba,读出的内容。
如果返回1表示操作成功,否则操作有误。
*注意: 使用前必须已结束总线。
********************************************************************/
uint8_t IRcvByte(uint8_t sla,uint8_t suba,uint8_t rdat)
{
StartI2C(); //启动总线
if(!SendByte(sla)) //发送器件地址
{ return(0); }
ShortDelay(I2CDLY);
if(!SendByte(suba)) //发送器件子地址
{ return(0); }
StartI2C();
if(!SendByte(sla+1))
return(0);
rdat = RcvByte();
rdat = rdat;
Ack_I2c(1); //发送非应答位
StopI2C(); //结束总线
return(1);
}
/*******************************************************************
*向有子地址器件读取多字节数据函数
*函数原型: uint8 ISendStr(uint8 sla,uint8 suba,ucahr *s,uint8 no);
*功能: 从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
如果返回1表示操作成功,否则操作有误。
*注意: 使用前必须已结束总线。
********************************************************************/
uint8_t IRcvStr(uint8_t sla,uint8_t suba,uint8_t *s,uint8_t no)
{
uint8_t i;
StartI2C(); //启动总线
if(!SendByte(sla)) //发送器件地址
{ return(0); }
ShortDelay(I2CDLY);
if(!SendByte(suba)) //发送器件子地址
{ return(0); }
StartI2C();
if(!SendByte(sla+1))
return(0);
for(i=0;i<no-1;i++)
{
*s++ = RcvByte(); //接收数据
Ack_I2c(0); //发送就答位
}
*s = RcvByte();
Ack_I2c(1); //发送非应答位
StopI2C(); //结束总线
return(1);
}
/****************************************************************************
* 函数名称:PCF8563Read()
* 功能描述:读时钟芯片
* 入口参数:无
* 出口参数:无
****************************************************************************/
uint8_t PCF8563Read(uint8_t addr)
{
uint8_t rdat;
IRcvByte(PCF8536SLA,addr,rdat);
return(rdat);
}
/****************************************************************************
* 函数名称:PCF8563Write()
* 功能描述:写时钟芯片
* 入口参数:无
* 出口参数:无
****************************************************************************/
void PCF8563Write(uint8_t addr,uint8_t sdat)
{
ISendByte(PCF8536SLA,addr,sdat);
}
/*******************************************************
*名称:void Init_PCF8563(void)
*说明:给PCF8563写入一个初始的值
*功能:写入日期,和时钟的值
*调用:PCF8563Write()
*输入:无
*输出:无
*********************************************************/
//*********调试时钟程序,芯片第一次上电用
void Init1_PCF8563(void)
{
InitI2C();
PCF8563Write(RegCtlSta1,0x00); //启动PCF8563
PCF8563Write(RegCtlSta2,0x00);
//初始化时间寄存器2019.05.01 08:00:00 星期三
PCF8563Write(TimeSecond,0x00);
PCF8563Write(TimeMinute,0x00);
PCF8563Write(TimeHour,0x08);
PCF8563Write(TimeDay,0x01);
PCF8563Write(TimeMonth,0x05);
PCF8563Write(TimeYear,0x19);
PCF8563Write(TimeWeek,0x03);
}
//*************/
/****************************************************************************
* 函数名称:InitPCF8563()
* 功能描述:时钟芯片初始化函数
* 入口参数:无
* 出口参数:无
****************************************************************************/
void Init_PCF8563(void) //芯片上电后以后用的初始化程序
{
InitI2C();
}
/**** 读取完整时间 ****/
/****************************************************************************
* 函数名称:read_complete_Time()
* 功能描述:读取实时时间函数
* 入口参数:无
* 出口参数:无
****************************************************************************/
void read_complete_Time(void)
{
uint8_t TimeBuf[8];
IRcvStr(PCF8536SLA,TimeSecond,TimeBuf,7); //从时钟芯片8536读取当前日期与时间
TimeBuf[0] &= 0x7f;
TimeBuf[1] &= 0x7f;
TimeBuf[2] &= 0x3f;
TimeBuf[3] &= 0x3f;
TimeBuf[5] &= 0x1f;
TimeBuf[0] = ((TimeBuf[0]>>4)*10)+(TimeBuf[0]&0x0f);
TimeBuf[1] = ((TimeBuf[1]>>4)*10)+(TimeBuf[1]&0x0f);
TimeBuf[2] = ((TimeBuf[2]>>4)*10)+(TimeBuf[2]&0x0f);
TimeBuf[3] = ((TimeBuf[3]>>4)*10)+(TimeBuf[3]&0x0f);
TimeBuf[5] = ((TimeBuf[5]>>4)*10)+(TimeBuf[5]&0x0f);
TimeBuf[6] = ((TimeBuf[6]>>4)*10)+(TimeBuf[6]&0x0f);
PortableProject.time_year=TimeBuf[6];
PortableProject.time_month=TimeBuf[5];
PortableProject.time_day=TimeBuf[3];
PortableProject.time_hour=TimeBuf[2];
PortableProject.time_minute=TimeBuf[1];
PortableProject.time_second=TimeBuf[0];
}
/**** 设置完整时间 ****/
/****************************************************************************
* 函数名称:ModifyTime()
* 功能描述:修改实时时间函数
* 入口参数:无
* 出口参数:无
****************************************************************************/
void write_complete_Time(void)
{
uint8_t TimeBuf[10];
TimeBuf[8] = ((PortableProject.time_year/10)<<4)+(PortableProject.time_year%10);
TimeBuf[7] = ((PortableProject.time_month/10)<<4)+(PortableProject.time_month%10);
TimeBuf[5] = ((PortableProject.time_day/10)<<4)+(PortableProject.time_day%10);
TimeBuf[4] = ((PortableProject.time_hour/10)<<4)+(PortableProject.time_hour%10);
TimeBuf[3] = ((PortableProject.time_minute/10)<<4)+(PortableProject.time_minute%10);
TimeBuf[2] = ((PPU.PortableProject.time_second/10)<<4)+(PortableProject.time_second%10);
TimeBuf[1] = 0;
TimeBuf[0] = 0;
ISendStr(PCF8536SLA,0x00,TimeBuf,9); //将要修改的日期与时间送入时钟芯片
}
//判断是否是闰年函数 //月份 1 2 3 4 5 6 7 8 9 10 11 12 //闰年 31 29 31 30 31 30 31 31 30 31 30 31 //非闰年 31 28 31 30 31 30 31 31 30 31 30 31 //输入:年份 //输出:该年份是不是闰年.1,是.0,不是
unsigned char Is_Leap_Year(unsigned int year)
{
year+=2000;//年变量从xx转换为20xx
if(year%4==0) //必须能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00结尾,还要能被400整除
else return 0;
}
else return 1;
}
else return 0;
}
//设置时钟 //把输入的时钟转换为秒钟 //以1970年1月1日为基准 //1970~2099年为合法年份
//const unsigned char table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表 //平年的月份日期表
const unsigned char mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
unsigned int RTC_Set(unsigned short int syear,unsigned char smon,unsigned char sday,unsigned char hour,unsigned char min,unsigned char sec)
{
unsigned int t;
unsigned int seccount=0;
syear+=2000;//年变量从xx转换为20xx
//if(syear<00||syear>99)return 1;//syear范围1970-2099,此处设置范围为2000-2099
for(t=1980;t<syear;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒钟数相加
{
seccount+=(unsigned int)mon_table[t]*86400;//月份秒钟数相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
}
seccount+=(unsigned int)(sday-1)*86400;//把前面日期的秒钟数相加
seccount+=(unsigned int)hour*3600;//小时秒钟数
seccount+=(unsigned int)min*60; //分钟秒钟数
seccount+=sec;//最后的秒钟加上去
return seccount;
}
//-------------------the end-----------------------//
PCF8563.h文件如下
============================================================
MODEL NAME : PCF8563.h
MODEL FUNCTION : Declare
RELATION MODEL : Others models in project
============================================================
*/
#ifndef __PCF8563_H
#define __PCF8563_H
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define PCF8563_I2C_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define PCF8563_I2C_PORT GPIOB /* GPIO端口 */
#define PCF8563_I2C_SCL_PIN GPIO_Pin_8 /* PB.08 */ /* 连接到SCL时钟线的GPIO */
#define PCF8563_I2C_SDA_PIN GPIO_Pin_9 /* PB.09 */ /* 连接到SDA数据线的GPIO */
#define I2CDLY 100
//定义PCF8563的各寄存器地址
#define PCF8536SLA 0xA2 //PCF8563从器件地址
#define RegCtlSta1 0x00 //控制/状态寄存器1
#define RegCtlSta2 0x01 //控制/状态寄存器2
#define TimeSecond 0x02
#define TimeMinute 0x03
#define TimeHour 0x04
#define TimeDay 0x05
#define TimeWeek 0x06
#define TimeMonth 0x07
#define TimeYear 0x08
#define AlarmMinute 0x09
#define AlarmHour 0x0a
#define AlarmDay 0x0b
#define AlarmWeek 0x0c
#define ClkFre 0x0d //CLOCKOUT频率寄存器
#define TimerCtl 0x0e //定时控制寄存器
#define TimerVal 0x0f //定时器倒计时数值寄存器
#define PCF8563_I2C_SCL_H GPIO_SetBits(PCF8563_I2C_PORT, PCF8563_I2C_SCL_PIN) //PB.08 //SCL = 1
#define PCF8563_I2C_SCL_L GPIO_ResetBits(PCF8563_I2C_PORT, PCF8563_I2C_SCL_PIN) //PB.08 //SCL = 0
#define PCF8563_I2C_SDA_H GPIO_SetBits(PCF8563_I2C_PORT, PCF8563_I2C_SDA_PIN) //PB.09 //SDA = 1
#define PCF8563_I2C_SDA_L GPIO_ResetBits(PCF8563_I2C_PORT, PCF8563_I2C_SDA_PIN) //PB.09 //SDA = 0
//IO方向设置
#define PCF8563_I2C_SDAModeIn() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=(uint32_t)8<<4;} //PB.09 //SDA 输入数据
#define PCF8563_I2C_SDAModeOut() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=(uint32_t)3<<4;} //PB.09 //SDA 输出数据
#define PCF8563_I2C_SDA_READ() GPIO_ReadInputDataBit(PCF8563_I2C_PORT, PCF8563_I2C_SDA_PIN) /* 读SDA口线状态 */
void ShortDelay(uint8_t n);
void LongDelay(uint16_t n);
void InitI2C(void);
unsigned char PCF8563Read(unsigned char add);
void PCF8563Write(unsigned char add, unsigned char wdata);
void Init1_PCF8563(void);
void Init_PCF8563(void);
void read_complete_Time(void);//读取完整时间
void write_complete_Time(void);//设置完整时间
//判断是否是闰年函数 //月份 1 2 3 4 5 6 7 8 9 10 11 12 //闰年 31 29 31 30 31 30 31 31 30 31 30 31 //非闰年 31 28 31 30 31 30 31 31 30 31 30 31 //输入:年份 //输出:该年份是不是闰年.1,是.0,不是
unsigned char Is_Leap_Year(unsigned int year);
//设置时钟 //把输入的时钟转换为秒钟 //以1980年1月1日为基准 //1970~2099年为合法年份
unsigned int RTC_Set(unsigned short int syear,unsigned char smon,unsigned char sday,unsigned char hour,unsigned char min,unsigned char sec) ;
#endif
上述代码为PCF8563驱动,本人亲自验证,验证环境STM32F103VETX和STM32L431VETX。验证上述代码可以使用。