【模块系列】STM32&PCF8563

前言

  看着网上的PCF8563例程,挺乱的,我就照着中文手册自己写了个。主要还是基于STM32的HAL库,下的硬件I2C写的。I2C的接口层是用HAL库的,不过只用到了两个关键接口,I2C的连续发和连续收,所以要移植到如模拟I2C上,只需要更换为自己的I2C接口即可。

  代码流程,主要是基于器件对象化,编写调用。函数已经包括完了芯片寄存器的4个功能模块,状态、时间、警报(闹钟)、计时。只是没有对所有细致功能方向,进行分离出来单独编写,如警报(闹钟)设置触发清除的全流程等等,就没有独立出来,要使用的话,还是要在器件对象类进行配置。

环境

开发板:自制STM32RET6开发板

案例的代码环境:Keil5+STM32CubeMX生成的HAL库,硬件I2C+FreeRTOS

注意事项:购买PCF8563模块,有些是没有上拉电阻的,需要自己加的,这个坑我在调试的时候遇到了。

手册

下述介绍参考数据手册,让大家对PCF8563有个基本的了解。

简介

  PCF8563 是一款低功耗的 CMOS1实时时钟/日历芯片,支持可编程时钟输出、中断输出 和低压检测。所有地址和数据通过双线双向 I 2C 总线串联传输,最高速率:400 kbps。每 次读写数据字节后,寄存器地址自动累加。

