#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 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; }