Skip to main content

esp_hal/
lib.rs

1#![cfg_attr(
2    all(docsrs, not(not_really_docsrs)),
3    doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.espressif.com/projects/rust/'>browse the <code>esp-hal</code> documentation on the esp-rs website</a> instead.</p><p>The documentation here on <a href='https://docs.rs'>docs.rs</a> is built for a single chip only (ESP32-C6, in particular), while on the esp-rs website you can select your exact chip from the list of supported devices. Available peripherals and their APIs change depending on the chip.</p></div>\n\n<br/>\n\n"
4)]
5//! # Bare-metal (`no_std`) HAL for all Espressif ESP32 devices.
6//!
7//! This documentation is built for the
8#![doc = concat!("**", chip_pretty!(), "**")]
9//! . Please ensure you are reading the correct [documentation] for your target
10//! device.
11//!
12//! ## Overview
13//!
14//! esp-hal is a Hardware Abstraction Layer (HAL) for Espressif's ESP32 lineup of
15//! microcontrollers offering safe, idiomatic APIs to control hardware peripherals.
16//!
17//! ### Peripheral drivers
18//!
19//! The HAL implements both [`Blocking`] _and_ [`Async`] APIs for all applicable peripherals.
20//! Where applicable, driver implement the [embedded-hal] and
21//! [embedded-hal-async] traits. Drivers that do not currently have a stable API
22//! are marked as `unstable` in the documentation.
23//!
24//! ### Peripheral singletons
25//!
26//! Each peripheral driver needs a peripheral singleton that tells the driver
27//! which hardware block to use. The peripheral singletons are created by the
28//! HAL initialization, and are returned from [`init`] as fields of the
29//! [`Peripherals`] struct.
30//!
31//! These singletons, by default, represent peripherals for the entire lifetime
32//! of the program. To allow for reusing peripherals, the HAL provides a
33//! `reborrow` method on each peripheral singleton. This method creates a new
34//! handle to the peripheral with a shorter lifetime. This lets the handle be
35//! passed to a driver while keeping the original handle alive. Once the driver
36//! is dropped, the peripheral can be reborrowed again.
37#![cfg_attr(
38    // Feature-gated so that this doesn't prevent gradual device bringup. Any
39    // stable driver would serve the purpose here, so this block will be part
40    // of the released documentation.
41    i2c_master_driver_supported,
42    doc = r#"
43For example, if you want to use the [`I2c`](i2c::master::I2c) driver and you
44do not intend to drop the driver, you can pass the peripheral singleton to
45the driver by value:
46
47```rust, ignore
48// Peripheral singletons are returned from the `init` function.
49let peripherals = esp_hal::init(esp_hal::Config::default());
50
51let mut i2c = I2c::new(peripherals.I2C0, /* ... */);
52```
53"#
54)]
55//! If you want to use the peripheral in multiple places (for example, you want
56//! to drop the driver for some period of time to minimize power consumption),
57//! you can reborrow the peripheral singleton and pass it to the driver by
58//! reference:
59//!
60//! ```rust, ignore
61//! // Note that in this case, `peripherals` needs to be mutable.
62//! let mut peripherals = esp_hal::init(esp_hal::Config::default());
63//!
64//! let i2c = I2C::new(peripherals.I2C0.reborrow(), /* ... */);
65//!
66//! // Do something with the I2C driver...
67//!
68//! core::mem::drop(i2c); // Drop the driver to minimize power consumption.
69//!
70//! // Do something else...
71//!
72//! // You can then take or reborrow the peripheral singleton again.
73//! let i2c = I2C::new(peripherals.I2C0.reborrow(), /* ... */);
74//! ```
75//!
76//! ## Examples
77//!
78//! We have a plethora of [examples] in the esp-hal repository. We use
79//! an [xtask] to automate the building, running, and testing of code and
80//! examples within esp-hal.
81//!
82//! Invoke the following command in the root of the esp-hal repository to get
83//! started:
84//!
85//! ```bash
86//! cargo xtask help
87//! ```
88//!
89//! ## Creating a Project
90//!
91//! We have a [book] that explains the full esp-hal ecosystem
92//! and how to get started, it is advisable to give that a read
93//! before proceeding. We also have a [training] that covers some common
94//! scenarios with examples.
95//!
96//! We have developed a project generation tool, [esp-generate], which we
97//! recommend when starting new projects. It can be installed and run, e.g.
98//! for the ESP32-C6, as follows:
99//!
100//! ```bash
101//! cargo install esp-generate
102//! esp-generate --chip=esp32c6 your-project
103//! ```
104#![cfg_attr(
105    // Feature-gated so that this doesn't prevent gradual device bringup. Any
106    // stable driver would serve the purpose here, so this block will be part
107    // of the released documentation.
108    gpio_driver_supported,
109    doc = r#"
110## Blinky
111
112Some minimal code to blink an LED looks like this:
113
114```rust, no_run
115#![no_std]
116#![no_main]
117
118use esp_hal::{
119    clock::CpuClock,
120    gpio::{Io, Level, Output, OutputConfig},
121    main,
122    time::{Duration, Instant},
123};
124
125// You need a panic handler. Usually, you would use esp_backtrace, panic-probe, or
126// something similar, but you can also bring your own like this:
127#[panic_handler]
128fn panic(_: &core::panic::PanicInfo) -> ! {
129    esp_hal::system::software_reset()
130}
131
132#[main]
133fn main() -> ! {
134    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
135    let peripherals = esp_hal::init(config);
136
137    // Set GPIO0 as an output, and set its state high initially.
138    let mut led = Output::new(peripherals.GPIO0, Level::High, OutputConfig::default());
139
140    loop {
141        led.toggle();
142        // Wait for half a second
143        let delay_start = Instant::now();
144        while delay_start.elapsed() < Duration::from_millis(500) {}
145    }
146}
147```
148"#
149)]
150//! ## Additional configuration
151//!
152//! Some configuration options do not fit into cargo
153//! features. These can be set via environment variables, or via cargo's `[env]`
154//! section inside `.cargo/config.toml`. Unstable options can only be
155//! enabled when the `unstable` feature is enabled for the crate. Below is a
156//! table of tunable parameters for this crate:
157#![doc = ""]
158#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_hal_config_table.md"))]
159#![doc = ""]
160//! ## Don't use `core::mem::forget`
161//!
162//! You should never use `core::mem::forget` on any type defined in [esp crates].
163//! Many types heavily rely on their `Drop` implementation to not leave the
164//! hardware in undefined state which can cause undefined behavior in your program.
165//!
166//! You might want to consider using [`#[deny(clippy::mem_forget)`](https://rust-lang.github.io/rust-clippy/v0.0.212/index.html#mem_forget) in your project.
167//!
168//! ## Library usage
169//!
170//! If you intend to write a library that uses esp-hal, you should import it as follows:
171//!
172//! ```toml
173//! [dependencies]
174//! esp-hal = { version = "1", default-features = false } }
175//! ```
176//!
177//! This ensures that the `rt` feature is not enabled, nor any chip features. The application that
178//! uses your library will then be able to choose the chip feature it needs and enable `rt` such
179//! that only the final user application calls [`init`].
180//!
181//! If your library depends on `unstable` features, you *must* use the `requires-unstable` feature,
182//! and *not* the unstable feature itself. Doing so, improves the quality of the error messages if a
183//! user hasn't enabled the unstable feature of esp-hal.
184//!
185//! [documentation]: https://docs.espressif.com/projects/rust/esp-hal/latest/
186//! [examples]: https://github.com/esp-rs/esp-hal/tree/main/examples
187//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
188//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/
189//! [xtask]: https://github.com/matklad/cargo-xtask
190//! [esp-generate]: https://github.com/esp-rs/esp-generate
191//! [book]: https://docs.espressif.com/projects/rust/book/
192//! [training]: https://docs.espressif.com/projects/rust/no_std-training/
193//! [esp crates]: https://docs.espressif.com/projects/rust/book/introduction/ancillary-crates.html#esp-hal-ecosystem
194//!
195//! ## Feature Flags
196#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
197#![doc(html_logo_url = "https://docs.espressif.com/projects/rust/esp-rs-grey-bg.svg")]
198#![allow(asm_sub_register, async_fn_in_trait, stable_features)]
199#![cfg_attr(xtensa, feature(asm_experimental_arch))]
200#![deny(missing_docs, rust_2018_idioms, rustdoc::all)]
201#![allow(rustdoc::private_doc_tests)] // compile tests are done via rustdoc
202#![cfg_attr(docsrs, feature(doc_cfg, custom_inner_attributes, proc_macro_hygiene))]
203// Don't trip up on broken/private links when running semver-checks
204#![cfg_attr(
205    semver_checks,
206    allow(rustdoc::private_intra_doc_links, rustdoc::broken_intra_doc_links)
207)]
208// Do not document `cfg` gates by default.
209#![cfg_attr(docsrs, allow(invalid_doc_attributes))] // doc(auto_cfg = false) requires a new nightly (~2025-10-09+)
210#![cfg_attr(docsrs, doc(auto_cfg = false))]
211#![no_std]
212
213// MUST be the first module
214mod fmt;
215
216#[macro_use]
217extern crate esp_metadata_generated;
218
219// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
220#[doc(hidden)]
221macro_rules! unstable_module {
222    ($(
223        $(#[$meta:meta])*
224        pub mod $module:ident;
225    )*) => {
226        $(
227            $(#[$meta])*
228            #[cfg(feature = "unstable")]
229            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
230            pub mod $module;
231
232            $(#[$meta])*
233            #[cfg(not(feature = "unstable"))]
234            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
235            #[allow(unused)]
236            pub(crate) mod $module;
237        )*
238    };
239}
240
241// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
242// we don't want unstable drivers to be compiled even, unless enabled
243#[doc(hidden)]
244macro_rules! unstable_driver {
245    ($(
246        $(#[$meta:meta])*
247        pub mod $module:ident;
248    )*) => {
249        $(
250            $(#[$meta])*
251            #[cfg(feature = "unstable")]
252            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
253            pub mod $module;
254        )*
255    };
256}
257
258use core::marker::PhantomData;
259
260pub use esp_metadata_generated::chip;
261use esp_rom_sys as _;
262#[allow(unused)]
263pub(crate) use unstable_driver;
264pub(crate) use unstable_module;
265
266metadata!("build_info", CHIP_NAME, chip!());
267metadata!(
268    "build_info",
269    MIN_CHIP_REVISION,
270    esp_config::esp_config_str!("ESP_HAL_CONFIG_MIN_CHIP_REVISION")
271);
272
273#[cfg(feature = "rt")]
274cfg_select! {
275    riscv => {
276        #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
277        #[cfg_attr(not(feature = "unstable"), doc(hidden))]
278        pub use esp_riscv_rt::{self, riscv};
279    }
280    xtensa => {
281        #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
282        #[cfg_attr(not(feature = "unstable"), doc(hidden))]
283        pub use xtensa_lx_rt::{self, xtensa_lx};
284    }
285}
286
287pub(crate) use peripherals::pac;
288pub(crate) mod private;
289
290#[cfg(any(soc_has_dport, soc_has_hp_sys, soc_has_pcr, soc_has_system))]
291pub mod clock;
292#[cfg(gpio_driver_supported)]
293pub mod gpio;
294#[cfg(i2c_master_driver_supported)]
295pub mod i2c;
296// The I2S driver is unstable, but its clock tree nodes are part of every build.
297#[cfg(i2s_driver_supported)]
298#[path = "i2s/clocks/mod.rs"]
299mod i2s_clocks;
300pub mod peripherals;
301#[cfg(all(
302    feature = "unstable",
303    any(
304        hmac_driver_supported,
305        sha_driver_supported,
306        ethernet_driver_supported,
307        mipi_dsi_driver_supported
308    )
309))]
310mod reg_access;
311#[cfg(rng_driver_supported)]
312pub mod rng;
313#[cfg(any(spi_master_driver_supported, spi_slave_driver_supported))]
314pub mod spi;
315pub mod system;
316pub mod time;
317#[cfg(uart_driver_supported)]
318pub mod uart;
319
320mod macros;
321
322#[instability::unstable]
323pub use procmacros::handler;
324#[instability::unstable]
325#[cfg(ulp_riscv_driver_supported)]
326pub use procmacros::load_lp_code;
327#[cfg(feature = "rt")]
328pub use procmacros::main;
329pub use procmacros::ram;
330
331#[instability::unstable]
332#[cfg(ulp_riscv_driver_supported)]
333pub use self::soc::lp_core;
334
335#[cfg(all(feature = "rt", feature = "exception-handler"))]
336mod exception_handler;
337
338pub mod efuse;
339pub mod interrupt;
340
341unstable_module! {
342    pub mod asynch;
343    pub mod debugger;
344    pub mod rom;
345    #[doc(hidden)]
346    pub mod sync;
347    // Drivers needed for initialization or they are tightly coupled to something else.
348    #[cfg(any(adc_driver_supported, dac_driver_supported))]
349    pub mod analog;
350    #[cfg(any(systimer_driver_supported, timergroup_driver_supported))]
351    pub mod timer;
352    #[cfg(temp_sensor_driver_supported)]
353    pub mod tsens;
354    #[cfg(soc_has_lpwr)]
355    pub mod rtc_cntl;
356    #[cfg(dma_driver_supported)]
357    pub mod dma;
358    #[cfg(etm_driver_supported)]
359    pub mod etm;
360    #[cfg(soc_has_psram)] // DMA needs some things from here
361    pub mod psram;
362}
363
364#[cfg(any(
365    sha_driver_supported,
366    rsa_driver_supported,
367    aes_driver_supported,
368    ecc_driver_supported
369))]
370mod work_queue;
371
372unstable_driver! {
373    #[cfg(aes_driver_supported)]
374    pub mod aes;
375    #[cfg(assist_debug_driver_supported)]
376    pub mod assist_debug;
377    #[cfg(canfd_driver_supported)]
378    pub mod canfd;
379    pub mod delay;
380    #[cfg(ecc_driver_supported)]
381    pub mod ecc;
382    #[cfg(flash_driver_supported)]
383    pub mod flash;
384    #[cfg(hmac_driver_supported)]
385    pub mod hmac;
386    #[cfg(i2s_driver_supported)]
387    pub mod i2s;
388    #[cfg(soc_has_lcd_cam)]
389    pub mod lcd_cam;
390    #[cfg(ledc_driver_supported)]
391    pub mod ledc;
392    #[cfg(mcpwm_driver_supported)]
393    pub mod mcpwm;
394    #[cfg(parl_io_driver_supported)]
395    pub mod parl_io;
396    #[cfg(pcnt_driver_supported)]
397    pub mod pcnt;
398    #[cfg(rmt_driver_supported)]
399    pub mod rmt;
400    #[cfg(rsa_driver_supported)]
401    pub mod rsa;
402    #[cfg(sdmmc_driver_supported)]
403    pub mod sdmmc;
404    #[cfg(sha_driver_supported)]
405    pub mod sha;
406    #[cfg(sdm_driver_supported)]
407    pub mod sdm;
408    #[cfg(touch_driver_supported)]
409    pub mod touch;
410    #[cfg(soc_has_trace0)]
411    pub mod trace;
412    #[cfg(twai_driver_supported)]
413    pub mod twai;
414    #[cfg(any(
415        usb_otg_driver_supported,
416        usb_otg_hs_driver_supported,
417        usb_serial_jtag_driver_supported,
418    ))]
419    pub mod usb;
420    #[cfg(ethernet_driver_supported)]
421    pub mod ethernet;
422    #[cfg(mipi_dsi_driver_supported)]
423    pub mod mipi_dsi;
424}
425
426/// State of the CPU saved when entering exception or interrupt
427#[instability::unstable]
428#[cfg(feature = "rt")]
429#[allow(unused_imports)]
430pub mod trapframe {
431    #[cfg(riscv)]
432    pub use esp_riscv_rt::TrapFrame;
433    #[cfg(xtensa)]
434    pub use xtensa_lx_rt::exception::Context as TrapFrame;
435}
436
437// The `soc` module contains chip-specific implementation details and should not
438// be directly exposed.
439mod soc;
440
441// Some PAC-related utility
442use crate::pac::generic::{Readable, Reg, Resettable, W, Writable};
443
444#[allow(unused)]
445trait RegisterToggle {
446    type Reg: Readable + Resettable + Writable;
447
448    /// Toggles bits in the register, applying the given operation to set and clear them.
449    ///
450    /// More efficient than two modify calls, because it does not read the register
451    /// value twice.
452    fn toggle(&self, op: impl Fn(&mut W<Self::Reg>, bool) -> &mut W<Self::Reg>);
453}
454
455impl<REG> RegisterToggle for Reg<REG>
456where
457    REG: Readable + Resettable + Writable,
458{
459    type Reg = REG;
460
461    fn toggle(&self, op: impl Fn(&mut W<REG>, bool) -> &mut W<REG>) {
462        let bits = self.modify(|_, w| op(w, true));
463
464        self.write(|w| {
465            unsafe { w.bits(bits) };
466            op(w, false)
467        });
468    }
469}
470
471#[cfg(is_debug_build)]
472procmacros::warning! {"
473WARNING: use --release
474  We *strongly* recommend using release profile when building esp-hal.
475  The dev profile can potentially be one or more orders of magnitude
476  slower than release, and may cause issues with timing-sensitive
477  peripherals or devices.
478"}
479
480/// A marker trait for driver modes.
481///
482/// Different driver modes offer different features and different API. Using
483/// this trait as a generic parameter ensures that the driver is initialized in
484/// the correct mode.
485pub trait DriverMode: crate::private::Sealed {}
486
487#[procmacros::doc_replace]
488/// Marker type signaling that a driver is initialized in blocking mode.
489///
490/// Drivers are constructed in blocking mode by default. To learn about the
491/// differences between blocking and async drivers, see the [`Async`] mode
492/// documentation.
493///
494/// [`Async`] drivers can be converted to a [`Blocking`] driver using the
495/// `into_blocking` method, for example:
496#[cfg_attr(
497    // Feature-gated so that this doesn't prevent gradual device bringup. Any
498    // stable driver would serve the purpose here, so this block will be part
499    // of the released documentation.
500    all(uart_driver_supported, gpio_driver_supported),
501    doc = r#"
502```rust, no_run
503# {before_snippet}
504# use esp_hal::uart::{Config, Uart};
505let uart = Uart::new(peripherals.UART0, Config::default())?
506    .with_rx(peripherals.GPIO1)
507    .with_tx(peripherals.GPIO2)
508    .into_async();
509let blocking_uart = uart.into_blocking();
510# {after_snippet}
511```
512"#
513)]
514#[derive(Debug)]
515#[non_exhaustive]
516pub struct Blocking;
517
518#[procmacros::doc_replace]
519/// Marker type signaling that a driver is initialized in async mode.
520///
521/// Drivers are constructed in blocking mode by default. To set up an async
522/// driver, a [`Blocking`] driver must be converted to an `Async` driver using
523/// the `into_async` method, for example:
524#[cfg_attr(
525    // Feature-gated so that this doesn't prevent gradual device bringup. Any
526    // stable driver would serve the purpose here, so this block will be part
527    // of the released documentation.
528    all(uart_driver_supported, gpio_driver_supported),
529    doc = r#"
530```rust, no_run
531# {before_snippet}
532# use esp_hal::uart::{Config, Uart};
533let uart = Uart::new(peripherals.UART0, Config::default())?
534    .with_rx(peripherals.GPIO1)
535    .with_tx(peripherals.GPIO2)
536    .into_async();
537///
538# {after_snippet}
539```
540"#
541)]
542/// Drivers can be converted back to blocking mode using the `into_blocking`
543/// method, see [`Blocking`] documentation for more details.
544///
545/// Async mode drivers offer most of the same features as blocking drivers, but
546/// with the addition of async APIs. Interrupt-related functions are not
547/// available in async mode, as they are handled by the driver's interrupt
548/// handlers.
549///
550/// Async functions usually take up more space than their blocking counterparts,
551/// and they are generally slower. This is because async functions are implemented
552/// using a state machine that is driven by interrupts and is polled by a runtime.
553/// For short operations, the overhead of the state machine can be significant.
554/// Consider using the blocking functions on the async driver for small transfers.
555///
556/// When initializing an async driver, the driver disables user-specified
557/// interrupt handlers, and sets up internal interrupt handlers that drive the
558/// driver's async API. The driver's interrupt handlers run on the same core as
559/// the driver was initialized on. This means that the driver can not be sent
560/// across threads, to prevent incorrect concurrent access to the peripheral.
561///
562/// Switching back to blocking mode will disable the interrupt handlers and
563/// return the driver to a state where it can be sent across threads.
564#[derive(Debug)]
565#[non_exhaustive]
566pub struct Async(PhantomData<*const ()>);
567
568unsafe impl Sync for Async {}
569
570impl crate::DriverMode for Blocking {}
571impl crate::DriverMode for Async {}
572impl crate::private::Sealed for Blocking {}
573impl crate::private::Sealed for Async {}
574
575#[doc(hidden)]
576pub use private::Internal;
577
578/// Marker trait for types that can be safely used in `#[ram(unstable(persistent))]`.
579///
580/// # Safety
581///
582/// - The type must be inhabited
583/// - The type must be valid for any bit pattern of its backing memory in case a reset occurs during
584///   a write or a reset interrupts the zero initialization on first boot.
585/// - Structs must contain only `Persistable` fields and padding
586#[instability::unstable]
587pub unsafe trait Persistable: Sized {}
588
589/// Marker trait for types that can be safely used in `#[ram(reclaimed)]`.
590///
591/// # Safety
592///
593/// - The type must be some form of `MaybeUninit<T>`
594#[doc(hidden)]
595pub unsafe trait Uninit: Sized {}
596
597macro_rules! impl_persistable {
598    ($($t:ty),+) => {$(
599        unsafe impl Persistable for $t {}
600    )+};
601    (atomic $($t:ident),+) => {$(
602        unsafe impl Persistable for portable_atomic::$t {}
603    )+};
604}
605
606impl_persistable!(
607    u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64
608);
609impl_persistable!(atomic AtomicU8, AtomicI8, AtomicU16, AtomicI16, AtomicU32, AtomicI32, AtomicUsize, AtomicIsize);
610
611unsafe impl<T: Persistable, const N: usize> Persistable for [T; N] {}
612
613unsafe impl<T> Uninit for core::mem::MaybeUninit<T> {}
614unsafe impl<T, const N: usize> Uninit for [core::mem::MaybeUninit<T>; N] {}
615
616#[doc(hidden)]
617pub mod __macro_implementation {
618    //! Private implementation details of esp-hal-procmacros.
619
620    #[instability::unstable]
621    pub const fn assert_is_zeroable<T: bytemuck::Zeroable>() {}
622
623    #[instability::unstable]
624    pub const fn assert_is_persistable<T: super::Persistable>() {}
625
626    pub const fn assert_is_uninit<T: super::Uninit>() {}
627
628    #[cfg(feature = "rt")]
629    #[cfg(riscv)]
630    pub use esp_riscv_rt::entry as __entry;
631    pub use static_cell;
632    #[cfg(feature = "rt")]
633    #[cfg(xtensa)]
634    pub use xtensa_lx_rt::entry as __entry;
635}
636
637use crate::clock::{ClockConfig, CpuClock};
638#[cfg(feature = "rt")]
639use crate::peripherals::Peripherals;
640
641/// A spinlock for seldom called stuff. Users assume that lock contention is not an issue.
642#[cfg(feature = "rt")]
643pub(crate) static ESP_HAL_LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
644
645#[procmacros::doc_replace]
646/// System configuration.
647///
648/// This `struct` is marked with `#[non_exhaustive]` and cannot be instantiated
649/// directly. This is done to prevent breaking changes when new fields are added
650/// to the `struct`. Instead, use the [`Config::default()`] method to create a
651/// new instance.
652///
653/// # Examples
654///
655/// ### Default initialization
656///
657/// ```rust, no_run
658/// # {before_snippet}
659/// let peripherals = esp_hal::init(esp_hal::Config::default());
660/// # {after_snippet}
661/// ```
662///
663/// ### Custom initialization
664/// ```rust, no_run
665/// # {before_snippet}
666/// use esp_hal::{clock::CpuClock, time::Duration};
667/// let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
668/// let peripherals = esp_hal::init(config);
669/// # {after_snippet}
670/// ```
671#[non_exhaustive]
672#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
673pub struct Config {
674    /// The CPU clock configuration.
675    #[builder_lite(skip)]
676    cpu_clock: ClockConfig,
677}
678
679impl Config {
680    /// Applies a clock configuration.
681    #[cfg_attr(
682        feature = "unstable",
683        doc = r"
684
685With the `unstable` feature enabled, this function accepts both [`ClockConfig`] and [`CpuClock`].
686"
687    )]
688    #[cfg(feature = "unstable")]
689    pub fn with_cpu_clock(self, cpu_clock: impl Into<ClockConfig>) -> Self {
690        Self {
691            cpu_clock: cpu_clock.into(),
692            ..self
693        }
694    }
695
696    /// Applies a clock configuration.
697    #[cfg(not(feature = "unstable"))]
698    pub fn with_cpu_clock(self, cpu_clock: CpuClock) -> Self {
699        Self {
700            cpu_clock: cpu_clock.into(),
701            ..self
702        }
703    }
704
705    /// The CPU clock configuration preset.
706    ///
707    /// # Panics
708    ///
709    /// Panics if the CPU clock configuration is not **exactly** one of the [`CpuClock`] presets
710    #[cfg_attr(feature = "unstable", deprecated(note = "Use `clock_config` instead."))] // TODO: mention ClockTree APIs once they are exposed to the user.
711    pub fn cpu_clock(&self) -> CpuClock {
712        unwrap!(
713            self.cpu_clock.try_get_preset(),
714            "CPU clock configuration is not a preset"
715        )
716    }
717
718    /// The CPU clock configuration.
719    #[instability::unstable]
720    pub fn clock_config(&self) -> ClockConfig {
721        self.cpu_clock
722    }
723}
724
725#[procmacros::doc_replace]
726/// Initializes the system.
727///
728/// Sets up the CPU clock and watchdog, then returns the peripherals and clocks.
729///
730/// # Examples
731///
732/// ```rust, no_run
733/// # {before_snippet}
734/// use esp_hal::{Config, init};
735/// let peripherals = init(Config::default());
736/// # {after_snippet}
737/// ```
738#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
739#[cfg(feature = "rt")]
740pub fn init(config: Config) -> Peripherals {
741    crate::soc::pre_init();
742
743    let min_rev = esp_config::esp_config_int!(u16, "ESP_HAL_CONFIG_MIN_CHIP_REVISION");
744    assert!(
745        crate::efuse::chip_revision() >= crate::efuse::ChipRevision::from_combined(min_rev),
746        "This chip's hardware revision is older than the minimum required \
747         v{}.{} (ESP_HAL_CONFIG_MIN_CHIP_REVISION).",
748        min_rev / 100,
749        min_rev % 100,
750    );
751
752    #[cfg(soc_cpu_has_branch_predictor)]
753    crate::soc::branch_predictor::enable();
754
755    // Have we already overflown the stack?
756    #[cfg(init_stack_ptr_range_check)]
757    crate::soc::ensure_stack_pointer_in_range();
758
759    #[cfg(stack_guard_monitoring)]
760    crate::soc::enable_main_stack_guard_monitoring();
761
762    #[cfg(all(feature = "rt", enable_pmp, riscv))]
763    crate::soc::enable_pmp();
764
765    system::disable_peripherals();
766
767    let mut peripherals = Peripherals::take();
768
769    crate::clock::init(config.clock_config());
770
771    // RTC domain must be enabled before we try to disable
772    let mut rtc = crate::rtc_cntl::Rtc::new(peripherals.RTC_TIMER.reborrow());
773
774    #[cfg(sleep_driver_supported)]
775    crate::rtc_cntl::sleep::init(&rtc);
776
777    // Disable watchdog timers
778    #[cfg(soc_has_swd_watchdog)]
779    rtc.swd.disable();
780
781    rtc.rwdt.disable();
782
783    #[cfg(timergroup_timg0)]
784    crate::timer::timg::Wdt::<crate::peripherals::TIMG0<'static>>::new().disable();
785
786    #[cfg(timergroup_timg1)]
787    crate::timer::timg::Wdt::<crate::peripherals::TIMG1<'static>>::new().disable();
788
789    crate::time::implem::time_init();
790
791    #[cfg(gpio_driver_supported)]
792    crate::gpio::interrupt::bind_default_interrupt_handler();
793
794    unsafe {
795        esp_rom_sys::init_syscall_table();
796    }
797
798    #[cfg(all(riscv, write_vec_table_monitoring))]
799    crate::soc::setup_trap_section_protection();
800
801    #[cfg(all(feature = "unstable", multi_core))]
802    crate::interrupt::ipc::install();
803
804    peripherals
805}