Files
stc32g128k/Drivers/pcf8563.c
edisondeng 1146bade70 feat(rtc): add PCF8563 full date read/write (day/month/year/weekday)
PCF8563's second/minute/hour/day/weekday/month/year registers are
contiguous (0x02-0x08), so PCF8563_ReadDateTime() bursts all 7 bytes
in one transaction. PCF8563_ReadTime() becomes a thin wrapper that
discards the date fields. WriteTime() is kept as its own 3-byte-only
implementation (writing only sec/min/hour) rather than routed through
the new WriteDateTime(), since forcing it through a 7-byte write would
require synthesizing date values and risk corrupting the stored date;
WriteDateTime() is added as a separate full 7-byte write for callers
that actually have real date+weekday values to persist.
2026-07-31 19:30:02 +08:00

498 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pcf8563.h"
#include "../App/system.h"
// 动态引脚对调全局标志 (0: 默认 SCL=P3.2, SDA=P3.3; 1: 对调 SCL=P3.3, SDA=P3.2)
volatile u8 i2c_swapped = 0;
static void SCL_W(u8 val)
{
if (!i2c_swapped) P32 = val;
else P33 = val;
}
static void SDA_W(u8 val)
{
if (!i2c_swapped) P33 = val;
else P32 = val;
}
static u8 SDA_R(void)
{
if (!i2c_swapped) return P33;
else return P32;
}
// 动态延时参数 (单位: us)
volatile u8 i2c_delay_us = 30;
/**
* @brief I2C 产生起始信号 (START)
*/
static void I2C_Start(void)
{
SDA_W(1);
SCL_W(1);
Delay_us(i2c_delay_us);
SDA_W(0);
Delay_us(i2c_delay_us);
SCL_W(0);
Delay_us(i2c_delay_us);
}
/**
* @brief I2C 产生停止信号 (STOP)
*/
static void I2C_Stop(void)
{
SDA_W(0);
Delay_us(i2c_delay_us);
SCL_W(1);
Delay_us(i2c_delay_us);
SDA_W(1);
Delay_us(i2c_delay_us);
}
/**
* @brief I2C 发送应答/非应答信号
* @param ack 1: ACK(0), 0: NACK(1)
*/
static void I2C_SendAck(bit ack)
{
SDA_W(!ack);
Delay_us(i2c_delay_us);
SCL_W(1);
Delay_us(i2c_delay_us);
SCL_W(0);
Delay_us(i2c_delay_us);
SDA_W(1);
Delay_us(i2c_delay_us);
}
/**
* @brief I2C 等待从机应答 (Wait ACK)
* @return bit 1: 收到应答, 0: 未收到应答
*/
static bit I2C_WaitAck(void)
{
u8 retry = 0;
SDA_W(1); // 释放 SDA 接收 ACK
Delay_us(i2c_delay_us);
SCL_W(1);
Delay_us(i2c_delay_us);
while (SDA_R()) {
retry++;
Delay_us(5);
if (retry > 250) {
I2C_Stop();
return 0; // 超时未收到 ACK 应答,立即优雅退出,防止死锁
}
}
SCL_W(0);
Delay_us(i2c_delay_us);
return 1;
}
/**
* @brief I2C 发送单字节数据
* @param dat 待发送 of 字节数据
*/
static void I2C_SendByte(u8 dat)
{
u8 i;
for (i = 0; i < 8; i++) {
SDA_W((dat & 0x80) ? 1 : 0);
Delay_us(i2c_delay_us);
SCL_W(1);
Delay_us(i2c_delay_us);
SCL_W(0);
Delay_us(i2c_delay_us);
dat <<= 1;
}
SDA_W(1); // 释放 SDA 接收 ACK
Delay_us(i2c_delay_us);
}
/**
* @brief I2C 读取单字节数据
* @return u8 接收到的字节数据
*/
static u8 I2C_ReadByte(void)
{
u8 i, dat = 0;
SDA_W(1); // 设为输入
Delay_us(i2c_delay_us);
for (i = 0; i < 8; i++) {
dat <<= 1;
SCL_W(1);
Delay_us(i2c_delay_us);
if (SDA_R()) {
dat |= 0x01;
}
SCL_W(0);
Delay_us(i2c_delay_us);
}
return dat;
}
/* ====== BCD 与二进制 10 进制互转辅助函数 ====== */
static u8 BCD2DEC(u8 bcd)
{
return ((bcd >> 4) * 10) + (bcd & 0x0F);
}
static u8 DEC2BCD(u8 dec)
{
return (((dec / 10) << 4) | (dec % 10));
}
static void I2C_ReleaseBus(void)
{
u8 i;
SDA_W(1);
SCL_W(1);
Delay_us(30);
// 如果 SDA 被拉低,说明总线被死锁占用
if (SDA_R() == 0) {
for (i = 0; i < 9; i++) {
SCL_W(0);
Delay_us(30);
SCL_W(1);
Delay_us(30);
if (SDA_R() == 1) {
break; // 从机已释放总线,提前退出
}
}
}
// 强制产生停止条件恢复空闲
SDA_W(0);
Delay_us(30);
SCL_W(1);
Delay_us(30);
SDA_W(1);
Delay_us(30);
}
/* ====== PCF8563 驱动核心接口 ====== */
bit PCF8563_Init(void)
{
// 1. 默认引脚尝试 (SCL=P3.2, SDA=P3.3)
i2c_swapped = 0;
I2C_ReleaseBus(); // 强力总线死锁释放
I2C_Start();
I2C_SendByte(PCF8563_ADDR_WRITE);
if (I2C_WaitAck()) {
Uart_SendString("[RTC] Detected Normal pin mapping (SCL=P32, SDA=P33)\r\n");
} else {
// 2. 尝试对调引脚 (SCL=P3.3, SDA=P3.2)
I2C_Stop();
i2c_swapped = 1;
I2C_ReleaseBus(); // 强力总线死锁释放
I2C_Start();
I2C_SendByte(PCF8563_ADDR_WRITE);
if (I2C_WaitAck()) {
Uart_SendString("[RTC] Detected SWAPPED pin mapping (SCL=P33, SDA=P32)!\r\n");
} else {
// 恢复默认,防止干扰
i2c_swapped = 0;
Uart_SendString("[RTC-ERR] PCF8563 Offline on both normal/swapped pins!\r\n");
return 0;
}
}
I2C_SendByte(REG_CTRL_STATUS1);
if (!I2C_WaitAck()) return 0;
I2C_SendByte(0x00); // 正常模式,不停止芯片计数
if (!I2C_WaitAck()) return 0;
I2C_Stop();
return 1;
}
void PCF8563_ScanDiagnostic(void)
{
u16 addr;
u8 found = 0;
Uart_SendString("[I2C-SCAN] --- 1. Scanning with default pins (SCL=P32, SDA=P33) ---\r\n");
i2c_swapped = 0;
for (addr = 0x02; addr < 0x100; addr += 2) {
I2C_Start();
I2C_SendByte((u8)addr);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-SCAN] Device Found: 0x");
Uart_SendHex8((u8)addr);
Uart_SendString("\r\n");
found = 1;
}
I2C_Stop();
Delay_ms(2);
}
Uart_SendString("[I2C-SCAN] --- 2. Scanning with SWAPPED pins (SCL=P33, SDA=P32) ---\r\n");
i2c_swapped = 1;
for (addr = 0x02; addr < 0x100; addr += 2) {
I2C_Start();
I2C_SendByte((u8)addr);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-SCAN] Device Found: 0x");
Uart_SendHex8((u8)addr);
Uart_SendString("\r\n");
found = 1;
}
I2C_Stop();
Delay_ms(2);
}
// 扫描结束后,根据 init 侦测结果重新绑定,此处临时归零
i2c_swapped = 0;
Uart_SendString("[I2C-SCAN] Dual-mode Address Scan Completed!\r\n");
}
/**
* @brief 从 PCF8563 连续读取时间与日期 (秒/分/时/日/星期/月/年,寄存器 0x02~0x08 地址连续)
*/
bit PCF8563_ReadDateTime(u8 *hour, u8 *min, u8 *sec, u8 *day, u8 *month, u8 *year)
{
u8 raw_sec, raw_min, raw_hour, raw_day, raw_weekday, raw_month, raw_year;
I2C_Start();
I2C_SendByte(PCF8563_ADDR_WRITE);
if (!I2C_WaitAck()) return 0;
// 设置起始读取地址为 0x02 (REG_VL_SECONDS)
I2C_SendByte(REG_VL_SECONDS);
if (!I2C_WaitAck()) return 0;
// 重新产生 Start 读取数据
I2C_Start();
I2C_SendByte(PCF8563_ADDR_READ);
if (!I2C_WaitAck()) return 0;
raw_sec = I2C_ReadByte();
I2C_SendAck(1);
raw_min = I2C_ReadByte();
I2C_SendAck(1);
raw_hour = I2C_ReadByte();
I2C_SendAck(1);
raw_day = I2C_ReadByte();
I2C_SendAck(1);
raw_weekday = I2C_ReadByte(); // 星期寄存器不使用,芯片自身的星期在改日期后可能与实际不符
I2C_SendAck(1);
(void)raw_weekday;
raw_month = I2C_ReadByte();
I2C_SendAck(1);
raw_year = I2C_ReadByte();
I2C_SendAck(0); // 发送 NACK 结束连续读取
I2C_Stop();
// 解析 BCD 数据 (掩码屏蔽无效位)
*sec = BCD2DEC(raw_sec & 0x7F);
*min = BCD2DEC(raw_min & 0x7F);
*hour = BCD2DEC(raw_hour & 0x3F);
*day = BCD2DEC(raw_day & 0x3F);
*month = BCD2DEC(raw_month & 0x1F); // 忽略 bit7 世纪位,固定按 2000~2099 处理
*year = BCD2DEC(raw_year);
return 1;
}
/**
* @brief 从 PCF8563 读取当前时间 (兼容旧接口,只取时分秒,丢弃日期部分)
*/
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec)
{
u8 day, month, year;
return PCF8563_ReadDateTime(hour, min, sec, &day, &month, &year);
}
/**
* @brief 将时间精准写入 PCF8563 RTC 芯片 (只写时分秒三个寄存器,不触碰日期寄存器)
*/
bit PCF8563_WriteTime(u8 hour, u8 min, u8 sec)
{
I2C_Start();
I2C_SendByte(PCF8563_ADDR_WRITE);
if (!I2C_WaitAck()) return 0;
// 定位写入起始寄存器 0x02
I2C_SendByte(REG_VL_SECONDS);
if (!I2C_WaitAck()) return 0;
// 写入秒
I2C_SendByte(DEC2BCD(sec & 0x7F));
if (!I2C_WaitAck()) return 0;
// 写入分
I2C_SendByte(DEC2BCD(min & 0x7F));
if (!I2C_WaitAck()) return 0;
// 写入时
I2C_SendByte(DEC2BCD(hour & 0x3F));
if (!I2C_WaitAck()) return 0;
I2C_Stop();
return 1;
}
/**
* @brief 将时间与日期一次性连续写入 PCF8563 RTC 芯片 (寄存器 0x02~0x08 共 7 字节连续写入)
*/
bit PCF8563_WriteDateTime(u8 hour, u8 min, u8 sec, u8 day, u8 month, u8 year, u8 weekday)
{
I2C_Start();
I2C_SendByte(PCF8563_ADDR_WRITE);
if (!I2C_WaitAck()) return 0;
I2C_SendByte(REG_VL_SECONDS);
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(sec & 0x7F));
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(min & 0x7F));
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(hour & 0x3F));
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(day & 0x3F));
if (!I2C_WaitAck()) return 0;
I2C_SendByte(weekday & 0x07); // 星期不是 BCD直接写 0~6
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(month & 0x1F)); // bit7 世纪位固定写 0不处理跨世纪
if (!I2C_WaitAck()) return 0;
I2C_SendByte(DEC2BCD(year));
if (!I2C_WaitAck()) return 0;
I2C_Stop();
return 1;
}
bit PCF8563_TryReadDiagnostic(void)
{
u8 val = 0;
Uart_SendString("[I2C-DIAG] --- Test 1: Trying 30us Delay (30kHz) ---\r\n");
i2c_delay_us = 30;
i2c_swapped = 0;
I2C_ReleaseBus();
I2C_Start();
I2C_SendByte(0xA2);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) 0xA2 ACK OK\r\n");
I2C_SendByte(0x00);
if (I2C_WaitAck()) {
I2C_Start();
I2C_SendByte(0xA3);
if (I2C_WaitAck()) {
val = I2C_ReadByte();
I2C_SendAck(0);
I2C_Stop();
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) Success! Reg 0x00=0x");
Uart_SendHex8(val);
Uart_SendString("\r\n");
return 1;
}
}
}
I2C_Stop();
i2c_swapped = 1;
I2C_ReleaseBus();
I2C_Start();
I2C_SendByte(0xA2);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) 0xA2 ACK OK\r\n");
I2C_SendByte(0x00);
if (I2C_WaitAck()) {
I2C_Start();
I2C_SendByte(0xA3);
if (I2C_WaitAck()) {
val = I2C_ReadByte();
I2C_SendAck(0);
I2C_Stop();
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) Success! Reg 0x00=0x");
Uart_SendHex8(val);
Uart_SendString("\r\n");
return 1;
}
}
}
I2C_Stop();
Uart_SendString("[I2C-DIAG] --- Test 2: Retrying with ultra-slow 200us Delay (2.5kHz) ---\r\n");
i2c_delay_us = 200;
i2c_swapped = 0;
I2C_ReleaseBus();
I2C_Start();
I2C_SendByte(0xA2);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) 0xA2 ACK OK\r\n");
I2C_SendByte(0x00);
if (I2C_WaitAck()) {
I2C_Start();
I2C_SendByte(0xA3);
if (I2C_WaitAck()) {
val = I2C_ReadByte();
I2C_SendAck(0);
I2C_Stop();
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) Success! Reg 0x00=0x");
Uart_SendHex8(val);
Uart_SendString("\r\n");
return 1;
}
}
}
I2C_Stop();
i2c_swapped = 1;
I2C_ReleaseBus();
I2C_Start();
I2C_SendByte(0xA2);
if (I2C_WaitAck()) {
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) 0xA2 ACK OK\r\n");
I2C_SendByte(0x00);
if (I2C_WaitAck()) {
I2C_Start();
I2C_SendByte(0xA3);
if (I2C_WaitAck()) {
val = I2C_ReadByte();
I2C_SendAck(0);
I2C_Stop();
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) Success! Reg 0x00=0x");
Uart_SendHex8(val);
Uart_SendString("\r\n");
return 1;
}
}
}
I2C_Stop();
// 恢复默认设置
i2c_swapped = 0;
i2c_delay_us = 30;
Uart_SendString("[I2C-DIAG] Both 30us and 200us diagnostic trial failed.\r\n");
return 0;
}