Skip to content

Commit

Permalink
Implement esp_hal::try_init API
Browse files Browse the repository at this point in the history
  • Loading branch information
speelbarrow committed Nov 27, 2024
1 parent cfb83b1 commit 090b8b8
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 21 deletions.
4 changes: 4 additions & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ 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)
- Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614)
- Improve safety for verifying `init` is only called once,
add `try_init` function for safely initializing peripheral when they may already have been (#2618)

### Changed

Expand All @@ -42,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The timer drivers `OneShotTimer` & `PeriodicTimer` now have a `Mode` parameter and type erase the underlying driver by default (#2586)
- `timer::Timer` has new trait requirements of `Into<AnyTimer>`, `'static` and `InterruptConfigurable` (#2586)
- `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586)
- A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614)

### Fixed

Expand Down
40 changes: 30 additions & 10 deletions esp-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,15 +494,7 @@ pub struct Config {
pub psram: psram::PsramConfig,
}

/// Initialize the system.
///
/// This function sets up the CPU clock and watchdog, then, returns the
/// peripherals and clocks.
pub fn init(config: Config) -> Peripherals {
system::disable_peripherals();

let mut peripherals = Peripherals::take();

fn init_internal<'a>(peripherals: &'a mut Peripherals, config: Config) {
// RTC domain must be enabled before we try to disable
let mut rtc = crate::rtc_cntl::Rtc::new(&mut peripherals.LPWR);

Expand Down Expand Up @@ -556,6 +548,34 @@ pub fn init(config: Config) -> Peripherals {

#[cfg(any(feature = "quad-psram", feature = "octal-psram"))]
crate::psram::init_psram(config.psram);
}

peripherals
/// Initialize the system.
///
/// This function sets up the CPU clock and watchdog, then, returns the
/// peripherals and clocks.
pub fn init(config: Config) -> Peripherals {
system::disable_peripherals();

if let Some(mut peripherals) = Peripherals::try_take() {
init_internal(&mut peripherals, config);
peripherals
} else {
panic!("init called more than once!");
}
}

/// Initializes the system only if necessary, then returns the peripherals.
///
/// If [`init`] has already been executed, simply steals and returns the
/// peripherals. (This check uses [`AtomicBool`](core::sync::atomic::AtomicBool)
/// to be as thread-safe as possible)
///
/// Otherwise, the initialization process is executed (and then the peripherals
/// are stolen and handed off to you).
pub unsafe fn try_init(config: Config) -> Peripherals {
if let Some(mut peripherals) = Peripherals::try_take() {
init_internal(&mut peripherals, config);
}
Peripherals::steal()
}
23 changes: 12 additions & 11 deletions esp-hal/src/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,14 @@ mod peripheral_macros {
),* $(,)?
]
) => {
use portable_atomic::Ordering;


/// Contains the generated peripherals which implement [`Peripheral`]
mod peripherals {
use portable_atomic::AtomicBool;
pub(crate) static INITIALIZED: AtomicBool = AtomicBool::new(false);

pub use super::pac::*;
$(
$crate::create_peripheral!($name <= $from_pac);
Expand Down Expand Up @@ -295,17 +300,13 @@ mod peripheral_macros {
impl Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub(crate) fn take() -> Self {
#[no_mangle]
static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;

critical_section::with(|_| unsafe {
if _ESP_HAL_DEVICE_PERIPHERALS {
panic!("init called more than once!")
}
_ESP_HAL_DEVICE_PERIPHERALS = true;
Self::steal()
})
pub(crate) fn try_take() -> Option<Self> {
if let Ok(_) = peripherals::INITIALIZED.compare_exchange(false, true,
Ordering::Acquire, Ordering::Relaxed) {
Some(unsafe { Self::steal() })
} else {
None
}
}

/// Unsafely create an instance of this peripheral out of thin air.
Expand Down
15 changes: 15 additions & 0 deletions hil-test/tests/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,19 @@ mod tests {
let delay = Delay::new();
delay.delay(2000.millis());
}

#[test]
#[timeout(3)]
fn test_init_then_try_init() {
esp_hal::init(Config::default());

esp_hal::try_init(Config::default());
}

#[test]
#[should_panic]
fn test_try_init_then_init() {
esp_hal::try_init(Config::default());
esp_hal::init(Config::default());
}
}

0 comments on commit 090b8b8

Please sign in to comment.