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:
parent
a83684d0cb
commit
544d459c9b
11 changed files with 1288 additions and 6 deletions
248
exercises/06_RTC_check/src/main.cpp
Normal file
248
exercises/06_RTC_check/src/main.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue