raspberrypi / pico-extras

BSD 3-Clause "New" or "Revised" License
480 stars 120 forks source link

RP2040/pico sleep alarms/timingusing ROSC not accurate #84

Closed Zarickan closed 1 month ago

Zarickan commented 1 month ago

I am working on a project using a pico w, where the pico needs to be in sleep and wake up every minute to log a measurement. To acheive this I have implemented sleeping and waking from an rtc alarm using the ROSC based on the code in this repository, the RTC alarm to wake up the pico is:

static datetime_t wakeup = { -1, -1, -1, -1, -1, -1, 0 };

The problem is that the RTC time seems to run slower than the real time when the pico is sleeping, with this alarm the pico will wake up at an interval of ~69 seconds, as oposed to 60, while according to the RTC time from the pico it is wakng up in 1 minute intervals.

Comparing the expected sleeping time with the actual sleeping the ratio is 60/69 ~= 0.8696, apply this ratio to the wakeup time and increment by 52 seconds each interval, results in the pico waking up with 60 second intervals. This ratio also applies to other time ranges, I have tested with adding 30 seconds to the current time as the wakeup time, which results in ~34½ seconds in practice, while adding 26 seconds results in 30 second intervals.

Curiously setting an RTC alarm to a specific time before doing any sleeping results in the alarm firing at the correct intervals, so the sleep seems to have something to do with it. Does anyone know why this might be?

My code for sleeping, waking up, and the initialization is this (running on a pico w):

static datetime_t initial_time = { 0, 1, 1, 0, 0, 0, 0 };
static datetime_t wakeup = { -1, -1, -1, -1, -1, -1, 0 };

static u08 scr_orig = 0x00;
static u08 en0_orig = 0x00;
static u08 en1_orig = 0x00;

void go_to_sleep(void) {
    scr_orig = scb_hw->scr;
    en0_orig = clocks_hw->sleep_en0;
    en1_orig = clocks_hw->sleep_en1;
    sleep_run_from_rosc();
    sleep_goto_sleep_until(&wakeup, wake_up_from_sleep);
}

void leave_low_power_mode(void) {
    rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS);
    clocks_init();

    clocks_hw->sleep_en0 = en0_orig;
    clocks_hw->sleep_en1 = en1_orig;
    scb_hw->scr = scr_orig;

    clocks_init();
}

// Startup
rtc_init();
rtc_set_datetime(&initial_time);
sleep_us(64);

go_to_sleep();
lurch commented 1 month ago

Yes, the ROSC is known to be much less accurate than an external crystal (i.e. XOSC). See section 2.17 of https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

Zarickan commented 1 month ago

I think there is more to it than this, unless I am missing something I don't se anything abnout the accuracy varying depending on whether the chip is sleeping or not. It is strange that if I set an alarm without sleeping the accuracy is fine, down to the second, while if I use the alarm to wake from sleep it is off by a lot (9+ seconds).

Am I missing something about the ROSC when sleeping?

lurch commented 1 month ago

I don't se anything abnout the accuracy varying depending on whether the chip is sleeping or not.

The datasheet says that the ROSC frequency is dependent on PVT, so perhaps when the Pico is sleeping, the Voltage and/or Temperature are different? :shrug:

if I set an alarm without sleeping the accuracy is fine, down to the second, while if I use the alarm to wake from sleep it is off by a lot (9+ seconds).

I see that your go_to_sleep function above has a call to sleep_run_from_rosc, so are you sure that you're running from ROSC (and not the default XOSC) when you're not sleeping?

Zarickan commented 1 month ago

You are right, I am using the ROSC when sleeping, if I use sleep_run_from_xosc() instead then the interval is accurate.

Interesting that there is so much difference between the two, I am running from USB for testing, so I can't see why voltages would differ when sleeping, same with temperature as I don't really do a lot calculation when waking up.

Thank you for helping.