特点

  • 基于 32.768kHz 的晶振,提供年、月、日、星期、时、分和秒计时
  • 世纪标志位
  • 时钟工作电压:1.0 – 5.5 V(室温)
  • 低备用电流;典型值为 0.25 μA(VDD = 3.0 V,Tamb =25 °C)
  • 400 kHz 双线 I 2C 总线接口(VDD = 1.8 – 5.5 V)
  • 可编程时钟输出(32.768 kHz、1.024 kHz、32 Hz 和 1Hz)
  • 报警和定时器功能
  • 集成晶振电容器
  • 内部上电复位(POR)
  • I2C总线从机地址:读:A3h;写:A2h
  • 开漏中断管脚
  • 代码

      下面仅展示PCF8563库,完整的工程文件会放在末尾。该库对基本操作,设置获取时间,设置获取状态,设置警报时间等常规操作,都是支持的,都验证过,至于其它的还没做验证或者扩写,如果有错误的地方,也欢迎大家提出来!

    PCF8563.H

    #ifndef __PCF8563_H__
    #define __PCF8563_H__
    
    // PCF8563对象结构
    typedef struct{
    	/* 控制(原始数据) */
    	uint8_t status1;		// 状态1
    	uint8_t status2;		// 状态2
    	uint8_t clkout;		// 时钟输出
    	/* 时间(10进制) */
    	uint8_t seconds;		// 秒
      uint8_t minutes;		// 分钟
      uint8_t hours;			// 小时
      uint8_t days;			// 天
      uint8_t weekdays;	// 周天(星期几)
      uint8_t months;		// 月
      uint8_t years;			// 年
    	/* 报警功能(10进制) */
    	uint8_t Minute_alarm;		// 分钟报警
    	uint8_t Minute_alarm_en;	// 分钟报警是否启用
    	uint8_t Hour_alarm;			// 小时报警
    	uint8_t Hour_alarm_en;		// 小时报警是否启用
    	uint8_t Day_alarm;				// 天数报警
    	uint8_t Day_alarm_en;		// 天数报警是否启用
    	uint8_t Weekday_alarm;		// 周数报警
    	uint8_t Weekday_alarm_en;// 周数报警是否启用
    	/* 定时器功能(原始数据) */
    	uint8_t Timer_control;		// 定时器控制
    	uint8_t Timer_en;				// 定时器是否启用
    	uint8_t Timer;						// 定时器
    	/* 其它标志位 */
    	uint8_t vl;							// 时钟信息是否完整
    }PCF8563_Obj;
    
    typedef enum {
    	Hz4K 		= 0x00,	// 4.096 KHz
    	Hz64 		= 0x01,	// 64 		Hz
    	Hz1	 		= 0x02,	// 1 			Hz
    	Hz1d60	= 0x03	// 1/60		Hz
    }PCF8563_TimerFrequency;
    
    typedef enum {
    	Disenabled 		= 0x00,	// 禁用
    	Enabled 			= 0x01,	// 启用
    }PCF8563_TimerEn;
    
    // PCF8563下定时器对象结构
    typedef struct {
    	PCF8563_TimerEn timerEn;								// 定时器是否启用
    	uint8_t value;													// 定时器值
    	PCF8563_TimerFrequency frequency;				// 定时器频率选择
    }PCF8563_Timer_Obj;
    
    void PCF8563_Init(void);		// PCF8563初始化
    
    /* 全部配置 */
    void PCF8563_GetStatus(PCF8563_Obj *obj);							// 获取状态数据
    void PCF8563_GetClkout(PCF8563_Obj *obj);							// 获取输出数据
    void PCF8563_GetTimeData(PCF8563_Obj *obj);						// 获取时间数据
    void PCF8563_GetAlarmFunction(PCF8563_Obj *obj);			// 获取报警数据
    void PCF8563_GetTimerFunction(PCF8563_Obj *obj);			// 获取定时器数据
    
    void PCF8563_SetStatus(PCF8563_Obj *obj);							// 设置状态数据
    void PCF8563_SetClkout(PCF8563_Obj *obj);							// 设置输出数据
    void PCF8563_SetTimeData(PCF8563_Obj *obj);						// 设置时间数据
    void PCF8563_SetAlarmFunction(PCF8563_Obj *obj);			// 设置报警数据
    void PCF8563_SetTimerFunction(PCF8563_Obj *obj);			// 设置定时器数据
    
    void PCF8563_GetVL(PCF8563_Obj *obj);									// 获取 VL 标志符
    
    /* 细化配置 */
    void PCF8563_ControlTimer(PCF8563_Timer_Obj *timer);	// 控制定时器
    
    #endif
    
    

    PCF8563.C

    #include "main.h"
    #include "cmsis_os.h"
    #include "i2c.h"
    
    #include "PCF8563.h"
    
    #define  PCF8563_7BitAddress  0x51
    #define  PCF8563_8BitAddress_Read		(PCF8563_7BitAddress << 1) | 0x01
    #define  PCF8563_8BitAddress_Write	(PCF8563_7BitAddress << 1)
    
    #define  PCF8563_Delay(ms)  osDelay(ms)
    // #define  PCF8563_BcdToTen(data, ) 
    
    // PCF8563初始化
    void PCF8563_Init()
    {
    	
    }
    
    // 获取状态
    void PCF8563_GetStatus(PCF8563_Obj *obj)
    {
    	uint8_t pData_GetStatus1[] = { 0x00 };	// 写入指针命令	
    	uint8_t pData_Receive[2];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_GetStatus1, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 2, 100);
    	obj->status1 = pData_Receive[0];
    	obj->status2 = pData_Receive[1];
    }
    
    // 获取输出数据
    void PCF8563_GetClkout(PCF8563_Obj *obj)
    {
    	uint8_t pData_GetClkout[] = { 0x0D };	// 写入指针命令	
    	uint8_t pData_Receive[1];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_GetClkout, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 1, 100);
    	
    	obj->clkout = pData_Receive[0];
    }	
    
    // 获取时间数据
    void PCF8563_GetTimeData(PCF8563_Obj *obj)
    {
    	uint8_t pData_PointerReset[] = { 0x02 };	// 写入指针命令	
    	uint8_t pData_Receive[7];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_PointerReset, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 7, 100);
    	
    	// 数据稳定下读取秒(BCD转10进制)
    	// if(pData_Receive[0] >> 7 == 0x01) obj->seconds = (pData_Receive[0] & 0x0F) + ((pData_Receive[0] & 0x70 ) >> 4) * 10;
    	obj->seconds = (pData_Receive[0] & 0x0F) + ((pData_Receive[0] & 0x70 ) >> 4) * 10;
    	obj->minutes = (pData_Receive[1] & 0x0F) + ((pData_Receive[1] & 0x70 ) >> 4) * 10;	// 分钟
    	obj->hours = (pData_Receive[2] & 0x0F) + ((pData_Receive[2] & 0x30 ) >> 4) * 10;		// 小时
    	obj->days = (pData_Receive[3] & 0x0F) + ((pData_Receive[3] & 0x30 ) >> 4) * 10;			// 天
    	obj->weekdays = (pData_Receive[4] & 0x07);																					// 周
    	obj->months = (pData_Receive[5] & 0x0F) + ((pData_Receive[5] & 0x10 ) >> 4) * 10;		// 月(世纪未加入计算)
    	obj->years = (pData_Receive[6] & 0x0F) + ((pData_Receive[6] & 0xF0 ) >> 4) * 10;		// 年
    }
    
    // 获取报警数据
    void PCF8563_GetAlarmFunction(PCF8563_Obj *obj)
    {
    	uint8_t pData_AlarmFunction[] = { 0x09 };	// 写入指针命令	
    	uint8_t pData_Receive[4];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_AlarmFunction, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 4, 100);
    	
    	obj->Minute_alarm 		= (pData_Receive[0] & 0x0F) + ((pData_Receive[0] & 0x70 ) >> 4) * 10;
    	obj->Minute_alarm_en 	= (pData_Receive[0] >> 7);
    	obj->Hour_alarm 			= (pData_Receive[1] & 0x0F) + ((pData_Receive[1] & 0x30 ) >> 4) * 10;
    	obj->Hour_alarm_en 		= (pData_Receive[1] >> 7);
    	obj->Day_alarm 				= (pData_Receive[2] & 0x0F) + ((pData_Receive[2] & 0x30 ) >> 4) * 10;
    	obj->Day_alarm_en 		= (pData_Receive[2] >> 7);
    	obj->Weekday_alarm 		= (pData_Receive[3] & 0x07);
    	obj->Weekday_alarm_en = (pData_Receive[3] >> 7);
    }
    
    // 获取定时器数据
    void PCF8563_GetTimerFunction(PCF8563_Obj *obj)
    {
    	uint8_t pData_TimerFunction[] = { 0x0E };	// 写入指针命令	
    	uint8_t pData_Receive[2];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_TimerFunction, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 2, 100);
    	
    	obj->Timer_control = pData_Receive[0];
    	obj->Timer = pData_Receive[1];
    }
    
    // 设置状态数据
    void PCF8563_SetStatus(PCF8563_Obj *obj)
    {
    	uint8_t pData_SetStatus[] = { 0x00, obj->status1, obj->status2};	// 写入状态命令	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_SetStatus, 1 + 2, 100);
    }
    
    // 设置输出数据
    void PCF8563_SetClkout(PCF8563_Obj *obj)
    {
    	uint8_t pData_GetClkout[] = { 0x0D, obj->clkout};	// 写入指针命令	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_GetClkout, 1 + 1, 100);
    }
    
    // 设置时间数据
    void PCF8563_SetTimeData(PCF8563_Obj *obj)
    {
    	// 写入指针命令	
    	uint8_t pData_PointerReset[] = { 
    		0x02,
    		((obj->seconds / 10) << 4)  | (obj->seconds % 10),
    		((obj->minutes / 10) << 4)  | (obj->minutes % 10),
    		((obj->hours / 10) << 4)  	| (obj->hours % 10),
    		((obj->days / 10) << 4)  		| (obj->days % 10),
    		(obj->weekdays % 10),
    		((obj->months / 10) << 4)  	| (obj->months % 10),
    		((obj->years / 10) << 4)  	| (obj->years % 10),
    	};	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_PointerReset, 1 + 7, 100);
    }
    
    // 设置报警数据
    void PCF8563_SetAlarmFunction(PCF8563_Obj *obj)
    {
    	// 写入指针命令	
    	uint8_t pData_AlarmFunction[1 + 4] = { 
    		0x09,
    		(obj->Minute_alarm_en << 7) 	| ((obj->Minute_alarm % 10) + ((obj->Minute_alarm / 10) << 4)),
    		(obj->Hour_alarm_en << 7) 		| ((obj->Hour_alarm % 10) + ((obj->Hour_alarm / 10) << 4)),
    		(obj->Day_alarm_en << 7) 			| ((obj->Day_alarm % 10) + ((obj->Day_alarm / 10) << 4)),
    		(obj->Weekday_alarm_en << 7) 	| ((obj->Weekday_alarm % 10) + ((obj->Weekday_alarm / 10) << 4)),
    	};	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_AlarmFunction, 1 + 4, 100);
    }
    
    // 设置定时器数据
    void PCF8563_SetTimerFunction(PCF8563_Obj *obj)
    {
    	// 写入指针命令
    	uint8_t pData_TimerFunction[1 + 2] = { 
    		0x0E,
    		obj->Timer_control,
    		obj->Timer
    	};
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_TimerFunction, 1 + 2, 100);
    }
    
    // 获取 VL 标志符
    // 描述:当时钟信息的完整性得不到保障时,如VDD掉到Vlow以下时,VL被置‘1’,必需手动清除
    void PCF8563_GetVL(PCF8563_Obj *obj)
    {
    	uint8_t pData_GetVL[] = { 0x00 };	// 写入指针命令	
    	uint8_t pData_Receive[1];	// 接受数据
    	
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_GetVL, 1, 100);
    	HAL_I2C_Master_Receive(&hi2c2, PCF8563_8BitAddress_Read, pData_Receive, 1, 100);
    	obj->vl = pData_Receive[0] >> 7;
    }
    
    // 控制定时器
    void PCF8563_ControlTimer(PCF8563_Timer_Obj *timer)
    {
    	// 写入指针命令
    	uint8_t pData_TimerFunction[1 + 2] = { 
    		0x0E,
    		(timer->timerEn << 7) | (timer->frequency),
    		timer->value
    	};
    	HAL_I2C_Master_Transmit(&hi2c2, PCF8563_8BitAddress_Write, pData_TimerFunction, 1 + 2, 100);
    }
    
    
    

    main.c(节选)

    void Task1_PCF8563(void *argument)
    {
    	PCF8563_Obj PCF8563 = {0};
    	printf("Task1_PCF8563();\r\n");
    	
    	// 设置时间数据(测试)
    	PCF8563.seconds = 50;		// 秒
    	PCF8563.minutes = 59;		// 分钟
    	PCF8563.hours		= 23;		// 小时
    	PCF8563.days		=	31;		// 天
    	PCF8563.weekdays= 00;		// 周数
    	PCF8563.months  = 12;		// 月
    	PCF8563.years		= 24;		// 年
    	PCF8563_SetTimeData(&PCF8563);
    	// 设置状态数据(测试)
    	PCF8563.status1 = 0x00;	// 状态1
    	PCF8563.status2 = 0x00; // 状态2
    	PCF8563_SetStatus(&PCF8563);
    	// 设置报警数据(测试)
    	PCF8563.Minute_alarm = 00;			// 分钟报警
    	PCF8563.Minute_alarm_en = 0;		// 分钟报警是否启用
    	PCF8563.Hour_alarm = 20;				// 小时报警
    	PCF8563.Hour_alarm_en = 1;			// 小时报警是否启用
    	PCF8563.Day_alarm = 30;					// 天数报警
    	PCF8563.Day_alarm_en = 1;				// 天数报警是否启用
    	PCF8563.Weekday_alarm = 6;			// 周数报警
    	PCF8563.Weekday_alarm_en = 1;		// 周数报警是否启用
    	PCF8563_SetAlarmFunction(&PCF8563);
    	
    	for(;;)
    	{
    		taskENTER_CRITICAL();		//任务级进入临界段
    		// 获取状态(测试)
    		PCF8563_GetStatus(&PCF8563);
    		// 获取时间数据(测试)
    		PCF8563_GetTimeData(&PCF8563);
    		// 报警功能(测试)
    		PCF8563_GetAlarmFunction(&PCF8563);
    		taskEXIT_CRITICAL();		//任务级退出临界段
    		
    		// 状态1 2
    		printf("PCF8563Status:%x %x\r\n",PCF8563.status1,PCF8563.status2);
    		// 报警功能
    		printf("PCF8563Alarm:%d %d %d %d %d %d %d %d\r\n",PCF8563.Minute_alarm,PCF8563.Minute_alarm_en,PCF8563.Hour_alarm,PCF8563.Hour_alarm_en,PCF8563.Day_alarm,PCF8563.Day_alarm_en,PCF8563.Weekday_alarm,PCF8563.Weekday_alarm_en);
    		// 年 月 周天(星期几) 天 时 分 秒
    		printf("PCF8563Time:%d %d %d %d %d %d %d\r\n",PCF8563.years,PCF8563.months,PCF8563.weekdays,PCF8563.days,PCF8563.hours,PCF8563.minutes,PCF8563.seconds);
    		osDelay(1000);
    	}
    }
    
    

    现象

    工程

    PCF8563中文手册*1 , Keil5工程代码*1
    链接: https://pan.baidu.com/s/1V9BH01WMhuAOnOhM0ywZew  提取码: fy7x

    作者:紫阡星影

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【模块系列】STM32&PCF8563

    发表回复