mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 23:37:34 +00:00
Merge remote-tracking branch 'upstream/inline-micros-esp32' into integration
# Conflicts: # esphome/core/hal.h
This commit is contained in:
@@ -84,7 +84,7 @@ void HOT delay(uint32_t ms) {
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
}
|
||||
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
|
||||
// delayMicroseconds(), arch_feed_wdt(), and progmem_read_*() are inlined in hal/hal_esp8266.h.
|
||||
void arch_restart() {
|
||||
system_restart();
|
||||
// restart() doesn't always end execution
|
||||
@@ -93,17 +93,6 @@ void arch_restart() {
|
||||
}
|
||||
}
|
||||
void arch_init() {}
|
||||
void HOT arch_feed_wdt() { system_soft_wdt_feed(); }
|
||||
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||
return pgm_read_byte(addr); // NOLINT
|
||||
}
|
||||
const char *progmem_read_ptr(const char *const *addr) {
|
||||
return reinterpret_cast<const char *>(pgm_read_ptr(addr)); // NOLINT
|
||||
}
|
||||
uint16_t progmem_read_uint16(const uint16_t *addr) {
|
||||
return pgm_read_word(addr); // NOLINT
|
||||
}
|
||||
uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() { return esp_get_cycle_count(); }
|
||||
uint32_t arch_get_cpu_freq_hz() { return F_CPU; }
|
||||
|
||||
|
||||
+24
-170
@@ -6,190 +6,44 @@
|
||||
#include "esphome/core/time_64.h"
|
||||
#include "esphome/core/time_conversion.h"
|
||||
|
||||
// Per-platform HAL bits (IRAM_ATTR / PROGMEM macros, in_isr_context(),
|
||||
// inline yield/delay/micros/millis/millis_64 wrappers, ESP8266 progmem
|
||||
// helpers) live under esphome/core/hal/ and are dispatched here based on
|
||||
// the active USE_* platform define. Each header guards its body with the
|
||||
// matching #ifdef USE_<platform> and re-enters namespace esphome {} so it
|
||||
// is safe to be re-included.
|
||||
#if defined(USE_ESP32)
|
||||
#include <esp_attr.h>
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
#include "esphome/core/hal/hal_esp32.h"
|
||||
#elif defined(USE_ESP8266)
|
||||
|
||||
#include <c_types.h>
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM ICACHE_RODATA_ATTR
|
||||
#endif
|
||||
|
||||
#elif defined(USE_RP2040)
|
||||
|
||||
#define IRAM_ATTR __attribute__((noinline, long_call, section(".time_critical")))
|
||||
#define PROGMEM
|
||||
|
||||
#include "esphome/core/hal/hal_esp8266.h"
|
||||
#elif defined(USE_LIBRETINY)
|
||||
|
||||
// IRAM_ATTR places a function in executable RAM so it is callable from an
|
||||
// ISR even while flash is busy (XIP stall, OTA, logger flash write).
|
||||
// Each family uses a section its stock linker already routes to RAM:
|
||||
// RTL8710B → .image2.ram.text, RTL8720C → .sram.text. LN882H is the
|
||||
// exception: its stock linker has no matching glob, so patch_linker.py
|
||||
// injects KEEP(*(.sram.text*)) into .flash_copysection at pre-link.
|
||||
//
|
||||
// BK72xx (all variants) are left as a no-op: their SDK wraps flash
|
||||
// operations in GLOBAL_INT_DISABLE() which masks FIQ + IRQ at the CPU for
|
||||
// the duration of every write, so no ISR fires while flash is stalled and
|
||||
// the race IRAM_ATTR guards against cannot occur. The trade-off is that
|
||||
// interrupts are delayed (not dropped) by up to ~20 ms during a sector
|
||||
// erase, but that is an SDK-level choice and cannot be changed from this
|
||||
// layer.
|
||||
#if defined(USE_BK72XX)
|
||||
#define IRAM_ATTR
|
||||
#elif defined(USE_LIBRETINY_VARIANT_RTL8710B)
|
||||
// Stock linker consumes *(.image2.ram.text*) into .ram_image2.text (> BD_RAM).
|
||||
#define IRAM_ATTR __attribute__((noinline, section(".image2.ram.text")))
|
||||
#include "esphome/core/hal/hal_libretiny.h"
|
||||
#elif defined(USE_RP2040)
|
||||
#include "esphome/core/hal/hal_rp2040.h"
|
||||
#elif defined(USE_HOST)
|
||||
#include "esphome/core/hal/hal_host.h"
|
||||
#elif defined(USE_ZEPHYR)
|
||||
#include "esphome/core/hal/hal_zephyr.h"
|
||||
#else
|
||||
// RTL8720C: stock linker consumes *(.sram.text*) into .ram.code_text.
|
||||
// LN882H: patch_linker.py.script injects *(.sram.text*) into
|
||||
// .flash_copysection (> RAM0 AT> FLASH).
|
||||
#define IRAM_ATTR __attribute__((noinline, section(".sram.text")))
|
||||
#endif
|
||||
#define PROGMEM
|
||||
|
||||
#else
|
||||
|
||||
#define IRAM_ATTR
|
||||
#define PROGMEM
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIBRETINY
|
||||
// For the inline millis() fast paths (xTaskGetTickCount, portTICK_PERIOD_MS).
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
// Declared in the Beken FreeRTOS port (portmacro.h) and built in ARM mode so
|
||||
// it is callable from Thumb code via interworking. The MRS CPSR instruction
|
||||
// is ARM-only and user code here may be built in Thumb, so in_isr_context()
|
||||
// defers to this port helper on BK72xx instead of reading CPSR inline.
|
||||
extern "C" uint32_t platform_is_in_interrupt_context(void);
|
||||
#endif
|
||||
|
||||
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
|
||||
// NOLINT covers TUs that also include Arduino.h.
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
||||
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
extern "C" void yield(void);
|
||||
extern "C" void delay(unsigned long ms);
|
||||
extern "C" unsigned long micros(void);
|
||||
extern "C" unsigned long millis(void);
|
||||
// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
// Forward decl from <pico/time.h>.
|
||||
extern "C" uint64_t time_us_64(void);
|
||||
#error "hal.h: not implemented for this platform"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
/// always_inline so callers placed in IRAM keep the detection in IRAM.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() {
|
||||
#if defined(USE_ESP32)
|
||||
return xPortInIsrContext() != 0;
|
||||
#elif defined(USE_ESP8266)
|
||||
// ESP8266 has no reliable single-register ISR detection: PS.INTLEVEL is
|
||||
// non-zero both in a real ISR and when user code masks interrupts. The
|
||||
// ESP8266 wake path is context-agnostic (wake_loop_impl uses esp_schedule
|
||||
// which is ISR-safe) so this helper is unused on this platform.
|
||||
return false;
|
||||
#elif defined(USE_RP2040)
|
||||
uint32_t ipsr;
|
||||
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
|
||||
return ipsr != 0;
|
||||
#elif defined(USE_BK72XX)
|
||||
// BK72xx is ARM968E-S (ARM9); see extern declaration above.
|
||||
return platform_is_in_interrupt_context() != 0;
|
||||
#elif defined(USE_LIBRETINY)
|
||||
// Cortex-M (AmebaZ, AmebaZ2, LN882H). IPSR is the active exception number;
|
||||
// non-zero means we're in a handler.
|
||||
uint32_t ipsr;
|
||||
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
|
||||
return ipsr != 0;
|
||||
#else
|
||||
// Host and any future platform without an ISR concept.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// yield()/delay()/micros()/millis()/millis_64() are inlined per platform to
|
||||
// drop the wrapper call/return — most relevant to runtime_stats and the main
|
||||
// loop. ESP8266/LibreTiny/RP2040 share Arduino's ::yield/::delay/::micros.
|
||||
#if defined(USE_ESP32)
|
||||
// Forward decl from <esp_timer.h>.
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
extern "C" int64_t esp_timer_get_time(void);
|
||||
__attribute__((always_inline)) inline void yield() { vPortYield(); }
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(esp_timer_get_time()); }
|
||||
uint32_t millis();
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() {
|
||||
return micros_to_millis<uint64_t>(static_cast<uint64_t>(esp_timer_get_time()));
|
||||
}
|
||||
#elif defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
||||
__attribute__((always_inline)) inline void yield() { ::yield(); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
|
||||
#if defined(USE_ESP8266)
|
||||
void delay(uint32_t ms);
|
||||
uint32_t millis();
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
|
||||
#elif defined(USE_LIBRETINY)
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
|
||||
// Per-variant millis() fast path — matches MillisInternal::get().
|
||||
#if defined(USE_RTL87XX) || defined(USE_LN882X)
|
||||
static_assert(configTICK_RATE_HZ == 1000, "millis() fast path requires 1 kHz FreeRTOS tick");
|
||||
__attribute__((always_inline)) inline uint32_t millis() {
|
||||
// xTaskGetTickCountFromISR is mandatory in interrupt context per the FreeRTOS API contract.
|
||||
return in_isr_context() ? xTaskGetTickCountFromISR() : xTaskGetTickCount();
|
||||
}
|
||||
#elif defined(USE_BK72XX)
|
||||
static_assert(configTICK_RATE_HZ == 500, "BK72xx millis() fast path assumes 500 Hz FreeRTOS tick");
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return xTaskGetTickCount() * portTICK_PERIOD_MS; }
|
||||
#else
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return static_cast<uint32_t>(::millis()); }
|
||||
#endif
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
|
||||
#else // USE_RP2040
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return micros_to_millis(::time_us_64()); }
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return micros_to_millis<uint64_t>(::time_us_64()); }
|
||||
#endif
|
||||
#else
|
||||
void yield();
|
||||
void delay(uint32_t ms);
|
||||
uint32_t micros();
|
||||
uint32_t millis();
|
||||
uint64_t millis_64();
|
||||
#endif
|
||||
// ESP8266 inlines delayMicroseconds() and arch_feed_wdt() in hal/hal_esp8266.h;
|
||||
// every other platform keeps them out-of-line in components/<platform>/core.cpp.
|
||||
#ifndef USE_ESP8266
|
||||
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
|
||||
void arch_feed_wdt();
|
||||
#endif
|
||||
void __attribute__((noreturn)) arch_restart();
|
||||
void arch_init();
|
||||
void arch_feed_wdt();
|
||||
uint32_t arch_get_cpu_cycle_count();
|
||||
uint32_t arch_get_cpu_freq_hz();
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
// ESP8266: pgm_read_* does real flash reads on Harvard architecture
|
||||
uint8_t progmem_read_byte(const uint8_t *addr);
|
||||
const char *progmem_read_ptr(const char *const *addr);
|
||||
uint16_t progmem_read_uint16(const uint16_t *addr);
|
||||
#else
|
||||
// All other platforms: PROGMEM is a no-op, so these are direct dereferences
|
||||
#ifndef USE_ESP8266
|
||||
// All non-ESP8266 platforms: PROGMEM is a no-op, so these are direct dereferences.
|
||||
// ESP8266's out-of-line declarations live in hal/hal_esp8266.h.
|
||||
inline uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||
inline const char *progmem_read_ptr(const char *const *addr) { return *addr; }
|
||||
inline uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <cstdint>
|
||||
#include <esp_attr.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "esphome/core/time_conversion.h"
|
||||
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() { return xPortInIsrContext() != 0; }
|
||||
|
||||
// Forward decl from <esp_timer.h>.
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
extern "C" int64_t esp_timer_get_time(void);
|
||||
|
||||
__attribute__((always_inline)) inline void yield() { vPortYield(); }
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(esp_timer_get_time()); }
|
||||
uint32_t millis();
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() {
|
||||
return micros_to_millis<uint64_t>(static_cast<uint64_t>(esp_timer_get_time()));
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include <c_types.h>
|
||||
#include <cstdint>
|
||||
#include <pgmspace.h>
|
||||
|
||||
#include "esphome/core/time_64.h"
|
||||
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM ICACHE_RODATA_ATTR
|
||||
#endif
|
||||
|
||||
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
|
||||
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
extern "C" void yield(void);
|
||||
extern "C" void delay(unsigned long ms);
|
||||
extern "C" unsigned long micros(void);
|
||||
extern "C" unsigned long millis(void);
|
||||
// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
|
||||
// Forward decl from <user_interface.h> for arch_feed_wdt() inline below.
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
extern "C" void system_soft_wdt_feed(void);
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// Forward decl from helpers.h so this header stays cheap.
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
void delay_microseconds_safe(uint32_t us);
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
/// ESP8266 has no reliable single-register ISR detection: PS.INTLEVEL is
|
||||
/// non-zero both in a real ISR and when user code masks interrupts. The
|
||||
/// ESP8266 wake path is context-agnostic (wake_loop_impl uses esp_schedule
|
||||
/// which is ISR-safe) so this helper is unused on this platform.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
|
||||
|
||||
__attribute__((always_inline)) inline void yield() { ::yield(); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
|
||||
void delay(uint32_t ms);
|
||||
uint32_t millis();
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
|
||||
|
||||
// ESP8266: pgm_read_* does aligned 32-bit flash reads on Harvard architecture.
|
||||
// Inline-forward to the platform macros so the wrappers themselves don't
|
||||
// occupy IRAM/flash on every call site.
|
||||
__attribute__((always_inline)) inline uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||
return pgm_read_byte(addr); // NOLINT
|
||||
}
|
||||
__attribute__((always_inline)) inline const char *progmem_read_ptr(const char *const *addr) {
|
||||
return reinterpret_cast<const char *>(pgm_read_ptr(addr)); // NOLINT
|
||||
}
|
||||
__attribute__((always_inline)) inline uint16_t progmem_read_uint16(const uint16_t *addr) {
|
||||
return pgm_read_word(addr); // NOLINT
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
__attribute__((always_inline)) inline void delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
|
||||
__attribute__((always_inline)) inline void arch_feed_wdt() { system_soft_wdt_feed(); }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP8266
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define IRAM_ATTR
|
||||
#define PROGMEM
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
/// Host has no ISR concept.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
|
||||
|
||||
void yield();
|
||||
void delay(uint32_t ms);
|
||||
uint32_t micros();
|
||||
uint32_t millis();
|
||||
uint64_t millis_64();
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_LIBRETINY
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// For the inline millis() fast paths (xTaskGetTickCount, portTICK_PERIOD_MS).
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#include "esphome/core/time_64.h"
|
||||
|
||||
// IRAM_ATTR places a function in executable RAM so it is callable from an
|
||||
// ISR even while flash is busy (XIP stall, OTA, logger flash write).
|
||||
// Each family uses a section its stock linker already routes to RAM:
|
||||
// RTL8710B → .image2.ram.text, RTL8720C → .sram.text. LN882H is the
|
||||
// exception: its stock linker has no matching glob, so patch_linker.py
|
||||
// injects KEEP(*(.sram.text*)) into .flash_copysection at pre-link.
|
||||
//
|
||||
// BK72xx (all variants) are left as a no-op: their SDK wraps flash
|
||||
// operations in GLOBAL_INT_DISABLE() which masks FIQ + IRQ at the CPU for
|
||||
// the duration of every write, so no ISR fires while flash is stalled and
|
||||
// the race IRAM_ATTR guards against cannot occur. The trade-off is that
|
||||
// interrupts are delayed (not dropped) by up to ~20 ms during a sector
|
||||
// erase, but that is an SDK-level choice and cannot be changed from this
|
||||
// layer.
|
||||
#if defined(USE_BK72XX)
|
||||
#define IRAM_ATTR
|
||||
#elif defined(USE_LIBRETINY_VARIANT_RTL8710B)
|
||||
// Stock linker consumes *(.image2.ram.text*) into .ram_image2.text (> BD_RAM).
|
||||
#define IRAM_ATTR __attribute__((noinline, section(".image2.ram.text")))
|
||||
#else
|
||||
// RTL8720C: stock linker consumes *(.sram.text*) into .ram.code_text.
|
||||
// LN882H: patch_linker.py.script injects *(.sram.text*) into
|
||||
// .flash_copysection (> RAM0 AT> FLASH).
|
||||
#define IRAM_ATTR __attribute__((noinline, section(".sram.text")))
|
||||
#endif
|
||||
#define PROGMEM
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
// Declared in the Beken FreeRTOS port (portmacro.h) and built in ARM mode so
|
||||
// it is callable from Thumb code via interworking. The MRS CPSR instruction
|
||||
// is ARM-only and user code here may be built in Thumb, so in_isr_context()
|
||||
// defers to this port helper on BK72xx instead of reading CPSR inline.
|
||||
extern "C" uint32_t platform_is_in_interrupt_context(void);
|
||||
#endif
|
||||
|
||||
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
|
||||
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
extern "C" void yield(void);
|
||||
extern "C" void delay(unsigned long ms);
|
||||
extern "C" unsigned long micros(void);
|
||||
extern "C" unsigned long millis(void);
|
||||
// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() {
|
||||
#if defined(USE_BK72XX)
|
||||
// BK72xx is ARM968E-S (ARM9); see extern declaration above.
|
||||
return platform_is_in_interrupt_context() != 0;
|
||||
#else
|
||||
// Cortex-M (AmebaZ, AmebaZ2, LN882H). IPSR is the active exception number;
|
||||
// non-zero means we're in a handler.
|
||||
uint32_t ipsr;
|
||||
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
|
||||
return ipsr != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void yield() { ::yield(); }
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
|
||||
|
||||
// Per-variant millis() fast path — matches MillisInternal::get().
|
||||
#if defined(USE_RTL87XX) || defined(USE_LN882X)
|
||||
static_assert(configTICK_RATE_HZ == 1000, "millis() fast path requires 1 kHz FreeRTOS tick");
|
||||
__attribute__((always_inline)) inline uint32_t millis() {
|
||||
// xTaskGetTickCountFromISR is mandatory in interrupt context per the FreeRTOS API contract.
|
||||
return in_isr_context() ? xTaskGetTickCountFromISR() : xTaskGetTickCount();
|
||||
}
|
||||
#elif defined(USE_BK72XX)
|
||||
static_assert(configTICK_RATE_HZ == 500, "BK72xx millis() fast path assumes 500 Hz FreeRTOS tick");
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return xTaskGetTickCount() * portTICK_PERIOD_MS; }
|
||||
#else
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return static_cast<uint32_t>(::millis()); }
|
||||
#endif
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_LIBRETINY
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "esphome/core/time_conversion.h"
|
||||
|
||||
#define IRAM_ATTR __attribute__((noinline, long_call, section(".time_critical")))
|
||||
#define PROGMEM
|
||||
|
||||
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
|
||||
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
extern "C" void yield(void);
|
||||
extern "C" void delay(unsigned long ms);
|
||||
extern "C" unsigned long micros(void);
|
||||
extern "C" unsigned long millis(void);
|
||||
// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
|
||||
|
||||
// Forward decl from <pico/time.h>.
|
||||
extern "C" uint64_t time_us_64(void);
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() {
|
||||
uint32_t ipsr;
|
||||
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
|
||||
return ipsr != 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void yield() { ::yield(); }
|
||||
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
|
||||
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
|
||||
__attribute__((always_inline)) inline uint32_t millis() { return micros_to_millis(::time_us_64()); }
|
||||
__attribute__((always_inline)) inline uint64_t millis_64() { return micros_to_millis<uint64_t>(::time_us_64()); }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define IRAM_ATTR
|
||||
#define PROGMEM
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Returns true when executing inside an interrupt handler.
|
||||
/// Zephyr/nRF52: not currently consulted — wake path is platform-specific.
|
||||
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
|
||||
|
||||
void yield();
|
||||
void delay(uint32_t ms);
|
||||
uint32_t micros();
|
||||
uint32_t millis();
|
||||
uint64_t millis_64();
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ZEPHYR
|
||||
Reference in New Issue
Block a user