diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 68ab0ade890..154ea8f40b7 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -42,6 +42,7 @@ esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs fugit = "0.3.7" log = { version = "0.4.22", optional = true } nb = "1.1.0" +once_cell = { version = "1.20.2", optional = true, default-features = false, features = ["race"] } paste = "1.0.15" portable-atomic = { version = "1.9.0", default-features = false } procmacros = { version = "0.15.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } @@ -152,6 +153,9 @@ octal-psram = [] # This feature is intended for testing; you probably don't want to enable it: ci = ["defmt", "bluetooth"] +# Enable `try_init` -- requires once_cell +try-init = ["once_cell"] + [lints.clippy] mixed_attributes_style = "allow" diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index aafe8b21ea8..b689d6844f0 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -559,3 +559,18 @@ pub fn init(config: Config) -> Peripherals { peripherals } + +/// Attempts to initialize the system. +/// +/// If [`init`] has already been executed, returns [`None`]. +/// (This check is [thread-safe](once_cell::race::OnceBool)) +/// +/// Otherwise, initializes and returns the [`Peripherals`]. +#[cfg(feature = "try-init")] +pub fn try_init(config: Config) -> Option { + if !Peripherals::initialized() { + Some(init(config)) + } else { + None + } +} diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 4a817111a84..5d55dbec1a0 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -257,6 +257,8 @@ mod peripheral_macros { ),* $(,)? ] ) => { + #[cfg(feature = "try-init")] + use once_cell::race::OnceBool; /// Contains the generated peripherals which implement [`Peripheral`] mod peripherals { @@ -293,8 +295,25 @@ mod peripheral_macros { } impl Peripherals { + #[cfg(feature = "try-init")] + const INITIALIZED: OnceBool = OnceBool::new(); + + #[cfg(feature = "try-init")] + pub(crate) fn initialized() -> bool { + Self::INITIALIZED.get().is_some() + } + /// Returns all the peripherals *once* #[inline] + #[cfg(feature = "try-init")] + pub(crate) fn take() -> Self { + if let Ok(_) = Self::INITIALIZED.set(true) { + unsafe { Self::steal() } + } else { + panic!("init called more than once!") + } + } + #[cfg(not(feature = "try-init"))] pub(crate) fn take() -> Self { #[no_mangle] static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;