STM32单片机PCF8563源码详解

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。验证上述代码可以使用。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值