This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new b4f04a85609 rtc/ptc85263A: Add handling of stop_enable flag.
b4f04a85609 is described below
commit b4f04a8560979e908721815b06780704adf8f820
Author: Marin Doetterer <[email protected]>
AuthorDate: Wed May 27 13:47:35 2026 +0200
rtc/ptc85263A: Add handling of stop_enable flag.
On some boards, the PCF85263 RTC does not count between reboots. Due to
STOP_ENABLE (register 0x2E), bit=0 = 1, which freezes the RTC counter.
The exact trigger is unknown - not all boards exhibit the issue. The bit is
battery-backed and
persists across reboots, so once set (e.g. by a power glitch or undefined
hardware state) the
RTC stays frozen until explicitly cleared. The old driver never did this.
Fix: write `0x00` to `STOP_ENABLE` on init, which is the correct reset
value per the datasheet.
Fix: set time properly:
Due to datasheet the set_time should be as follow:
1. set stop_enable
2. clear prescaler
3. set time
4. clear stop_enable
Signed-off-by: Marin Doetterer <[email protected]>
---
drivers/timers/pcf85263.c | 236 +++++++++++++++++++++++++++++-----------------
drivers/timers/pcf85263.h | 1 +
2 files changed, 153 insertions(+), 84 deletions(-)
diff --git a/drivers/timers/pcf85263.c b/drivers/timers/pcf85263.c
index 7e621c248a5..6420098341d 100644
--- a/drivers/timers/pcf85263.c
+++ b/drivers/timers/pcf85263.c
@@ -178,6 +178,89 @@ static int rtc_bcd2bin(uint8_t value)
return tens + (value & 0x0f);
}
+/****************************************************************************
+ * Name: pcf85263_write_reg
+ *
+ * Description:
+ * Write one or more registers starting at regaddr.
+ *
+ * Input Parameters:
+ * regaddr - The first register address to write.
+ * buf - Data to write.
+ * len - Number of bytes to write.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int pcf85263_write_reg(uint8_t regaddr, const uint8_t *buf,
+ uint8_t len)
+{
+ struct i2c_msg_s msg;
+ uint8_t buffer[len + 1];
+ int ret;
+
+ buffer[0] = regaddr;
+ memcpy(&buffer[1], buf, len);
+
+ msg.frequency = CONFIG_PCF85263_I2C_FREQUENCY;
+ msg.addr = PCF85263_I2C_ADDRESS;
+ msg.flags = 0;
+ msg.buffer = buffer;
+ msg.length = len + 1;
+
+ ret = I2C_TRANSFER(g_pcf85263.i2c, &msg, 1);
+ if (ret < 0)
+ {
+ rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pcf85263_read_reg
+ *
+ * Description:
+ * Read one or more registers starting at regaddr.
+ *
+ * Input Parameters:
+ * regaddr - The first register address to read.
+ * buf - Buffer to store the read data.
+ * len - Number of bytes to read.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int pcf85263_read_reg(uint8_t regaddr, uint8_t *buf, uint8_t len)
+{
+ struct i2c_msg_s msg[2];
+ int ret;
+
+ msg[0].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
+ msg[0].addr = PCF85263_I2C_ADDRESS;
+ msg[0].flags = 0;
+ msg[0].buffer = ®addr;
+ msg[0].length = 1;
+
+ msg[1].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
+ msg[1].addr = PCF85263_I2C_ADDRESS;
+ msg[1].flags = I2C_M_READ;
+ msg[1].buffer = buf;
+ msg[1].length = len;
+
+ ret = I2C_TRANSFER(g_pcf85263.i2c, msg, 2);
+ if (ret < 0)
+ {
+ rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -208,9 +291,19 @@ static int rtc_bcd2bin(uint8_t value)
int pcf85263_rtc_initialize(FAR struct i2c_master_s *i2c)
{
+ uint8_t val = 0x00;
+ int ret;
+
/* Remember the i2c device and claim that the RTC is enabled */
g_pcf85263.i2c = i2c;
+
+ ret = pcf85263_write_reg(PCF85263_CTL_STOP_ENABLE, &val, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
g_rtc_enabled = true;
return OK;
}
@@ -241,8 +334,6 @@ int pcf85263_rtc_initialize(FAR struct i2c_master_s *i2c)
int up_rtc_getdatetime(FAR struct tm *tp)
{
- struct i2c_msg_s msg[4];
- uint8_t secaddr;
uint8_t buffer[7];
uint8_t seconds;
int ret;
@@ -267,51 +358,21 @@ int up_rtc_getdatetime(FAR struct tm *tp)
return -EAGAIN;
}
- /* Select to begin reading at the seconds register */
-
- secaddr = PCF85263_RTC_SECONDS;
-
- msg[0].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[0].addr = PCF85263_I2C_ADDRESS;
- msg[0].flags = 0;
- msg[0].buffer = &secaddr;
- msg[0].length = 1;
-
- /* Set up to read 7 registers: secondss, minutes, hour, day-of-week, date,
- * month, year
- */
-
- msg[1].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[1].addr = PCF85263_I2C_ADDRESS;
- msg[1].flags = I2C_M_READ;
- msg[1].buffer = buffer;
- msg[1].length = 7;
-
- /* Read the seconds register again */
-
- msg[2].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[2].addr = PCF85263_I2C_ADDRESS;
- msg[2].flags = 0;
- msg[2].buffer = &secaddr;
- msg[2].length = 1;
-
- msg[3].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[3].addr = PCF85263_I2C_ADDRESS;
- msg[3].flags = I2C_M_READ;
- msg[3].buffer = &seconds;
- msg[3].length = 1;
-
- /* Perform the transfer. The transfer may be performed repeatedly of the
- * seconds values decreases, meaning that that was a rollover in the
- * seconds.
+ /* Read 7 registers starting at seconds, then re-read seconds to detect
+ * a rollover during the burst read.
*/
do
{
- ret = I2C_TRANSFER(g_pcf85263.i2c, msg, 4);
+ ret = pcf85263_read_reg(PCF85263_RTC_SECONDS, buffer, 7);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = pcf85263_read_reg(PCF85263_RTC_SECONDS, &seconds, 1);
if (ret < 0)
{
- rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret);
return ret;
}
}
@@ -371,11 +432,10 @@ int up_rtc_getdatetime(FAR struct tm *tp)
int up_rtc_settime(FAR const struct timespec *tp)
{
- struct i2c_msg_s msg[3];
struct tm newtm;
time_t newtime;
- uint8_t buffer[9];
- uint8_t cmd;
+ uint8_t time_buf[8];
+ uint8_t val;
uint8_t seconds;
int ret;
@@ -406,84 +466,92 @@ int up_rtc_settime(FAR const struct timespec *tp)
return -EINVAL;
}
- rtc_dumptime(&tm, "New time");
+ rtc_dumptime(&newtm, "New time");
- /* Construct the message */
-
- /* Write starting with the 100ths of seconds register */
-
- buffer[0] = PCF85263_RTC_100TH_SECONDS;
+ /* Build the time register buffer starting at PCF85263_RTC_100TH_SECONDS */
/* Clear the 100ths of seconds */
- buffer[1] = 0;
+ time_buf[0] = 0;
/* Save seconds (0-59) converted to BCD */
- buffer[2] = rtc_bin2bcd(newtm.tm_sec);
+ time_buf[1] = rtc_bin2bcd(newtm.tm_sec);
/* Save minutes (0-59) converted to BCD */
- buffer[3] = rtc_bin2bcd(newtm.tm_min);
+ time_buf[2] = rtc_bin2bcd(newtm.tm_min);
/* Save hour (0-23) with 24-hour time indication */
- buffer[4] = rtc_bin2bcd(newtm.tm_hour);
+ time_buf[3] = rtc_bin2bcd(newtm.tm_hour);
/* Save the day of the month (1-31) */
- buffer[5] = rtc_bin2bcd(newtm.tm_mday);
+ time_buf[4] = rtc_bin2bcd(newtm.tm_mday);
/* Save the day of the week (1-7) */
- buffer[6] = rtc_bin2bcd(newtm.tm_wday);
+ time_buf[5] = rtc_bin2bcd(newtm.tm_wday);
/* Save the month (1-12) */
- buffer[7] = rtc_bin2bcd(newtm.tm_mon + 1);
+ time_buf[6] = rtc_bin2bcd(newtm.tm_mon + 1);
/* Save the year. Use years since 1968 (a leap year like 2000) */
- buffer[8] = rtc_bin2bcd(newtm.tm_year - 68);
+ time_buf[7] = rtc_bin2bcd(newtm.tm_year - 68);
- /* Setup the I2C message */
+ /* Perform the transfer. This transfer will be repeated if the seconds
+ * count rolls over to a smaller value while writing.
+ */
- msg[0].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[0].addr = PCF85263_I2C_ADDRESS;
- msg[0].flags = 0;
- msg[0].buffer = buffer;
- msg[0].length = 9;
+ do
+ {
+ /* Stop the RTC */
- /* Read back the seconds register */
+ val = 0x01;
+ ret = pcf85263_write_reg(PCF85263_CTL_STOP_ENABLE, &val, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
- cmd = PCF85263_RTC_SECONDS;
+ /* Register reset */
- msg[1].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[1].addr = PCF85263_I2C_ADDRESS;
- msg[1].flags = 0;
- msg[1].buffer = &cmd;
- msg[1].length = 1;
+ val = 0xa4;
+ ret = pcf85263_write_reg(PCF85263_CTL_RESET_REGISTER, &val, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
- msg[2].frequency = CONFIG_PCF85263_I2C_FREQUENCY;
- msg[2].addr = PCF85263_I2C_ADDRESS;
- msg[2].flags = I2C_M_READ;
- msg[2].buffer = &seconds;
- msg[2].length = 1;
+ /* Write all time registers in a single burst */
- /* Perform the transfer. This transfer will be repeated if the seconds
- * count rolls over to a smaller value while writing.
- */
+ ret = pcf85263_write_reg(PCF85263_RTC_100TH_SECONDS, time_buf, 8);
+ if (ret < 0)
+ {
+ return ret;
+ }
- do
- {
- ret = I2C_TRANSFER(g_pcf85263.i2c, msg, 3);
+ /* Read back seconds to detect a rollover */
+
+ ret = pcf85263_read_reg(PCF85263_RTC_SECONDS, &seconds, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Restart the RTC */
+
+ val = 0x00;
+ ret = pcf85263_write_reg(PCF85263_CTL_STOP_ENABLE, &val, 1);
if (ret < 0)
{
- rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret);
return ret;
}
}
- while ((buffer[2] & PCF85263_RTC_SECONDS_MASK) >
+ while ((time_buf[1] & PCF85263_RTC_SECONDS_MASK) >
(seconds & PCF85263_RTC_SECONDS_MASK));
return OK;
diff --git a/drivers/timers/pcf85263.h b/drivers/timers/pcf85263.h
index 01845ed8db5..30ac336e270 100644
--- a/drivers/timers/pcf85263.h
+++ b/drivers/timers/pcf85263.h
@@ -458,6 +458,7 @@
/* Stop */
#define PCF85263_CTL_STOP_ENABLE 0x2e /* Stop enable register */
+#define PCF85263_CTL_RESET_REGISTER 0x2f /* Reset register */
# define PCF85263_CTL_STOP (1 << 0) /* Bit 0: Stop bit */
/* Reset */