Skip to content

Conversation

@XuGuohui
Copy link
Member

@XuGuohui XuGuohui commented Nov 20, 2025

Note: This PR makes the AM18x5 driver generally available for all Particle platforms, except for B-series due to flash size constraint.

Features

  • Allow user app to detect the presence of AM18x5
  • Allow user app to configure the RTC source, either using internal RTC or the AM18x5
  • Allow user app to set alarm using AM18x5
  • Allow user app to configure the AM18x5 settings, e.g. pins, osc source, xt calibration data etc
  • Allow user app to use the watchdog from Am18x5 (MCU's reset pin is connected with AM18x5)
  • Allow user app to power gate the MCU by Am18x5 (MCU's power is controlled by AM18x5's PSW pin)

Known issues

  • AM18x5's interrupt pin cannot connect to IO expander, i.e. the interrupt pin must connect to MCU's GPIO directly, otherwise, the alarm function won't work.
  • AM18x5 watchdog is not cancelled in bootloader

Example app

#include "application.h"

SYSTEM_MODE(MANUAL);

SerialLogHandler l(115200, LOG_LEVEL_ALL);

STARTUP (
    System.enableFeature(FEATURE_EXRTC_DETECTION);

    SystemExternalRtcConfiguration config;
    config.defaultRtc(true)
          .watchdogInputPin(PIN_INVALID)
          .interruptPin(A7)
          .i2cInterface(HAL_I2C_INTERFACE1)
          .rcFallbackOnXtalFailure(true)
          .rcOnBatteryPowered(true)
          .oscSource(Am18x5Oscillator::EXTERNAL_CRYSTAL)
          .xtalCalibrationValue(-45);
    System.setExternalRtcConfiguration(config);
);

void setup() {
    while (!Serial.isConnected()) {
    }
    delay(1s);

    Log.info("External RTC present: %s", System.isExternalRtcPresent() ? "yes" : "no");

    SystemExternalRtcConfiguration config = {};
    if (System.getExternalRtcConfiguration(config) == SYSTEM_ERROR_NONE) {
        Log.info("Current EXRTC config: default_rtc=%d, wdi_pin=%d, int_pin=%d, i2c_if=%d, rc_fallback=%d, rc_on_battery=%d, osc_src=%d, osc_cal_xt=%d",
                config.defaultRtc(),
                config.watchdogInputPin(),
                config.interruptPin(),
                config.i2cInterface(),
                config.rcFallbackOnXtalFailure(),
                config.rcOnBatteryPowered(),
                config.oscSource(),
                config.xtalCalibrationValue());
    } else {
        Log.info("Failed to get EXRTC config");
    }

    // SystemExternalRtcSleepConfiguration sleepConf = {};
    // sleepConf.duration(5s);
    // sleepConf.exti(Am18x5ExtiPolarity::FALLING);
    // System.powerGatedByExternalRtc(sleepConf);

    struct timeval now;
    hal_rtc_get_time(&now, nullptr);
    Log.info("Current RTC time: %lld", now.tv_sec);
    now.tv_sec += 5;
    static volatile bool alarmFired = false;
    auto ms = millis();
    Log.info("Alarm RTC time: %lld", now.tv_sec);
    int r = hal_rtc_set_alarm(&now, 0, [](void* ctx) -> void {
        volatile bool* alarmFired = (volatile bool*)ctx;
        *alarmFired = true;
    }, (void*)&alarmFired, nullptr);
    if (r != 0) {
        Log.error("Failed to set RTC alarm: %d!!!!!", r);
        return;
    }
    Log.info("Waiting for RTC alarm...");
    while (!alarmFired && (millis() - ms) <= 8000) {
        delay(1);
    }
    if (alarmFired) {
        Log.info("RTC alarm fired!");
    } else {
        Log.error("RTC alarm did not fire within expected time!");
    }

    WatchdogConfiguration wdgConf = {};
    wdgConf.timeout(10s);
    ExternalWatchdog.init(wdgConf);
    ExternalWatchdog.start();
}

void loop() {
    static bool flag = false;
    if (!flag) {
        Time.setTimeSource(HAL_RTC_SOURCE_INTERNAL);
        flag = true;
    } else {
        Time.setTimeSource(HAL_RTC_SOURCE_EXTERNAL);
        flag = false;
    }
    Log.info("Time source: %d, %lld", Time.getTimeSource(), Time.now());
    ExternalWatchdog.refresh();
    delay(1s);
}

Completeness

  • User is totes amazing for contributing!
  • Contributor has signed CLA (Info here)
  • Problem and Solution clearly stated
  • Run unit/integration/application tests on device
  • Added documentation
  • Added to CHANGELOG.md after merging (add links to docs and issues)

@XuGuohui XuGuohui marked this pull request as ready for review November 20, 2025 19:19
@XuGuohui XuGuohui added this to the 6.3.5 milestone Nov 20, 2025
@XuGuohui XuGuohui changed the title Feature/built in am18x5 Built-in am18x5 driver in DVOS Nov 20, 2025
extern "C" {
#endif // __cplusplus

int system_external_rtc_set_config(const particle::hal_am18x5_config_t* conf, void* reserved);
Copy link
Member

@avtolstoy avtolstoy Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be under HAL. We have for example sleep in both system and HAL, but the reason to have system-level APIs for it is that it deals with connectivity (network/cloud) and other somewhat higher level things. here you are just configuring/querying a peripheral albeit external one.

@XuGuohui XuGuohui force-pushed the feature/built_in_am18x5 branch from 0c0beb6 to 896ec3d Compare November 26, 2025 03:30
@XuGuohui XuGuohui requested a review from avtolstoy November 26, 2025 03:30
@scott-brust
Copy link
Member

scott-brust commented Dec 12, 2025

Should I expect this PR to work on Muon? I am running the test app and I see Time.now() is always incorrect.

0000539055 [app] INFO: Time source: 1, 4294967297
0000540186 [app] INFO: Time source: 0, 1
0000541316 [app] INFO: Time source: 1, 4294967297
0000542445 [app] INFO: Time source: 0, 1

I added logging to am18x5.cpp and can see the right values coming in/out of the rtc, so something is getting messed up in the wiring/hal translation layer. Ill keep looking into it

fwiw this is logged on boot

0000021435 [app] INFO: Current EXRTC config: default_rtc=1, wdi_pin=255, int_pin=28, i2c_if=0, rc_fallback=1, rc_on_battery=1, osc_src=1, osc_cal_xt=-45```

@XuGuohui
Copy link
Member Author

Should I expect this PR to work on Muon? I am running the test app and I see Time.now() is always incorrect.

Were you using MSoM + Muon? Just make sure the pins are configured as:

STARTUP(
#if PLATFORM_ID == PLATFORM_MSOM
    System.enableFeature(FEATURE_ETHERNET_DETECTION);
    if_wiznet_pin_remap remap = {};
    remap.base.type = IF_WIZNET_DRIVER_SPECIFIC_PIN_REMAP;
    remap.cs_pin = A3;
    remap.reset_pin = PIN_INVALID;
    remap.int_pin = A4;
    if_request(nullptr, IF_REQ_DRIVER_SPECIFIC, &remap, sizeof(remap), nullptr);

    SystemPowerConfiguration powerConfig = System.getPowerConfiguration();
    powerConfig.auxiliaryPowerControlPin(D7).interruptPin(A7);
    System.setPowerConfiguration(powerConfig);
#endif
);

Also, from your log, the external RTC wasn't even ticking.

@Kategrode Kategrode modified the milestones: 6.3.5, 6.4.0 Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants