RTC keeps time between POWER OFF & ON, SD Card at start still needs work -- if card is in the slot, it is not readable until it is pulled on and then inserted.

This commit is contained in:
John Poole 2026-02-13 18:52:17 -08:00
commit 544d459c9b
11 changed files with 1288 additions and 6 deletions

View file

@ -0,0 +1,248 @@
// 20260214 ChatGPT
// $Id$
// $HeadURL$
#include <Arduino.h>
#include <Wire.h>
#include <XPowersLib.h>
#ifndef I2C_SDA1
#define I2C_SDA1 42
#endif
#ifndef I2C_SCL1
#define I2C_SCL1 41
#endif
#ifndef RTC_I2C_ADDR
#define RTC_I2C_ADDR 0x51
#endif
static XPowersLibInterface* g_pmu = nullptr;
static uint32_t g_logSeq = 0;
static uint32_t g_lastPrintMs = 0;
static String g_cmdBuf;
static bool g_lastWasCR = false;
struct RtcDateTime {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t weekday;
};
static uint8_t toBcd(uint8_t v) {
return ((v / 10U) << 4U) | (v % 10U);
}
static uint8_t fromBcd(uint8_t b) {
return ((b >> 4U) * 10U) + (b & 0x0FU);
}
static void logf(const char* fmt, ...) {
char msg[196];
va_list args;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
Serial.printf("[%10lu][%06lu] %s\r\n", (unsigned long)millis(), (unsigned long)g_logSeq++, msg);
}
static bool initPmuForRtc() {
Wire1.begin(I2C_SDA1, I2C_SCL1);
if (!g_pmu) {
g_pmu = new XPowersAXP2101(Wire1);
}
if (!g_pmu->init()) {
logf("PMU: AXP2101 init failed");
return false;
}
// Keep RTC/I2C-related rails on, matching tbeam-s3-core behavior.
g_pmu->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
g_pmu->enablePowerOutput(XPOWERS_ALDO2);
g_pmu->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
g_pmu->enablePowerOutput(XPOWERS_ALDO1);
logf("PMU: AXP2101 ready, ALDO2=%s",
g_pmu->isPowerChannelEnable(XPOWERS_ALDO2) ? "ON" : "OFF");
return true;
}
static bool rtcRead(RtcDateTime& out, bool& lowVoltageFlag) {
Wire1.beginTransmission(RTC_I2C_ADDR);
Wire1.write(0x02); // seconds register
if (Wire1.endTransmission(false) != 0) {
return false;
}
const uint8_t need = 7;
uint8_t got = Wire1.requestFrom((int)RTC_I2C_ADDR, (int)need);
if (got != need) {
return false;
}
uint8_t sec = Wire1.read();
uint8_t min = Wire1.read();
uint8_t hour = Wire1.read();
uint8_t day = Wire1.read();
uint8_t weekday = Wire1.read();
uint8_t month = Wire1.read();
uint8_t year = Wire1.read();
lowVoltageFlag = (sec & 0x80U) != 0;
out.second = fromBcd(sec & 0x7FU);
out.minute = fromBcd(min & 0x7FU);
out.hour = fromBcd(hour & 0x3FU);
out.day = fromBcd(day & 0x3FU);
out.weekday = fromBcd(weekday & 0x07U);
out.month = fromBcd(month & 0x1FU);
uint8_t yy = fromBcd(year);
bool century = (month & 0x80U) != 0;
out.year = century ? (1900U + yy) : (2000U + yy);
return true;
}
static bool rtcWrite(const RtcDateTime& in) {
bool century = in.year < 2000;
uint8_t yy = (uint8_t)(in.year % 100);
uint8_t monthReg = toBcd(in.month) & 0x1FU;
if (century) {
monthReg |= 0x80U;
}
Wire1.beginTransmission(RTC_I2C_ADDR);
Wire1.write(0x02); // seconds register
Wire1.write(toBcd(in.second) & 0x7FU);
Wire1.write(toBcd(in.minute) & 0x7FU);
Wire1.write(toBcd(in.hour) & 0x3FU);
Wire1.write(toBcd(in.day) & 0x3FU);
Wire1.write(toBcd(in.weekday) & 0x07U);
Wire1.write(monthReg);
Wire1.write(toBcd(yy));
return Wire1.endTransmission() == 0;
}
static bool parseSetCommand(const String& line, RtcDateTime& dt) {
int y, mo, d, h, mi, s;
if (sscanf(line.c_str(), "set %d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6) {
return false;
}
if (y < 1900 || y > 2099) return false;
if (mo < 1 || mo > 12) return false;
if (d < 1 || d > 31) return false;
if (h < 0 || h > 23) return false;
if (mi < 0 || mi > 59) return false;
if (s < 0 || s > 59) return false;
dt.year = (uint16_t)y;
dt.month = (uint8_t)mo;
dt.day = (uint8_t)d;
dt.hour = (uint8_t)h;
dt.minute = (uint8_t)mi;
dt.second = (uint8_t)s;
dt.weekday = 0; // Not critical for this persistence check.
return true;
}
static void printRtcNow(const char* label) {
RtcDateTime now{};
bool lowV = false;
if (!rtcRead(now, lowV)) {
logf("%s: RTC read failed", label);
return;
}
logf("%s: %04u-%02u-%02u %02u:%02u:%02u weekday=%u%s",
label,
(unsigned)now.year, (unsigned)now.month, (unsigned)now.day,
(unsigned)now.hour, (unsigned)now.minute, (unsigned)now.second,
(unsigned)now.weekday,
lowV ? " [LOW_VOLTAGE_FLAG]" : "");
}
static void handleCommand(const String& raw) {
String line = raw;
line.trim();
if (line.length() == 0) {
return;
}
if (line == "help") {
logf("Commands:");
logf(" show");
logf(" set YYYY-MM-DD HH:MM:SS");
logf(" help");
return;
}
if (line == "show") {
printRtcNow("RTC");
return;
}
RtcDateTime dt{};
if (parseSetCommand(line, dt)) {
if (rtcWrite(dt)) {
logf("RTC set succeeded");
printRtcNow("RTC");
} else {
logf("RTC set failed");
}
return;
}
logf("Unknown command: %s", line.c_str());
}
void setup() {
Serial.begin(115200);
Serial.println("Sleeping for 5 seconds to allow Serial Monitor connection...");
delay(5000);
Serial.println("\r\n==================================================");
Serial.println("Exercise 06: RTC check (PCF8563)");
Serial.println("==================================================");
Serial.printf("RTC I2C: SDA1=%d SCL1=%d ADDR=0x%02X\r\n", I2C_SDA1, I2C_SCL1, RTC_I2C_ADDR);
initPmuForRtc();
logf("Type 'help' for commands.");
logf("Power-off persistence test:");
logf(" 1) set time");
logf(" 2) power off for a few minutes");
logf(" 3) power on and run 'show' before any GPS sync");
printRtcNow("RTC startup");
}
void loop() {
while (Serial.available() > 0) {
char c = (char)Serial.read();
if (c == '\r' || c == '\n') {
// Handle CR, LF, and CRLF/LFCR cleanly as one line ending.
if ((c == '\n' && g_lastWasCR) || (c == '\r' && !g_lastWasCR && g_cmdBuf.length() == 0)) {
g_lastWasCR = (c == '\r');
continue;
}
handleCommand(g_cmdBuf);
g_cmdBuf = "";
g_lastWasCR = (c == '\r');
} else {
g_lastWasCR = false;
g_cmdBuf += c;
if (g_cmdBuf.length() > 120) {
g_cmdBuf = "";
logf("Input line too long, buffer cleared");
}
}
}
uint32_t nowMs = millis();
if ((uint32_t)(nowMs - g_lastPrintMs) >= 10000) {
g_lastPrintMs = nowMs;
printRtcNow("RTC periodic");
}
}