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#![cfg_attr(esp32, doc = "**ESP32**")]
9#![cfg_attr(esp32s2, doc = "**ESP32-S2**")]
10#![cfg_attr(esp32s3, doc = "**ESP32-S3**")]
11#![cfg_attr(esp32c2, doc = "**ESP32-C2**")]
12#![cfg_attr(esp32c3, doc = "**ESP32-C3**")]
13#![cfg_attr(esp32c6, doc = "**ESP32-C6**")]
14#![cfg_attr(esp32h2, doc = "**ESP32-H2**")]
15//! . Please ensure you are reading the correct [documentation] for your target
16//! device.
17//!
18//! ## Overview
19//!
20//! esp-hal is a Hardware Abstraction Layer (HAL) for Espressif's ESP32 lineup of
21//! microcontrollers offering safe, idiotmatic APIs to control hardware peripherals.
22//!
23//! ### Peripheral drivers
24//!
25//! The HAL implements both [`Blocking`] _and_ [`Async`] APIs for all applicable peripherals.
26//! Where applicable, driver implement the [embedded-hal] and
27//! [embedded-hal-async] traits. Drivers that don't currently have a stable API
28//! are marked as `unstable` in the documentation.
29//!
30//! ### Peripheral singletons
31//!
32//! Each peripheral driver needs a peripheral singleton that tells the driver
33//! which hardware block to use. The peripheral singletons are created by the
34//! HAL initialization, and are returned from [`init`] as fields of the
35//! [`Peripherals`] struct.
36//!
37//! These singletons, by default, represent peripherals for the entire lifetime
38//! of the program. To allow for reusing peripherals, the HAL provides a
39//! `reborrow` method on each peripheral singleton. This method creates a new
40//! handle to the peripheral with a shorter lifetime. This allows you to pass
41//! the handle to a driver, while still keeping the original handle alive. Once
42//! you drop the driver, you will be able to reborrow the peripheral again.
43//!
44//! For example, if you want to use the [`I2c`](i2c::master::I2c) driver and you
45//! don't intend to drop the driver, you can pass the peripheral singleton to
46//! the driver by value:
47//!
48//! ```rust, ignore
49//! // Peripheral singletons are returned from the `init` function.
50//! let peripherals = esp_hal::init(esp_hal::Config::default());
51//!
52//! let mut i2c = I2C::new(peripherals.I2C0, /* ... */);
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//!
105//! ## Blinky
106//!
107//! Some minimal code to blink an LED looks like this:
108//!
109//! ```rust, no_run
110//! #![no_std]
111//! #![no_main]
112//!
113//! use esp_hal::{
114//! clock::CpuClock,
115//! gpio::{Io, Level, Output, OutputConfig},
116//! main,
117//! time::{Duration, Instant},
118//! };
119//!
120//! // You need a panic handler. Usually, you you would use esp_backtrace, panic-probe, or
121//! // something similar, but you can also bring your own like this:
122//! #[panic_handler]
123//! fn panic(_: &core::panic::PanicInfo) -> ! {
124//! esp_hal::system::software_reset()
125//! }
126//!
127//! #[main]
128//! fn main() -> ! {
129//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
130//! let peripherals = esp_hal::init(config);
131//!
132//! // Set GPIO0 as an output, and set its state high initially.
133//! let mut led = Output::new(peripherals.GPIO0, Level::High, OutputConfig::default());
134//!
135//! loop {
136//! led.toggle();
137//! // Wait for half a second
138//! let delay_start = Instant::now();
139//! while delay_start.elapsed() < Duration::from_millis(500) {}
140//! }
141//! }
142//! ```
143//!
144//! ## Additional configuration
145//!
146//! We've exposed some configuration options that don't fit into cargo
147//! features. These can be set via environment variables, or via cargo's `[env]`
148//! section inside `.cargo/config.toml`. Note that unstable options can only be
149//! enabled when the `unstable` feature is enabled for the crate. Below is a
150//! table of tunable parameters for this crate:
151#![doc = ""]
152#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_hal_config_table.md"))]
153#![doc = ""]
154//! ## Don't use `core::mem::forget`
155//!
156//! You should never use `core::mem::forget` on any type defined in [esp crates].
157//! Many types heavily rely on their `Drop` implementation to not leave the
158//! hardware in undefined state which can cause undefined behaviour in your program.
159//!
160//! 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.
161//!
162//! ## Library usage
163//!
164//! If you intend to write a library that uses esp-hal, you should import it as follows:
165//!
166//! ```toml
167//! [dependencies]
168//! esp-hal = { version = "1", default-features = false } }
169//! ```
170//!
171//! This ensures that the `rt` feature is not enabled, nor any chip features. The application that
172//! uses your library will then be able to choose the chip feature it needs and enable `rt` such
173//! that only the final user application calls [`init`].
174//!
175//! If your library depends on `unstable` features, you *must* use the `requires-unstable` feature,
176//! and *not* the unstable feature itself. Doing so, improves the quality of the error messages if a
177//! user hasn't enabled the unstable feature of esp-hal.
178//!
179//! [documentation]: https://docs.espressif.com/projects/rust/esp-hal/latest/
180//! [examples]: https://github.com/esp-rs/esp-hal/tree/main/examples
181//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
182//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/
183//! [xtask]: https://github.com/matklad/cargo-xtask
184//! [esp-generate]: https://github.com/esp-rs/esp-generate
185//! [book]: https://docs.espressif.com/projects/rust/book/
186//! [training]: https://docs.espressif.com/projects/rust/no_std-training/
187//! [esp crates]: https://docs.espressif.com/projects/rust/book/introduction/ancillary-crates.html#esp-hal-ecosystem
188//!
189//! ## Feature Flags
190#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
191#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
192#![allow(asm_sub_register, async_fn_in_trait, stable_features)]
193#![cfg_attr(xtensa, feature(asm_experimental_arch))]
194#![deny(missing_docs, rust_2018_idioms, rustdoc::all)]
195#![allow(rustdoc::private_doc_tests)] // compile tests are done via rustdoc
196#![cfg_attr(docsrs, feature(doc_cfg, custom_inner_attributes, proc_macro_hygiene))]
197// Don't trip up on broken/private links when running semver-checks
198#![cfg_attr(
199 semver_checks,
200 allow(rustdoc::private_intra_doc_links, rustdoc::broken_intra_doc_links)
201)]
202// Do not document `cfg` gates by default.
203#![cfg_attr(docsrs, allow(invalid_doc_attributes))] // doc(auto_cfg = false) requires a new nightly (~2025-10-09+)
204#![cfg_attr(docsrs, doc(auto_cfg = false))]
205#![no_std]
206
207// MUST be the first module
208mod fmt;
209
210#[macro_use]
211extern crate esp_metadata_generated;
212
213// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
214#[doc(hidden)]
215macro_rules! unstable_module {
216 ($(
217 $(#[$meta:meta])*
218 pub mod $module:ident;
219 )*) => {
220 $(
221 $(#[$meta])*
222 #[cfg(feature = "unstable")]
223 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
224 pub mod $module;
225
226 $(#[$meta])*
227 #[cfg(not(feature = "unstable"))]
228 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
229 #[allow(unused)]
230 pub(crate) mod $module;
231 )*
232 };
233}
234
235// we can't use instability because it mucks up the short description in rustdoc
236#[doc(hidden)]
237macro_rules! unstable_reexport {
238 ($(
239 $(#[$meta:meta])*
240 pub use $path:path;
241 )*) => {
242 $(
243 $(#[$meta])*
244 #[cfg(feature = "unstable")]
245 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
246 pub use $path;
247
248 $(#[$meta])*
249 #[cfg(not(feature = "unstable"))]
250 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
251 #[allow(unused)]
252 pub(crate) use $path;
253 )*
254 };
255}
256
257// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
258// we don't want unstable drivers to be compiled even, unless enabled
259#[doc(hidden)]
260macro_rules! unstable_driver {
261 ($(
262 $(#[$meta:meta])*
263 pub mod $module:ident;
264 )*) => {
265 $(
266 $(#[$meta])*
267 #[cfg(feature = "unstable")]
268 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
269 pub mod $module;
270 )*
271 };
272}
273
274use core::marker::PhantomData;
275
276pub use esp_metadata_generated::chip;
277use esp_rom_sys as _;
278pub(crate) use unstable_module;
279
280metadata!("build_info", CHIP_NAME, chip!());
281
282#[cfg(all(riscv, feature = "rt"))]
283#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
284#[cfg_attr(not(feature = "unstable"), doc(hidden))]
285pub use esp_riscv_rt::{self, riscv};
286use esp_sync::RawMutex;
287pub(crate) use peripherals::pac;
288#[cfg(xtensa)]
289#[cfg(all(xtensa, feature = "rt"))]
290#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
291#[cfg_attr(not(feature = "unstable"), doc(hidden))]
292pub use xtensa_lx_rt::{self, xtensa_lx};
293
294#[cfg(any(soc_has_dport, soc_has_hp_sys, soc_has_pcr, soc_has_system))]
295pub mod clock;
296#[cfg(soc_has_gpio)]
297pub mod gpio;
298#[cfg(any(soc_has_i2c0, soc_has_i2c1))]
299pub mod i2c;
300pub mod peripherals;
301#[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))]
302mod reg_access;
303#[cfg(any(soc_has_spi0, soc_has_spi1, soc_has_spi2, soc_has_spi3))]
304pub mod spi;
305pub mod system;
306pub mod time;
307#[cfg(any(soc_has_uart0, soc_has_uart1, soc_has_uart2))]
308pub mod uart;
309
310mod macros;
311
312#[cfg(feature = "rt")]
313pub use procmacros::blocking_main as main;
314pub use procmacros::ram;
315
316unstable_reexport! {
317 pub use procmacros::handler;
318
319 #[cfg(any(lp_core, ulp_riscv_core))]
320 pub use procmacros::load_lp_code;
321
322 #[cfg(lp_core)]
323 pub use self::soc::lp_core;
324
325 #[cfg(ulp_riscv_core)]
326 pub use self::soc::ulp_core;
327}
328
329#[cfg(all(feature = "rt", feature = "exception-handler"))]
330mod exception_handler;
331
332unstable_module! {
333 pub mod asynch;
334 pub mod debugger;
335 #[cfg(any(soc_has_dport, soc_has_interrupt_core0, soc_has_interrupt_core1))]
336 pub mod interrupt;
337 pub mod rom;
338 #[doc(hidden)]
339 pub mod sync;
340 // Drivers needed for initialization or they are tightly coupled to something else.
341 #[cfg(any(adc, dac))]
342 pub mod analog;
343 #[cfg(any(systimer, timergroup))]
344 pub mod timer;
345 #[cfg(soc_has_lpwr)]
346 pub mod rtc_cntl;
347 #[cfg(any(gdma, pdma))]
348 pub mod dma;
349 #[cfg(soc_has_etm)]
350 pub mod etm;
351 #[cfg(soc_has_usb0)]
352 pub mod otg_fs;
353 #[cfg(psram)] // DMA needs some things from here
354 pub mod psram;
355 pub mod efuse;
356}
357
358mod work_queue;
359
360unstable_driver! {
361 #[cfg(soc_has_aes)]
362 pub mod aes;
363 #[cfg(soc_has_assist_debug)]
364 pub mod assist_debug;
365 pub mod delay;
366 #[cfg(soc_has_ecc)]
367 pub mod ecc;
368 #[cfg(soc_has_hmac)]
369 pub mod hmac;
370 #[cfg(any(soc_has_i2s0, soc_has_i2s1))]
371 pub mod i2s;
372 #[cfg(soc_has_lcd_cam)]
373 pub mod lcd_cam;
374 #[cfg(soc_has_ledc)]
375 pub mod ledc;
376 #[cfg(any(soc_has_mcpwm0, soc_has_mcpwm1))]
377 pub mod mcpwm;
378 #[cfg(soc_has_parl_io)]
379 pub mod parl_io;
380 #[cfg(soc_has_pcnt)]
381 pub mod pcnt;
382 #[cfg(soc_has_rmt)]
383 pub mod rmt;
384 #[cfg(soc_has_rng)]
385 pub mod rng;
386 #[cfg(soc_has_rsa)]
387 pub mod rsa;
388 #[cfg(soc_has_sha)]
389 pub mod sha;
390 #[cfg(touch)]
391 pub mod touch;
392 #[cfg(soc_has_trace0)]
393 pub mod trace;
394 #[cfg(soc_has_tsens)]
395 pub mod tsens;
396 #[cfg(any(soc_has_twai0, soc_has_twai1))]
397 pub mod twai;
398 #[cfg(soc_has_usb_device)]
399 pub mod usb_serial_jtag;
400}
401
402/// State of the CPU saved when entering exception or interrupt
403#[instability::unstable]
404#[cfg(feature = "rt")]
405#[allow(unused_imports)]
406pub mod trapframe {
407 #[cfg(riscv)]
408 pub use esp_riscv_rt::TrapFrame;
409 #[cfg(xtensa)]
410 pub use xtensa_lx_rt::exception::Context as TrapFrame;
411}
412
413// The `soc` module contains chip-specific implementation details and should not
414// be directly exposed.
415mod soc;
416
417#[cfg(is_debug_build)]
418procmacros::warning! {"
419WARNING: use --release
420 We *strongly* recommend using release profile when building esp-hal.
421 The dev profile can potentially be one or more orders of magnitude
422 slower than release, and may cause issues with timing-senstive
423 peripherals and/or devices.
424"}
425
426/// A marker trait for driver modes.
427///
428/// Different driver modes offer different features and different API. Using
429/// this trait as a generic parameter ensures that the driver is initialized in
430/// the correct mode.
431pub trait DriverMode: crate::private::Sealed {}
432
433#[procmacros::doc_replace]
434/// Marker type signalling that a driver is initialized in blocking mode.
435///
436/// Drivers are constructed in blocking mode by default. To learn about the
437/// differences between blocking and async drivers, see the [`Async`] mode
438/// documentation.
439///
440/// [`Async`] drivers can be converted to a [`Blocking`] driver using the
441/// `into_blocking` method, for example:
442///
443/// ```rust, no_run
444/// # {before_snippet}
445/// # use esp_hal::uart::{Config, Uart};
446/// let uart = Uart::new(peripherals.UART0, Config::default())?
447/// .with_rx(peripherals.GPIO1)
448/// .with_tx(peripherals.GPIO2)
449/// .into_async();
450///
451/// let blocking_uart = uart.into_blocking();
452///
453/// # {after_snippet}
454/// ```
455#[derive(Debug)]
456#[non_exhaustive]
457pub struct Blocking;
458
459#[procmacros::doc_replace]
460/// Marker type signalling that a driver is initialized in async mode.
461///
462/// Drivers are constructed in blocking mode by default. To set up an async
463/// driver, a [`Blocking`] driver must be converted to an `Async` driver using
464/// the `into_async` method, for example:
465///
466/// ```rust, no_run
467/// # {before_snippet}
468/// # use esp_hal::uart::{Config, Uart};
469/// let uart = Uart::new(peripherals.UART0, Config::default())?
470/// .with_rx(peripherals.GPIO1)
471/// .with_tx(peripherals.GPIO2)
472/// .into_async();
473///
474/// # {after_snippet}
475/// ```
476///
477/// Drivers can be converted back to blocking mode using the `into_blocking`
478/// method, see [`Blocking`] documentation for more details.
479///
480/// Async mode drivers offer most of the same features as blocking drivers, but
481/// with the addition of async APIs. Interrupt-related functions are not
482/// available in async mode, as they are handled by the driver's interrupt
483/// handlers.
484///
485/// Note that async functions usually take up more space than their blocking
486/// counterparts, and they are generally slower. This is because async functions
487/// are implemented using a state machine that is driven by interrupts and is
488/// polled by a runtime. For short operations, the overhead of the state machine
489/// can be significant. Consider using the blocking functions on the async
490/// driver for small transfers.
491///
492/// When initializing an async driver, the driver disables user-specified
493/// interrupt handlers, and sets up internal interrupt handlers that drive the
494/// driver's async API. The driver's interrupt handlers run on the same core as
495/// the driver was initialized on. This means that the driver can not be sent
496/// across threads, to prevent incorrect concurrent access to the peripheral.
497///
498/// Switching back to blocking mode will disable the interrupt handlers and
499/// return the driver to a state where it can be sent across threads.
500#[derive(Debug)]
501#[non_exhaustive]
502pub struct Async(PhantomData<*const ()>);
503
504unsafe impl Sync for Async {}
505
506impl crate::DriverMode for Blocking {}
507impl crate::DriverMode for Async {}
508impl crate::private::Sealed for Blocking {}
509impl crate::private::Sealed for Async {}
510
511pub(crate) mod private {
512 use core::mem::ManuallyDrop;
513
514 pub trait Sealed {}
515
516 #[non_exhaustive]
517 #[doc(hidden)]
518 /// Magical incantation to gain access to internal APIs.
519 pub struct Internal;
520
521 impl Internal {
522 /// Obtain magical powers to access internal APIs.
523 ///
524 /// # Safety
525 ///
526 /// By calling this function, you accept that you are using an internal
527 /// API that is not guaranteed to be documented, stable, working
528 /// and may change at any time.
529 ///
530 /// You declare that you have tried to look for other solutions, that
531 /// you have opened a feature request or an issue to discuss the
532 /// need for this function.
533 pub unsafe fn conjure() -> Self {
534 Self
535 }
536 }
537
538 pub(crate) struct OnDrop<F: FnOnce()>(ManuallyDrop<F>);
539 impl<F: FnOnce()> OnDrop<F> {
540 pub fn new(cb: F) -> Self {
541 Self(ManuallyDrop::new(cb))
542 }
543
544 pub fn defuse(self) {
545 core::mem::forget(self);
546 }
547 }
548
549 impl<F: FnOnce()> Drop for OnDrop<F> {
550 fn drop(&mut self) {
551 unsafe { (ManuallyDrop::take(&mut self.0))() }
552 }
553 }
554}
555
556#[doc(hidden)]
557pub use private::Internal;
558
559/// Marker trait for types that can be safely used in `#[ram(unstable(persistent))]`.
560///
561/// # Safety
562///
563/// - The type must be inhabited
564/// - The type must be valid for any bit pattern of its backing memory in case a reset occurs during
565/// a write or a reset interrupts the zero initialization on first boot.
566/// - Structs must contain only `Persistable` fields and padding
567#[instability::unstable]
568pub unsafe trait Persistable: Sized {}
569
570/// Marker trait for types that can be safely used in `#[ram(reclaimed)]`.
571///
572/// # Safety
573///
574/// - The type must be some form of `MaybeUninit<T>`
575#[doc(hidden)]
576pub unsafe trait Uninit: Sized {}
577
578macro_rules! impl_persistable {
579 ($($t:ty),+) => {$(
580 unsafe impl Persistable for $t {}
581 )+};
582 (atomic $($t:ident),+) => {$(
583 unsafe impl Persistable for portable_atomic::$t {}
584 )+};
585}
586
587impl_persistable!(
588 u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64
589);
590impl_persistable!(atomic AtomicU8, AtomicI8, AtomicU16, AtomicI16, AtomicU32, AtomicI32, AtomicUsize, AtomicIsize);
591
592unsafe impl<T: Persistable, const N: usize> Persistable for [T; N] {}
593
594unsafe impl<T> Uninit for core::mem::MaybeUninit<T> {}
595unsafe impl<T, const N: usize> Uninit for [core::mem::MaybeUninit<T>; N] {}
596
597#[doc(hidden)]
598pub mod __macro_implementation {
599 //! Private implementation details of esp-hal-procmacros.
600
601 #[instability::unstable]
602 pub const fn assert_is_zeroable<T: bytemuck::Zeroable>() {}
603
604 #[instability::unstable]
605 pub const fn assert_is_persistable<T: super::Persistable>() {}
606
607 pub const fn assert_is_uninit<T: super::Uninit>() {}
608
609 #[cfg(feature = "rt")]
610 #[cfg(riscv)]
611 pub use esp_riscv_rt::entry as __entry;
612 #[cfg(feature = "rt")]
613 #[cfg(xtensa)]
614 pub use xtensa_lx_rt::entry as __entry;
615}
616
617use crate::clock::CpuClock;
618#[cfg(feature = "rt")]
619use crate::{clock::Clocks, peripherals::Peripherals};
620
621/// A spinlock for seldom called stuff. Users assume that lock contention is not an issue.
622pub(crate) static ESP_HAL_LOCK: RawMutex = RawMutex::new();
623
624#[procmacros::doc_replace]
625/// System configuration.
626///
627/// This `struct` is marked with `#[non_exhaustive]` and can't be instantiated
628/// directly. This is done to prevent breaking changes when new fields are added
629/// to the `struct`. Instead, use the [`Config::default()`] method to create a
630/// new instance.
631///
632/// ## Examples
633///
634/// ### Default initialization
635///
636/// ```rust, no_run
637/// # {before_snippet}
638/// let peripherals = esp_hal::init(esp_hal::Config::default());
639/// # {after_snippet}
640/// ```
641///
642/// ### Custom initialization
643/// ```rust, no_run
644/// # {before_snippet}
645/// use esp_hal::{clock::CpuClock, time::Duration};
646/// let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
647/// let peripherals = esp_hal::init(config);
648/// # {after_snippet}
649/// ```
650#[non_exhaustive]
651#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
652pub struct Config {
653 /// The CPU clock configuration.
654 cpu_clock: CpuClock,
655
656 /// PSRAM configuration.
657 #[cfg(feature = "unstable")]
658 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
659 #[cfg(feature = "psram")]
660 #[builder_lite(unstable)]
661 psram: psram::PsramConfig,
662}
663
664#[procmacros::doc_replace]
665/// Initialize the system.
666///
667/// This function sets up the CPU clock and watchdog, then, returns the
668/// peripherals and clocks.
669///
670/// # Example
671///
672/// ```rust, no_run
673/// # {before_snippet}
674/// use esp_hal::{Config, init};
675/// let peripherals = init(Config::default());
676/// # {after_snippet}
677/// ```
678#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
679#[cfg(feature = "rt")]
680pub fn init(config: Config) -> Peripherals {
681 crate::soc::pre_init();
682
683 #[cfg(stack_guard_monitoring)]
684 crate::soc::enable_main_stack_guard_monitoring();
685
686 system::disable_peripherals();
687
688 let mut peripherals = Peripherals::take();
689
690 Clocks::init(config.cpu_clock());
691
692 crate::rtc_cntl::rtc::configure_clock();
693
694 // RTC domain must be enabled before we try to disable
695 let mut rtc = crate::rtc_cntl::Rtc::new(peripherals.LPWR.reborrow());
696
697 #[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
698 crate::rtc_cntl::sleep::RtcSleepConfig::base_settings(&rtc);
699
700 // Disable watchdog timers
701 #[cfg(not(any(esp32, esp32s2)))]
702 rtc.swd.disable();
703
704 rtc.rwdt.disable();
705
706 #[cfg(timergroup_timg0)]
707 crate::timer::timg::Wdt::<crate::peripherals::TIMG0<'static>>::new().disable();
708
709 #[cfg(timergroup_timg1)]
710 crate::timer::timg::Wdt::<crate::peripherals::TIMG1<'static>>::new().disable();
711
712 #[cfg(esp32)]
713 crate::time::time_init();
714
715 crate::gpio::interrupt::bind_default_interrupt_handler();
716
717 #[cfg(feature = "psram")]
718 crate::psram::init_psram(config.psram);
719
720 unsafe {
721 esp_rom_sys::init_syscall_table();
722 }
723
724 #[cfg(all(riscv, write_vec_table_monitoring))]
725 crate::soc::setup_trap_section_protection();
726
727 peripherals
728}