time_edit_field now cycles through 5 fields (hour/min/day/month/year) via long-press CONFIRM. Up/down adjusts whichever field is focused, with day clamped to Days_In_Month() when month/year changes, and Clock_RecalcWeekday() called after every date edit so the weekday label stays in sync. UI_UpdateSetTimeDigit() now also refreshes the weekday abbreviation when editing day/month/year, since the computed weekday can change independently of which digit was actually touched. Save writes the full date+weekday via PCF8563_WriteDateTime(). Also removes PCF8563_ReadTime()/WriteTime(), now unused since every caller was updated to the DateTime variants directly.
459 lines
11 KiB
C
459 lines
11 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 连续读取时间与日期 (秒/分/时/日/星期/月/年,寄存器 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;
|
||
}
|