435 lines
10 KiB
C
435 lines
10 KiB
C
|
|
#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 读取当前时间
|
||
|
|
*/
|
||
|
|
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec)
|
||
|
|
{
|
||
|
|
u8 raw_sec, raw_min, raw_hour;
|
||
|
|
|
||
|
|
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); // 发送 ACK
|
||
|
|
|
||
|
|
raw_min = I2C_ReadByte();
|
||
|
|
I2C_SendAck(1); // 发送 ACK
|
||
|
|
|
||
|
|
raw_hour = I2C_ReadByte();
|
||
|
|
I2C_SendAck(0); // 发送 NACK 结束连续读取
|
||
|
|
|
||
|
|
I2C_Stop();
|
||
|
|
|
||
|
|
// 解析 BCD 数据 (掩码屏蔽无效位)
|
||
|
|
*sec = BCD2DEC(raw_sec & 0x7F);
|
||
|
|
*min = BCD2DEC(raw_min & 0x7F);
|
||
|
|
*hour = BCD2DEC(raw_hour & 0x3F);
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @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;
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|