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 don't 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 allows you to pass
35//! the handle to a driver, while still keeping the original handle alive. Once
36//! you drop the driver, you will be able to reborrow the peripheral 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
44don't 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's 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//! We've exposed some configuration options that don't fit into cargo
153//! features. These can be set via environment variables, or via cargo's `[env]`
154//! section inside `.cargo/config.toml`. Note that 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 behaviour 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://avatars.githubusercontent.com/u/46717278")]
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#[cfg_attr(esp32c61, 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(all(riscv, feature = "rt"))]
274#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
275#[cfg_attr(not(feature = "unstable"), doc(hidden))]
276pub use esp_riscv_rt::{self, riscv};
277use esp_sync::RawMutex;
278pub(crate) use peripherals::pac;
279#[cfg(xtensa)]
280#[cfg(all(xtensa, feature = "rt"))]
281#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
282#[cfg_attr(not(feature = "unstable"), doc(hidden))]
283pub use xtensa_lx_rt::{self, xtensa_lx};
284
285pub(crate) mod private;
286
287#[cfg(any(soc_has_dport, soc_has_hp_sys, soc_has_pcr, soc_has_system))]
288pub mod clock;
289#[cfg(gpio_driver_supported)]
290pub mod gpio;
291#[cfg(i2c_master_driver_supported)]
292pub mod i2c;
293pub mod peripherals;
294#[cfg(all(
295    feature = "unstable",
296    any(ecc_driver_supported, hmac_driver_supported, sha_driver_supported)
297))]
298mod reg_access;
299#[cfg(rng_driver_supported)]
300pub mod rng;
301#[cfg(any(spi_master_driver_supported, spi_slave_driver_supported))]
302pub mod spi;
303#[cfg_attr(any(esp32c5, esp32c61), allow(dead_code))]
304pub mod system;
305pub mod time;
306#[cfg(uart_driver_supported)]
307pub mod uart;
308
309mod macros;
310
311#[cfg(feature = "rt")]
312pub use procmacros::blocking_main as main;
313#[instability::unstable]
314pub use procmacros::handler;
315#[instability::unstable]
316#[cfg(any(lp_core, ulp_riscv_core))]
317pub use procmacros::load_lp_code;
318pub use procmacros::ram;
319
320#[instability::unstable]
321#[cfg(lp_core)]
322pub use self::soc::lp_core;
323#[instability::unstable]
324#[cfg(ulp_riscv_core)]
325pub use self::soc::ulp_core;
326
327#[cfg(all(feature = "rt", feature = "exception-handler"))]
328mod exception_handler;
329
330pub mod efuse;
331pub mod interrupt;
332
333unstable_module! {
334    pub mod asynch;
335    pub mod debugger;
336    pub mod rom;
337    #[doc(hidden)]
338    pub mod sync;
339    // Drivers needed for initialization or they are tightly coupled to something else.
340    #[cfg(any(adc_driver_supported, dac_driver_supported))]
341    pub mod analog;
342    #[cfg(any(systimer_driver_supported, timergroup_driver_supported))]
343    pub mod timer;
344    #[cfg(soc_has_lpwr)]
345    pub mod rtc_cntl;
346    #[cfg(dma_driver_supported)]
347    pub mod dma;
348    #[cfg(etm_driver_supported)]
349    pub mod etm;
350    #[cfg(usb_otg_driver_supported)]
351    pub mod otg_fs;
352    #[cfg(soc_has_psram)] // DMA needs some things from here
353    pub mod psram;
354}
355
356#[cfg(any(
357    sha_driver_supported,
358    rsa_driver_supported,
359    aes_driver_supported,
360    ecc_driver_supported
361))]
362mod work_queue;
363
364unstable_driver! {
365    #[cfg(aes_driver_supported)]
366    pub mod aes;
367    #[cfg(assist_debug_driver_supported)]
368    pub mod assist_debug;
369    pub mod delay;
370    #[cfg(ecc_driver_supported)]
371    pub mod ecc;
372    #[cfg(hmac_driver_supported)]
373    pub mod hmac;
374    #[cfg(i2s_driver_supported)]
375    pub mod i2s;
376    #[cfg(soc_has_lcd_cam)]
377    pub mod lcd_cam;
378    #[cfg(ledc_driver_supported)]
379    pub mod ledc;
380    #[cfg(mcpwm_driver_supported)]
381    pub mod mcpwm;
382    #[cfg(parl_io_driver_supported)]
383    pub mod parl_io;
384    #[cfg(pcnt_driver_supported)]
385    pub mod pcnt;
386    #[cfg(rmt_driver_supported)]
387    pub mod rmt;
388    #[cfg(rsa_driver_supported)]
389    pub mod rsa;
390    #[cfg(sha_driver_supported)]
391    pub mod sha;
392    #[cfg(touch)]
393    pub mod touch;
394    #[cfg(soc_has_trace0)]
395    pub mod trace;
396    #[cfg(soc_has_tsens)]
397    pub mod tsens;
398    #[cfg(twai_driver_supported)]
399    pub mod twai;
400    #[cfg(usb_serial_jtag_driver_supported)]
401    pub mod usb_serial_jtag;
402}
403
404/// State of the CPU saved when entering exception or interrupt
405#[instability::unstable]
406#[cfg(feature = "rt")]
407#[allow(unused_imports)]
408pub mod trapframe {
409    #[cfg(riscv)]
410    pub use esp_riscv_rt::TrapFrame;
411    #[cfg(xtensa)]
412    pub use xtensa_lx_rt::exception::Context as TrapFrame;
413}
414
415// The `soc` module contains chip-specific implementation details and should not
416// be directly exposed.
417mod soc;
418
419// Some PAC-related utility
420use crate::pac::generic::{Readable, Reg, Resettable, W, Writable};
421
422trait RegisterToggle {
423    type Reg: Readable + Resettable + Writable;
424
425    /// Toggles bits in the register, applying the given operation to set and clear them.
426    ///
427    /// This method is more efficient than two modify calls, as it will not read the register
428    /// value twice.
429    fn toggle(&self, op: impl Fn(&mut W<Self::Reg>, bool) -> &mut W<Self::Reg>);
430}
431
432impl<REG> RegisterToggle for Reg<REG>
433where
434    REG: Readable + Resettable + Writable,
435{
436    type Reg = REG;
437
438    fn toggle(&self, op: impl Fn(&mut W<REG>, bool) -> &mut W<REG>) {
439        let bits = self.modify(|_, w| op(w, true));
440
441        self.write(|w| {
442            unsafe { w.bits(bits) };
443            op(w, false)
444        });
445    }
446}
447
448#[cfg(is_debug_build)]
449procmacros::warning! {"
450WARNING: use --release
451  We *strongly* recommend using release profile when building esp-hal.
452  The dev profile can potentially be one or more orders of magnitude
453  slower than release, and may cause issues with timing-sensitive
454  peripherals and/or devices.
455"}
456
457/// A marker trait for driver modes.
458///
459/// Different driver modes offer different features and different API. Using
460/// this trait as a generic parameter ensures that the driver is initialized in
461/// the correct mode.
462pub trait DriverMode: crate::private::Sealed {}
463
464#[procmacros::doc_replace]
465/// Marker type signalling that a driver is initialized in blocking mode.
466///
467/// Drivers are constructed in blocking mode by default. To learn about the
468/// differences between blocking and async drivers, see the [`Async`] mode
469/// documentation.
470///
471/// [`Async`] drivers can be converted to a [`Blocking`] driver using the
472/// `into_blocking` method, for example:
473#[cfg_attr(
474    // Feature-gated so that this doesn't prevent gradual device bringup. Any
475    // stable driver would serve the purpose here, so this block will be part
476    // of the released documentation.
477    all(uart_driver_supported, gpio_driver_supported),
478    doc = r#"
479```rust, no_run
480# {before_snippet}
481# use esp_hal::uart::{Config, Uart};
482let uart = Uart::new(peripherals.UART0, Config::default())?
483    .with_rx(peripherals.GPIO1)
484    .with_tx(peripherals.GPIO2)
485    .into_async();
486let blocking_uart = uart.into_blocking();
487# {after_snippet}
488```
489"#
490)]
491#[derive(Debug)]
492#[non_exhaustive]
493pub struct Blocking;
494
495#[procmacros::doc_replace]
496/// Marker type signalling that a driver is initialized in async mode.
497///
498/// Drivers are constructed in blocking mode by default. To set up an async
499/// driver, a [`Blocking`] driver must be converted to an `Async` driver using
500/// the `into_async` method, for example:
501#[cfg_attr(
502    // Feature-gated so that this doesn't prevent gradual device bringup. Any
503    // stable driver would serve the purpose here, so this block will be part
504    // of the released documentation.
505    all(uart_driver_supported, gpio_driver_supported),
506    doc = r#"
507```rust, no_run
508# {before_snippet}
509# use esp_hal::uart::{Config, Uart};
510let uart = Uart::new(peripherals.UART0, Config::default())?
511    .with_rx(peripherals.GPIO1)
512    .with_tx(peripherals.GPIO2)
513    .into_async();
514///
515# {after_snippet}
516```
517"#
518)]
519/// Drivers can be converted back to blocking mode using the `into_blocking`
520/// method, see [`Blocking`] documentation for more details.
521///
522/// Async mode drivers offer most of the same features as blocking drivers, but
523/// with the addition of async APIs. Interrupt-related functions are not
524/// available in async mode, as they are handled by the driver's interrupt
525/// handlers.
526///
527/// Note that async functions usually take up more space than their blocking
528/// counterparts, and they are generally slower. This is because async functions
529/// are implemented using a state machine that is driven by interrupts and is
530/// polled by a runtime. For short operations, the overhead of the state machine
531/// can be significant. Consider using the blocking functions on the async
532/// driver for small transfers.
533///
534/// When initializing an async driver, the driver disables user-specified
535/// interrupt handlers, and sets up internal interrupt handlers that drive the
536/// driver's async API. The driver's interrupt handlers run on the same core as
537/// the driver was initialized on. This means that the driver can not be sent
538/// across threads, to prevent incorrect concurrent access to the peripheral.
539///
540/// Switching back to blocking mode will disable the interrupt handlers and
541/// return the driver to a state where it can be sent across threads.
542#[derive(Debug)]
543#[non_exhaustive]
544pub struct Async(PhantomData<*const ()>);
545
546unsafe impl Sync for Async {}
547
548impl crate::DriverMode for Blocking {}
549impl crate::DriverMode for Async {}
550impl crate::private::Sealed for Blocking {}
551impl crate::private::Sealed for Async {}
552
553#[doc(hidden)]
554pub use private::Internal;
555
556/// Marker trait for types that can be safely used in `#[ram(unstable(persistent))]`.
557///
558/// # Safety
559///
560/// - The type must be inhabited
561/// - The type must be valid for any bit pattern of its backing memory in case a reset occurs during
562///   a write or a reset interrupts the zero initialization on first boot.
563/// - Structs must contain only `Persistable` fields and padding
564#[instability::unstable]
565pub unsafe trait Persistable: Sized {}
566
567/// Marker trait for types that can be safely used in `#[ram(reclaimed)]`.
568///
569/// # Safety
570///
571/// - The type must be some form of `MaybeUninit<T>`
572#[doc(hidden)]
573pub unsafe trait Uninit: Sized {}
574
575macro_rules! impl_persistable {
576    ($($t:ty),+) => {$(
577        unsafe impl Persistable for $t {}
578    )+};
579    (atomic $($t:ident),+) => {$(
580        unsafe impl Persistable for portable_atomic::$t {}
581    )+};
582}
583
584impl_persistable!(
585    u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64
586);
587impl_persistable!(atomic AtomicU8, AtomicI8, AtomicU16, AtomicI16, AtomicU32, AtomicI32, AtomicUsize, AtomicIsize);
588
589unsafe impl<T: Persistable, const N: usize> Persistable for [T; N] {}
590
591unsafe impl<T> Uninit for core::mem::MaybeUninit<T> {}
592unsafe impl<T, const N: usize> Uninit for [core::mem::MaybeUninit<T>; N] {}
593
594#[doc(hidden)]
595pub mod __macro_implementation {
596    //! Private implementation details of esp-hal-procmacros.
597
598    #[instability::unstable]
599    pub const fn assert_is_zeroable<T: bytemuck::Zeroable>() {}
600
601    #[instability::unstable]
602    pub const fn assert_is_persistable<T: super::Persistable>() {}
603
604    pub const fn assert_is_uninit<T: super::Uninit>() {}
605
606    #[cfg(feature = "rt")]
607    #[cfg(riscv)]
608    pub use esp_riscv_rt::entry as __entry;
609    #[cfg(feature = "rt")]
610    #[cfg(xtensa)]
611    pub use xtensa_lx_rt::entry as __entry;
612}
613
614use crate::clock::{ClockConfig, CpuClock};
615#[cfg(feature = "rt")]
616use crate::{clock::Clocks, peripherals::Peripherals};
617
618/// A spinlock for seldom called stuff. Users assume that lock contention is not an issue.
619pub(crate) static ESP_HAL_LOCK: RawMutex = RawMutex::new();
620
621#[procmacros::doc_replace]
622/// System configuration.
623///
624/// This `struct` is marked with `#[non_exhaustive]` and can't be instantiated
625/// directly. This is done to prevent breaking changes when new fields are added
626/// to the `struct`. Instead, use the [`Config::default()`] method to create a
627/// new instance.
628///
629/// ## Examples
630///
631/// ### Default initialization
632///
633/// ```rust, no_run
634/// # {before_snippet}
635/// let peripherals = esp_hal::init(esp_hal::Config::default());
636/// # {after_snippet}
637/// ```
638///
639/// ### Custom initialization
640/// ```rust, no_run
641/// # {before_snippet}
642/// use esp_hal::{clock::CpuClock, time::Duration};
643/// let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
644/// let peripherals = esp_hal::init(config);
645/// # {after_snippet}
646/// ```
647#[non_exhaustive]
648#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
649pub struct Config {
650    /// The CPU clock configuration.
651    #[builder_lite(skip)]
652    cpu_clock: ClockConfig,
653}
654
655impl Config {
656    /// Apply a clock configuration.
657    #[cfg_attr(
658        feature = "unstable",
659        doc = r"
660
661With the `unstable` feature enabled, this function accepts both [`ClockConfig`] and [`CpuClock`].
662"
663    )]
664    #[cfg(feature = "unstable")]
665    pub fn with_cpu_clock(self, cpu_clock: impl Into<ClockConfig>) -> Self {
666        Self {
667            cpu_clock: cpu_clock.into(),
668            ..self
669        }
670    }
671
672    /// Apply a clock configuration.
673    #[cfg(not(feature = "unstable"))]
674    pub fn with_cpu_clock(self, cpu_clock: CpuClock) -> Self {
675        Self {
676            cpu_clock: cpu_clock.into(),
677            ..self
678        }
679    }
680
681    /// The CPU clock configuration preset.
682    ///
683    /// # Panics
684    ///
685    /// This function will panic if the CPU clock configuration is not **exactly** one of the
686    /// [`CpuClock`] presets.
687    #[cfg_attr(feature = "unstable", deprecated(note = "Use `clock_config` instead."))] // TODO: mention ClockTree APIs once they are exposed to the user.
688    pub fn cpu_clock(&self) -> CpuClock {
689        unwrap!(
690            self.cpu_clock.try_get_preset(),
691            "CPU clock configuration is not a preset"
692        )
693    }
694
695    /// The CPU clock configuration.
696    #[instability::unstable]
697    pub fn clock_config(&self) -> ClockConfig {
698        self.cpu_clock
699    }
700}
701
702#[procmacros::doc_replace]
703/// Initialize the system.
704///
705/// This function sets up the CPU clock and watchdog, then, returns the
706/// peripherals and clocks.
707///
708/// # Example
709///
710/// ```rust, no_run
711/// # {before_snippet}
712/// use esp_hal::{Config, init};
713/// let peripherals = init(Config::default());
714/// # {after_snippet}
715/// ```
716#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
717#[cfg(feature = "rt")]
718pub fn init(config: Config) -> Peripherals {
719    crate::soc::pre_init();
720
721    #[cfg(soc_cpu_has_branch_predictor)]
722    {
723        // Enable branch predictor
724        // Note that the branch predictor will start cache requests and needs to be disabled when
725        // the cache is disabled.
726        // MHCR: CSR 0x7c1
727        const MHCR_RS: u32 = 1 << 4; // R/W, address return stack set bit
728        const MHCR_BFE: u32 = 1 << 5; // R/W, allow predictive jump set bit
729        const MHCR_BTB: u32 = 1 << 12; // R/W, branch target prediction enable bit
730        unsafe {
731            core::arch::asm!("csrrs x0, 0x7c1, {0}", in(reg) MHCR_RS | MHCR_BFE | MHCR_BTB);
732        }
733    }
734
735    crate::soc::ensure_stack_pointer_in_range();
736    #[cfg(stack_guard_monitoring)]
737    crate::soc::enable_main_stack_guard_monitoring();
738
739    system::disable_peripherals();
740
741    let mut peripherals = Peripherals::take();
742
743    Clocks::init(config.clock_config());
744
745    // RTC domain must be enabled before we try to disable
746    let mut rtc = crate::rtc_cntl::Rtc::new(peripherals.LPWR.reborrow());
747
748    #[cfg(sleep_driver_supported)]
749    crate::rtc_cntl::sleep::RtcSleepConfig::base_settings(&rtc);
750
751    // Disable watchdog timers
752    #[cfg(swd)]
753    rtc.swd.disable();
754
755    rtc.rwdt.disable();
756
757    #[cfg(timergroup_timg0)]
758    crate::timer::timg::Wdt::<crate::peripherals::TIMG0<'static>>::new().disable();
759
760    #[cfg(timergroup_timg1)]
761    crate::timer::timg::Wdt::<crate::peripherals::TIMG1<'static>>::new().disable();
762
763    crate::time::implem::time_init();
764
765    #[cfg(gpio_driver_supported)]
766    crate::gpio::interrupt::bind_default_interrupt_handler();
767
768    unsafe {
769        esp_rom_sys::init_syscall_table();
770    }
771
772    #[cfg(all(riscv, write_vec_table_monitoring))]
773    crate::soc::setup_trap_section_protection();
774
775    peripherals
776}