diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 360c1427d7..4de91499b9 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `esp_hal::psram::psram_raw_parts` (#2546) - The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586) - `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586) +- Add `try_init` function behind non-default feature `try-init` (#2618) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 68ab0ade89..154ea8f40b 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 aafe8b21ea..b689d6844f 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 4a817111a8..5d55dbec1a 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; diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index cc273714e9..186152cb1c 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -297,6 +297,8 @@ integrated-timers = [ "esp-hal-embassy/integrated-timers", ] octal-psram = ["esp-hal/octal-psram", "esp-alloc"] +# `try-init`: +try-init = ["esp-hal/try-init"] # https://doc.rust-lang.org/cargo/reference/profiles.html#test # Test and bench profiles inherit from dev and release respectively. diff --git a/hil-test/tests/init.rs b/hil-test/tests/init.rs index cec885c731..d255073a58 100644 --- a/hil-test/tests/init.rs +++ b/hil-test/tests/init.rs @@ -107,4 +107,11 @@ mod tests { let delay = Delay::new(); delay.delay(2000.millis()); } + + #[cfg(feature = "try-init")] + #[test] + fn test_try_init() { + assert!(esp_hal::try_init(Config::default()).is_some()); + assert!(esp_hal::try_init(Config::default()).is_none()); + } }