前言
看着网上的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
c
#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
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(节选)
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