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.esp-rs.org/esp-hal/'>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//! ## Overview
8//! The HAL implements both blocking _and_ async
9//! APIs for many peripherals. Where applicable, driver implement
10//! the [embedded-hal] and [embedded-hal-async] traits.
11//!
12//! This documentation is built for the
13#![cfg_attr(esp32, doc = "**ESP32**")]
14#![cfg_attr(esp32s2, doc = "**ESP32-S2**")]
15#![cfg_attr(esp32s3, doc = "**ESP32-S3**")]
16#![cfg_attr(esp32c2, doc = "**ESP32-C2**")]
17#![cfg_attr(esp32c3, doc = "**ESP32-C3**")]
18#![cfg_attr(esp32c6, doc = "**ESP32-C6**")]
19#![cfg_attr(esp32h2, doc = "**ESP32-H2**")]
20//! . Please ensure you are reading the correct [documentation] for your target
21//! device.
22//!
23//! ## Choosing a Device
24//!
25//! Depending on your target device, you need to enable the chip feature
26//! for that device. You may also need to do this on ancillary esp-hal crates.
27//!
28//! ## Examples
29//!
30//! We have a plethora of [examples] in the esp-hal repository. We use
31//! an [xtask] to automate the building, running, and testing of code and
32//! examples within esp-hal.
33//!
34//! Invoke the following command in the root of the esp-hal repository to get
35//! started:
36//!
37//! ```bash
38//! cargo xtask help
39//! ```
40//!
41//! ## Creating a Project
42//!
43//! We have a [book] that explains the full esp-rs ecosystem
44//! and how to get started, it's advisable to give that a read
45//! before proceeding. We also have a [training] that covers some common
46//! scenarios with examples.
47//!
48//! We have developed a project generation tool, [esp-generate], which we
49//! recommend when starting new projects. It can be installed and run, e.g.
50//! for the ESP32-C6, as follows:
51//!
52//! ```bash
53//! cargo install esp-generate
54//! esp-generate --chip=esp32c6 your-project
55//! ```
56//!
57//! ## Blinky
58//!
59//! Some minimal code to blink an LED looks like this:
60//!
61//! ```rust, no_run
62//! #![no_std]
63//! #![no_main]
64//!
65//! use esp_hal::{
66//!     clock::CpuClock,
67//!     gpio::{Io, Level, Output, OutputConfig},
68//!     main,
69//!     time::{Duration, Instant},
70//! };
71//!
72//! // You need a panic handler. Usually, you you would use esp_backtrace, panic-probe, or
73//! // something similar, but you can also bring your own like this:
74//! #[panic_handler]
75//! fn panic(_: &core::panic::PanicInfo) -> ! {
76//!     esp_hal::system::software_reset()
77//! }
78//!
79//! #[main]
80//! fn main() -> ! {
81//!     let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
82//!     let peripherals = esp_hal::init(config);
83//!
84//!     // Set GPIO0 as an output, and set its state high initially.
85//!     let mut led = Output::new(peripherals.GPIO0, Level::High, OutputConfig::default());
86//!
87//!     loop {
88//!         led.toggle();
89//!         // Wait for half a second
90//!         let delay_start = Instant::now();
91//!         while delay_start.elapsed() < Duration::from_millis(500) {}
92//!     }
93//! }
94//! ```
95//!
96//! ## Additional configuration
97//!
98//! We've exposed some configuration options that don't fit into cargo
99//! features. These can be set via environment variables, or via cargo's `[env]`
100//! section inside `.cargo/config.toml`. Below is a table of tunable parameters
101//! for this crate:
102#![doc = ""]
103#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_hal_config_table.md"))]
104#![doc = ""]
105//! It's important to note that due to a [bug in cargo](https://github.com/rust-lang/cargo/issues/10358),
106//! any modifications to the environment, local or otherwise will only get
107//! picked up on a full clean build of the project.
108//!
109//! ## `Peripheral` Pattern
110//!
111//! Drivers take pins and peripherals as [peripheral::Peripheral] in most
112//! circumstances. This means you can pass the pin/peripheral or a mutable
113//! reference to the pin/peripheral.
114//!
115//! The latter can be used to regain access to the pin when the driver gets
116//! dropped. Then it's possible to reuse the pin/peripheral for a different
117//! purpose.
118//!
119//! ## Don't use `core::mem::forget`
120//!
121//! You should never use `core::mem::forget` on any type defined in the HAL.
122//! Some types heavily rely on their `Drop` implementation to not leave the
123//! hardware in undefined state and causing UB.
124//!
125//! 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.
126//!
127//! [documentation]: https://docs.esp-rs.org/esp-hal
128//! [examples]: https://github.com/esp-rs/esp-hal/tree/main/examples
129//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
130//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/
131//! [xtask]: https://github.com/matklad/cargo-xtask
132//! [esp-generate]: https://github.com/esp-rs/esp-generate
133//! [book]: https://docs.esp-rs.org/book/
134//! [training]: https://docs.esp-rs.org/no_std-training/
135//!
136//! ## Feature Flags
137#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
138#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
139#![allow(asm_sub_register, async_fn_in_trait, stable_features)]
140#![cfg_attr(xtensa, feature(asm_experimental_arch))]
141#![deny(missing_docs, rust_2018_idioms, rustdoc::all)]
142#![cfg_attr(docsrs, feature(doc_cfg))]
143#![no_std]
144
145// MUST be the first module
146mod fmt;
147
148use core::marker::PhantomData;
149
150#[cfg(riscv)]
151#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
152#[cfg_attr(not(feature = "unstable"), doc(hidden))]
153pub use esp_riscv_rt::{self, riscv};
154#[cfg(xtensa)]
155#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
156#[cfg_attr(not(feature = "unstable"), doc(hidden))]
157pub use xtensa_lx_rt::{self, xtensa_lx};
158
159#[cfg(efuse)]
160#[instability::unstable]
161#[cfg_attr(not(feature = "unstable"), allow(unused))]
162pub use self::soc::efuse;
163#[cfg(lp_core)]
164#[instability::unstable]
165#[cfg_attr(not(feature = "unstable"), allow(unused))]
166pub use self::soc::lp_core;
167pub use self::soc::peripherals;
168pub(crate) use self::soc::peripherals::pac;
169#[instability::unstable]
170#[cfg(feature = "psram")]
171pub use self::soc::psram;
172#[cfg(ulp_riscv_core)]
173#[instability::unstable]
174#[cfg_attr(not(feature = "unstable"), allow(unused))]
175pub use self::soc::ulp_core;
176
177#[cfg(any(dport, hp_sys, pcr, system))]
178pub mod clock;
179#[cfg(gpio)]
180pub mod gpio;
181#[cfg(any(i2c0, i2c1))]
182pub mod i2c;
183pub mod peripheral;
184#[cfg(any(hmac, sha))]
185mod reg_access;
186#[cfg(any(spi0, spi1, spi2, spi3))]
187pub mod spi;
188pub mod system;
189pub mod time;
190#[cfg(any(uart0, uart1, uart2))]
191pub mod uart;
192
193mod macros;
194
195pub use procmacros::blocking_main as main;
196#[cfg(any(lp_core, ulp_riscv_core))]
197#[cfg(feature = "unstable")]
198#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
199pub use procmacros::load_lp_code;
200#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
201#[instability::unstable]
202#[cfg_attr(not(feature = "unstable"), allow(unused))]
203pub use procmacros::{handler, ram};
204
205// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
206#[doc(hidden)]
207macro_rules! unstable_module {
208    ($(
209        $(#[$meta:meta])*
210        pub mod $module:ident;
211    )*) => {
212        $(
213            $(#[$meta])*
214            #[cfg(feature = "unstable")]
215            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
216            pub mod $module;
217
218            $(#[$meta])*
219            #[cfg(not(feature = "unstable"))]
220            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
221            #[allow(unused)]
222            pub(crate) mod $module;
223        )*
224    };
225}
226
227pub(crate) use unstable_module;
228
229unstable_module! {
230    #[cfg(aes)]
231    pub mod aes;
232    #[cfg(any(adc1, adc2, dac))]
233    pub mod analog;
234    pub mod asynch;
235    #[cfg(assist_debug)]
236    pub mod assist_debug;
237    pub mod config;
238    pub mod debugger;
239    #[cfg(any(xtensa, all(riscv, systimer)))]
240    pub mod delay;
241    #[cfg(any(gdma, pdma))]
242    pub mod dma;
243    #[cfg(ecc)]
244    pub mod ecc;
245    #[cfg(soc_etm)]
246    pub mod etm;
247    #[cfg(hmac)]
248    pub mod hmac;
249    #[cfg(any(i2s0, i2s1))]
250    pub mod i2s;
251    #[cfg(any(dport, interrupt_core0, interrupt_core1))]
252    pub mod interrupt;
253    #[cfg(lcd_cam)]
254    pub mod lcd_cam;
255    #[cfg(ledc)]
256    pub mod ledc;
257    #[cfg(any(mcpwm0, mcpwm1))]
258    pub mod mcpwm;
259    #[cfg(usb0)]
260    pub mod otg_fs;
261    #[cfg(parl_io)]
262    pub mod parl_io;
263    #[cfg(pcnt)]
264    pub mod pcnt;
265    #[cfg(rmt)]
266    pub mod rmt;
267    #[cfg(rng)]
268    pub mod rng;
269    pub mod rom;
270    #[cfg(rsa)]
271    pub mod rsa;
272    #[cfg(any(lp_clkrst, rtc_cntl))]
273    pub mod rtc_cntl;
274    #[cfg(sha)]
275    pub mod sha;
276    #[doc(hidden)]
277    pub mod sync;
278    #[cfg(any(systimer, timg0, timg1))]
279    pub mod timer;
280    #[cfg(touch)]
281    pub mod touch;
282    #[cfg(trace0)]
283    pub mod trace;
284    #[cfg(tsens)]
285    pub mod tsens;
286    #[cfg(any(twai0, twai1))]
287    pub mod twai;
288    #[cfg(usb_device)]
289    pub mod usb_serial_jtag;
290}
291
292/// State of the CPU saved when entering exception or interrupt
293#[instability::unstable]
294#[allow(unused_imports)]
295pub mod trapframe {
296    #[cfg(riscv)]
297    pub use esp_riscv_rt::TrapFrame;
298    #[cfg(xtensa)]
299    pub use xtensa_lx_rt::exception::Context as TrapFrame;
300}
301
302// The `soc` module contains chip-specific implementation details and should not
303// be directly exposed.
304mod soc;
305
306#[cfg(is_debug_build)]
307esp_build::warning! {"
308WARNING: use --release
309  We *strongly* recommend using release profile when building esp-hal.
310  The dev profile can potentially be one or more orders of magnitude
311  slower than release, and may cause issues with timing-senstive
312  peripherals and/or devices.
313"}
314
315/// A marker trait for initializing drivers in a specific mode.
316pub trait DriverMode: crate::private::Sealed {}
317
318/// Driver initialized in blocking mode.
319#[derive(Debug)]
320pub struct Blocking;
321
322/// Driver initialized in async mode.
323#[derive(Debug)]
324pub struct Async(PhantomData<*const ()>);
325
326unsafe impl Sync for Async {}
327
328impl crate::DriverMode for Blocking {}
329impl crate::DriverMode for Async {}
330impl crate::private::Sealed for Blocking {}
331impl crate::private::Sealed for Async {}
332
333pub(crate) mod private {
334    pub trait Sealed {}
335
336    #[non_exhaustive]
337    #[doc(hidden)]
338    /// Magical incantation to gain access to internal APIs.
339    pub struct Internal;
340
341    impl Internal {
342        /// Obtain magical powers to access internal APIs.
343        ///
344        /// # Safety
345        ///
346        /// By calling this function, you accept that you are using an internal
347        /// API that is not guaranteed to be documented, stable, working
348        /// and may change at any time.
349        ///
350        /// You declare that you have tried to look for other solutions, that
351        /// you have opened a feature request or an issue to discuss the
352        /// need for this function.
353        pub unsafe fn conjure() -> Self {
354            Self
355        }
356    }
357}
358
359#[cfg(feature = "unstable")]
360#[doc(hidden)]
361pub use private::Internal;
362
363/// Marker trait for types that can be safely used in `#[ram(persistent)]`.
364///
365/// # Safety
366///
367/// - The type must be inhabited
368/// - The type must be valid for any bit pattern of its backing memory in case a
369///   reset occurs during a write or a reset interrupts the zero initialization
370///   on first boot.
371/// - Structs must contain only `Persistable` fields and padding
372#[instability::unstable]
373pub unsafe trait Persistable: Sized {}
374
375macro_rules! impl_persistable {
376    ($($t:ty),+) => {$(
377        unsafe impl Persistable for $t {}
378    )+};
379    (atomic $($t:ident),+) => {$(
380        unsafe impl Persistable for portable_atomic::$t {}
381    )+};
382}
383
384impl_persistable!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64);
385impl_persistable!(atomic AtomicU8, AtomicI8, AtomicU16, AtomicI16, AtomicU32, AtomicI32, AtomicUsize, AtomicIsize);
386
387unsafe impl<T: Persistable, const N: usize> Persistable for [T; N] {}
388
389#[doc(hidden)]
390pub mod __macro_implementation {
391    //! Private implementation details of esp-hal-procmacros.
392
393    #[instability::unstable]
394    pub const fn assert_is_zeroable<T: bytemuck::Zeroable>() {}
395
396    #[instability::unstable]
397    pub const fn assert_is_persistable<T: super::Persistable>() {}
398
399    #[cfg(riscv)]
400    pub use esp_riscv_rt::entry as __entry;
401    #[cfg(xtensa)]
402    pub use xtensa_lx_rt::entry as __entry;
403}
404
405#[cfg(riscv)]
406#[export_name = "hal_main"]
407fn hal_main(a0: usize, a1: usize, a2: usize) -> ! {
408    extern "Rust" {
409        // This symbol will be provided by the user via `#[entry]`
410        fn main(a0: usize, a1: usize, a2: usize) -> !;
411    }
412
413    extern "C" {
414        static mut __stack_chk_guard: u32;
415    }
416
417    unsafe {
418        let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
419        // we _should_ use a random value but we don't have a good source for random
420        // numbers here
421        stack_chk_guard.write_volatile(0xdeadbabe);
422
423        main(a0, a1, a2);
424    }
425}
426
427#[export_name = "__stack_chk_fail"]
428unsafe extern "C" fn stack_chk_fail() {
429    panic!("Stack corruption detected");
430}
431
432#[cfg(feature = "unstable")]
433use crate::config::{WatchdogConfig, WatchdogStatus};
434use crate::{
435    clock::{Clocks, CpuClock},
436    peripherals::Peripherals,
437};
438
439/// System configuration.
440///
441/// This `struct` is marked with `#[non_exhaustive]` and can't be instantiated
442/// directly. This is done to prevent breaking changes when new fields are added
443/// to the `struct`. Instead, use the [`Config::default()`] method to create a
444/// new instance.
445///
446/// For usage examples, see the [config module documentation](crate::config).
447#[non_exhaustive]
448#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
449pub struct Config {
450    /// The CPU clock configuration.
451    cpu_clock: CpuClock,
452
453    /// Enable watchdog timer(s).
454    #[cfg(any(doc, feature = "unstable"))]
455    #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
456    watchdog: WatchdogConfig,
457
458    /// PSRAM configuration.
459    #[cfg(any(doc, feature = "unstable"))]
460    #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
461    #[cfg(feature = "psram")]
462    psram: psram::PsramConfig,
463}
464
465/// Initialize the system.
466///
467/// This function sets up the CPU clock and watchdog, then, returns the
468/// peripherals and clocks.
469pub fn init(config: Config) -> Peripherals {
470    crate::soc::pre_init();
471
472    system::disable_peripherals();
473
474    let mut peripherals = Peripherals::take();
475
476    // RTC domain must be enabled before we try to disable
477    let mut rtc = crate::rtc_cntl::Rtc::new(&mut peripherals.LPWR);
478
479    // Handle watchdog configuration with defaults
480    cfg_if::cfg_if! {
481        if #[cfg(feature = "unstable")]
482        {
483            #[cfg(not(any(esp32, esp32s2)))]
484            if config.watchdog.swd() {
485                rtc.swd.enable();
486            } else {
487                rtc.swd.disable();
488            }
489
490            match config.watchdog.rwdt() {
491                WatchdogStatus::Enabled(duration) => {
492                    rtc.rwdt.enable();
493                    rtc.rwdt
494                        .set_timeout(crate::rtc_cntl::RwdtStage::Stage0, duration);
495                }
496                WatchdogStatus::Disabled => {
497                    rtc.rwdt.disable();
498                }
499            }
500
501            match config.watchdog.timg0() {
502                WatchdogStatus::Enabled(duration) => {
503                    let mut timg0_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG0>::new();
504                    timg0_wd.enable();
505                    timg0_wd.set_timeout(crate::timer::timg::MwdtStage::Stage0, duration);
506                }
507                WatchdogStatus::Disabled => {
508                    crate::timer::timg::Wdt::<crate::peripherals::TIMG0>::new().disable();
509                }
510            }
511
512            #[cfg(timg1)]
513            match config.watchdog.timg1() {
514                WatchdogStatus::Enabled(duration) => {
515                    let mut timg1_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG1>::new();
516                    timg1_wd.enable();
517                    timg1_wd.set_timeout(crate::timer::timg::MwdtStage::Stage0, duration);
518                }
519                WatchdogStatus::Disabled => {
520                    crate::timer::timg::Wdt::<crate::peripherals::TIMG1>::new().disable();
521                }
522            }
523        }
524        else
525        {
526            #[cfg(not(any(esp32, esp32s2)))]
527            rtc.swd.disable();
528
529            rtc.rwdt.disable();
530
531            crate::timer::timg::Wdt::<crate::peripherals::TIMG0>::new().disable();
532
533            #[cfg(timg1)]
534            crate::timer::timg::Wdt::<crate::peripherals::TIMG1>::new().disable();
535        }
536    }
537
538    Clocks::init(config.cpu_clock);
539
540    #[cfg(esp32)]
541    crate::time::time_init();
542
543    crate::gpio::bind_default_interrupt_handler();
544
545    #[cfg(feature = "psram")]
546    crate::psram::init_psram(config.psram);
547
548    peripherals
549}