From feb734c8b70d08e51ed639b5ef2fecb4f7b65f94 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Apr 2026 09:11:41 -1000 Subject: [PATCH] [rp2040] Add interrupt guard to millis() accumulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wiegand and ZyAura call millis() from ISR handlers on all platforms including RP2040 (no platform restrictions on either component). The accumulator's mutable statics need protection from concurrent ISR access. Uses save_and_disable_interrupts()/restore_interrupts() — same pattern as InterruptLock in rp2040/helpers.cpp. --- esphome/components/rp2040/core.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/esphome/components/rp2040/core.cpp b/esphome/components/rp2040/core.cpp index 032669e129..d10b41ee64 100644 --- a/esphome/components/rp2040/core.cpp +++ b/esphome/components/rp2040/core.cpp @@ -26,32 +26,36 @@ void HOT yield() { ::yield(); } // s_last_us and ::micros() start at 0, so no special initialization needed. // // Also installed as __wrap_millis (via -Wl,--wrap=millis) so Arduino library -// code calling ::millis() directly gets the fast version. No interrupt guard -// needed — no ESPHome ISR calls millis() on RP2040. +// code calling ::millis() directly gets the fast version. Interrupts are +// briefly disabled to protect the static state — Wiegand and ZyAura call +// millis() from ISR handlers on all platforms including RP2040. +static constexpr uint32_t MILLIS_RARE_PATH_THRESHOLD_US = 10000; +static constexpr uint32_t US_PER_MS = 1000; + uint32_t HOT millis() { static struct { uint32_t cache; uint32_t remainder; uint32_t last_us; } state = {0, 0, 0}; + uint32_t ps = save_and_disable_interrupts(); uint32_t now_us = ::micros(); uint32_t delta = now_us - state.last_us; state.last_us = now_us; state.remainder += delta; - if (state.remainder >= 10000) { - // Rare path: large gap (>10 ms — boot, long block). Constant-time - // multiply-by-reciprocal via /1000. - uint32_t ms = state.remainder / 1000; + if (state.remainder >= MILLIS_RARE_PATH_THRESHOLD_US) { + uint32_t ms = state.remainder / US_PER_MS; state.cache += ms; - state.remainder -= ms * 1000; + state.remainder -= ms * US_PER_MS; } else { - // Common path: small gap. Loop runs at most 10 times. - while (state.remainder >= 1000) { + while (state.remainder >= US_PER_MS) { state.cache++; - state.remainder -= 1000; + state.remainder -= US_PER_MS; } } - return state.cache; + uint32_t result = state.cache; + restore_interrupts(ps); + return result; } // millis_64() keeps the full 64-bit timer for precision — called once per loop by Scheduler. uint64_t millis_64() { return micros_to_millis(time_us_64()); }