├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── openocd.cfg ├── .gitignore ├── partitions.csv ├── ram.x ├── rom.x ├── .cargo └── config ├── procmacros └── Cargo.toml ├── src ├── delay.rs ├── analog │ ├── hall.rs │ ├── mod.rs │ ├── config.rs │ ├── dac.rs │ └── adc.rs ├── prelude.rs ├── dprint.rs ├── external_ram.rs ├── ledc │ └── mod.rs ├── lib.rs ├── clock_control │ ├── cpu.rs │ ├── pll.rs │ ├── config.rs │ └── watchdog.rs ├── efuse.rs ├── timer │ └── watchdog.rs └── dport.rs ├── CHANGELOG.md ├── LICENSE-MIT ├── README.md ├── Cargo.toml ├── examples ├── blinky.rs ├── dac.rs ├── serial.rs ├── ledc.rs ├── hall.rs ├── adc.rs ├── i2c.rs ├── gpio.rs ├── rtccntl.rs ├── alloc.rs ├── mem.rs ├── multicore.rs ├── spi.rs ├── ram.rs ├── exception.rs ├── timer.rs └── leds.rs ├── memory.x ├── flash └── LICENSE-APACHE /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "x86_64-unknown-linux-gnu" 3 | } -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | 2 | adapter_khz 4000 3 | 4 | source [find interface/jlink.cfg] 5 | 6 | source [find board/esp-wroom-32.cfg] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | procmacros/target 3 | **/*.rs.bk 4 | Cargo.lock 5 | .vscode/.cortex-debug* 6 | core 7 | .embuild/ 8 | .idea/ -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # Partition table 2 | # Name, Type, SubType, Offset, Size, Flags 3 | factory, app, factory, 0x10000, 0x3f0000, 4 | -------------------------------------------------------------------------------- /ram.x: -------------------------------------------------------------------------------- 1 | REGION_ALIAS("ROTEXT", iram_seg); 2 | REGION_ALIAS("RWTEXT", iram_seg); 3 | REGION_ALIAS("RODATA", dram_seg); 4 | REGION_ALIAS("RWDATA", dram_seg); 5 | -------------------------------------------------------------------------------- /rom.x: -------------------------------------------------------------------------------- 1 | REGION_ALIAS("ROTEXT", irom_seg); 2 | REGION_ALIAS("RWTEXT", iram_seg); 3 | REGION_ALIAS("RODATA", drom_seg); 4 | REGION_ALIAS("RWDATA", dram_seg); 5 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.xtensa-esp32-none-elf] 2 | runner = "xtensa-esp32-elf-gdb -q -x xtensa.gdb" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-nostartfiles", 7 | "-C", "link-arg=-Wl,-Tlink.x", 8 | ] 9 | target = "xtensa-esp32-none-elf" 10 | 11 | [unstable] 12 | build-std=["core", "alloc"] -------------------------------------------------------------------------------- /procmacros/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | authors = ["Arjan Mels "] 4 | categories = ["embedded", "no-std"] 5 | description = "Attributes re-exported in `esp32-hal`" 6 | keywords = ["esp32", "esp32-hal", "runtime", "startup"] 7 | license = "MIT OR Apache-2.0" 8 | name = "esp32-hal-proc-macros" 9 | repository = "https://github.com/esp-rs/xtensa-lx6-rt" 10 | version = "0.2.0" 11 | edition = "2018" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | external_ram=[] 18 | 19 | [dependencies] 20 | quote = "1.0" 21 | proc-macro2 = "1.0" 22 | darling = "0.10" 23 | syn = {version = "1.0", features = ["extra-traits", "full"]} 24 | 25 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of embedded hal delay traits using busy waiting 2 | use crate::units::{MicroSeconds, MilliSeconds}; 3 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 4 | 5 | #[derive(Clone, Default)] 6 | pub struct Delay {} 7 | 8 | impl Delay { 9 | pub fn new() -> Delay { 10 | Self::default() 11 | } 12 | } 13 | 14 | /// Delay in ms 15 | /// 16 | /// *Note: Maximum duration is 2e32-1 ns ~ 4.29s * 17 | impl> DelayMs for Delay { 18 | fn delay_ms(&mut self, ms: UXX) { 19 | crate::clock_control::sleep(MilliSeconds(ms.into())) 20 | } 21 | } 22 | 23 | /// Delay in us 24 | /// 25 | /// *Note: Maximum duration is 2e32-1 ns ~ 4.29s * 26 | impl> DelayUs for Delay { 27 | fn delay_us(&mut self, us: UXX) { 28 | crate::clock_control::sleep(MicroSeconds(us.into())) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## [Unreleased] 7 | 8 | ## [v0.3.0] - 2021-08-12 9 | 10 | ### Additions 11 | - Basic I2C Support 12 | 13 | ### Fixed 14 | - Fix compilication errors around the `const_fn` feature. 15 | - Bumped `xtensa-lx`, `xtensa-lx-rt` & `esp32` to support newer compilers. 16 | 17 | ## [v0.2.0] - 2020-09-23 18 | 19 | ### Changed 20 | - Replace `xtenxa-lx6` with `xtensa-lx`, a silicon agnostic crate for the runtime and peripheral access of xtensa CPU's. 21 | 22 | ### Fixed 23 | - Update alloc to support the new `alloc_ref` nightly changes. 24 | - Clean up examples 25 | 26 | ## [v0.1.0] - 2020-09-15 27 | 28 | - Initial release 29 | 30 | [Unreleased]: https://github.com/esp-rs/esp32-hal/compare/v0.3.0...HEAD 31 | [v0.3.0]: https://github.com/esp-rs/esp32-hal/compare/v0.2.0...v0.3.0 32 | [v0.2.0]: https://github.com/esp-rs/esp32-hal/compare/v0.1.0...v0.2.0 33 | [v0.1.0]: https://github.com/esp-rs/esp32-hal/tree/v0.1.0 34 | -------------------------------------------------------------------------------- /src/analog/hall.rs: -------------------------------------------------------------------------------- 1 | //! Built-in hall sensor readout. 2 | //! 3 | //! This module provides a function for reading current value of the built-in 4 | //! hall sensor. 5 | //! 6 | 7 | use embedded_hal::adc::OneShot; 8 | 9 | use crate::analog::adc::ADC; 10 | use crate::analog::ADC1; 11 | use crate::gpio::{Analog, Gpio36, Gpio39}; 12 | use crate::target::RTCIO; 13 | 14 | impl ADC { 15 | pub fn read_hall_sensor( 16 | &mut self, 17 | vp_pin: &mut Gpio36, 18 | vn_pin: &mut Gpio39, 19 | ) -> i32 { 20 | let rtcio = unsafe { &*RTCIO::ptr() }; 21 | 22 | rtcio.hall_sens.modify(|_, w| w.hall_phase().clear_bit()); 23 | let vp1: u16 = nb::block!(self.read(vp_pin)).unwrap(); 24 | let vn1: u16 = nb::block!(self.read(vn_pin)).unwrap(); 25 | 26 | rtcio.hall_sens.modify(|_, w| w.hall_phase().set_bit()); 27 | let vp2: u16 = nb::block!(self.read(vp_pin)).unwrap(); 28 | let vn2: u16 = nb::block!(self.read(vn_pin)).unwrap(); 29 | 30 | (vp2 as i32 - vp1 as i32) - (vn2 as i32 - vn1 as i32) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! The prelude. 2 | //! 3 | //! To use the esp32_hal effectively, a lot of traits and types need to be imported. 4 | //! Instead of importing them one by one manually, the prelude contains the most 5 | //! commonly used imports that are used around application runtime management. 6 | //! 7 | //! This can be imported as use `esp32_hal::prelude::*`. 8 | 9 | #[cfg(feature = "rt")] 10 | pub use xtensa_lx_rt::{entry, exception}; 11 | 12 | pub use crate::analog::SensExt; 13 | pub use crate::dport::{self, Peripheral}; 14 | pub use crate::gpio::GpioExt; 15 | pub use crate::interrupt; 16 | pub use crate::proc_macros::*; 17 | pub use crate::units::*; 18 | 19 | pub use embedded_hal::digital::v2::InputPin as _; 20 | pub use embedded_hal::digital::v2::OutputPin as _; 21 | pub use embedded_hal::digital::v2::StatefulOutputPin as _; 22 | pub use embedded_hal::digital::v2::ToggleableOutputPin as _; 23 | pub use embedded_hal::prelude::*; 24 | pub use embedded_hal::timer::{Cancel, CountDown, Periodic}; 25 | 26 | pub use xtensa_lx::mutex::mutex_trait::prelude::*; 27 | pub use xtensa_lx::mutex::CriticalSectionSpinLockMutex; 28 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2019-2020 Contributors to xtensa-lx6-rt 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/analog/mod.rs: -------------------------------------------------------------------------------- 1 | //! Analog peripherals control. 2 | //! 3 | //! Provides functionality for using analog peripherals, such as ADCs, DACs 4 | //! and other sesnsors. 5 | //! 6 | 7 | pub mod adc; 8 | pub mod config; 9 | pub mod dac; 10 | pub mod hall; 11 | 12 | use crate::target::SENS; 13 | use core::marker::PhantomData; 14 | 15 | pub struct ADC1 { 16 | _private: PhantomData<()>, 17 | } 18 | pub struct ADC2 { 19 | _private: PhantomData<()>, 20 | } 21 | 22 | pub struct DAC1 { 23 | _private: PhantomData<()>, 24 | } 25 | 26 | pub struct DAC2 { 27 | _private: PhantomData<()>, 28 | } 29 | 30 | pub struct AvailableAnalog { 31 | pub adc1: ADC1, 32 | pub adc2: ADC2, 33 | pub dac1: DAC1, 34 | pub dac2: DAC2, 35 | } 36 | 37 | pub trait SensExt { 38 | fn split(self) -> AvailableAnalog; 39 | } 40 | 41 | impl SensExt for SENS { 42 | fn split(self) -> AvailableAnalog { 43 | AvailableAnalog { 44 | adc1: ADC1 { 45 | _private: PhantomData, 46 | }, 47 | adc2: ADC2 { 48 | _private: PhantomData, 49 | }, 50 | dac1: DAC1 { 51 | _private: PhantomData, 52 | }, 53 | dac2: DAC2 { 54 | _private: PhantomData, 55 | }, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-hal 2 | 3 | ⚠️ **NOTE** this HAL is now in maintenence mode only, and will be archived when we have ported all drivers to the new multichip HAL: [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) 4 | 5 | A hardware abstraction layer for the [esp32](https://en.wikipedia.org/wiki/ESP32) written in Rust. 6 | 7 | Join in on the discussion: https://matrix.to/#/#esp-rs:matrix.org! 8 | 9 | ## Running examples 10 | 11 | There are currently two ways to flash the esp32: 12 | 13 | * The `flash` script using `esptool` 14 | - If you are familiar with the esp ecosystem, there is a `flash` script in this repo which utilizes the espressif esptool to flash the esp32 over usb. 15 | Example usage: 16 | ```rust 17 | ./flash -p /dev/ttyUSB0 -e blinky --release 18 | ``` 19 | 20 | * The [`espflash`](https://github.com/icewind1991/espflash) cargo subcommand 21 | - A Rust rewrite of the esptool, with a cargo subcommand. Example usage: 22 | ```rust 23 | cargo espflash --example blinky --release /dev/ttyUSB0 24 | ``` 25 | 26 | ## License 27 | 28 | Licensed under either of 29 | 30 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 31 | http://www.apache.org/licenses/LICENSE-2.0) 32 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 33 | 34 | at your option. 35 | 36 | ### Contribution 37 | 38 | Unless you explicitly state otherwise, any contribution intentionally submitted 39 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 40 | dual licensed as above, without any additional terms or conditions. 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "esp32-hal" 3 | version = "0.3.0" 4 | description = "A Hardware abstraction layer for Espressif's ESP32 WiFi microcontroller." 5 | authors = ["Scott Mabin ", "Arjan Mels "] 6 | categories = ["embedded", "hardware-support", "no-std"] 7 | keywords = ["xtensa", "esp32", "hal", "esp"] 8 | license = "MIT OR Apache-2.0" 9 | readme = "README.md" 10 | repository = "https://github.com/esp-rs/esp32-hal" 11 | edition = "2018" 12 | 13 | 14 | # Allow overriding of `memcpy`, `memset`, etc. 15 | [package.metadata.cargo-xbuild] 16 | memcpy = false 17 | 18 | 19 | [features] 20 | default = ["mem", "rt"] 21 | 22 | # Place program completely in RAM (needed when e.g. using only ROM bootloader, or for debugging). 23 | all_in_ram = [] 24 | 25 | # Allow use of external RAM. (Needs customized bootloader.) 26 | external_ram = ["esp32-hal-proc-macros/external_ram"] 27 | 28 | # Add support for global allocator. 29 | alloc = ["linked_list_allocator"] 30 | 31 | # Define `memcpy`, `memset`, etc. as replacement of standard functions. 32 | mem = [] 33 | 34 | # Enable the `rt` feature of the `esp32` crate. 35 | rt = ["esp32/rt", "xtensa-lx-rt"] 36 | 37 | 38 | [dependencies] 39 | esp32-hal-proc-macros = { version = "=0.2.0", path = "procmacros" } 40 | 41 | xtensa-lx-rt = { version = "0.7.0", optional = true, features = ["lx6"] } 42 | xtensa-lx = { version = "0.4.0", features = ["lx6"] } 43 | esp32 = "0.11.0" 44 | bare-metal = "0.2" 45 | nb = "0.1.2" 46 | embedded-hal = { version = "0.2.3", features = ["unproven"] } 47 | linked_list_allocator = { version = "=0.8.11", optional = true, default-features = false, features = ["alloc_ref"] } 48 | void = { version = "1.0.2", default-features = false } 49 | paste = "1.0.6" 50 | 51 | [dev-dependencies] 52 | panic-halt = "0.2.0" 53 | ili9341 = { version = "0.3.0", features = ["graphics"] } 54 | ssd1306 = "0.3.1" 55 | embedded-graphics = "0.6.2" 56 | mpu6050 = "0.1.3" 57 | ws2812-spi = "0.4.0" 58 | smart-leds = "0.3.0" 59 | 60 | [[example]] 61 | name = "alloc" 62 | required-features = ["alloc"] 63 | 64 | [[example]] 65 | name = "mem" 66 | required-features = ["alloc"] 67 | -------------------------------------------------------------------------------- /src/analog/config.rs: -------------------------------------------------------------------------------- 1 | //! Configuration of analog modules. 2 | 3 | use crate::analog::{ADC1, ADC2}; 4 | use embedded_hal::adc::Channel; 5 | 6 | /// The sampling/readout resolution of the ADC 7 | #[derive(PartialEq, Eq, Clone, Copy)] 8 | pub enum Resolution { 9 | Resolution9Bit = 0b00, 10 | Resolution10Bit = 0b01, 11 | Resolution11Bit = 0b10, 12 | Resolution12Bit = 0b11, 13 | } 14 | 15 | /// The attenuation of the ADC pin 16 | #[derive(PartialEq, Eq, Clone, Copy)] 17 | pub enum Attenuation { 18 | Attenuation0dB = 0b00, 19 | Attenuation2p5dB = 0b01, 20 | Attenuation6dB = 0b10, 21 | Attenuation11dB = 0b11, 22 | } 23 | 24 | pub struct Adc1Config { 25 | pub resolution: Resolution, 26 | pub hall_sensor: bool, 27 | pub attenuations: [Option; 10], 28 | } 29 | 30 | impl Adc1Config { 31 | pub fn new() -> Adc1Config { 32 | Self::default() 33 | } 34 | 35 | pub fn enable_pin>( 36 | &mut self, 37 | _pin: &PIN, 38 | attenuation: Attenuation, 39 | ) { 40 | self.attenuations[PIN::channel() as usize] = Some(attenuation); 41 | } 42 | 43 | pub fn enable_hall_sensor(&mut self) { 44 | self.hall_sensor = true; 45 | } 46 | } 47 | impl Default for Adc1Config { 48 | fn default() -> Self { 49 | Adc1Config { 50 | resolution: Resolution::Resolution12Bit, 51 | hall_sensor: false, 52 | attenuations: [None; 10], 53 | } 54 | } 55 | } 56 | 57 | pub struct Adc2Config { 58 | pub resolution: Resolution, 59 | pub attenuations: [Option; 10], 60 | } 61 | 62 | impl Adc2Config { 63 | pub fn new() -> Adc2Config { 64 | Self::default() 65 | } 66 | 67 | pub fn enable_pin>( 68 | &mut self, 69 | _pin: &PIN, 70 | attenuation: Attenuation, 71 | ) { 72 | self.attenuations[PIN::channel() as usize] = Some(attenuation); 73 | } 74 | } 75 | impl Default for Adc2Config { 76 | fn default() -> Self { 77 | Adc2Config { 78 | resolution: Resolution::Resolution12Bit, 79 | attenuations: [None; 10], 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/analog/dac.rs: -------------------------------------------------------------------------------- 1 | //! Digital to analog (DAC) conversion. 2 | //! 3 | //! This module provides functions for controling two digital to 4 | //! analog converters, available on ESP32: `DAC1` and `DAC2`. 5 | //! 6 | //! The DAC1 is avilable on the GPIO pin 25, and DAC2 on pin 26. 7 | //! 8 | 9 | use core::marker::PhantomData; 10 | 11 | use crate::analog::{DAC1, DAC2}; 12 | use crate::gpio::{Analog, Gpio25, Gpio26}; 13 | use crate::target::{RTCIO, SENS}; 14 | 15 | pub struct DAC { 16 | _dac: PhantomData, 17 | } 18 | 19 | impl DAC { 20 | pub fn dac1(_dac: DAC1, _pin: Gpio25) -> Result { 21 | let dac = DAC:: { _dac: PhantomData }.set_power(); 22 | 23 | Ok(dac) 24 | } 25 | 26 | fn set_power(self) -> Self { 27 | let rtcio = unsafe { &*RTCIO::ptr() }; 28 | 29 | rtcio.pad_dac1.modify(|_, w| { 30 | w.pdac1_dac_xpd_force().set_bit(); 31 | w.pdac1_xpd_dac().set_bit() 32 | }); 33 | 34 | self 35 | } 36 | 37 | pub fn write(&mut self, value: u8) { 38 | let rtcio = unsafe { &*RTCIO::ptr() }; 39 | let sensors = unsafe { &*SENS::ptr() }; 40 | 41 | sensors 42 | .sar_dac_ctrl2 43 | .modify(|_, w| w.dac_cw_en1().clear_bit()); 44 | rtcio 45 | .pad_dac1 46 | .modify(|_, w| unsafe { w.pdac1_dac().bits(value) }); 47 | } 48 | } 49 | 50 | impl DAC { 51 | pub fn dac2(_dac: DAC2, _pin: Gpio26) -> Result { 52 | let dac = DAC:: { _dac: PhantomData }.set_power(); 53 | 54 | Ok(dac) 55 | } 56 | 57 | fn set_power(self) -> Self { 58 | let rtcio = unsafe { &*RTCIO::ptr() }; 59 | 60 | rtcio.pad_dac2.modify(|_, w| { 61 | w.pdac2_dac_xpd_force().set_bit(); 62 | w.pdac2_xpd_dac().set_bit() 63 | }); 64 | 65 | self 66 | } 67 | 68 | pub fn write(&mut self, value: u8) { 69 | let rtcio = unsafe { &*RTCIO::ptr() }; 70 | let sensors = unsafe { &*SENS::ptr() }; 71 | 72 | sensors 73 | .sar_dac_ctrl2 74 | .modify(|_, w| w.dac_cw_en2().clear_bit()); 75 | rtcio 76 | .pad_dac2 77 | .modify(|_, w| unsafe { w.pdac2_dac().bits(value) }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use esp32_hal as hal; 5 | use esp32_hal::target; 6 | use hal::prelude::*; 7 | use panic_halt as _; 8 | use xtensa_lx::timer::get_cycle_count; 9 | 10 | /// The default clock source is the onboard crystal 11 | /// In most cases 40mhz (but can be as low as 2mhz depending on the board) 12 | const CORE_HZ: u32 = 40_000_000; 13 | 14 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 19 | 20 | let mut rtccntl = dp.RTCCNTL; 21 | let mut timg0 = dp.TIMG0; 22 | let mut timg1 = dp.TIMG1; 23 | 24 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 25 | // openocd disables the wdt's on halt 26 | // we will do it manually on startup 27 | disable_timg_wdts(&mut timg0, &mut timg1); 28 | disable_rtc_wdt(&mut rtccntl); 29 | 30 | let pins = dp.GPIO.split(); 31 | let mut led = pins.gpio2.into_push_pull_output(); 32 | 33 | loop { 34 | led.set_high().unwrap(); 35 | delay(CORE_HZ); 36 | led.set_low().unwrap(); 37 | delay(CORE_HZ); 38 | } 39 | } 40 | 41 | fn disable_rtc_wdt(rtccntl: &mut target::RTCCNTL) { 42 | /* Disables the RTCWDT */ 43 | rtccntl 44 | .wdtwprotect 45 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 46 | rtccntl.wdtconfig0.modify(|_, w| unsafe { 47 | w.wdt_stg0() 48 | .bits(0x0) 49 | .wdt_stg1() 50 | .bits(0x0) 51 | .wdt_stg2() 52 | .bits(0x0) 53 | .wdt_stg3() 54 | .bits(0x0) 55 | .wdt_flashboot_mod_en() 56 | .clear_bit() 57 | .wdt_en() 58 | .clear_bit() 59 | }); 60 | rtccntl.wdtwprotect.write(|w| unsafe { w.bits(0x0) }); 61 | } 62 | 63 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 64 | timg0 65 | .wdtwprotect 66 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 67 | timg1 68 | .wdtwprotect 69 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 70 | 71 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 72 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 73 | } 74 | 75 | /// cycle accurate delay using the cycle counter register 76 | pub fn delay(clocks: u32) { 77 | let start = get_cycle_count(); 78 | loop { 79 | if get_cycle_count().wrapping_sub(start) >= clocks { 80 | break; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/dac.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::panic::PanicInfo; 5 | 6 | use esp32_hal::prelude::*; 7 | 8 | use esp32_hal::analog::dac::DAC; 9 | use esp32_hal::clock_control::sleep; 10 | use esp32_hal::dport::Split; 11 | use esp32_hal::target; 12 | 13 | #[no_mangle] 14 | fn main() -> ! { 15 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 16 | 17 | let mut timg0 = dp.TIMG0; 18 | let mut timg1 = dp.TIMG1; 19 | 20 | let (_dport, dport_clock_control) = dp.DPORT.split(); 21 | 22 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 23 | // openocd disables the watchdog timer on halt 24 | // we will do it manually on startup 25 | disable_timg_wdts(&mut timg0, &mut timg1); 26 | 27 | let clkcntrl = esp32_hal::clock_control::ClockControl::new( 28 | dp.RTCCNTL, 29 | dp.APB_CTRL, 30 | dport_clock_control, 31 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 32 | ) 33 | .unwrap(); 34 | 35 | let (_clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 36 | watchdog.disable(); 37 | 38 | /* Set DAC pins to analog mode. The pins are specified by hardware and cannot be changed */ 39 | let gpios = dp.GPIO.split(); 40 | let pin25 = gpios.gpio25.into_analog(); 41 | let pin26 = gpios.gpio26.into_analog(); 42 | 43 | /* Create DAC instances */ 44 | let analog = dp.SENS.split(); 45 | let mut dac1 = DAC::dac1(analog.dac1, pin25).unwrap(); 46 | let mut dac2 = DAC::dac2(analog.dac2, pin26).unwrap(); 47 | 48 | let mut voltage_dac1: u8 = 0; 49 | let mut voltage_dac2: u8 = 255; 50 | loop { 51 | /* Change voltage on the pins using write function */ 52 | voltage_dac1 = voltage_dac1.wrapping_add(1); 53 | dac1.write(voltage_dac1); 54 | 55 | voltage_dac2 = voltage_dac2.wrapping_sub(1); 56 | dac2.write(voltage_dac2); 57 | 58 | sleep(250.ms()); 59 | } 60 | } 61 | 62 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 63 | 64 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 65 | timg0 66 | .wdtwprotect 67 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 68 | timg1 69 | .wdtwprotect 70 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 71 | 72 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 73 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 74 | } 75 | 76 | /// Basic panic handler - just loops 77 | #[panic_handler] 78 | fn panic(_info: &PanicInfo) -> ! { 79 | loop {} 80 | } 81 | -------------------------------------------------------------------------------- /examples/serial.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::{fmt::Write, panic::PanicInfo}; 5 | 6 | use esp32_hal::{ 7 | clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, 8 | dport::Split, 9 | dprintln, 10 | prelude::*, 11 | serial::{config::Config, Pins, Serial}, 12 | target, 13 | timer::Timer, 14 | }; 15 | 16 | const BLINK_HZ: Hertz = Hertz(2); 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 21 | 22 | let (_, dport_clock_control) = dp.DPORT.split(); 23 | 24 | let clkcntrl = ClockControl::new( 25 | dp.RTCCNTL, 26 | dp.APB_CTRL, 27 | dport_clock_control, 28 | XTAL_FREQUENCY_AUTO, 29 | ) 30 | .unwrap(); 31 | 32 | let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 33 | watchdog.disable(); 34 | 35 | let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); 36 | let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); 37 | watchdog0.disable(); 38 | watchdog1.disable(); 39 | 40 | let pins = dp.GPIO.split(); 41 | 42 | let mut blinky = pins.gpio0.into_push_pull_output(); 43 | 44 | // Use UART1 as example: will cause dprintln statements not to be printed 45 | let serial: Serial<_, _, _> = Serial::new( 46 | dp.UART1, 47 | Pins { 48 | tx: pins.gpio1, 49 | rx: pins.gpio3, 50 | cts: None, 51 | rts: None, 52 | }, 53 | Config { 54 | // default configuration is 19200 baud, 8 data bits, 1 stop bit & no parity (8N1) 55 | baudrate: 115200.Hz(), 56 | ..Config::default() 57 | }, 58 | clkcntrl_config, 59 | ) 60 | .unwrap(); 61 | 62 | let (mut tx, mut rx) = serial.split(); 63 | 64 | writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); 65 | 66 | // line will not be printed as using UART1 67 | dprintln!("UART0\n"); 68 | 69 | loop { 70 | writeln!(tx, "Characters received: {:?}", rx.count()).unwrap(); 71 | 72 | while let Ok(x) = rx.read() { 73 | write!(tx, "{} ({:#x}) ", if x >= 32 { x as char } else { '?' }, x).unwrap() 74 | } 75 | writeln!(tx).unwrap(); 76 | 77 | blinky.set_high().unwrap(); 78 | sleep((Hertz(1_000_000) / BLINK_HZ).us()); 79 | blinky.set_low().unwrap(); 80 | sleep((Hertz(1_000_000) / BLINK_HZ).us()); 81 | } 82 | } 83 | 84 | #[panic_handler] 85 | fn panic(info: &PanicInfo) -> ! { 86 | dprintln!("\n\n*** {:?}", info); 87 | loop {} 88 | } 89 | -------------------------------------------------------------------------------- /examples/ledc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::{fmt::Write, panic::PanicInfo}; 5 | use esp32_hal::{ 6 | clock_control::{self, ClockControl}, 7 | dport::Split, 8 | dprintln, 9 | ledc::{ 10 | channel::{self, ChannelIFace}, 11 | timer::{self, TimerIFace}, 12 | LSGlobalClkSource, LowSpeed, LEDC, 13 | }, 14 | prelude::*, 15 | serial::{self, Serial}, 16 | target::Peripherals, 17 | timer::Timer, 18 | }; 19 | 20 | #[entry] 21 | fn main() -> ! { 22 | let dp = Peripherals::take().expect("Failed to obtain peripherals"); 23 | 24 | let (_, dport_clock_control) = dp.DPORT.split(); 25 | let clock_control = ClockControl::new( 26 | dp.RTCCNTL, 27 | dp.APB_CTRL, 28 | dport_clock_control, 29 | clock_control::XTAL_FREQUENCY_AUTO, 30 | ) 31 | .unwrap(); 32 | 33 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 34 | watchdog.disable(); 35 | 36 | let (.., mut watchdog0) = Timer::new(dp.TIMG0, clock_control_config); 37 | let (.., mut watchdog1) = Timer::new(dp.TIMG1, clock_control_config); 38 | watchdog0.disable(); 39 | watchdog1.disable(); 40 | 41 | let pins = dp.GPIO.split(); 42 | 43 | let mut serial: Serial<_, _, _> = Serial::new( 44 | dp.UART0, 45 | serial::Pins { 46 | tx: pins.gpio1, 47 | rx: pins.gpio3, 48 | cts: None, 49 | rts: None, 50 | }, 51 | serial::config::Config { 52 | baudrate: 115200.Hz(), 53 | ..serial::config::Config::default() 54 | }, 55 | clock_control_config, 56 | ) 57 | .unwrap(); 58 | 59 | writeln!(serial, "\nESP32 Started\n\n").unwrap(); 60 | 61 | let mut ledc = LEDC::new(clock_control_config); 62 | ledc.set_global_slow_clock(LSGlobalClkSource::ABPClk); 63 | let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); 64 | lstimer0 65 | .configure(timer::config::Config { 66 | duty: timer::config::Duty::Duty1Bit, 67 | clock_source: timer::LSClockSource::SlowClk, 68 | frequency: 24_000_000.Hz(), 69 | }) 70 | .unwrap(); 71 | 72 | let mut channel0 = ledc.get_channel(channel::Number::Channel0, pins.gpio4); 73 | channel0 74 | .configure(channel::config::Config { 75 | timer: &lstimer0, 76 | duty_pct: 0.5, 77 | }) 78 | .unwrap(); 79 | 80 | loop {} 81 | } 82 | 83 | #[panic_handler] 84 | fn panic(info: &PanicInfo) -> ! { 85 | dprintln!("\n\n*** {:?}", info); 86 | loop {} 87 | } 88 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | // more info at: https://github.com/Marus/cortex-debug/blob/master/package.json 9 | "name": "Attach", 10 | "type": "cortex-debug", 11 | "request": "attach", // attach instead of launch, because otherwise flash write is attempted, but fails 12 | "cwd": "${workspaceRoot}", 13 | "executable": "target/current.elf", 14 | "servertype": "openocd", 15 | "interface": "jtag", 16 | "svdFile": "../esp32/svd/esp32.svd", 17 | "toolchainPrefix": "xtensa-esp32-elf", 18 | "openOCDPreConfigLaunchCommands": [ 19 | "set ESP_RTOS none" 20 | ], 21 | "configFiles": [ 22 | "board/esp32-wrover-kit-3.3v.cfg" 23 | ], 24 | "overrideAttachCommands": [ 25 | "set remote hardware-watchpoint-limit 2", 26 | "mon halt", 27 | "flushregs" 28 | ], 29 | "overrideRestartCommands": [ 30 | "mon reset halt", 31 | "flushregs", 32 | "c", 33 | ] 34 | }, 35 | { 36 | // more info at: https://github.com/Marus/cortex-debug/blob/master/package.json 37 | "name": "Flash & Launch", 38 | "type": "cortex-debug", 39 | "preLaunchTask": "Build Example", 40 | "request": "launch", // attach instead of launch, because otherwise flash write is attempted, but fails 41 | "cwd": "${workspaceRoot}", 42 | "executable": "target/current.elf", 43 | "servertype": "openocd", 44 | "interface": "jtag", 45 | "svdFile": "../esp32/svd/esp32.svd", 46 | "toolchainPrefix": "xtensa-esp32-elf", 47 | "openOCDPreConfigLaunchCommands": [ 48 | "set ESP_RTOS none" 49 | ], 50 | "configFiles": [ 51 | "board/esp32-wrover-kit-3.3v.cfg" 52 | ], 53 | "overrideLaunchCommands": [ 54 | "mon program_esp32 target/current.bin 0x10000 verify reset hex", 55 | "flushregs", 56 | ], 57 | "postStartSessionCommands": [ 58 | "thb main", 59 | "continue" 60 | ], 61 | "overrideRestartCommands": [ 62 | "mon program_esp32 target/current.bin 0x10000 verify reset hex", 63 | "flushregs", 64 | "thb main", 65 | "continue", 66 | ] 67 | }, 68 | ] 69 | } -------------------------------------------------------------------------------- /src/dprint.rs: -------------------------------------------------------------------------------- 1 | //! Print debug information to UART0 2 | //! 3 | //! Directly writes to the UART0 TX uart queue. 4 | //! This is unsafe! It is asynchronous with normal UART0 usage and 5 | //! interrupts are not disabled. 6 | 7 | use crate::target::UART0; 8 | 9 | pub struct DebugLog {} 10 | 11 | pub enum Error {} 12 | 13 | impl DebugLog { 14 | pub fn count(&mut self) -> u8 { 15 | unsafe { (*UART0::ptr()).status.read().txfifo_cnt().bits() } 16 | } 17 | 18 | pub fn is_idle(&mut self) -> bool { 19 | unsafe { (*UART0::ptr()).status.read().st_utx_out().is_tx_idle() } 20 | } 21 | 22 | pub fn write(&mut self, byte: u8) -> nb::Result<(), Error> { 23 | if self.count() < 128 { 24 | unsafe { (*UART0::ptr()).tx_fifo.write_with_zero(|w| w.bits(byte)) } 25 | Ok(()) 26 | } else { 27 | Err(nb::Error::WouldBlock) 28 | } 29 | } 30 | } 31 | 32 | impl core::fmt::Write for DebugLog { 33 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 34 | s.as_bytes() 35 | .iter() 36 | .try_for_each(|c| nb::block!(self.write(*c))) 37 | .map_err(|_| core::fmt::Error) 38 | } 39 | } 40 | 41 | pub static mut DEBUG_LOG: DebugLog = DebugLog {}; 42 | 43 | /// Macro for sending a formatted string to UART0 for debugging 44 | #[macro_export] 45 | macro_rules! dprint { 46 | ($s:expr) => { 47 | #[allow(unused_unsafe)] 48 | unsafe { 49 | use core::fmt::Write; 50 | $crate::dprint::DEBUG_LOG.write_str($s).unwrap(); 51 | } 52 | }; 53 | ($($arg:tt)*) => { 54 | #[allow(unused_unsafe)] 55 | unsafe { 56 | use core::fmt::Write; 57 | $crate::dprint::DEBUG_LOG.write_fmt(format_args!($($arg)*)).unwrap(); 58 | } 59 | }; 60 | } 61 | 62 | /// Macro for sending a formatted string to UART0 for debugging, with a newline. 63 | #[macro_export] 64 | macro_rules! dprintln { 65 | () => { 66 | #[allow(unused_unsafe)] 67 | unsafe { 68 | use core::fmt::Write; 69 | $crate::dprint::DEBUG_LOG.write_str("\n").unwrap(); 70 | } 71 | }; 72 | ($fmt:expr) => { 73 | #[allow(unused_unsafe)] 74 | unsafe { 75 | use core::fmt::Write; 76 | $crate::dprint::DEBUG_LOG.write_str(concat!($fmt, "\n")).unwrap(); 77 | } 78 | }; 79 | ($fmt:expr, $($arg:tt)*) => { 80 | #[allow(unused_unsafe)] 81 | unsafe { 82 | use core::fmt::Write; 83 | $crate::dprint::DEBUG_LOG.write_fmt(format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap(); 84 | } 85 | }; 86 | } 87 | 88 | /// Macro for flushing the UART0 TX buffer 89 | #[macro_export] 90 | macro_rules! dflush { 91 | () => { 92 | #[allow(unused_unsafe)] 93 | unsafe { 94 | while !$crate::dprint::DEBUG_LOG.is_idle() {} 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /src/external_ram.rs: -------------------------------------------------------------------------------- 1 | //! External RAM (PSRAM) control 2 | //! 3 | 4 | use core::mem::size_of; 5 | use core::ptr::{read_volatile, write_volatile}; 6 | 7 | // must be uninitialized, because otherwise will be zeroed in the Reset routine 8 | #[crate::ram(uninitialized)] 9 | static mut EXTERNAL_RAM_SIZE: core::mem::MaybeUninit> = 10 | core::mem::MaybeUninit::uninit(); 11 | 12 | /// Info about the cache to be able to trash it 13 | const CACHE_LINE_SIZE: usize = 32; 14 | const NR_CACHE_LINES: usize = 1024; 15 | /// number of steps to check RAM in. 8 steps limits to 0.5MB steps 16 | const RAM_STEPS: usize = 8; 17 | 18 | // These symbols come from `memory.x` 19 | extern "C" { 20 | static mut _external_bss_start: u32; 21 | static mut _external_bss_end: u32; 22 | 23 | static _external_ram_start: u32; 24 | static _external_ram_end: u32; 25 | 26 | static mut _external_heap_start: u32; 27 | } 28 | 29 | /// Get the size of the external RAM (also called PSRAM). 30 | pub fn get_size() -> usize { 31 | unsafe { EXTERNAL_RAM_SIZE.assume_init().unwrap() } 32 | } 33 | 34 | /// Initialize external RAM 35 | pub(super) unsafe fn init() { 36 | EXTERNAL_RAM_SIZE = core::mem::MaybeUninit::new(Some(calculate_external_ram_size())); 37 | 38 | if &_external_heap_start as *const u32 > (&_external_ram_start as *const u32).add(get_size()) { 39 | panic!("External RAM too small for data"); 40 | } 41 | xtensa_lx_rt::zero_bss(&mut _external_bss_start, &mut _external_bss_end); 42 | } 43 | 44 | /// Calculate the size of external RAM by reading and writing at defined intervals while 45 | /// thrashing the cache in between. 46 | /// 47 | /// TODO: should be replaced by reading the size via SPI 48 | unsafe fn calculate_external_ram_size() -> usize { 49 | let ram_start_addr: usize = &_external_ram_start as *const u32 as usize; 50 | let ram_end_addr: usize = &_external_ram_end as *const u32 as usize; 51 | 52 | let ram: *mut u32 = ram_start_addr as _; 53 | let mut buffer = [0u32; RAM_STEPS]; 54 | 55 | let step_size = (ram_end_addr - ram_start_addr) / RAM_STEPS / size_of::(); 56 | 57 | // write recognition pattern in the ram 58 | for i in 0..RAM_STEPS { 59 | buffer[i] = read_volatile(ram.add(i * step_size)); 60 | write_volatile(ram.add(i * step_size), 0xdeadbeef + i as u32); 61 | } 62 | 63 | // trash the cache 64 | for i in (1..=NR_CACHE_LINES).step_by(CACHE_LINE_SIZE) { 65 | let addr = ram.add(i * CACHE_LINE_SIZE / size_of::()); 66 | write_volatile(addr, read_volatile(addr)); 67 | } 68 | 69 | // check the recognition pattern and restore content 70 | let mut ram_size = 0; 71 | let mut end_found = false; 72 | for i in 0..RAM_STEPS { 73 | if !end_found && read_volatile(ram.add(i * step_size)) == 0xdeadbeef + i as u32 { 74 | ram_size += step_size; 75 | } else { 76 | end_found = true; 77 | } 78 | write_volatile(ram.add(i * step_size), buffer[i]); 79 | } 80 | 81 | ram_size * size_of::() 82 | } 83 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build and Flash Example", 8 | "type": "shell", 9 | "runOptions": {"reevaluateOnRerun": false}, 10 | "command": "./flash -t --example ${input:example}", 11 | "problemMatcher": { 12 | "base": "$rustc", 13 | "fileLocation": [ 14 | "relative" 15 | ] 16 | }, 17 | "group": "build", 18 | "presentation": { 19 | "reveal": "silent", 20 | "panel": "shared", 21 | "clear": true 22 | }, 23 | }, 24 | { 25 | "label": "Build Example", 26 | "type": "shell", 27 | "runOptions": {"reevaluateOnRerun": false}, 28 | "command": "./flash -s --example ${input:example}", 29 | "problemMatcher": { 30 | "base": "$rustc", 31 | "fileLocation": [ 32 | "relative" 33 | ] 34 | }, 35 | "group": { 36 | "kind": "build", 37 | "isDefault": true 38 | }, 39 | "presentation": { 40 | "reveal": "silent", 41 | "panel": "shared", 42 | "clear": true 43 | } 44 | }, 45 | { 46 | "label": "Compile All Examples", 47 | "type": "shell", 48 | "command": "cargo xbuild --examples", 49 | "problemMatcher": { 50 | "base": "$rustc", 51 | "fileLocation": [ 52 | "relative" 53 | ] 54 | }, 55 | "group": "build", 56 | "presentation": { 57 | "reveal": "always", 58 | "panel": "shared", 59 | "clear": true 60 | } 61 | }, 62 | { 63 | "label": "Build Documentation", 64 | "type": "shell", 65 | "command": "cargo xdoc --open --all-features", 66 | "problemMatcher": { 67 | "base": "$rustc", 68 | "fileLocation": [ 69 | "relative" 70 | ] 71 | }, 72 | "group": "build", 73 | "presentation": { 74 | "reveal": "silent", 75 | "panel": "shared", 76 | "clear": true 77 | } 78 | }, 79 | ], 80 | "inputs": [ 81 | // requires Tasks Shell Input extension 82 | { 83 | "id": "example", 84 | "type": "command", 85 | "command": "shellCommand.execute", 86 | "args": { 87 | "command": "cargo check --offline --example 2>&1 |egrep '^ '| sed -E 's/[ \t]*//g'", 88 | "cwd": "${workspaceFolder}/../esp32-hal" 89 | } 90 | }, 91 | // alternatively type in manually 92 | { 93 | "id": "examplePrompt", 94 | "description": "Example:", 95 | "default": "serial", 96 | "type": "promptString" 97 | } 98 | ] 99 | } -------------------------------------------------------------------------------- /examples/hall.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use core::panic::PanicInfo; 6 | 7 | use esp32_hal::prelude::*; 8 | 9 | use esp32_hal::analog::adc::ADC; 10 | use esp32_hal::analog::config::{Adc1Config, Attenuation}; 11 | use esp32_hal::clock_control::sleep; 12 | use esp32_hal::dport::Split; 13 | use esp32_hal::serial::{config::Config, Serial}; 14 | use esp32_hal::target; 15 | 16 | #[no_mangle] 17 | fn main() -> ! { 18 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 19 | 20 | let mut timg0 = dp.TIMG0; 21 | let mut timg1 = dp.TIMG1; 22 | 23 | let (_, dport_clock_control) = dp.DPORT.split(); 24 | 25 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 26 | // openocd disables the watchdog timer on halt 27 | // we will do it manually on startup 28 | disable_timg_wdts(&mut timg0, &mut timg1); 29 | 30 | let clkcntrl = esp32_hal::clock_control::ClockControl::new( 31 | dp.RTCCNTL, 32 | dp.APB_CTRL, 33 | dport_clock_control, 34 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 35 | ) 36 | .unwrap(); 37 | 38 | let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 39 | watchdog.disable(); 40 | 41 | let gpios = dp.GPIO.split(); 42 | // setup serial controller 43 | let serial: Serial<_, _, _> = Serial::new( 44 | dp.UART0, 45 | esp32_hal::serial::Pins { 46 | tx: gpios.gpio1, 47 | rx: gpios.gpio3, 48 | cts: None, 49 | rts: None, 50 | }, 51 | Config::default(), 52 | clkcntrl_config, 53 | ) 54 | .unwrap(); 55 | 56 | let (mut tx, _rx) = serial.split(); 57 | 58 | /* Set ADC pins to analog mode */ 59 | let mut pin36 = gpios.gpio36.into_analog(); 60 | let mut pin39 = gpios.gpio39.into_analog(); 61 | 62 | /* In the configuration enable hall sensor and its pins (36 and 39) */ 63 | let mut adc_config = Adc1Config::new(); 64 | adc_config.enable_hall_sensor(); 65 | adc_config.enable_pin(&pin36, Attenuation::Attenuation0dB); 66 | adc_config.enable_pin(&pin39, Attenuation::Attenuation0dB); 67 | 68 | /* Hall sensor is only available on the ADC1 */ 69 | let analog = dp.SENS.split(); 70 | let mut adc1 = ADC::adc1(analog.adc1, adc_config).unwrap(); 71 | 72 | loop { 73 | /* Read the sensor and print out the raw value once per second */ 74 | let hall_sensor_value: i32 = adc1.read_hall_sensor(&mut pin36, &mut pin39); 75 | writeln!(tx, "Hall sensor raw value: {:?}", hall_sensor_value).unwrap(); 76 | 77 | sleep(1.s()); 78 | } 79 | } 80 | 81 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 82 | 83 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 84 | timg0 85 | .wdtwprotect 86 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 87 | timg1 88 | .wdtwprotect 89 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 90 | 91 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 92 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 93 | } 94 | 95 | /// Basic panic handler - just loops 96 | #[panic_handler] 97 | fn panic(_info: &PanicInfo) -> ! { 98 | loop {} 99 | } 100 | -------------------------------------------------------------------------------- /src/ledc/mod.rs: -------------------------------------------------------------------------------- 1 | //! LEDC (LED PWM Controller) peripheral control 2 | //! 3 | //! Currently only supports fixed frequency output. Hardware fade support and interrupts are not currently 4 | //! implemented. High Speed and Low Speed channels are available. 5 | //! 6 | //! # Example: 7 | //! The following will configure the Low Speed channel 0 to 24Mhz output with 50% duty using the ABP Clock 8 | //! ``` 9 | //! let mut ledc = LEDC::new(clock_control_config); 10 | //! 11 | //! ledc.set_global_slow_clock(LSGlobalClkSource::ABPClk); 12 | //! let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); 13 | //! lstimer0 14 | //! .configure(timer::config::Config { 15 | //! duty: timer::config::Duty::Duty1Bit, 16 | //! clock_source: timer::LSClockSource::SlowClk, 17 | //! frequency: 24_000_000.Hz(), 18 | //! }) 19 | //! .unwrap(); 20 | //! 21 | //! let mut channel0 = ledc.get_channel(channel::Number::Channel0, pins.gpio4); 22 | //! channel0 23 | //! .configure(channel::config::Config { 24 | //! timer: &lstimer0, 25 | //! duty: 0.5, 26 | //! }) 27 | //! .unwrap(); 28 | //! ``` 29 | //! # TODO 30 | //! - Hardware fade support 31 | //! - Interrupts 32 | 33 | use crate::{clock_control::ClockControlConfig, dport, gpio::OutputPin}; 34 | use channel::Channel; 35 | use timer::Timer; 36 | 37 | use self::timer::TimerSpeed; 38 | 39 | pub mod channel; 40 | pub mod timer; 41 | 42 | /// Global slow clock source 43 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 44 | pub enum LSGlobalClkSource { 45 | EightMHz, 46 | ABPClk, 47 | } 48 | 49 | /// LEDC (LED PWM Controller) 50 | pub struct LEDC<'a> { 51 | ledc: &'a esp32::ledc::RegisterBlock, 52 | clock_control_config: ClockControlConfig, 53 | } 54 | 55 | /// Used to specify HighSpeed Timer/Channel 56 | pub struct HighSpeed {} 57 | /// Used to specify LowSpeed Timer/Channel 58 | pub struct LowSpeed {} 59 | 60 | pub trait Speed {} 61 | 62 | impl Speed for HighSpeed {} 63 | impl Speed for LowSpeed {} 64 | 65 | impl<'a> LEDC<'a> { 66 | /// Return a new LEDC 67 | pub fn new(clock_control_config: ClockControlConfig) -> Self { 68 | dport::enable_peripheral(dport::Peripheral::LEDC); 69 | dport::reset_peripheral(dport::Peripheral::LEDC); 70 | 71 | let ledc = unsafe { &*esp32::LEDC::ptr() }; 72 | LEDC { 73 | ledc, 74 | clock_control_config, 75 | } 76 | } 77 | 78 | /// Set global slow clock source 79 | pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) { 80 | match clock_source { 81 | LSGlobalClkSource::EightMHz => self.ledc.conf.write(|w| w.apb_clk_sel().clear_bit()), 82 | LSGlobalClkSource::ABPClk => self.ledc.conf.write(|w| w.apb_clk_sel().set_bit()), 83 | }; 84 | } 85 | 86 | /// Return a new timer 87 | pub fn get_timer(&self, number: timer::Number) -> timer::Timer { 88 | Timer::new(self.ledc, self.clock_control_config, number) 89 | } 90 | 91 | /// Return a new channel 92 | pub fn get_channel( 93 | &self, 94 | number: channel::Number, 95 | output_pin: O, 96 | ) -> channel::Channel { 97 | Channel::new(number, output_pin) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/adc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use core::panic::PanicInfo; 6 | 7 | use esp32_hal::prelude::*; 8 | 9 | use esp32_hal::analog::adc::ADC; 10 | use esp32_hal::analog::config::{Adc1Config, Adc2Config, Attenuation}; 11 | use esp32_hal::clock_control::sleep; 12 | use esp32_hal::dport::Split; 13 | use esp32_hal::serial::{config::Config, Serial}; 14 | use esp32_hal::target; 15 | 16 | #[no_mangle] 17 | fn main() -> ! { 18 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 19 | 20 | let mut timg0 = dp.TIMG0; 21 | let mut timg1 = dp.TIMG1; 22 | 23 | let (_, dport_clock_control) = dp.DPORT.split(); 24 | 25 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 26 | // openocd disables the watchdog timer on halt 27 | // we will do it manually on startup 28 | disable_timg_wdts(&mut timg0, &mut timg1); 29 | 30 | let clkcntrl = esp32_hal::clock_control::ClockControl::new( 31 | dp.RTCCNTL, 32 | dp.APB_CTRL, 33 | dport_clock_control, 34 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 35 | ) 36 | .unwrap(); 37 | 38 | let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 39 | watchdog.disable(); 40 | 41 | let gpios = dp.GPIO.split(); 42 | 43 | let serial: Serial<_, _, _> = Serial::new( 44 | dp.UART0, 45 | esp32_hal::serial::Pins { 46 | tx: gpios.gpio1, 47 | rx: gpios.gpio3, 48 | cts: None, 49 | rts: None, 50 | }, 51 | Config::default(), 52 | clkcntrl_config, 53 | ) 54 | .unwrap(); 55 | 56 | let (mut tx, _rx) = serial.split(); 57 | 58 | /* Set ADC pins to analog mode */ 59 | let mut pin36 = gpios.gpio36.into_analog(); 60 | let mut pin25 = gpios.gpio25.into_analog(); 61 | 62 | /* Prepare ADC configs and enable pins, which will be used */ 63 | let mut adc1_config = Adc1Config::new(); 64 | adc1_config.enable_pin(&pin36, Attenuation::Attenuation11dB); 65 | 66 | let mut adc2_config = Adc2Config::new(); 67 | adc2_config.enable_pin(&pin25, Attenuation::Attenuation11dB); 68 | 69 | /* Create ADC instances */ 70 | let analog = dp.SENS.split(); 71 | let mut adc1 = ADC::adc1(analog.adc1, adc1_config).unwrap(); 72 | let mut adc2 = ADC::adc2(analog.adc2, adc2_config).unwrap(); 73 | 74 | loop { 75 | /* Read ADC values every second and print them out */ 76 | let pin36_value: u16 = nb::block!(adc1.read(&mut pin36)).unwrap(); 77 | writeln!(tx, "ADC1 pin 36 raw value: {:?}", pin36_value).unwrap(); 78 | 79 | let pin25_value: u16 = nb::block!(adc2.read(&mut pin25)).unwrap(); 80 | writeln!(tx, "ADC2 pin 25 raw value: {:?}", pin25_value).unwrap(); 81 | 82 | sleep(1.s()); 83 | } 84 | } 85 | 86 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 87 | 88 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 89 | timg0 90 | .wdtwprotect 91 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 92 | timg1 93 | .wdtwprotect 94 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 95 | 96 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 97 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 98 | } 99 | 100 | /// Basic panic handler - just loops 101 | #[panic_handler] 102 | fn panic(_info: &PanicInfo) -> ! { 103 | loop {} 104 | } 105 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This ESP32 hal crate provides support for the ESP32 peripherals 2 | //! 3 | //! ## Features 4 | //! - `external_ram` (enabled by default) 5 | //! - Enables support for external ram (psram). However proper initialization 6 | //! of external ram relies on a customized bootloader 7 | //! - `all_in_ram` 8 | //! - Forces all code and data in RAM instead of flash. This allows usage with 9 | //! the ROM bootloader and eases debugging 10 | //! - `alloc` 11 | //! - Enables support for dynamic memory allocations via a GlobalAllocator 12 | //! and/or AllocRef 13 | //! - `mem` 14 | //! - Include customized memcpy, memset, etc. which use word (4-byte) sized and aligned 15 | //! instructions to support IRAM usage and as optimization 16 | 17 | #![no_std] 18 | #![cfg_attr(feature = "alloc", feature(allocator_api))] 19 | #![cfg_attr(feature = "alloc", feature(alloc_layout_extra))] 20 | #![cfg_attr(feature = "alloc", feature(nonnull_slice_from_raw_parts))] 21 | #![cfg_attr(feature = "alloc", feature(const_fn_trait_bound))] 22 | 23 | pub use embedded_hal as hal; 24 | pub use esp32 as target; 25 | 26 | extern crate esp32_hal_proc_macros as proc_macros; 27 | pub use proc_macros::interrupt; 28 | pub use proc_macros::ram; 29 | 30 | pub mod analog; 31 | pub mod clock_control; 32 | pub mod delay; 33 | pub mod dport; 34 | pub mod efuse; 35 | #[cfg(feature = "external_ram")] 36 | pub mod external_ram; 37 | pub mod gpio; 38 | pub mod i2c; 39 | #[cfg(feature = "rt")] 40 | pub mod interrupt; 41 | pub mod ledc; 42 | pub mod prelude; 43 | pub mod serial; 44 | pub mod spi; 45 | pub mod timer; 46 | pub mod units; 47 | 48 | #[cfg(feature = "alloc")] 49 | pub mod alloc; 50 | 51 | #[macro_use] 52 | pub mod dprint; 53 | 54 | #[cfg(feature = "mem")] 55 | pub mod mem; 56 | 57 | /// Function initializes ESP32 specific memories (RTC slow and fast) and 58 | /// then calls original Reset function 59 | /// 60 | /// ENTRY point is defined in memory.x 61 | /// *Note: the pre_init function is called in the original reset handler 62 | /// after the initializations done in this function* 63 | #[cfg(feature = "rt")] 64 | #[doc(hidden)] 65 | #[no_mangle] 66 | pub unsafe extern "C" fn ESP32Reset() -> ! { 67 | // These symbols come from `memory.x` 68 | extern "C" { 69 | static mut _rtc_fast_bss_start: u32; 70 | static mut _rtc_fast_bss_end: u32; 71 | 72 | static mut _rtc_slow_bss_start: u32; 73 | static mut _rtc_slow_bss_end: u32; 74 | 75 | static mut _stack_end_cpu0: u32; 76 | } 77 | 78 | // copying data from flash to various data segments is done by the bootloader 79 | // initialization to zero needs to be done by the application 80 | 81 | // Initialize RTC RAM 82 | xtensa_lx_rt::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); 83 | xtensa_lx_rt::zero_bss(&mut _rtc_slow_bss_start, &mut _rtc_slow_bss_end); 84 | 85 | #[cfg(feature = "external_ram")] 86 | external_ram::init(); 87 | 88 | // set stack pointer to end of memory: no need to retain stack up to this point 89 | xtensa_lx::set_stack_pointer(&mut _stack_end_cpu0); 90 | 91 | // continue with default reset handler 92 | xtensa_lx_rt::Reset(); 93 | } 94 | 95 | /// The esp32 has a first stage bootloader that handles loading program data into the right place 96 | /// therefore we skip loading it again. 97 | #[no_mangle] 98 | #[rustfmt::skip] 99 | pub extern "Rust" fn __init_data() -> bool { 100 | false 101 | } 102 | 103 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 104 | pub enum Core { 105 | PRO = 0, 106 | APP = 1, 107 | } 108 | 109 | pub fn get_core() -> Core { 110 | match ((xtensa_lx::get_processor_id() >> 13) & 1) != 0 { 111 | false => Core::PRO, 112 | true => Core::APP, 113 | } 114 | } 115 | 116 | pub fn get_other_core() -> Core { 117 | match get_core() { 118 | Core::PRO => Core::APP, 119 | Core::APP => Core::PRO, 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /examples/i2c.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::panic::PanicInfo; 5 | use embedded_graphics::{ 6 | fonts::{Font8x16, Text}, 7 | pixelcolor::BinaryColor, 8 | prelude::*, 9 | style::TextStyle, 10 | }; 11 | use embedded_hal::blocking::i2c::{Write, WriteRead}; 12 | use esp32_hal::{ 13 | clock_control::{self, sleep, CPUSource, ClockControl}, 14 | delay::Delay, 15 | dport::Split, 16 | dprintln, 17 | i2c::{self, Error, I2C}, 18 | prelude::*, 19 | target::{Peripherals, I2C0}, 20 | timer::Timer, 21 | }; 22 | use mpu6050::Mpu6050; 23 | use ssd1306::{prelude::*, Builder}; 24 | use xtensa_lx::mutex::SpinLockMutex; 25 | 26 | #[entry] 27 | fn main() -> ! { 28 | let dp = Peripherals::take().unwrap(); 29 | 30 | let (mut dport, dport_clock_control) = dp.DPORT.split(); 31 | 32 | // setup clocks & watchdog 33 | let mut clkcntrl = ClockControl::new( 34 | dp.RTCCNTL, 35 | dp.APB_CTRL, 36 | dport_clock_control, 37 | clock_control::XTAL_FREQUENCY_AUTO, 38 | ) 39 | .unwrap(); 40 | 41 | // set desired clock frequencies 42 | clkcntrl 43 | .set_cpu_frequencies( 44 | CPUSource::PLL, 45 | 80.MHz(), 46 | CPUSource::PLL, 47 | 240.MHz(), 48 | CPUSource::PLL, 49 | 80.MHz(), 50 | ) 51 | .unwrap(); 52 | 53 | // disable RTC watchdog 54 | let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 55 | watchdog.disable(); 56 | 57 | // disable MST watchdogs 58 | let (.., mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); 59 | let (.., mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); 60 | watchdog0.disable(); 61 | watchdog1.disable(); 62 | 63 | let pins = dp.GPIO.split(); 64 | let i2c0 = i2c::I2C::new( 65 | dp.I2C0, 66 | i2c::Pins { 67 | sda: pins.gpio4, 68 | scl: pins.gpio15, 69 | }, 70 | 400_000, 71 | &mut dport, 72 | ); 73 | let i2c0 = SpinLockMutex::new(i2c0); 74 | 75 | // Display 76 | let mut display = { 77 | let i2c_wrapper = I2CWrapper::new(&i2c0); 78 | let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c_wrapper).into(); 79 | 80 | let mut rst = pins.gpio16.into_push_pull_output(); 81 | rst.set_low().unwrap(); 82 | sleep(10.ms()); 83 | rst.set_high().unwrap(); 84 | 85 | display.init().unwrap(); 86 | display.clear(); 87 | display.flush().unwrap(); 88 | 89 | display 90 | }; 91 | 92 | // IMU 93 | let mut imu = { 94 | let i2c_wrapper = I2CWrapper::new(&i2c0); 95 | let mut imu = Mpu6050::new(i2c_wrapper); 96 | 97 | let mut delay = Delay::new(); 98 | imu.init(&mut delay).unwrap(); 99 | imu 100 | }; 101 | 102 | Text::new("Hello world!", Point::new(2, 28)) 103 | .into_styled(TextStyle::new(Font8x16, BinaryColor::On)) 104 | .draw(&mut display) 105 | .unwrap(); 106 | display.flush().unwrap(); 107 | 108 | sleep(3.s()); 109 | 110 | loop { 111 | let temp = imu.get_temp().unwrap(); 112 | let gyro = imu.get_gyro().unwrap(); 113 | let acc = imu.get_acc().unwrap(); 114 | dprintln!("temp: {}, gyro: {:?}, acc: {:?}", temp, gyro, acc); 115 | sleep(1.s()); 116 | } 117 | } 118 | 119 | struct I2CWrapper<'a> { 120 | i2c: &'a SpinLockMutex>, 121 | } 122 | 123 | impl<'a> I2CWrapper<'a> { 124 | fn new(i2c: &'a SpinLockMutex>) -> Self { 125 | Self { i2c } 126 | } 127 | } 128 | 129 | impl<'a> Write for I2CWrapper<'a> { 130 | type Error = Error; 131 | 132 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 133 | self.i2c.lock(|x| x.write(addr, bytes)) 134 | } 135 | } 136 | 137 | impl<'a> WriteRead for I2CWrapper<'a> { 138 | type Error = Error; 139 | 140 | fn write_read( 141 | &mut self, 142 | address: u8, 143 | bytes: &[u8], 144 | buffer: &mut [u8], 145 | ) -> Result<(), Self::Error> { 146 | self.i2c.lock(|x| x.write_read(address, bytes, buffer)) 147 | } 148 | } 149 | 150 | #[panic_handler] 151 | fn panic(info: &PanicInfo) -> ! { 152 | dprintln!("----- PANIC -----"); 153 | dprintln!("{:?}", info); 154 | loop {} 155 | } 156 | -------------------------------------------------------------------------------- /examples/gpio.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::{fmt::Write, panic::PanicInfo}; 5 | 6 | use esp32_hal::{ 7 | clock_control::sleep, 8 | dport::Split, 9 | dprintln, 10 | gpio::{Event, Floating, InputPin, Pin, Pull, RTCInputPin}, 11 | interrupt::{Interrupt, InterruptLevel}, 12 | prelude::*, 13 | serial::{config::Config, Serial}, 14 | target, 15 | timer::Timer, 16 | Core, 17 | }; 18 | 19 | static SERIAL: CriticalSectionSpinLockMutex< 20 | Option< 21 | esp32_hal::serial::Serial< 22 | esp32::UART0, 23 | esp32_hal::gpio::Gpio1, 24 | esp32_hal::gpio::Gpio3, 25 | >, 26 | >, 27 | > = CriticalSectionSpinLockMutex::new(None); 28 | 29 | static GPIO: CriticalSectionSpinLockMutex< 30 | Option>>, 31 | > = CriticalSectionSpinLockMutex::new(None); 32 | 33 | #[entry] 34 | fn main() -> ! { 35 | let dp = target::Peripherals::take().unwrap(); 36 | 37 | let (_, dport_clock_control) = dp.DPORT.split(); 38 | 39 | let clkcntrl = esp32_hal::clock_control::ClockControl::new( 40 | dp.RTCCNTL, 41 | dp.APB_CTRL, 42 | dport_clock_control, 43 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 44 | ) 45 | .unwrap(); 46 | 47 | let (clkcntrl_config, mut watchdog_rtc) = clkcntrl.freeze().unwrap(); 48 | let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); 49 | let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); 50 | 51 | watchdog_rtc.disable(); 52 | watchdog0.disable(); 53 | watchdog1.disable(); 54 | 55 | let gpios = dp.GPIO.split(); 56 | 57 | let mut gpio = gpios.gpio26.into_floating_rtc_input(); 58 | gpio.internal_pull_up(true); 59 | gpio.enable_hold(false); 60 | gpio.enable_input(false); 61 | gpio.rtc_enable_input(true); 62 | 63 | gpio.listen_with_options(Event::LowLevel, true, false, true, false, false); 64 | 65 | // setup serial controller 66 | let mut serial: Serial<_, _, _> = Serial::new( 67 | dp.UART0, 68 | esp32_hal::serial::Pins { 69 | tx: gpios.gpio1, 70 | rx: gpios.gpio3, 71 | cts: None, 72 | rts: None, 73 | }, 74 | Config::default().baudrate(115_200.Hz()), 75 | clkcntrl_config, 76 | ) 77 | .unwrap(); 78 | 79 | writeln!(serial, "\n\nESP32 Started\n\n").unwrap(); 80 | 81 | (&SERIAL).lock(|val| *val = Some(serial)); 82 | (&GPIO).lock(|val| *val = Some(gpio)); 83 | 84 | interrupt::enable(Interrupt::GPIO_INTR).unwrap(); 85 | 86 | // Even though the interrupt is called GPIO_NMI is can be routed to any interrupt level. 87 | // Using NMI level (7) is in principle a risk for deadlocks because the 88 | // CriticalSectionSpinLockMutex does not disable the NMI. Therefore using level 5 instead. 89 | 90 | // Because the level 5 interrupt clears the interrupt, the regular level 1 handler 91 | // will not be called. 92 | // Comment out the next line to test the level 1 handler 93 | interrupt::enable_with_priority(Core::PRO, Interrupt::GPIO_NMI, InterruptLevel(5)).unwrap(); 94 | 95 | let mut x = 0; 96 | loop { 97 | x += 1; 98 | (&SERIAL, &GPIO).lock(|serial, gpio| { 99 | let serial = serial.as_mut().unwrap(); 100 | let gpio = gpio.as_mut().unwrap(); 101 | writeln!( 102 | serial, 103 | "Loop: {} {} {} {}", 104 | x, 105 | gpio.is_high().unwrap(), 106 | gpio.is_input_high(), 107 | gpio.rtc_is_input_high() 108 | ) 109 | .unwrap(); 110 | }); 111 | 112 | sleep(500.ms()); 113 | } 114 | } 115 | 116 | fn handle_gpio_interrupt() { 117 | (&GPIO, &SERIAL).lock(|gpio, serial| { 118 | let gpio = gpio.as_mut().unwrap(); 119 | let serial = serial.as_mut().unwrap(); 120 | 121 | if gpio.is_interrupt_set() || gpio.is_non_maskable_interrupt_set() { 122 | writeln!( 123 | serial, 124 | " Interrupt level: {}, pin state: {}", 125 | xtensa_lx::interrupt::get_level(), 126 | gpio.is_high().unwrap() 127 | ) 128 | .unwrap(); 129 | 130 | if gpio.is_high().unwrap() { 131 | gpio.listen_with_options(Event::LowLevel, true, false, true, false, false); 132 | } else { 133 | gpio.listen_with_options(Event::HighLevel, true, false, true, false, false); 134 | }; 135 | // need to change listen before clearing interrupt, otherwise will fire 136 | // immediately again. 137 | gpio.clear_interrupt(); 138 | } 139 | }); 140 | } 141 | 142 | #[interrupt] 143 | fn GPIO_INTR() { 144 | handle_gpio_interrupt(); 145 | } 146 | 147 | #[interrupt] 148 | fn GPIO_NMI() { 149 | handle_gpio_interrupt(); 150 | } 151 | 152 | #[panic_handler] 153 | fn panic(info: &PanicInfo) -> ! { 154 | dprintln!("\n\n*** {:?}", info); 155 | loop {} 156 | } 157 | -------------------------------------------------------------------------------- /examples/rtccntl.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use core::panic::PanicInfo; 6 | 7 | use esp32_hal::prelude::*; 8 | 9 | use esp32_hal::clock_control::{sleep, CPUSource, ClockControl}; 10 | use esp32_hal::dport::Split; 11 | use esp32_hal::dprintln; 12 | use esp32_hal::serial::{config::Config, Serial}; 13 | use esp32_hal::target; 14 | 15 | const BLINK_HZ: Hertz = Hertz(1); 16 | 17 | #[no_mangle] 18 | fn main() -> ! { 19 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 20 | 21 | let mut timg0 = dp.TIMG0; 22 | let mut timg1 = dp.TIMG1; 23 | 24 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 25 | // openocd disables the watchdog timers on halt 26 | // we will do it manually on startup 27 | disable_timg_wdts(&mut timg0, &mut timg1); 28 | 29 | let (_, dport_clock_control) = dp.DPORT.split(); 30 | 31 | // setup clocks & watchdog 32 | let mut clock_control = ClockControl::new( 33 | dp.RTCCNTL, 34 | dp.APB_CTRL, 35 | dport_clock_control, 36 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 37 | ) 38 | .unwrap(); 39 | 40 | // set desired clock frequencies 41 | clock_control 42 | .set_cpu_frequencies( 43 | CPUSource::Xtal, 44 | 10.MHz(), 45 | CPUSource::Xtal, 46 | 240.MHz(), 47 | CPUSource::PLL, 48 | 80.MHz(), 49 | ) 50 | .unwrap(); 51 | 52 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 53 | 54 | watchdog.start(3.s()); 55 | 56 | let gpios = dp.GPIO.split(); 57 | // setup serial controller 58 | let mut uart0: Serial<_, _, _> = Serial::new( 59 | dp.UART0, 60 | esp32_hal::serial::Pins { 61 | tx: gpios.gpio1, 62 | rx: gpios.gpio3, 63 | cts: None, 64 | rts: None, 65 | }, 66 | Config::default(), 67 | clock_control_config, 68 | ) 69 | .unwrap(); 70 | uart0.change_baudrate(115200).unwrap(); 71 | 72 | // print startup message 73 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 74 | 75 | writeln!(uart0, "Running on core {:?}\n", esp32_hal::get_core()).unwrap(); 76 | 77 | writeln!( 78 | uart0, 79 | "UART0 baudrate: {}, using apb clock instead of ref clock: {}\n", 80 | uart0.baudrate(), 81 | uart0.is_clock_apb() 82 | ) 83 | .unwrap(); 84 | 85 | writeln!(uart0, "{:?}\n", clock_control_config).unwrap(); 86 | writeln!(uart0, "{:?}\n", watchdog.config().unwrap()).unwrap(); 87 | 88 | // register callback which is called when the clock is switched 89 | clock_control_config 90 | .add_callback(&|before_source, 91 | before_freq, 92 | before_apb_freq, 93 | after_source, 94 | after_freq, 95 | after_apb_freq| { 96 | dprintln!( 97 | " Before: Source: {:?}, CPU: {}, APB: {}, After: Source: {:?}, CPU: {}, APB: {}", 98 | before_source, 99 | before_freq, 100 | before_apb_freq, 101 | after_source, 102 | after_freq, 103 | after_apb_freq 104 | ); 105 | }) 106 | .unwrap(); 107 | 108 | // uncomment next line to test panic exit 109 | // panic!("panic test"); 110 | 111 | // main loop, which in turn lock and unlocks apb and cpu locks 112 | let mut x: u32 = 0; 113 | let mut prev_ccount = 0; 114 | loop { 115 | for j in 0..2 { 116 | let apb_guard = if j == 1 { 117 | Some(clock_control_config.lock_apb_frequency()) 118 | } else { 119 | None 120 | }; 121 | 122 | for i in 0..2 { 123 | let cpu_guard = if i == 1 { 124 | Some(clock_control_config.lock_cpu_frequency()) 125 | } else { 126 | None 127 | }; 128 | 129 | x = x.wrapping_add(1); 130 | 131 | let ccount = xtensa_lx::timer::get_cycle_count(); 132 | let ccount_diff = ccount.wrapping_sub(prev_ccount); 133 | 134 | writeln!( 135 | uart0, 136 | "Loop: {}, cycles: {}, cycles since previous {}", 137 | x, ccount, ccount_diff 138 | ) 139 | .unwrap(); 140 | 141 | prev_ccount = ccount; 142 | 143 | sleep((Hertz(1_000_000) / BLINK_HZ).us()); 144 | 145 | // comment out next line to check watchdog behavior 146 | watchdog.feed(); 147 | 148 | if cpu_guard.is_some() { 149 | drop(cpu_guard.unwrap()) 150 | } 151 | } 152 | if apb_guard.is_some() { 153 | drop(apb_guard.unwrap()) 154 | } 155 | } 156 | } 157 | } 158 | 159 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 160 | 161 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 162 | timg0 163 | .wdtwprotect 164 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 165 | timg1 166 | .wdtwprotect 167 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 168 | 169 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 170 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 171 | } 172 | 173 | #[panic_handler] 174 | fn panic(info: &PanicInfo) -> ! { 175 | dprintln!("\n\n*** {:?}", info); 176 | loop {} 177 | } 178 | -------------------------------------------------------------------------------- /examples/alloc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | #![feature(raw_vec_internals)] 5 | 6 | use core::fmt::Write; 7 | use core::panic::PanicInfo; 8 | 9 | use esp32_hal::prelude::*; 10 | 11 | use alloc::raw_vec::RawVec; 12 | #[cfg(feature = "external_ram")] 13 | use esp32_hal::alloc::EXTERNAL_ALLOCATOR; 14 | use esp32_hal::alloc::{ 15 | Allocator, AllocatorSize, DEFAULT_ALLOCATOR, DRAM_ALLOCATOR, IRAM_ALLOCATOR, 16 | }; 17 | 18 | use esp32_hal::clock_control::{sleep, ClockControl}; 19 | use esp32_hal::dport::Split; 20 | use esp32_hal::dprintln; 21 | use esp32_hal::serial::{config::Config, Serial}; 22 | use esp32_hal::target; 23 | 24 | #[macro_use] 25 | extern crate alloc; 26 | 27 | #[global_allocator] 28 | pub static GLOBAL_ALLOCATOR: Allocator = DEFAULT_ALLOCATOR; 29 | 30 | // Macro to simplify printing of the various different memory allocations 31 | macro_rules! print_info { 32 | ( $uart:expr, $x:expr ) => { 33 | let mem_type = match &$x as *const _ as usize { 34 | 0x3f80_0000..=0x3fbf_ffff => "External", 35 | 0x3ff8_0000..=0x3fff_ffff => "DRAM", 36 | 0x4007_0000..=0x4009_ffff => "IRAM", 37 | _ => "?", 38 | }; 39 | writeln!( 40 | $uart, 41 | "{:<40}: {:#08x?} {}", 42 | stringify!($x), 43 | &$x as *const _, 44 | mem_type 45 | ) 46 | .unwrap(); 47 | }; 48 | } 49 | 50 | #[entry] 51 | fn main() -> ! { 52 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 53 | 54 | let mut timg0 = dp.TIMG0; 55 | let mut timg1 = dp.TIMG1; 56 | 57 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 58 | // openocd disables the watchdog timers on halt 59 | // we will do it manually on startup 60 | disable_timg_wdts(&mut timg0, &mut timg1); 61 | 62 | let (_, dport_clock_control) = dp.DPORT.split(); 63 | 64 | // setup clocks & watchdog 65 | let clock_control = ClockControl::new( 66 | dp.RTCCNTL, 67 | dp.APB_CTRL, 68 | dport_clock_control, 69 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 70 | ) 71 | .unwrap(); 72 | 73 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 74 | 75 | watchdog.start(15.s()); 76 | 77 | let gpios = dp.GPIO.split(); 78 | 79 | // setup serial controller 80 | let mut uart0: Serial<_, _, _> = Serial::new( 81 | dp.UART0, 82 | esp32_hal::serial::Pins { 83 | tx: gpios.gpio1, 84 | rx: gpios.gpio3, 85 | cts: None, 86 | rts: None, 87 | }, 88 | Config::default(), 89 | clock_control_config, 90 | ) 91 | .unwrap(); 92 | 93 | uart0.change_baudrate(115200).unwrap(); 94 | 95 | // print startup message 96 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 97 | 98 | unsafe { 99 | print_heap_info(&mut uart0); 100 | 101 | let global_initialized_vec_unique = vec![1, 2, 3]; 102 | print_info!(uart0, global_initialized_vec_unique[0]); 103 | 104 | let global_initialized_vec_same = vec![0x23u8; 12]; 105 | print_info!(uart0, global_initialized_vec_same[0]); 106 | 107 | let dram_rawvec: RawVec = RawVec::with_capacity_in(50, DRAM_ALLOCATOR); 108 | print_info!(uart0, *dram_rawvec.ptr()); 109 | 110 | let iram_rawvec: RawVec = RawVec::with_capacity_in(50, IRAM_ALLOCATOR); 111 | print_info!(uart0, *iram_rawvec.ptr()); 112 | 113 | #[cfg(feature = "external_ram")] 114 | { 115 | writeln!( 116 | uart0, 117 | "\nExternal RAM size: {}\n", 118 | esp32_hal::external_ram::get_size() 119 | ) 120 | .unwrap(); 121 | 122 | let global_initialized_vec_large = vec![0u8; 1024 * 1024]; 123 | print_info!(uart0, global_initialized_vec_large[0]); 124 | 125 | let external_ram_rawvec: RawVec = 126 | RawVec::with_capacity_in(50, EXTERNAL_ALLOCATOR); 127 | print_info!(uart0, *external_ram_rawvec.ptr()); 128 | 129 | print_heap_info(&mut uart0); 130 | } 131 | #[cfg(not(feature = "external_ram"))] 132 | { 133 | print_heap_info(&mut uart0); 134 | } 135 | } 136 | 137 | loop { 138 | sleep(1.s()); 139 | writeln!(uart0, "Alive and waiting for watchdog reset").unwrap(); 140 | } 141 | } 142 | 143 | fn print_single_heap_info(output: &mut dyn core::fmt::Write, allocator: &Allocator, text: &str) { 144 | writeln!( 145 | output, 146 | "{:>15}: free {:>8.3}KB, used {:>8.3}KB out of {:>8.3}KB", 147 | text, 148 | allocator.free() as f32 / 1024.0, 149 | allocator.used() as f32 / 1024.0, 150 | allocator.size() as f32 / 1024.0 151 | ) 152 | .unwrap(); 153 | } 154 | 155 | fn print_heap_info(output: &mut dyn core::fmt::Write) { 156 | writeln!(output).unwrap(); 157 | print_single_heap_info(output, &GLOBAL_ALLOCATOR, "Global"); 158 | print_single_heap_info(output, &DRAM_ALLOCATOR, "DRAM"); 159 | print_single_heap_info(output, &IRAM_ALLOCATOR, "IRAM"); 160 | #[cfg(feature = "external_ram")] 161 | print_single_heap_info(output, &EXTERNAL_ALLOCATOR, "External RAM"); 162 | writeln!(output).unwrap(); 163 | } 164 | 165 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 166 | 167 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 168 | timg0 169 | .wdtwprotect 170 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 171 | timg1 172 | .wdtwprotect 173 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 174 | 175 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 176 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 177 | } 178 | 179 | #[panic_handler] 180 | fn panic(info: &PanicInfo) -> ! { 181 | dprintln!("\n\n*** {:?}", info); 182 | loop {} 183 | } 184 | 185 | #[alloc_error_handler] 186 | fn alloc_error_handler(layout: core::alloc::Layout) -> ! { 187 | panic!( 188 | "Error allocating {} bytes of memory with alignment {}", 189 | layout.size(), 190 | layout.align() 191 | ); 192 | } 193 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | /* This memory map assumes the flash cache is on; 2 | the blocks used are excluded from the various memory ranges 3 | 4 | see: https://github.com/espressif/esp-idf/blob/master/components/soc/src/esp32/soc_memory_layout.c 5 | for details 6 | */ 7 | 8 | /* override entry point */ 9 | ENTRY(ESP32Reset) 10 | 11 | /* reserved at the start of DRAM for e.g. teh BT stack */ 12 | RESERVE_DRAM = 0; 13 | 14 | /* reserved at the start of the RTC memories for use by the ULP processor */ 15 | RESERVE_RTC_FAST = 0; 16 | RESERVE_RTC_SLOW = 0; 17 | 18 | /* define stack size for both cores */ 19 | STACK_SIZE = 8k; 20 | 21 | /* Specify main memory areas */ 22 | MEMORY 23 | { 24 | reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/ 25 | vectors_seg ( RX ) : ORIGIN = 0x40080000, len = 1k /* SRAM0 */ 26 | iram_seg ( RX ) : ORIGIN = 0x40080400, len = 128k-0x400 /* SRAM0 */ 27 | 28 | reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */ 29 | dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */ 30 | reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* SRAM1; reserved for static ROM usage; can be used for heap */ 31 | 32 | /* external flash 33 | The 0x20 offset is a convenience for the app binary image generation. 34 | Flash cache has 64KB pages. The .bin file which is flashed to the chip 35 | has a 0x18 byte file header, and each segment has a 0x08 byte segment 36 | header. Setting this offset makes it simple to meet the flash cache MMU's 37 | constraint that (paddr % 64KB == vaddr % 64KB).) 38 | */ 39 | irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20 40 | drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20 41 | 42 | 43 | /* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */ 44 | rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k 45 | 46 | /* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */ 47 | rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST 48 | 49 | /* RTC slow memory (data accessible). Persists over deep sleep. */ 50 | rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW 51 | 52 | /* external memory, including data and text, 53 | 4MB is the maximum, if external psram is bigger, paging is required */ 54 | psram_seg(RWX) : ORIGIN = 0x3F800000, len = 4M 55 | } 56 | 57 | /* map generic regions to output sections */ 58 | INCLUDE "alias.x" 59 | 60 | /* esp32 specific regions */ 61 | SECTIONS { 62 | .rtc_fast.text : { 63 | . = ALIGN(4); 64 | *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) 65 | } > rtc_fast_iram_seg AT > RODATA 66 | 67 | /* 68 | This section is required to skip rtc.text area because rtc_iram_seg and 69 | rtc_data_seg are reflect the same address space on different buses. 70 | */ 71 | .rtc_fast.dummy (NOLOAD) : 72 | { 73 | _rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */ 74 | . = SIZEOF(.rtc_fast.text); 75 | _rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */ 76 | } > rtc_fast_dram_seg 77 | 78 | 79 | .rtc_fast.data : 80 | { 81 | _rtc_fast_data_start = ABSOLUTE(.); 82 | . = ALIGN(4); 83 | *(.rtc_fast.data .rtc_fast.data.*) 84 | _rtc_fast_data_end = ABSOLUTE(.); 85 | } > rtc_fast_dram_seg AT > RODATA 86 | 87 | .rtc_fast.bss (NOLOAD) : 88 | { 89 | _rtc_fast_bss_start = ABSOLUTE(.); 90 | . = ALIGN(4); 91 | *(.rtc_fast.bss .rtc_fast.bss.*) 92 | _rtc_fast_bss_end = ABSOLUTE(.); 93 | } > rtc_fast_dram_seg 94 | 95 | .rtc_fast.noinit (NOLOAD) : 96 | { 97 | . = ALIGN(4); 98 | *(.rtc_fast.noinit .rtc_fast.noinit.*) 99 | } > rtc_fast_dram_seg 100 | 101 | 102 | .rtc_slow.text : { 103 | . = ALIGN(4); 104 | *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) 105 | } > rtc_slow_seg AT > RODATA 106 | 107 | .rtc_slow.data : 108 | { 109 | _rtc_slow_data_start = ABSOLUTE(.); 110 | . = ALIGN(4); 111 | *(.rtc_slow.data .rtc_slow.data.*) 112 | _rtc_slow_data_end = ABSOLUTE(.); 113 | } > rtc_slow_seg AT > RODATA 114 | 115 | .rtc_slow.bss (NOLOAD) : 116 | { 117 | _rtc_slow_bss_start = ABSOLUTE(.); 118 | . = ALIGN(4); 119 | *(.rtc_slow.bss .rtc_slow.bss.*) 120 | _rtc_slow_bss_end = ABSOLUTE(.); 121 | } > rtc_slow_seg 122 | 123 | .rtc_slow.noinit (NOLOAD) : 124 | { 125 | . = ALIGN(4); 126 | *(.rtc_slow.noinit .rtc_slow.noinit.*) 127 | } > rtc_slow_seg 128 | 129 | .external.data : 130 | { 131 | _external_data_start = ABSOLUTE(.); 132 | . = ALIGN(4); 133 | *(.external.data .external.data.*) 134 | _external_data_end = ABSOLUTE(.); 135 | } > psram_seg AT > RODATA 136 | 137 | .external.bss (NOLOAD) : 138 | { 139 | _external_bss_start = ABSOLUTE(.); 140 | . = ALIGN(4); 141 | *(.external.bss .external.bss.*) 142 | _external_bss_end = ABSOLUTE(.); 143 | } > psram_seg 144 | 145 | .external.noinit (NOLOAD) : 146 | { 147 | . = ALIGN(4); 148 | *(.external.noinit .external.noinit.*) 149 | } > psram_seg 150 | 151 | /* must be last segment using psram_seg */ 152 | .external_heap_start (NOLOAD) : 153 | { 154 | . = ALIGN (4); 155 | _external_heap_start = ABSOLUTE(.); 156 | } > psram_seg 157 | 158 | 159 | /* wifi data */ 160 | 161 | .rwtext.wifi : 162 | { 163 | . = ALIGN(4); 164 | *( .wifi0iram .wifi0iram.*) 165 | *( .wifirxiram .wifirxiram.*) 166 | *( .iram1 .iram1.*) 167 | } > RWTEXT AT > RODATA 168 | 169 | .data.wifi : 170 | { 171 | . = ALIGN(4); 172 | *( .dram1 .dram1.*) 173 | } > RWDATA AT > RODATA 174 | } 175 | 176 | _external_ram_start = ABSOLUTE(ORIGIN(psram_seg)); 177 | _external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); 178 | 179 | _heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE; 180 | _text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg)); 181 | _external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); 182 | 183 | _stack_start_cpu1 = _heap_end; 184 | _stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE; 185 | _stack_start_cpu0 = _stack_end_cpu1; 186 | _stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE; 187 | 188 | EXTERN(DefaultHandler); 189 | 190 | EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */ 191 | 192 | INCLUDE "device.x" 193 | 194 | -------------------------------------------------------------------------------- /src/clock_control/cpu.rs: -------------------------------------------------------------------------------- 1 | //! Control Cores 2 | //! 3 | 4 | use super::Error; 5 | use crate::target; 6 | use crate::Core::{self, APP, PRO}; 7 | use xtensa_lx::set_stack_pointer; 8 | 9 | static mut START_CORE1_FUNCTION: Option !> = None; 10 | 11 | impl super::ClockControl { 12 | pub unsafe fn park_core(&mut self, core: Core) { 13 | match core { 14 | PRO => { 15 | self.rtc_control 16 | .sw_cpu_stall 17 | .modify(|_, w| w.sw_stall_procpu_c1().bits(0x21)); 18 | self.rtc_control 19 | .options0 20 | .modify(|_, w| w.sw_stall_procpu_c0().bits(0x02)); 21 | } 22 | APP => { 23 | self.rtc_control 24 | .sw_cpu_stall 25 | .modify(|_, w| w.sw_stall_appcpu_c1().bits(0x21)); 26 | self.rtc_control 27 | .options0 28 | .modify(|_, w| w.sw_stall_appcpu_c0().bits(0x02)); 29 | } 30 | } 31 | } 32 | 33 | pub fn unpark_core(&mut self, core: Core) { 34 | match core { 35 | PRO => { 36 | self.rtc_control 37 | .sw_cpu_stall 38 | .modify(|_, w| unsafe { w.sw_stall_procpu_c1().bits(0) }); 39 | self.rtc_control 40 | .options0 41 | .modify(|_, w| unsafe { w.sw_stall_procpu_c0().bits(0) }); 42 | } 43 | APP => { 44 | self.rtc_control 45 | .sw_cpu_stall 46 | .modify(|_, w| unsafe { w.sw_stall_appcpu_c1().bits(0) }); 47 | self.rtc_control 48 | .options0 49 | .modify(|_, w| unsafe { w.sw_stall_appcpu_c0().bits(0) }); 50 | } 51 | } 52 | } 53 | 54 | fn flush_cache(&mut self, core: Core) { 55 | match core { 56 | PRO => { 57 | self.dport_control 58 | .pro_cache_ctrl() 59 | .modify(|_, w| w.pro_cache_flush_ena().clear_bit()); 60 | self.dport_control 61 | .pro_cache_ctrl() 62 | .modify(|_, w| w.pro_cache_flush_ena().set_bit()); 63 | while self 64 | .dport_control 65 | .pro_cache_ctrl() 66 | .read() 67 | .pro_cache_flush_done() 68 | .bit_is_clear() 69 | {} 70 | self.dport_control 71 | .pro_cache_ctrl() 72 | .modify(|_, w| w.pro_cache_flush_ena().clear_bit()); 73 | } 74 | APP => { 75 | self.dport_control 76 | .app_cache_ctrl() 77 | .modify(|_, w| w.app_cache_flush_ena().clear_bit()); 78 | self.dport_control 79 | .app_cache_ctrl() 80 | .modify(|_, w| w.app_cache_flush_ena().set_bit()); 81 | while self 82 | .dport_control 83 | .app_cache_ctrl() 84 | .read() 85 | .app_cache_flush_done() 86 | .bit_is_clear() 87 | {} 88 | self.dport_control 89 | .app_cache_ctrl() 90 | .modify(|_, w| w.app_cache_flush_ena().clear_bit()); 91 | } 92 | }; 93 | } 94 | 95 | fn enable_cache(&mut self, core: Core) { 96 | // get timer group 0 registers, do it this way instead of 97 | // having to pass in yet another peripheral for this clock control 98 | let spi0 = unsafe { &(*target::SPI0::ptr()) }; 99 | 100 | match core { 101 | PRO => { 102 | spi0.cache_fctrl.modify(|_, w| w.cache_req_en().set_bit()); 103 | self.dport_control 104 | .pro_cache_ctrl() 105 | .modify(|_, w| w.pro_cache_enable().set_bit()); 106 | } 107 | APP => { 108 | spi0.cache_fctrl.modify(|_, w| w.cache_req_en().set_bit()); 109 | self.dport_control 110 | .app_cache_ctrl() 111 | .modify(|_, w| w.app_cache_enable().set_bit()); 112 | } 113 | }; 114 | } 115 | 116 | unsafe fn start_core1_init() -> ! { 117 | extern "C" { 118 | static mut _stack_end_cpu1: u32; 119 | } 120 | 121 | // disables interrupts 122 | xtensa_lx::interrupt::set_mask(0); 123 | 124 | // reset cycle compare registers 125 | xtensa_lx::timer::set_ccompare0(0); 126 | xtensa_lx::timer::set_ccompare1(0); 127 | xtensa_lx::timer::set_ccompare2(0); 128 | 129 | // set stack pointer to end of memory: no need to retain stack up to this point 130 | set_stack_pointer(&mut _stack_end_cpu1); 131 | 132 | START_CORE1_FUNCTION.unwrap()(); 133 | } 134 | 135 | /// Start the APP (second) core 136 | /// 137 | /// The second core will start running with the function `entry`. 138 | pub fn start_app_core(&mut self, entry: fn() -> !) -> Result<(), Error> { 139 | if !xtensa_lx::is_debugger_attached() 140 | && self 141 | .dport_control 142 | .appcpu_ctrl_b() 143 | .read() 144 | .appcpu_clkgate_en() 145 | .bit_is_set() 146 | { 147 | return Err(Error::CoreAlreadyRunning); 148 | } 149 | 150 | self.flush_cache(Core::APP); 151 | self.enable_cache(Core::APP); 152 | 153 | unsafe { 154 | START_CORE1_FUNCTION = Some(entry); 155 | } 156 | 157 | self.dport_control.appcpu_ctrl_d().write(|w| unsafe { 158 | w.appcpu_boot_addr() 159 | .bits(Self::start_core1_init as *const u32 as u32) 160 | }); 161 | 162 | self.dport_control 163 | .appcpu_ctrl_b() 164 | .modify(|_, w| w.appcpu_clkgate_en().set_bit()); 165 | self.dport_control 166 | .appcpu_ctrl_c() 167 | .modify(|_, w| w.appcpu_runstall().clear_bit()); 168 | self.dport_control 169 | .appcpu_ctrl_a() 170 | .modify(|_, w| w.appcpu_resetting().set_bit()); 171 | self.dport_control 172 | .appcpu_ctrl_a() 173 | .modify(|_, w| w.appcpu_resetting().clear_bit()); 174 | 175 | self.unpark_core(Core::APP); 176 | 177 | Ok(()) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /examples/mem.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | #![feature(raw_vec_internals)] 5 | 6 | use core::fmt::Write; 7 | use core::panic::PanicInfo; 8 | 9 | use esp32_hal::prelude::*; 10 | 11 | use esp32_hal::alloc::{Allocator, DRAM_ALLOCATOR}; 12 | 13 | use esp32_hal::clock_control::{sleep, CPUSource::PLL, ClockControl, ClockControlConfig}; 14 | use esp32_hal::dport::Split; 15 | use esp32_hal::dprintln; 16 | use esp32_hal::mem::{memcmp, memcpy, memcpy_reverse, memset}; 17 | use esp32_hal::serial::{config::Config, Serial}; 18 | use esp32_hal::target; 19 | 20 | use xtensa_lx::timer::get_cycle_count; 21 | 22 | #[macro_use] 23 | extern crate alloc; 24 | 25 | #[global_allocator] 26 | pub static GLOBAL_ALLOCATOR: Allocator = DRAM_ALLOCATOR; 27 | 28 | #[entry] 29 | fn main() -> ! { 30 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 31 | 32 | let mut timg0 = dp.TIMG0; 33 | let mut timg1 = dp.TIMG1; 34 | 35 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 36 | // openocd disables the watchdog timers on halt 37 | // we will do it manually on startup 38 | disable_timg_wdts(&mut timg0, &mut timg1); 39 | 40 | let (_, dport_clock_control) = dp.DPORT.split(); 41 | 42 | // setup clocks & watchdog 43 | let mut clock_control = ClockControl::new( 44 | dp.RTCCNTL, 45 | dp.APB_CTRL, 46 | dport_clock_control, 47 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 48 | ) 49 | .unwrap(); 50 | 51 | // set desired clock frequencies 52 | clock_control 53 | .set_cpu_frequencies(PLL, 240.MHz(), PLL, 240.MHz(), PLL, 240.MHz()) 54 | .unwrap(); 55 | 56 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 57 | 58 | watchdog.start(20.s()); 59 | 60 | let gpios = dp.GPIO.split(); 61 | 62 | // setup serial controller 63 | let mut uart0: Serial<_, _, _> = Serial::new( 64 | dp.UART0, 65 | esp32_hal::serial::Pins { 66 | tx: gpios.gpio1, 67 | rx: gpios.gpio3, 68 | cts: None, 69 | rts: None, 70 | }, 71 | Config::default(), 72 | clock_control_config, 73 | ) 74 | .unwrap(); 75 | 76 | uart0.change_baudrate(115200).unwrap(); 77 | 78 | // print startup message 79 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 80 | 81 | const BUF_LEN: usize = 1024 * 128; 82 | 83 | writeln!(uart0, "Initializing").unwrap(); 84 | 85 | let mut dst = vec![0u8; BUF_LEN]; 86 | let mut src = vec![0u8; BUF_LEN]; 87 | 88 | let start = get_cycle_count(); 89 | for i in 0..src.len() { 90 | src[i] = i as u8; 91 | } 92 | let end = get_cycle_count(); 93 | 94 | let inittime = end.wrapping_sub(start) as f32 / ClockControlConfig {}.cpu_frequency().0 as f32; 95 | 96 | writeln!( 97 | uart0, 98 | "{:>40}: {:.3}s, {:.3}KB/s", 99 | format!("initialized src: {}", src.len()), 100 | inittime, 101 | src.len() as f32 / inittime / 1024.0, 102 | ) 103 | .unwrap(); 104 | 105 | time( 106 | &mut uart0, 107 | "memset aligned, sized 4 bytes", 108 | BUF_LEN, 109 | &|| unsafe { 110 | memset(&(dst[0]) as *const _ as *mut _, 0, BUF_LEN); 111 | }, 112 | ); 113 | 114 | time(&mut uart0, "memset aligned", BUF_LEN, &|| unsafe { 115 | memset(&(dst[0]) as *const _ as *mut _, 0, BUF_LEN - 1); 116 | }); 117 | 118 | time(&mut uart0, "memset", BUF_LEN, &|| unsafe { 119 | memset(&(dst[1]) as *const _ as *mut _, 0, BUF_LEN - 1); 120 | }); 121 | 122 | let tx = &mut uart0; 123 | unsafe { 124 | for f in &[memcpy, memcpy_reverse] { 125 | time_memcpy(tx, &mut (dst[0]), &mut (src[0]), BUF_LEN, *f); 126 | time_memcpy(tx, &mut (dst[0]), &mut (src[0]), BUF_LEN - 1, *f); 127 | time_memcpy(tx, &mut (dst[1]), &mut (src[1]), BUF_LEN - 1, *f); 128 | time_memcpy(tx, &mut (dst[1]), &mut (src[0]), BUF_LEN - 1, *f); 129 | time_memcpy(tx, &mut (dst[0]), &mut (src[1]), BUF_LEN - 1, *f); 130 | } 131 | } 132 | 133 | loop { 134 | sleep(1.s()); 135 | writeln!(uart0, "Alive and waiting for watchdog reset").unwrap(); 136 | } 137 | } 138 | 139 | const REPEAT: usize = 20; 140 | 141 | fn time(output: &mut dyn core::fmt::Write, text: &str, bytes: usize, f: &dyn Fn() -> ()) { 142 | let start = get_cycle_count(); 143 | for _ in 0..REPEAT { 144 | f(); 145 | } 146 | let end = get_cycle_count(); 147 | 148 | let time = (end - start) as f32 / ClockControlConfig {}.cpu_frequency().0 as f32; 149 | writeln!( 150 | output, 151 | "{:>40}: {:.3}s, {:.3}KB/s", 152 | text, 153 | time, 154 | (bytes * REPEAT) as f32 / time / 1024.0 155 | ) 156 | .unwrap(); 157 | } 158 | 159 | unsafe fn time_memcpy( 160 | output: &mut dyn core::fmt::Write, 161 | dst: &mut u8, 162 | src: &mut u8, 163 | len: usize, 164 | f: unsafe extern "C" fn(dst: *mut u8, src: *const u8, n: usize) -> *mut u8, 165 | ) { 166 | let start = get_cycle_count(); 167 | for _ in 0..REPEAT { 168 | f(dst as *const _ as *mut _, src as *const _ as *mut _, len); 169 | } 170 | let end = get_cycle_count(); 171 | 172 | let time = end.wrapping_sub(start) as f32 / ClockControlConfig {}.cpu_frequency().0 as f32; 173 | 174 | let cmp_res = memcmp(dst as *const _ as *mut _, src as *const _ as *mut _, len); 175 | 176 | writeln!( 177 | output, 178 | "{:>40}: {:.3}s, {:.3}KB/s, Result: {}", 179 | format!( 180 | "memcpy: {} {} {}", 181 | (dst as *const _ as usize) % core::mem::size_of::(), 182 | (src as *const _ as usize) % core::mem::size_of::(), 183 | len 184 | ), 185 | time, 186 | (len * REPEAT) as f32 / time / 1024.0, 187 | cmp_res 188 | ) 189 | .unwrap(); 190 | 191 | memset(dst as *const _ as *mut _, 0, len); 192 | } 193 | 194 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 195 | 196 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 197 | timg0 198 | .wdtwprotect 199 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 200 | timg1 201 | .wdtwprotect 202 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 203 | 204 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 205 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 206 | } 207 | 208 | #[panic_handler] 209 | fn panic(info: &PanicInfo) -> ! { 210 | dprintln!("\n\n*** {:?}", info); 211 | loop {} 212 | } 213 | 214 | #[alloc_error_handler] 215 | fn alloc_error_handler(layout: core::alloc::Layout) -> ! { 216 | panic!( 217 | "Error allocating {} bytes of memory with alignment {}", 218 | layout.size(), 219 | layout.align() 220 | ); 221 | } 222 | -------------------------------------------------------------------------------- /examples/multicore.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use core::panic::PanicInfo; 6 | 7 | use esp32_hal::prelude::*; 8 | 9 | use esp32_hal::clock_control::{CPUSource, ClockControl, ClockControlConfig}; 10 | use esp32_hal::dport::Split; 11 | use esp32_hal::dprintln; 12 | use esp32_hal::serial::{config::Config, Serial}; 13 | use esp32_hal::target; 14 | 15 | use xtensa_lx::{get_stack_pointer, timer::get_cycle_count}; 16 | 17 | const BLINK_HZ: Hertz = Hertz(1); 18 | 19 | static GLOBAL_COUNT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); 20 | static TX: CriticalSectionSpinLockMutex>> = 21 | CriticalSectionSpinLockMutex::new(None); 22 | 23 | #[no_mangle] 24 | fn main() -> ! { 25 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 26 | 27 | let mut timg0 = dp.TIMG0; 28 | let mut timg1 = dp.TIMG1; 29 | 30 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 31 | // openocd disables the watchdog timers on halt 32 | // we will do it manually on startup 33 | disable_timg_wdts(&mut timg0, &mut timg1); 34 | 35 | let (_, dport_clock_control) = dp.DPORT.split(); 36 | 37 | // setup clocks & watchdog 38 | let mut clock_control = ClockControl::new( 39 | dp.RTCCNTL, 40 | dp.APB_CTRL, 41 | dport_clock_control, 42 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 43 | ) 44 | .unwrap(); 45 | 46 | // set desired clock frequencies 47 | clock_control 48 | .set_cpu_frequencies( 49 | CPUSource::Xtal, 50 | 10.MHz(), 51 | CPUSource::PLL, 52 | 240.MHz(), 53 | CPUSource::PLL, 54 | 80.MHz(), 55 | ) 56 | .unwrap(); 57 | 58 | let (mut clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 59 | 60 | watchdog.start(3.s()); 61 | 62 | let gpios = dp.GPIO.split(); 63 | // setup serial controller 64 | let mut uart0: Serial<_, _, _> = Serial::new( 65 | dp.UART0, 66 | esp32_hal::serial::Pins { 67 | tx: gpios.gpio1, 68 | rx: gpios.gpio3, 69 | cts: None, 70 | rts: None, 71 | }, 72 | Config::default(), 73 | clock_control_config, 74 | ) 75 | .unwrap(); 76 | 77 | uart0.change_baudrate(115200).unwrap(); 78 | 79 | // print startup message 80 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 81 | writeln!( 82 | uart0, 83 | "UART0 baudrate: {}, using apb clock instead of ref clock: {}\n", 84 | uart0.baudrate(), 85 | uart0.is_clock_apb() 86 | ) 87 | .unwrap(); 88 | 89 | writeln!(uart0, "Stack Pointer Core 0: {:08x?}", get_stack_pointer()).unwrap(); 90 | 91 | // uncomment next line to test panic exit 92 | // panic!("panic test"); 93 | 94 | let (tx, _) = uart0.split(); 95 | (&TX).lock(|locked_tx| *locked_tx = Some(tx)); 96 | 97 | let _lock = clock_control_config.lock_cpu_frequency(); 98 | 99 | // start core 1 (APP_CPU) 100 | clock_control_config.start_app_core(cpu1_start).unwrap(); 101 | 102 | // main loop, which in turn lock and unlocks apb and cpu locks 103 | let mut x: u32 = 0; 104 | let mut prev_ccount = 0; 105 | loop { 106 | for j in 0..2 { 107 | let apb_guard = if j == 1 { 108 | Some(clock_control_config.lock_apb_frequency()) 109 | } else { 110 | None 111 | }; 112 | 113 | for i in 0..2 { 114 | let cpu_guard = if i == 1 { 115 | Some(clock_control_config.lock_cpu_frequency()) 116 | } else { 117 | None 118 | }; 119 | 120 | x = x.wrapping_add(1); 121 | 122 | let cycles = clock_control_config.cpu_frequency() / BLINK_HZ; 123 | let start = get_cycle_count(); 124 | let mut loop_count: u32 = 0; 125 | while get_cycle_count().wrapping_sub(start) < cycles { 126 | loop_count += 1; 127 | } 128 | 129 | print_info(x, loop_count, &mut prev_ccount); 130 | 131 | // comment out next line to check watchdog behavior 132 | watchdog.feed(); 133 | 134 | if cpu_guard.is_some() { 135 | drop(cpu_guard.unwrap()) 136 | } 137 | } 138 | if apb_guard.is_some() { 139 | drop(apb_guard.unwrap()) 140 | } 141 | } 142 | } 143 | } 144 | 145 | fn cpu1_start() -> ! { 146 | let mut x: u32 = 0; 147 | let mut prev_ccount = 0; 148 | 149 | (&TX).lock(|tx| { 150 | writeln!( 151 | tx.as_mut().unwrap(), 152 | "Stack Pointer Core 1: {:08x?}", 153 | get_stack_pointer() 154 | ) 155 | .unwrap() 156 | }); 157 | 158 | loop { 159 | let cycles = ClockControlConfig {}.cpu_frequency() / BLINK_HZ; 160 | let start = get_cycle_count(); 161 | let mut loop_count = 0; 162 | while get_cycle_count().wrapping_sub(start) < cycles { 163 | loop_count += 1; 164 | } 165 | 166 | print_info(x, loop_count, &mut prev_ccount); 167 | x = x.wrapping_add(1); 168 | } 169 | } 170 | 171 | fn print_info(loop_count: u32, spin_loop_count: u32, prev_ccount: &mut u32) { 172 | let ccount = get_cycle_count(); 173 | let ccount_diff = ccount.wrapping_sub(*prev_ccount); 174 | 175 | let total = GLOBAL_COUNT.fetch_add(ccount_diff, core::sync::atomic::Ordering::Relaxed); 176 | 177 | (&TX).lock(|tx| { 178 | 179 | writeln!(tx.as_mut().unwrap(), 180 | "Core: {:?}, Loop: {}, Spin loops:{}, cycles: {}, cycles since previous {}, Total cycles: {}", 181 | esp32_hal::get_core(), 182 | loop_count, 183 | spin_loop_count, 184 | ccount, 185 | ccount_diff, 186 | total 187 | ) 188 | .unwrap(); 189 | }); 190 | 191 | *prev_ccount = ccount; 192 | } 193 | 194 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 195 | 196 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 197 | timg0 198 | .wdtwprotect 199 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 200 | timg1 201 | .wdtwprotect 202 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 203 | 204 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 205 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 206 | } 207 | 208 | #[panic_handler] 209 | fn panic(info: &PanicInfo) -> ! { 210 | // park the other core 211 | unsafe { ClockControlConfig {}.park_core(esp32_hal::get_other_core()) }; 212 | 213 | // print panic message 214 | dprintln!("\n\n*** {:?}", info); 215 | 216 | // park this core 217 | unsafe { ClockControlConfig {}.park_core(esp32_hal::get_core()) }; 218 | 219 | dprintln!("Not reached because core is parked."); 220 | 221 | // this statement will not be reached, but is needed to make this a diverging function 222 | loop {} 223 | } 224 | -------------------------------------------------------------------------------- /examples/spi.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the SPI interface using the ESP32 WROVER DEVKIT 2 | //! 3 | //! This examples writes to the display. As this pushes many pixels it is quite slow in debug mode, 4 | //! so please run it in release mode to get an impression of the obtainable speed. 5 | 6 | #![no_std] 7 | #![no_main] 8 | 9 | use core::{fmt::Write, panic::PanicInfo}; 10 | 11 | use esp32_hal::{ 12 | clock_control::{sleep, ClockControl, XTAL_FREQUENCY_AUTO}, 13 | dport::Split, 14 | dprintln, 15 | gpio::{InputPin, OutputPin}, 16 | prelude::*, 17 | serial::{self, Serial}, 18 | spi::{self, SPI}, 19 | target, 20 | timer::Timer, 21 | }; 22 | 23 | use embedded_hal::blocking::spi::WriteIter; 24 | 25 | use ili9341; 26 | 27 | use embedded_graphics::{ 28 | fonts::{Font12x16, Text}, 29 | pixelcolor::Rgb565, 30 | prelude::*, 31 | primitives::{Circle, Rectangle}, 32 | style::{PrimitiveStyleBuilder, TextStyle}, 33 | }; 34 | 35 | // Interface for ili9341 driver 36 | // ili9341 uses separate command/data pin, this interface set this pin to the appropriate state 37 | struct SPIInterface< 38 | CMD: embedded_hal::digital::v2::OutputPin, 39 | SCLK: OutputPin, 40 | SDO: OutputPin, 41 | SDI: InputPin + OutputPin, 42 | CS: OutputPin, 43 | > { 44 | spi: SPI, 45 | cmd: CMD, 46 | } 47 | 48 | impl< 49 | CMD: embedded_hal::digital::v2::OutputPin, 50 | SCLK: OutputPin, 51 | SDO: OutputPin, 52 | SDI: InputPin + OutputPin, 53 | CS: OutputPin, 54 | > ili9341::Interface for SPIInterface 55 | { 56 | type Error = esp32_hal::spi::Error; 57 | 58 | fn write(&mut self, command: u8, data: &[u8]) -> Result<(), Self::Error> { 59 | self.cmd 60 | .set_low() 61 | .map_err(|_| esp32_hal::spi::Error::PinError)?; 62 | self.spi.write(&[command])?; 63 | self.cmd 64 | .set_high() 65 | .map_err(|_| esp32_hal::spi::Error::PinError)?; 66 | self.spi.write(data)?; 67 | Ok(()) 68 | } 69 | 70 | fn write_iter( 71 | &mut self, 72 | command: u8, 73 | data: impl IntoIterator, 74 | ) -> Result<(), Self::Error> { 75 | self.cmd 76 | .set_low() 77 | .map_err(|_| esp32_hal::spi::Error::PinError)?; 78 | self.spi.write(&[command])?; 79 | self.cmd 80 | .set_high() 81 | .map_err(|_| esp32_hal::spi::Error::PinError)?; 82 | self.spi.write_iter(data)?; 83 | Ok(()) 84 | } 85 | } 86 | 87 | #[entry] 88 | fn main() -> ! { 89 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 90 | 91 | let (_, dport_clock_control) = dp.DPORT.split(); 92 | 93 | let clkcntrl = ClockControl::new( 94 | dp.RTCCNTL, 95 | dp.APB_CTRL, 96 | dport_clock_control, 97 | XTAL_FREQUENCY_AUTO, 98 | ) 99 | .unwrap(); 100 | 101 | let (clkcntrl_config, mut watchdog) = clkcntrl.freeze().unwrap(); 102 | let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); 103 | let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); 104 | 105 | watchdog.disable(); 106 | watchdog0.disable(); 107 | watchdog1.disable(); 108 | 109 | let _lock = clkcntrl_config.lock_cpu_frequency(); 110 | 111 | let pins = dp.GPIO.split(); 112 | 113 | let mut serial: Serial<_, _, _> = Serial::new( 114 | dp.UART0, 115 | serial::Pins { 116 | tx: pins.gpio1, 117 | rx: pins.gpio3, 118 | cts: None, 119 | rts: None, 120 | }, 121 | serial::config::Config { 122 | baudrate: 115200.Hz(), 123 | ..serial::config::Config::default() 124 | }, 125 | clkcntrl_config, 126 | ) 127 | .unwrap(); 128 | 129 | // Official ili9341 spec is 10MHz, but overdrive up to 80MHz actually works. 130 | // 26MHz chosen here: will be 26MHz when using 26MHz crystal, 20MHz when using 40MHz crystal, 131 | // due to integer clock division. 132 | // Faster is no use as the cpu is not keeping up with the embedded_graphics library. 133 | let spi: SPI<_, _, _, _, _> = SPI::::new( 134 | dp.SPI2, 135 | spi::Pins { 136 | sclk: pins.gpio19, 137 | sdo: pins.gpio23, 138 | sdi: Some(pins.gpio25), 139 | cs: Some(pins.gpio22), 140 | }, 141 | spi::config::Config { 142 | baudrate: 26.MHz().into(), 143 | bit_order: spi::config::BitOrder::MSBFirst, 144 | data_mode: spi::config::MODE_0, 145 | }, 146 | clkcntrl_config, 147 | ) 148 | .unwrap(); 149 | 150 | let mut gpio_backlight = pins.gpio5.into_push_pull_output(); 151 | let mut gpio_reset = pins.gpio18.into_push_pull_output(); 152 | let gpio_cmd = pins.gpio21.into_push_pull_output(); 153 | 154 | gpio_reset.set_low().unwrap(); 155 | sleep(100.ms()); 156 | gpio_reset.set_high().unwrap(); 157 | sleep(100.ms()); 158 | 159 | gpio_backlight.set_low().unwrap(); 160 | 161 | let spi_if = SPIInterface { spi, cmd: gpio_cmd }; 162 | 163 | let mut display = 164 | ili9341::Ili9341::new(spi_if, gpio_reset, &mut esp32_hal::delay::Delay::new()).unwrap(); 165 | 166 | display 167 | .set_orientation(ili9341::Orientation::Landscape) 168 | .unwrap(); 169 | 170 | Rectangle::new(Point::new(0, 0), Point::new(320, 240)) 171 | .into_styled( 172 | PrimitiveStyleBuilder::new() 173 | .fill_color(Rgb565::WHITE) 174 | .stroke_width(4) 175 | .stroke_color(Rgb565::BLUE) 176 | .build(), 177 | ) 178 | .draw(&mut display) 179 | .unwrap(); 180 | 181 | let rect = Rectangle::new(Point::new(10, 80), Point::new(30, 100)).into_styled( 182 | PrimitiveStyleBuilder::new() 183 | .fill_color(Rgb565::RED) 184 | .stroke_width(1) 185 | .stroke_color(Rgb565::WHITE) 186 | .build(), 187 | ); 188 | 189 | let circle = Circle::new(Point::new(20, 50), 10).into_styled( 190 | PrimitiveStyleBuilder::new() 191 | .fill_color(Rgb565::GREEN) 192 | .stroke_width(1) 193 | .stroke_color(Rgb565::WHITE) 194 | .build(), 195 | ); 196 | 197 | Text::new("Hello Rust!", Point::new(20, 16)) 198 | .into_styled(TextStyle::new(Font12x16, Rgb565::RED)) 199 | .draw(&mut display) 200 | .unwrap(); 201 | 202 | writeln!(serial, "\n\nESP32 Started\n\n").unwrap(); 203 | 204 | loop { 205 | for x in (0..280).chain((0..280).rev()) { 206 | rect.translate(Point::new(x, 0)).draw(&mut display).unwrap(); 207 | } 208 | 209 | for x in (0..280).chain((0..280).rev()) { 210 | circle 211 | .translate(Point::new(x, 0)) 212 | .draw(&mut display) 213 | .unwrap(); 214 | } 215 | } 216 | } 217 | 218 | #[panic_handler] 219 | fn panic(info: &PanicInfo) -> ! { 220 | dprintln!("\n\n*** {:?}", info); 221 | loop {} 222 | } 223 | -------------------------------------------------------------------------------- /examples/ram.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use core::panic::PanicInfo; 6 | 7 | use esp32_hal::prelude::*; 8 | 9 | use esp32_hal::clock_control::{sleep, ClockControl}; 10 | use esp32_hal::dport::Split; 11 | use esp32_hal::dprintln; 12 | use esp32_hal::serial::{config::Config, Serial}; 13 | use esp32_hal::target; 14 | 15 | use xtensa_lx::get_program_counter; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | let dp = target::Peripherals::take().expect("Failed to obtain Peripherals"); 20 | 21 | let mut timg0 = dp.TIMG0; 22 | let mut timg1 = dp.TIMG1; 23 | 24 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 25 | // openocd disables the watchdog timers on halt 26 | // we will do it manually on startup 27 | disable_timg_wdts(&mut timg0, &mut timg1); 28 | 29 | let (_, dport_clock_control) = dp.DPORT.split(); 30 | 31 | // setup clocks & watchdog 32 | let clock_control = ClockControl::new( 33 | dp.RTCCNTL, 34 | dp.APB_CTRL, 35 | dport_clock_control, 36 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 37 | ) 38 | .unwrap(); 39 | 40 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 41 | 42 | watchdog.start(15.s()); 43 | 44 | let gpios = dp.GPIO.split(); 45 | // setup serial controller 46 | let mut uart0: Serial<_, _, _> = Serial::new( 47 | dp.UART0, 48 | esp32_hal::serial::Pins { 49 | tx: gpios.gpio1, 50 | rx: gpios.gpio3, 51 | cts: None, 52 | rts: None, 53 | }, 54 | Config::default(), 55 | clock_control_config, 56 | ) 57 | .unwrap(); 58 | 59 | uart0.change_baudrate(115200).unwrap(); 60 | 61 | // print startup message 62 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 63 | 64 | writeln!(uart0, "Running on core {:?}\n", esp32_hal::get_core()).unwrap(); 65 | 66 | ram_tests(&mut uart0); 67 | 68 | loop { 69 | sleep(1.s()); 70 | writeln!(uart0, "Alive and waiting for watchdog reset").unwrap(); 71 | } 72 | } 73 | 74 | fn attr_none_fn(uart: &mut dyn core::fmt::Write) { 75 | writeln!( 76 | uart, 77 | "{:<40}: {:08x?}", 78 | "attr_none_fn", 79 | get_program_counter() 80 | ) 81 | .unwrap(); 82 | } 83 | 84 | #[ram] 85 | fn attr_ram_fn(uart: &mut dyn core::fmt::Write) { 86 | writeln!( 87 | uart, 88 | "{:<40}: {:08x?}", 89 | "attr_ram_fn", 90 | get_program_counter() 91 | ) 92 | .unwrap(); 93 | } 94 | 95 | #[ram(rtc_slow)] 96 | fn attr_ram_fn_rtc_slow(uart: &mut dyn core::fmt::Write) { 97 | writeln!( 98 | uart, 99 | "{:<40}: {:08x?}", 100 | "attr_ram_fn_rtc_slow", 101 | get_program_counter() 102 | ) 103 | .unwrap(); 104 | } 105 | 106 | #[ram(rtc_fast)] 107 | fn attr_ram_fn_rtc_fast(uart: &mut dyn core::fmt::Write) { 108 | writeln!( 109 | uart, 110 | "{:<40}: {:08x?}", 111 | "attr_ram_fn_rtc_fast", 112 | get_program_counter() 113 | ) 114 | .unwrap(); 115 | } 116 | 117 | static ATTR_NONE_STATIC: [u8; 16] = *b"ATTR_NONE_STATIC"; 118 | 119 | static mut ATTR_NONE_STATIC_MUT: [u8; 20] = *b"ATTR_NONE_STATIC_MUT"; 120 | 121 | static ATTR_NONE_STATIC_BSS: [u8; 32] = [0; 32]; 122 | 123 | static mut ATTR_NONE_STATIC_MUT_BSS: [u8; 32] = [0; 32]; 124 | 125 | #[ram] 126 | static ATTR_RAM_STATIC: [u8; 15] = *b"ATTR_RAM_STATIC"; 127 | 128 | #[ram(zeroed)] 129 | static ATTR_RAM_STATIC_BSS: [u8; 32] = [0; 32]; 130 | 131 | #[ram(uninitialized)] 132 | static ATTR_RAM_STATIC_UNINIT: [u8; 32] = [0; 32]; 133 | 134 | #[ram(rtc_slow)] 135 | static ATTR_RAM_STATIC_RTC_SLOW: [u8; 24] = *b"ATTR_RAM_STATIC_RTC_SLOW"; 136 | 137 | #[ram(rtc_slow, zeroed)] 138 | static ATTR_RAM_STATIC_RTC_SLOW_BSS: [u8; 32] = [0; 32]; 139 | 140 | #[ram(rtc_slow, uninitialized)] 141 | static ATTR_RAM_STATIC_RTC_SLOW_UNINIT: [u8; 32] = [0; 32]; 142 | 143 | #[ram(rtc_fast)] 144 | static ATTR_RAM_STATIC_RTC_FAST: [u8; 24] = *b"ATTR_RAM_STATIC_RTC_FAST"; 145 | 146 | #[ram(rtc_fast, zeroed)] 147 | static ATTR_RAM_STATIC_RTC_FAST_BSS: [u8; 32] = [0; 32]; 148 | 149 | #[ram(rtc_fast, uninitialized)] 150 | static ATTR_RAM_STATIC_RTC_FAST_UNINIT: [u8; 32] = [0; 32]; 151 | 152 | #[cfg(feature = "external_ram")] 153 | #[ram(external)] 154 | static mut ATTR_RAM_STATIC_EXTERNAL: [u8; 24] = *b"ATTR_RAM_STATIC_EXTERNAL"; 155 | 156 | #[cfg(feature = "external_ram")] 157 | #[ram(external, zeroed)] 158 | static mut ATTR_RAM_STATIC_EXTERNAL_BSS: [u8; 32] = [0; 32]; 159 | 160 | #[cfg(feature = "external_ram")] 161 | #[ram(external, uninitialized)] 162 | static mut ATTR_RAM_STATIC_EXTERNAL_UNINIT: [u8; 32] = [0; 32]; 163 | 164 | // Macro to simplify printing of the various different memory allocations 165 | macro_rules! print_info { 166 | ( $uart:expr, $x:expr ) => { 167 | writeln!( 168 | $uart, 169 | "{:<40}: {:#08x?}: {:02x?}", 170 | stringify!($x), 171 | &$x as *const u8 as usize, 172 | $x 173 | ) 174 | .unwrap(); 175 | }; 176 | } 177 | 178 | fn ram_tests(uart: &mut dyn core::fmt::Write) { 179 | writeln!(uart).unwrap(); 180 | 181 | attr_none_fn(uart); 182 | attr_ram_fn(uart); 183 | attr_ram_fn_rtc_slow(uart); 184 | attr_ram_fn_rtc_fast(uart); 185 | 186 | writeln!(uart).unwrap(); 187 | 188 | unsafe { 189 | print_info!(uart, ATTR_NONE_STATIC); 190 | print_info!(uart, ATTR_NONE_STATIC_MUT); 191 | print_info!(uart, ATTR_NONE_STATIC_BSS); 192 | print_info!(uart, ATTR_NONE_STATIC_MUT_BSS); 193 | 194 | print_info!(uart, ATTR_RAM_STATIC); 195 | print_info!(uart, ATTR_RAM_STATIC_BSS); 196 | print_info!(uart, ATTR_RAM_STATIC_UNINIT); 197 | 198 | print_info!(uart, ATTR_RAM_STATIC_RTC_SLOW); 199 | print_info!(uart, ATTR_RAM_STATIC_RTC_SLOW_BSS); 200 | print_info!(uart, ATTR_RAM_STATIC_RTC_SLOW_UNINIT); 201 | 202 | print_info!(uart, ATTR_RAM_STATIC_RTC_FAST); 203 | print_info!(uart, ATTR_RAM_STATIC_RTC_FAST_BSS); 204 | print_info!(uart, ATTR_RAM_STATIC_RTC_FAST_UNINIT); 205 | } 206 | 207 | if cfg!(feature = "external_ram") { 208 | external_ram(uart); 209 | } 210 | 211 | writeln!(uart).unwrap(); 212 | } 213 | 214 | #[cfg(not(feature = "external_ram"))] 215 | fn external_ram(_uart: &mut dyn core::fmt::Write) {} 216 | 217 | #[cfg(feature = "external_ram")] 218 | fn external_ram(uart: &mut dyn core::fmt::Write) { 219 | unsafe { 220 | print_info!(uart, ATTR_RAM_STATIC_EXTERNAL); 221 | print_info!(uart, ATTR_RAM_STATIC_EXTERNAL_BSS); 222 | print_info!(uart, ATTR_RAM_STATIC_EXTERNAL_UNINIT); 223 | } 224 | } 225 | 226 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 227 | 228 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 229 | timg0 230 | .wdtwprotect 231 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 232 | timg1 233 | .wdtwprotect 234 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 235 | 236 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 237 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 238 | } 239 | 240 | #[panic_handler] 241 | fn panic(info: &PanicInfo) -> ! { 242 | dprintln!("\n\n*** {:?}", info); 243 | loop {} 244 | } 245 | -------------------------------------------------------------------------------- /src/clock_control/pll.rs: -------------------------------------------------------------------------------- 1 | //! PLL control 2 | //! 3 | 4 | use super::Error; 5 | use crate::prelude::*; 6 | 7 | // Delays (in microseconds) for changing pll settings 8 | // TODO according to esp-idf: some of these are excessive, and should be reduced. 9 | const DELAY_PLL_ENABLE_WITH_150K: MicroSeconds = MicroSeconds(80); 10 | const DELAY_PLL_ENABLE_WITH_32K: MicroSeconds = MicroSeconds(160); 11 | 12 | // Addresses for internal I2C bus for PLL 13 | const I2C_BLOCK: u8 = 0x66; 14 | 15 | // Register addresses for internal I2C bus 16 | mod i2c { 17 | pub const IR_CAL_DELAY: u8 = 0; 18 | pub const IR_CAL_EXT_CAP: u8 = 1; 19 | pub const OC_LREF: u8 = 2; 20 | pub const OC_DIV_7_0: u8 = 3; 21 | pub const OC_ENB_FCAL: u8 = 4; 22 | pub const OC_DCUR: u8 = 5; 23 | pub const BBADC_DSMP: u8 = 9; 24 | pub const OC_ENB_VCON: u8 = 10; 25 | pub const ENDIV5: u8 = 11; 26 | pub const BBADC_CAL_7_0: u8 = 12; 27 | } 28 | 29 | // Values for internal I2C registers 30 | mod val { 31 | pub const ENDIV5_VAL_320M: u8 = 0x43; 32 | pub const BBADC_DSMP_VAL_320M: u8 = 0x84; 33 | pub const ENDIV5_VAL_480M: u8 = 0xc3; 34 | pub const BBADC_DSMP_VAL_480M: u8 = 0x74; 35 | pub const IR_CAL_DELAY_VAL: u8 = 0x18; 36 | pub const IR_CAL_EXT_CAP_VAL: u8 = 0x20; 37 | pub const OC_ENB_FCAL_VAL: u8 = 0x9a; 38 | pub const OC_ENB_VCON_VAL: u8 = 0x00; 39 | pub const BBADC_CAL_7_0_VAL: u8 = 0x00; 40 | } 41 | 42 | // COnfiguration values depending on Xtal frequency for internal I2C registers 43 | // div_ref, div7_0, div10_8, lref,dcur,bw 44 | struct Config(u8, u8, u8, u8, u8, u8); 45 | 46 | impl Config { 47 | const PLL_320M_XTAL_40M: Config = Config(0, 32, 0, 0, 6, 3); 48 | const PLL_320M_XTAL_26M: Config = Config(12, 224, 4, 1, 0, 1); 49 | const PLL_320M_XTAL_24M: Config = Config(11, 224, 4, 1, 0, 1); 50 | 51 | const PLL_480M_XTAL_40M: Config = Config(0, 28, 0, 0, 6, 3); 52 | const PLL_480M_XTAL_26M: Config = Config(12, 144, 4, 1, 0, 1); 53 | const PLL_480M_XTAL_24M: Config = Config(11, 144, 4, 1, 0, 1); 54 | 55 | fn get_lref(&self) -> u8 { 56 | (self.3 << 7) | (self.2 << 4) | self.0 57 | } 58 | fn get_div7_0(&self) -> u8 { 59 | self.1 60 | } 61 | fn get_dcur(&self) -> u8 { 62 | (self.5 << 6) | self.4 63 | } 64 | } 65 | 66 | impl super::ClockControl { 67 | /// write to internal I2C PLL bus 68 | fn write_i2c(&mut self, address: u8, data: u8) { 69 | self.rtc_control.pll.write(|w| unsafe { 70 | w.block() 71 | .bits(I2C_BLOCK) 72 | .addr() 73 | .bits(address) 74 | .data() 75 | .bits(data) 76 | .write() 77 | .set_bit() 78 | }); 79 | 80 | while self.rtc_control.pll.read().busy().bit_is_set() {} 81 | } 82 | 83 | /// read from internal I2C PLL bus 84 | fn _read_i2c(&mut self, address: u8) -> u8 { 85 | self.rtc_control.pll.write(|w| unsafe { 86 | w.block() 87 | .bits(I2C_BLOCK) 88 | .addr() 89 | .bits(address) 90 | .write() 91 | .clear_bit() 92 | }); 93 | 94 | while self.rtc_control.pll.read().busy().bit_is_set() {} 95 | 96 | self.rtc_control.pll.read().data().bits() 97 | } 98 | 99 | /// disable the PLL 100 | pub(crate) fn pll_disable(&mut self) { 101 | self.rtc_control.options0.modify(|_, w| { 102 | w.bias_i2c_force_pd() 103 | // is APLL under force power down? then also power down the internal I2C bus 104 | .bit(self.rtc_control.ana_conf.read().plla_force_pd().bit()) 105 | .bb_i2c_force_pd() 106 | .set_bit() 107 | .bbpll_force_pd() 108 | .set_bit() 109 | .bbpll_i2c_force_pd() 110 | .set_bit() 111 | }); 112 | self.pll_frequency = super::FREQ_OFF; 113 | self.pll_d2_frequency = super::FREQ_OFF; 114 | } 115 | 116 | /// enable the PLL 117 | pub(crate) fn pll_enable(&mut self, high: bool) -> Result<(), Error> { 118 | self.rtc_control.options0.modify(|_, w| { 119 | w.bias_i2c_force_pd() 120 | .clear_bit() 121 | .bb_i2c_force_pd() 122 | .clear_bit() 123 | .bbpll_force_pd() 124 | .clear_bit() 125 | .bbpll_i2c_force_pd() 126 | .clear_bit() 127 | }); 128 | 129 | // reset BBPLL configuration 130 | self.write_i2c(i2c::IR_CAL_DELAY, val::IR_CAL_DELAY_VAL); 131 | self.write_i2c(i2c::IR_CAL_EXT_CAP, val::IR_CAL_EXT_CAP_VAL); 132 | self.write_i2c(i2c::OC_ENB_FCAL, val::OC_ENB_FCAL_VAL); 133 | self.write_i2c(i2c::OC_ENB_VCON, val::OC_ENB_VCON_VAL); 134 | self.write_i2c(i2c::BBADC_CAL_7_0, val::BBADC_CAL_7_0_VAL); 135 | 136 | self.set_pll_frequency(high) 137 | } 138 | 139 | /// change PLL frequency between low (320MHz) and high (480MHz) 140 | pub(crate) fn set_pll_frequency(&mut self, high: bool) -> Result<(), Error> { 141 | let pll_config = match high { 142 | false => { 143 | self.pll_frequency = super::PLL_FREQ_320M; 144 | self.pll_d2_frequency = self.pll_frequency / 2; 145 | 146 | self.write_i2c(i2c::ENDIV5, val::ENDIV5_VAL_320M); 147 | self.write_i2c(i2c::BBADC_DSMP, val::BBADC_DSMP_VAL_320M); 148 | 149 | match self.xtal_frequency { 150 | Hertz(40_000_000) => Config::PLL_320M_XTAL_40M, 151 | Hertz(26_000_000) => Config::PLL_320M_XTAL_26M, 152 | Hertz(24_000_000) => Config::PLL_320M_XTAL_24M, 153 | _ => return Err(Error::UnsupportedPLLConfig), 154 | } 155 | } 156 | true => { 157 | self.pll_frequency = super::PLL_FREQ_480M; 158 | self.pll_d2_frequency = self.pll_frequency / 2; 159 | 160 | self.write_i2c(i2c::ENDIV5, val::ENDIV5_VAL_480M); 161 | self.write_i2c(i2c::BBADC_DSMP, val::BBADC_DSMP_VAL_480M); 162 | 163 | match self.xtal_frequency { 164 | Hertz(40_000_000) => Config::PLL_480M_XTAL_40M, 165 | Hertz(26_000_000) => Config::PLL_480M_XTAL_26M, 166 | Hertz(24_000_000) => Config::PLL_480M_XTAL_24M, 167 | _ => return Err(Error::UnsupportedPLLConfig), 168 | } 169 | } 170 | }; 171 | 172 | self.write_i2c(i2c::OC_LREF, pll_config.get_lref()); 173 | self.write_i2c(i2c::OC_DIV_7_0, pll_config.get_div7_0()); 174 | self.write_i2c(i2c::OC_DCUR, pll_config.get_dcur()); 175 | 176 | let delay_us = if let Ok(super::SlowRTCSource::RTC150k) = self.slow_rtc_source() { 177 | DELAY_PLL_ENABLE_WITH_150K 178 | } else { 179 | DELAY_PLL_ENABLE_WITH_32K 180 | }; 181 | 182 | self.delay(delay_us); 183 | Ok(()) 184 | } 185 | 186 | /// Get PLL frequency 187 | pub fn pll_frequency(&self) -> Hertz { 188 | if self.rtc_control.options0.read().bbpll_force_pd().bit() { 189 | return super::FREQ_OFF; 190 | } 191 | 192 | match self 193 | .dport_control 194 | .cpu_per_conf() 195 | .read() 196 | .cpuperiod_sel() 197 | .variant() 198 | { 199 | Some(super::CPUPERIOD_SEL_A::SEL_80) => super::PLL_FREQ_320M, 200 | Some(super::CPUPERIOD_SEL_A::SEL_160) => super::PLL_FREQ_320M, 201 | Some(super::CPUPERIOD_SEL_A::SEL_240) => super::PLL_FREQ_480M, 202 | _ => super::FREQ_OFF, 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/efuse.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use crate::prelude::*; 4 | use crate::target::EFUSE; 5 | 6 | pub struct Efuse; 7 | 8 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 9 | pub enum ChipType { 10 | ESP32_D0WDQ6, 11 | ESP32_D0WDQ5, 12 | ESP32_D2WDQ5, 13 | ESP32_PICOD2, 14 | ESP32_PICOD4, 15 | Unknown, 16 | } 17 | 18 | impl Efuse { 19 | /// Reads chip's MAC address from the eFuse storage. 20 | /// 21 | /// # Example 22 | /// 23 | /// ``` 24 | /// let mac_address = Efuse::get_mac_address(); 25 | /// writeln!(serial_tx, "MAC: {:#X}:{:#X}:{:#X}:{:#X}:{:#X}:{:#X}", 26 | /// mac_address[0], mac_address[1], mac_address[2], 27 | /// mac_address[3], mac_address[4], mac_address[5]); 28 | /// ``` 29 | pub fn get_mac_address() -> [u8; 6] { 30 | let efuse = unsafe { &*EFUSE::ptr() }; 31 | 32 | let mac_low: u32 = efuse.blk0_rdata1.read().rd_wifi_mac_crc_low().bits(); 33 | let mac_high: u32 = efuse.blk0_rdata2.read().rd_wifi_mac_crc_high().bits(); 34 | 35 | let mac_low_bytes = mac_low.to_be_bytes(); 36 | let mac_high_bytes = mac_high.to_be_bytes(); 37 | 38 | [ 39 | mac_high_bytes[2], 40 | mac_high_bytes[3], 41 | mac_low_bytes[0], 42 | mac_low_bytes[1], 43 | mac_low_bytes[2], 44 | mac_low_bytes[3], 45 | ] 46 | } 47 | 48 | /// Returns the number of CPUs available on the chip. 49 | /// 50 | /// While ESP32 chips usually come with two mostly equivalent CPUs (protocol CPU and 51 | /// application CPU), the application CPU is unavailable on some. 52 | /// 53 | pub fn get_core_count() -> u32 { 54 | let efuse = unsafe { &*EFUSE::ptr() }; 55 | 56 | let cpu_disabled = efuse.blk0_rdata3.read().rd_chip_ver_dis_app_cpu().bit(); 57 | if cpu_disabled { 58 | 1 59 | } else { 60 | 2 61 | } 62 | } 63 | 64 | /// Returns the maximum rated clock of the CPU in MHz. 65 | /// 66 | /// Note that the actual clock may be lower, depending on the current power 67 | /// configuration of the chip, clock source, and other settings. 68 | /// 69 | pub fn get_max_cpu_fequency() -> Hertz { 70 | let efuse = unsafe { &*EFUSE::ptr() }; 71 | 72 | let has_rating = efuse.blk0_rdata3.read().rd_chip_cpu_freq_rated().bit(); 73 | let has_low_rating = efuse.blk0_rdata3.read().rd_chip_cpu_freq_low().bit(); 74 | 75 | if has_rating && has_low_rating { 76 | Hertz(160_000_000) 77 | } else { 78 | Hertz(240_000_000) 79 | } 80 | } 81 | 82 | pub fn is_bluetooth_enabled() -> bool { 83 | let efuse = unsafe { &*EFUSE::ptr() }; 84 | 85 | !efuse.blk0_rdata3.read().rd_chip_ver_dis_bt().bit() 86 | } 87 | 88 | pub fn get_chip_type() -> ChipType { 89 | let efuse = unsafe { &*EFUSE::ptr() }; 90 | 91 | match efuse.blk0_rdata3.read().rd_chip_ver_pkg().bits() { 92 | 0 => ChipType::ESP32_D0WDQ6, 93 | 1 => ChipType::ESP32_D0WDQ5, 94 | 2 => ChipType::ESP32_D2WDQ5, 95 | 4 => ChipType::ESP32_PICOD2, 96 | 5 => ChipType::ESP32_PICOD4, 97 | _ => ChipType::Unknown, 98 | } 99 | } 100 | 101 | /// Returns the reference voltage for the SAR ADCs in mV on this chip. 102 | /// 103 | /// If the value is not available in the eFuse, `None` is returned. 104 | /// 105 | pub fn get_adc_vref() -> Option { 106 | let efuse = unsafe { &*EFUSE::ptr() }; 107 | 108 | // The base voltage of this calibration value is 1.1V 109 | let base_voltage: i32 = 1100; 110 | // The calibration is given as offset in 7mV steps 111 | let step: i32 = 7; 112 | 113 | let calibration = efuse.blk0_rdata4.read().rd_adc_vref().bits(); 114 | if calibration == 0 { 115 | return None; 116 | } 117 | 118 | // The calibration in the register is given as 5 bits: 119 | // 120 | // NOTE: on some older chips this value was given as two's complement 121 | let (sign, offset) = ((calibration >> 4) as i32, (calibration & 0x0F) as i32); 122 | 123 | if sign == 0 { 124 | Some(base_voltage + (offset * step)) 125 | } else { 126 | Some(base_voltage - (offset * step)) 127 | } 128 | } 129 | 130 | /// Returns the two point calibration for the ADC1. 131 | /// 132 | /// The returned tuple is in a form of `(low_value, high_value)`: 133 | /// - the `low_value` represents ADC1 reading at 150mV on this chip 134 | /// - the `high_value` represents ADC1 reading at 850mv on this chip 135 | /// 136 | /// If the values are not available in the eFuse, function returns `None`. 137 | /// 138 | pub fn get_adc1_two_point_cal() -> Option<(i32, i32)> { 139 | let efuse = unsafe { &*EFUSE::ptr() }; 140 | 141 | // Low point represents ADC's raw reading at 150mV. 142 | // It is given as a 7-bit number, in two's complement, 143 | // which represents number of steps of 4 (no units, as this 144 | // is raw ADC value). The offset is applied to predefined 145 | // base (e.g. 278). 146 | 147 | // Similar is true for high point, except that it's ADC's 148 | // reading at 850mV, and it's a 9-bit number. 149 | 150 | let adc1_low_base: i32 = 278; 151 | let adc1_high_base: i32 = 3265; 152 | let adc1_step: i32 = 4; 153 | let adc2_low_bit_size = 7; 154 | let adc2_high_bit_size = 9; 155 | 156 | let adc1_low = efuse.blk3_rdata3.read().rd_adc1_tp_low().bits() as u16; 157 | let adc1_high = efuse.blk3_rdata3.read().rd_adc1_tp_high().bits() as u16; 158 | 159 | if adc1_low == 0 || adc1_high == 0 { 160 | None 161 | } else { 162 | Some(( 163 | adc1_low_base 164 | + Efuse::from_twos_complement(adc1_low, adc2_low_bit_size) * adc1_step, 165 | adc1_high_base 166 | + Efuse::from_twos_complement(adc1_high, adc2_high_bit_size) * adc1_step, 167 | )) 168 | } 169 | } 170 | 171 | /// Returns the two point calibration for the ADC2. 172 | /// 173 | /// The returned tuple is in a form of `(low_value, high_value)`: 174 | /// - the `low_value` represents ADC2 reading at 150mV on this chip 175 | /// - the `high_value` represents ADC2 reading at 850mv on this chip 176 | /// 177 | /// If the values are not available in the eFuse, function returns `None`. 178 | /// 179 | pub fn get_adc2_two_point_cal() -> Option<(i32, i32)> { 180 | let efuse = unsafe { &*EFUSE::ptr() }; 181 | 182 | let adc2_low_base: i32 = 421; 183 | let adc2_high_base: i32 = 3406; 184 | let adc2_step: i32 = 4; 185 | let adc2_low_bit_size = 7; 186 | let adc2_high_bit_size = 9; 187 | 188 | let adc2_low = efuse.blk3_rdata3.read().rd_adc2_tp_low().bits() as u16; 189 | let adc2_high = efuse.blk3_rdata3.read().rd_adc2_tp_high().bits() as u16; 190 | 191 | if adc2_low == 0 || adc2_high == 0 { 192 | None 193 | } else { 194 | Some(( 195 | adc2_low_base 196 | + Efuse::from_twos_complement(adc2_low, adc2_low_bit_size) * adc2_step, 197 | adc2_high_base 198 | + Efuse::from_twos_complement(adc2_high, adc2_high_bit_size) * adc2_step, 199 | )) 200 | } 201 | } 202 | 203 | fn from_twos_complement(value: u16, bits: u8) -> i32 { 204 | let mask = 2_u16.pow(bits as u32 - 1) - 1; 205 | let complement_value = (value & mask) as i32; 206 | 207 | let sign_bit = (value >> bits - 1) & 0x01; 208 | if sign_bit == 0 { 209 | complement_value 210 | } else { 211 | complement_value - 2_i32.pow(bits as u32 - 1) 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/clock_control/config.rs: -------------------------------------------------------------------------------- 1 | use super::Error; 2 | use crate::prelude::*; 3 | use core::fmt; 4 | 5 | use super::{ 6 | dfs, CPUSource, ClockControlConfig, FastRTCSource, SlowRTCSource, CLOCK_CONTROL, 7 | CLOCK_CONTROL_MUTEX, 8 | }; 9 | 10 | impl<'a> super::ClockControlConfig { 11 | // All the single word reads of frequencies and sources are thread and interrupt safe 12 | // as these are atomic. 13 | 14 | /// The current CPU frequency 15 | pub fn cpu_frequency(&self) -> Hertz { 16 | unsafe { CLOCK_CONTROL.as_ref().unwrap().cpu_frequency } 17 | } 18 | 19 | /// The current APB frequency 20 | pub fn apb_frequency(&self) -> Hertz { 21 | unsafe { CLOCK_CONTROL.as_ref().unwrap().apb_frequency } 22 | } 23 | 24 | /// The CPU frequency in the default state 25 | pub fn cpu_frequency_default(&self) -> Hertz { 26 | unsafe { CLOCK_CONTROL.as_ref().unwrap().cpu_frequency_default } 27 | } 28 | 29 | /// The CPU frequency in the CPU lock state 30 | pub fn cpu_frequency_locked(&self) -> Hertz { 31 | unsafe { CLOCK_CONTROL.as_ref().unwrap().cpu_frequency_locked } 32 | } 33 | 34 | /// The CPU frequency in the APB lock state 35 | pub fn cpu_frequency_apb_locked(&self) -> Hertz { 36 | unsafe { CLOCK_CONTROL.as_ref().unwrap().cpu_frequency_apb_locked } 37 | } 38 | 39 | /// The APB frequency in the APB lock state 40 | pub fn apb_frequency_apb_locked(&self) -> Hertz { 41 | unsafe { CLOCK_CONTROL.as_ref().unwrap().apb_frequency_apb_locked } 42 | } 43 | 44 | /// Is the reference clock 1MHz under all clock conditions 45 | pub fn is_ref_clock_stable(&self) -> bool { 46 | unsafe { CLOCK_CONTROL.as_ref().unwrap().ref_clock_stable } 47 | } 48 | 49 | /// The current reference frequency 50 | pub fn ref_frequency(&self) -> Hertz { 51 | unsafe { CLOCK_CONTROL.as_ref().unwrap().ref_frequency } 52 | } 53 | 54 | /// The current slow RTC frequency 55 | pub fn slow_rtc_frequency(&self) -> Hertz { 56 | unsafe { CLOCK_CONTROL.as_ref().unwrap().slow_rtc_frequency } 57 | } 58 | 59 | /// The current fast RTC frequency 60 | pub fn fast_rtc_frequency(&self) -> Hertz { 61 | unsafe { CLOCK_CONTROL.as_ref().unwrap().fast_rtc_frequency } 62 | } 63 | 64 | /// The current APLL frequency 65 | pub fn apll_frequency(&self) -> Hertz { 66 | unsafe { CLOCK_CONTROL.as_ref().unwrap().apll_frequency } 67 | } 68 | 69 | /// The current PLL/2 frequency 70 | pub fn pll_d2_frequency(&self) -> Hertz { 71 | unsafe { CLOCK_CONTROL.as_ref().unwrap().pll_d2_frequency } 72 | } 73 | 74 | /// The Xtal frequency 75 | pub fn xtal_frequency(&self) -> Hertz { 76 | unsafe { CLOCK_CONTROL.as_ref().unwrap().xtal_frequency } 77 | } 78 | 79 | /// The 32kHz Xtal frequency 80 | pub fn xtal32k_frequency(&self) -> Hertz { 81 | unsafe { CLOCK_CONTROL.as_ref().unwrap().xtal32k_frequency } 82 | } 83 | 84 | /// The current PLL frequency 85 | pub fn pll_frequency(&self) -> Hertz { 86 | unsafe { CLOCK_CONTROL.as_ref().unwrap().pll_frequency } 87 | } 88 | 89 | /// The current 8MHz oscillator frequency 90 | pub fn rtc8m_frequency(&self) -> Hertz { 91 | unsafe { CLOCK_CONTROL.as_ref().unwrap().rtc8m_frequency } 92 | } 93 | 94 | /// The current 8MHz oscillator frequency / 256 95 | pub fn rtc8md256_frequency(&self) -> Hertz { 96 | unsafe { CLOCK_CONTROL.as_ref().unwrap().rtc8md256_frequency } 97 | } 98 | 99 | /// The current 150kHz oscillator frequency 100 | pub fn rtc_frequency(&self) -> Hertz { 101 | unsafe { CLOCK_CONTROL.as_ref().unwrap().rtc_frequency } 102 | } 103 | 104 | /// The current source for the CPU and APB frequencies 105 | pub fn cpu_source(&self) -> CPUSource { 106 | unsafe { CLOCK_CONTROL.as_ref().unwrap().cpu_source } 107 | } 108 | 109 | /// The current source for the slow RTC frequency 110 | pub fn slow_rtc_source(&self) -> SlowRTCSource { 111 | unsafe { CLOCK_CONTROL.as_ref().unwrap().slow_rtc_source } 112 | } 113 | 114 | /// The current source for the fast RTC frequency 115 | pub fn fast_rtc_source(&self) -> FastRTCSource { 116 | unsafe { CLOCK_CONTROL.as_ref().unwrap().fast_rtc_source } 117 | } 118 | 119 | // The lock and unlock calls are thread and interrupt safe because this is handled inside 120 | // the DFS routines 121 | 122 | /// Obtain a RAII lock to use the high CPU frequency 123 | pub fn lock_cpu_frequency(&self) -> dfs::LockCPU { 124 | unsafe { CLOCK_CONTROL.as_mut().unwrap().lock_cpu_frequency() } 125 | } 126 | 127 | /// Obtain a RAII lock to use the APB CPU frequency 128 | pub fn lock_apb_frequency(&self) -> dfs::LockAPB { 129 | unsafe { CLOCK_CONTROL.as_mut().unwrap().lock_apb_frequency() } 130 | } 131 | 132 | /// Obtain a RAII lock to keep the CPU from sleeping 133 | pub fn lock_awake(&self) -> dfs::LockAwake { 134 | unsafe { CLOCK_CONTROL.as_mut().unwrap().lock_awake() } 135 | } 136 | 137 | /// Obtain a RAII lock to keep the PLL/2 from being turned off 138 | pub fn lock_plld2(&self) -> dfs::LockPllD2 { 139 | unsafe { CLOCK_CONTROL.as_mut().unwrap().lock_plld2() } 140 | } 141 | 142 | /// Add callback which will be called when clock speeds are changed. 143 | /// 144 | /// NOTE: these callbacks are called in an interrupt free environment, 145 | /// so should be as short as possible 146 | // TODO: at the moment only static lifetime callbacks are allow 147 | pub fn add_callback(&self, f: &'static F) -> Result<(), Error> 148 | where 149 | F: Fn(super::CPUSource, Hertz, Hertz, super::CPUSource, Hertz, Hertz), 150 | { 151 | unsafe { CLOCK_CONTROL.as_mut().unwrap().add_callback(f) } 152 | } 153 | 154 | /// Get the current count of the PCU, APB, Awake and PLL/2 locks 155 | pub fn get_lock_count(&self) -> dfs::Locks { 156 | unsafe { CLOCK_CONTROL.as_mut().unwrap().get_lock_count() } 157 | } 158 | 159 | // The following routines are made thread and interrupt safe here 160 | 161 | /// Halt the designated core 162 | pub unsafe fn park_core(&mut self, core: crate::Core) { 163 | (&CLOCK_CONTROL_MUTEX).lock(|_| { 164 | CLOCK_CONTROL.as_mut().unwrap().park_core(core); 165 | }) 166 | } 167 | 168 | /// Start the APP (second) core 169 | /// 170 | /// The second core will start running with the function `entry`. 171 | pub fn unpark_core(&mut self, core: crate::Core) { 172 | (&CLOCK_CONTROL_MUTEX) 173 | .lock(|_| unsafe { CLOCK_CONTROL.as_mut().unwrap().unpark_core(core) }) 174 | } 175 | 176 | /// Start the APP (second) core 177 | /// 178 | /// The second core will start running with the function `entry`. 179 | pub fn start_app_core(&mut self, entry: fn() -> !) -> Result<(), Error> { 180 | (&CLOCK_CONTROL_MUTEX) 181 | .lock(|_| unsafe { CLOCK_CONTROL.as_mut().unwrap().start_app_core(entry) }) 182 | } 183 | 184 | // The following routines handle thread and interrupt safety themselves 185 | 186 | /// Get RTC tick count since boot 187 | /// 188 | /// *Note: this function takes up to one slow RTC clock cycle (can be up to 300us) and 189 | /// interrupts are blocked during this time.* 190 | pub fn rtc_tick_count(&self) -> TicksU64 { 191 | unsafe { CLOCK_CONTROL.as_mut().unwrap().rtc_tick_count() } 192 | } 193 | 194 | /// Get nanoseconds since boot based on RTC tick count 195 | /// 196 | /// *Note: this function takes up to one slow RTC clock cycle (can be up to 300us) and 197 | /// interrupts are blocked during this time.* 198 | pub fn rtc_nanoseconds(&self) -> NanoSecondsU64 { 199 | unsafe { CLOCK_CONTROL.as_mut().unwrap().rtc_nanoseconds() } 200 | } 201 | } 202 | 203 | impl fmt::Debug for ClockControlConfig { 204 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 205 | unsafe { CLOCK_CONTROL.as_ref().unwrap().fmt(f) } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /examples/exception.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | #![feature(naked_functions)] 5 | 6 | use core::fmt::Write; 7 | use core::panic::PanicInfo; 8 | 9 | use esp32_hal::prelude::*; 10 | 11 | use esp32_hal::clock_control::{sleep, CPUSource::PLL, ClockControl}; 12 | use esp32_hal::dport::Split; 13 | use esp32_hal::dprintln; 14 | use esp32_hal::interrupt::{clear_software_interrupt, Interrupt, Interrupt::*, InterruptLevel}; 15 | use esp32_hal::serial::{config::Config, Serial}; 16 | use esp32_hal::target; 17 | use esp32_hal::Core::PRO; 18 | 19 | // !!! Cannot use CriticalSectionSpinLockMutex here, because an NMI is fires from within a locked 20 | // section which leads to a deadlock in the NMI interrupt handler. This is not a problem in this 21 | // case as this is a single threaded example. !!! 22 | static TX: xtensa_lx::mutex::CriticalSectionMutex>> = 23 | xtensa_lx::mutex::CriticalSectionMutex::new(None); 24 | 25 | fn locked_print(str: &str) { 26 | (&TX).lock(|tx| { 27 | let tx = tx.as_mut().unwrap(); 28 | 29 | writeln!( 30 | tx, 31 | " {}, Level: {}", 32 | str, 33 | xtensa_lx::interrupt::get_level() 34 | ) 35 | .unwrap(); 36 | }); 37 | } 38 | 39 | #[interrupt] 40 | fn FROM_CPU_INTR0() { 41 | locked_print("FROM_CPU_INTR0"); 42 | clear_software_interrupt(Interrupt::FROM_CPU_INTR0).unwrap(); 43 | } 44 | 45 | #[interrupt] 46 | fn FROM_CPU_INTR1() { 47 | locked_print("Start FROM_CPU_INTR1"); 48 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR0).unwrap(); 49 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR2).unwrap(); 50 | locked_print("End FROM_CPU_INTR1"); 51 | clear_software_interrupt(Interrupt::FROM_CPU_INTR1).unwrap(); 52 | } 53 | 54 | #[interrupt] 55 | fn FROM_CPU_INTR2() { 56 | locked_print("FROM_CPU_INTR2"); 57 | clear_software_interrupt(Interrupt::FROM_CPU_INTR2).unwrap(); 58 | } 59 | 60 | #[interrupt] 61 | fn FROM_CPU_INTR3() { 62 | locked_print("FROM_CPU_INTR3"); 63 | clear_software_interrupt(Interrupt::FROM_CPU_INTR3).unwrap(); 64 | } 65 | 66 | #[interrupt(INTERNAL_SOFTWARE_LEVEL_3_INTR)] 67 | fn software_level_3() { 68 | locked_print("INTERNAL_SOFTWARE_LEVEL_3_INTR"); 69 | clear_software_interrupt(Interrupt::FROM_CPU_INTR3).unwrap(); 70 | } 71 | 72 | #[interrupt(INTERNAL_SOFTWARE_LEVEL_1_INTR)] 73 | fn random_name() { 74 | locked_print("INTERNAL_SOFTWARE_LEVEL_1_INTR"); 75 | clear_software_interrupt(Interrupt::FROM_CPU_INTR3).unwrap(); 76 | } 77 | 78 | #[exception] 79 | #[ram] 80 | fn other_exception( 81 | cause: xtensa_lx_rt::exception::ExceptionCause, 82 | frame: xtensa_lx_rt::exception::Context, 83 | ) { 84 | (&TX).lock(|tx| { 85 | let tx = tx.as_mut().unwrap(); 86 | writeln!(tx, "Exception {:?}, {:08x?}", cause, frame).unwrap(); 87 | }); 88 | loop {} 89 | } 90 | 91 | #[entry] 92 | fn main() -> ! { 93 | let dp = target::Peripherals::take().unwrap(); 94 | 95 | let mut timg0 = dp.TIMG0; 96 | let mut timg1 = dp.TIMG1; 97 | 98 | // (https://github.com/espressif/openocd-esp32/blob/97ba3a6bb9eaa898d91df923bbedddfeaaaf28c9/src/target/esp32.c#L431) 99 | // openocd disables the watchdog timers on halt 100 | // we will do it manually on startup 101 | disable_timg_wdts(&mut timg0, &mut timg1); 102 | 103 | let (_, dport_clock_control) = dp.DPORT.split(); 104 | 105 | // setup clocks & watchdog 106 | let mut clock_control = ClockControl::new( 107 | dp.RTCCNTL, 108 | dp.APB_CTRL, 109 | dport_clock_control, 110 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 111 | ) 112 | .unwrap(); 113 | 114 | // set desired clock frequencies 115 | clock_control 116 | .set_cpu_frequencies(PLL, 240.MHz(), PLL, 240.MHz(), PLL, 240.MHz()) 117 | .unwrap(); 118 | 119 | let (clock_control_config, mut watchdog) = clock_control.freeze().unwrap(); 120 | 121 | watchdog.start(20.s()); 122 | 123 | let gpios = dp.GPIO.split(); 124 | // setup serial controller 125 | let mut uart0: Serial<_, _, _> = Serial::new( 126 | dp.UART0, 127 | esp32_hal::serial::Pins { 128 | tx: gpios.gpio1, 129 | rx: gpios.gpio3, 130 | cts: None, 131 | rts: None, 132 | }, 133 | Config::default(), 134 | clock_control_config, 135 | ) 136 | .unwrap(); 137 | 138 | uart0.change_baudrate(115200).unwrap(); 139 | 140 | // print startup message 141 | writeln!(uart0, "\n\nReboot!\n",).unwrap(); 142 | 143 | let (tx, _) = uart0.split(); 144 | (&TX).lock(|tx_locked| *tx_locked = Some(tx)); 145 | 146 | interrupt::enable_with_priority(PRO, Interrupt::FROM_CPU_INTR0, InterruptLevel(2)).unwrap(); 147 | interrupt::enable_with_priority(PRO, Interrupt::FROM_CPU_INTR1, InterruptLevel(4)).unwrap(); 148 | interrupt::enable_with_priority(PRO, Interrupt::FROM_CPU_INTR2, InterruptLevel(5)).unwrap(); 149 | interrupt::enable_with_priority(PRO, Interrupt::FROM_CPU_INTR3, InterruptLevel(7)).unwrap(); 150 | interrupt::enable(INTERNAL_SOFTWARE_LEVEL_1_INTR).unwrap(); 151 | interrupt::enable(INTERNAL_SOFTWARE_LEVEL_3_INTR).unwrap(); 152 | 153 | // Trigger various software interrupts, because done in an interrupt free section will 154 | // actually trigger at the end in order of priority 155 | (&TX).lock(|tx| { 156 | let tx = tx.as_mut().unwrap(); 157 | 158 | writeln!(tx, "Start Trigger Interrupts",).unwrap(); 159 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR0).unwrap(); 160 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR1).unwrap(); 161 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR2).unwrap(); 162 | // this one will trigger immediately as level 7 is Non-Maskable Intterupt (NMI) 163 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR3).unwrap(); 164 | interrupt::set_software_interrupt(Interrupt::INTERNAL_SOFTWARE_LEVEL_1_INTR).unwrap(); 165 | interrupt::set_software_interrupt(Interrupt::INTERNAL_SOFTWARE_LEVEL_3_INTR).unwrap(); 166 | writeln!(tx, "End Trigger Interrupts",).unwrap(); 167 | }); 168 | 169 | // Trigger outside of interrupt free section, triggers immediately 170 | (&TX).lock(|tx| { 171 | let tx = tx.as_mut().unwrap(); 172 | writeln!(tx, "Start Trigger Interrupt",).unwrap(); 173 | }); 174 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR0).unwrap(); 175 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR1).unwrap(); 176 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR2).unwrap(); 177 | // this one will trigger immediately as level 7 is Non-Maskable Intterupt (NMI) 178 | interrupt::set_software_interrupt(Interrupt::FROM_CPU_INTR3).unwrap(); 179 | interrupt::set_software_interrupt(Interrupt::INTERNAL_SOFTWARE_LEVEL_1_INTR).unwrap(); 180 | interrupt::set_software_interrupt(Interrupt::INTERNAL_SOFTWARE_LEVEL_3_INTR).unwrap(); 181 | 182 | (&TX).lock(|tx| { 183 | let tx = tx.as_mut().unwrap(); 184 | writeln!(tx, "End Trigger Interrupt",).unwrap(); 185 | writeln!(tx, "\nTrigger exception:",).unwrap(); 186 | }); 187 | 188 | // Trigger a LoadStoreError due to unaligned access in the IRAM 189 | 190 | #[link_section = ".rwtext"] 191 | static mut IRAM: [u8; 12] = [0; 12]; 192 | unsafe { IRAM[1] = 10 }; 193 | 194 | // Trigger a DivideByZeroError 195 | unsafe { asm!("quos {0},{0},{0}",in(reg)0) } 196 | 197 | loop { 198 | sleep(1.s()); 199 | (&TX).lock(|tx| { 200 | let tx = tx.as_mut().unwrap(); 201 | 202 | writeln!(tx, "Wait for watchdog reset").unwrap() 203 | }); 204 | } 205 | } 206 | 207 | const WDT_WKEY_VALUE: u32 = 0x50D83AA1; 208 | 209 | fn disable_timg_wdts(timg0: &mut target::TIMG0, timg1: &mut target::TIMG1) { 210 | timg0 211 | .wdtwprotect 212 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 213 | timg1 214 | .wdtwprotect 215 | .write(|w| unsafe { w.bits(WDT_WKEY_VALUE) }); 216 | 217 | timg0.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 218 | timg1.wdtconfig0.write(|w| unsafe { w.bits(0x0) }); 219 | } 220 | 221 | #[ram] 222 | #[panic_handler] 223 | fn panic(info: &PanicInfo) -> ! { 224 | dprintln!("\n\n*** {:?}", info); 225 | loop {} 226 | } 227 | -------------------------------------------------------------------------------- /examples/timer.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::{fmt::Write, panic::PanicInfo}; 5 | 6 | use esp32_hal::{ 7 | clock_control::sleep, 8 | dport::Split, 9 | dprintln, 10 | interrupt::{Interrupt, InterruptLevel}, 11 | prelude::*, 12 | serial::{config::Config, Serial}, 13 | target, 14 | timer::{ 15 | watchdog::{self, WatchDogResetDuration, WatchdogAction, WatchdogConfig}, 16 | Timer, Timer0, Timer1, TimerLact, TimerWithInterrupt, 17 | }, 18 | Core::PRO, 19 | }; 20 | 21 | const BLINK_HZ: Hertz = Hertz(2); 22 | 23 | static TIMER0: CriticalSectionSpinLockMutex>> = 24 | CriticalSectionSpinLockMutex::new(None); 25 | static TIMER1: CriticalSectionSpinLockMutex>> = 26 | CriticalSectionSpinLockMutex::new(None); 27 | static TIMER2: CriticalSectionSpinLockMutex>> = 28 | CriticalSectionSpinLockMutex::new(None); 29 | static TIMER3: CriticalSectionSpinLockMutex>> = 30 | CriticalSectionSpinLockMutex::new(None); 31 | static TIMER4: CriticalSectionSpinLockMutex>> = 32 | CriticalSectionSpinLockMutex::new(None); 33 | static TIMER5: CriticalSectionSpinLockMutex>> = 34 | CriticalSectionSpinLockMutex::new(None); 35 | static WATCHDOG1: CriticalSectionSpinLockMutex>> = 36 | CriticalSectionSpinLockMutex::new(None); 37 | static TX: CriticalSectionSpinLockMutex>> = 38 | CriticalSectionSpinLockMutex::new(None); 39 | 40 | #[entry] 41 | fn main() -> ! { 42 | let dp = target::Peripherals::take().unwrap(); 43 | 44 | let (_, dport_clock_control) = dp.DPORT.split(); 45 | 46 | let clkcntrl = esp32_hal::clock_control::ClockControl::new( 47 | dp.RTCCNTL, 48 | dp.APB_CTRL, 49 | dport_clock_control, 50 | esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, 51 | ) 52 | .unwrap(); 53 | 54 | let (clkcntrl_config, mut watchdog_rtc) = clkcntrl.freeze().unwrap(); 55 | let (mut timer0, mut timer1, mut timer2, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); 56 | let (mut timer3, mut timer4, mut timer5, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); 57 | 58 | watchdog_rtc.disable(); 59 | watchdog0.disable(); 60 | 61 | let wdconfig = WatchdogConfig { 62 | action1: WatchdogAction::INTERRUPT, 63 | action2: WatchdogAction::RESETSYSTEM, 64 | action3: WatchdogAction::DISABLE, 65 | action4: WatchdogAction::DISABLE, 66 | period1: 6.s().into(), 67 | period2: 8.s().into(), 68 | period3: 0.us().into(), 69 | period4: 0.us().into(), 70 | cpu_reset_duration: WatchDogResetDuration::T800NS, 71 | sys_reset_duration: WatchDogResetDuration::T800NS, 72 | divider: 1, 73 | }; 74 | 75 | watchdog1.set_config(&wdconfig).unwrap(); 76 | 77 | (&WATCHDOG1).lock(|data| *data = Some(watchdog1)); 78 | 79 | let config = Config { 80 | baudrate: Hertz(115_200), 81 | ..Default::default() 82 | }; 83 | 84 | let gpios = dp.GPIO.split(); 85 | // setup serial controller 86 | let serial: Serial<_, _, _> = Serial::new( 87 | dp.UART0, 88 | esp32_hal::serial::Pins { 89 | tx: gpios.gpio1, 90 | rx: gpios.gpio3, 91 | cts: None, 92 | rts: None, 93 | }, 94 | config, 95 | clkcntrl_config, 96 | ) 97 | .unwrap(); 98 | 99 | let (mut tx, _) = serial.split(); 100 | 101 | writeln!(tx, "\n\nESP32 Started\n\n").unwrap(); 102 | writeln!(tx, "Clock Config: {:#?}", clkcntrl_config).unwrap(); 103 | 104 | timer0.start(1000.ms()); 105 | timer1.start(4.s()); 106 | timer2.start(2.s()); 107 | timer3.start(900.ms()); 108 | timer4.start(1100.ms()); 109 | timer5.start(1300.ms()); 110 | 111 | writeln!(tx, "Waiting for timer0").unwrap(); 112 | nb::block!(timer0.wait()).unwrap(); 113 | writeln!(tx, "Finished waiting for timer0").unwrap(); 114 | 115 | timer0.listen(esp32_hal::timer::Event::TimeOut); 116 | timer0.listen(esp32_hal::timer::Event::TimeOutEdge); 117 | 118 | timer2.listen(esp32_hal::timer::Event::TimeOut); 119 | timer2.listen(esp32_hal::timer::Event::TimeOutEdge); 120 | 121 | timer3.listen(esp32_hal::timer::Event::TimeOut); 122 | timer4.listen(esp32_hal::timer::Event::TimeOut); 123 | timer5.listen(esp32_hal::timer::Event::TimeOut); 124 | 125 | (&TIMER0).lock(|data| *data = Some(timer0)); 126 | (&TIMER1).lock(|data| *data = Some(timer1)); 127 | (&TIMER2).lock(|data| *data = Some(timer2)); 128 | (&TIMER3).lock(|data| *data = Some(timer3)); 129 | (&TIMER4).lock(|data| *data = Some(timer4)); 130 | (&TIMER5).lock(|data| *data = Some(timer5)); 131 | 132 | (&TX).lock(|data| *data = Some(tx)); 133 | 134 | interrupt::enable_with_priority(PRO, Interrupt::TG0_T0_LEVEL_INTR, InterruptLevel(1)).unwrap(); 135 | interrupt::enable_with_priority(PRO, Interrupt::TG0_T0_EDGE_INTR, InterruptLevel(1)).unwrap(); 136 | 137 | interrupt::enable_with_priority(PRO, Interrupt::TG1_WDT_LEVEL_INTR, InterruptLevel(1)).unwrap(); 138 | interrupt::enable_with_priority(PRO, Interrupt::TG1_WDT_EDGE_INTR, InterruptLevel(3)).unwrap(); 139 | 140 | interrupt::enable_with_priority(PRO, Interrupt::TG0_LACT_LEVEL_INTR, InterruptLevel(1)) 141 | .unwrap(); 142 | interrupt::enable_with_priority(PRO, Interrupt::TG0_LACT_EDGE_INTR, InterruptLevel(4)).unwrap(); 143 | 144 | interrupt::enable_with_priority(PRO, Interrupt::TG1_T0_LEVEL_INTR, InterruptLevel(1)).unwrap(); 145 | interrupt::enable_with_priority(PRO, Interrupt::TG1_T1_LEVEL_INTR, InterruptLevel(1)).unwrap(); 146 | interrupt::enable_with_priority(PRO, Interrupt::TG1_LACT_LEVEL_INTR, InterruptLevel(1)) 147 | .unwrap(); 148 | 149 | let mut x = 0; 150 | loop { 151 | x += 1; 152 | (&TX, &TIMER0, &TIMER1, &TIMER2).lock(|tx, timer0, timer1, timer2| { 153 | let tx = tx.as_mut().unwrap(); 154 | let timer0 = timer0.as_mut().unwrap(); 155 | let timer1 = timer1.as_mut().unwrap(); 156 | let timer2 = timer2.as_mut().unwrap(); 157 | writeln!( 158 | tx, 159 | "Loop: {} {} {} {} {}", 160 | x, 161 | timer0.get_value(), 162 | timer1.get_value(), 163 | timer2.get_value(), 164 | xtensa_lx::timer::get_cycle_count() 165 | ) 166 | .unwrap(); 167 | if timer1.wait().is_ok() { 168 | writeln!(tx, "CANCELLING Timers").unwrap(); 169 | timer0.cancel().unwrap(); 170 | timer1.cancel().unwrap(); 171 | } 172 | }); 173 | 174 | sleep((Hertz(1_000_000) / BLINK_HZ).us()); 175 | } 176 | } 177 | 178 | fn locked_print(str: &str) { 179 | (&TX).lock(|tx| { 180 | let tx = tx.as_mut().unwrap(); 181 | 182 | writeln!(tx, "{}", str).unwrap(); 183 | }); 184 | } 185 | 186 | fn locked_clear(mut timer_mutex: &CriticalSectionSpinLockMutex>) { 187 | timer_mutex.lock(|timer| { 188 | let timer = timer.as_mut().unwrap(); 189 | timer.clear_interrupt(); 190 | }); 191 | } 192 | 193 | #[interrupt] 194 | fn TG0_T0_LEVEL_INTR() { 195 | locked_print(" TG0_T0_LEVEL_INTR"); 196 | locked_clear(&TIMER0); 197 | } 198 | 199 | #[interrupt] 200 | fn TG0_T0_EDGE_INTR() { 201 | locked_print(" TG0_T0_EDGE_INTR"); 202 | } 203 | 204 | #[interrupt] 205 | fn TG0_LACT_LEVEL_INTR() { 206 | locked_print(" TG0_LACT_LEVEL_INTR"); 207 | locked_clear(&TIMER2); 208 | } 209 | 210 | #[interrupt] 211 | fn TG0_LACT_EDGE_INTR() { 212 | locked_print(" TG0_LACT_EDGE_INTR"); 213 | } 214 | 215 | #[interrupt] 216 | fn TG1_T0_LEVEL_INTR() { 217 | locked_print(" TG1_T0_LEVEL_INTR"); 218 | locked_clear(&TIMER3); 219 | } 220 | 221 | #[interrupt] 222 | fn TG1_T1_LEVEL_INTR() { 223 | locked_print(" TG1_T1_LEVEL_INTR"); 224 | locked_clear(&TIMER4); 225 | } 226 | 227 | #[interrupt] 228 | fn TG1_LACT_LEVEL_INTR() { 229 | locked_print(" TG1_LACT_LEVEL_INTR"); 230 | locked_clear(&TIMER5); 231 | } 232 | 233 | #[interrupt] 234 | fn TG1_WDT_LEVEL_INTR() { 235 | locked_print(" TG1_WDT_LEVEL_INTR"); 236 | 237 | (&WATCHDOG1).lock(|watchdog1| { 238 | let watchdog1 = watchdog1.as_mut().unwrap(); 239 | watchdog1.clear_interrupt(); 240 | }); 241 | } 242 | 243 | #[interrupt] 244 | fn TG1_WDT_EDGE_INTR() { 245 | locked_print(" TG1_WDT_EDGE_INTR"); 246 | } 247 | 248 | #[panic_handler] 249 | fn panic(info: &PanicInfo) -> ! { 250 | dprintln!("\n\n*** {:?}", info); 251 | loop {} 252 | } 253 | -------------------------------------------------------------------------------- /examples/leds.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | //! This is an example for the ESP32 with a WS2812B LED strip. 4 | use core::panic::PanicInfo; 5 | use esp32::SPI2; 6 | use esp32_hal::{dport::Split, dprintln, prelude::*, spi, target}; 7 | use smart_leds::{SmartLedsWrite, RGB, RGB8}; 8 | 9 | use esp32_hal::clock_control::{ClockControl, XTAL_FREQUENCY_AUTO}; 10 | use esp32_hal::gpio::{Gpio14, Gpio15, Gpio25, Output, PushPull, Unknown}; 11 | use esp32_hal::spi::SPI; 12 | use ws2812_spi::Ws2812; 13 | use xtensa_lx::timer::delay; 14 | 15 | const NUM_LEDS: usize = 23; 16 | const STEPS: u8 = 10; 17 | const TOP_ROW: usize = 4; 18 | const MID_ROW: usize = 10; 19 | const DT: u32 = 5; 20 | const BREATHING_MULTIPLIER: u32 = 10; 21 | 22 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 23 | struct LightData { 24 | leds: [RGB8; NUM_LEDS], 25 | } 26 | impl LightData { 27 | fn empty() -> Self { 28 | Self { 29 | leds: [RGB8::new(0, 0, 0); NUM_LEDS], 30 | } 31 | } 32 | fn from_gradient(from: RGB8, to: RGB8) -> Self { 33 | let mut result = [RGB8::default(); NUM_LEDS]; 34 | let r_delta = to.r as i16 - from.r as i16; 35 | let g_delta = to.g as i16 - from.g as i16; 36 | let b_delta = to.b as i16 - from.b as i16; 37 | for i in 0..NUM_LEDS { 38 | let r = (from.r + (r_delta * i as i16 / (NUM_LEDS - 1) as i16) as u8) as u8; 39 | let g = (from.g + (g_delta * i as i16 / (NUM_LEDS - 1) as i16) as u8) as u8; 40 | let b = (from.b + (b_delta * i as i16 / (NUM_LEDS - 1) as i16) as u8) as u8; 41 | result[i] = RGB8 { r, g, b }; 42 | } 43 | Self::from(result) 44 | } 45 | fn get_brightness(&self) -> u8 { 46 | self.leds 47 | .iter() 48 | .map(|led| led.r + led.g + led.b) 49 | .max() 50 | .unwrap() 51 | } 52 | fn write_to_strip( 53 | &self, 54 | strip: &mut Ws2812, Gpio15>, Gpio25>>, 55 | ) { 56 | strip.write(self.leds.iter().cloned()).unwrap(); 57 | } 58 | fn get_led(&self, index: usize) -> RGB8 { 59 | self.leds[index] 60 | } 61 | fn set_color_all(&mut self, color: RGB8) { 62 | for i in 0..NUM_LEDS { 63 | self.set_color(i, color); 64 | } 65 | } 66 | fn set_red(&mut self, index: usize, red: u8) { 67 | self.leds[index].r = red; 68 | } 69 | fn set_green(&mut self, index: usize, green: u8) { 70 | self.leds[index].g = green; 71 | } 72 | fn set_blue(&mut self, index: usize, blue: u8) { 73 | self.leds[index].b = blue; 74 | } 75 | fn set_color(&mut self, led: usize, color: RGB8) { 76 | self.leds[led] = color; 77 | } 78 | fn set_lightness_percent_all(&mut self, lightness: f32) { 79 | for led in 0..self.leds.len() { 80 | self.set_lightness_percent(lightness, led); 81 | } 82 | } 83 | fn set_lightness_percent(&mut self, lightness: f32, led: usize) { 84 | self.leds[led].r = (self.leds[led].r as f32 * lightness) as u8; 85 | self.leds[led].g = (self.leds[led].g as f32 * lightness) as u8; 86 | self.leds[led].b = (self.leds[led].b as f32 * lightness) as u8; 87 | } 88 | } 89 | impl Default for LightData { 90 | fn default() -> Self { 91 | Self { 92 | leds: [RGB8::new(STEPS, STEPS, STEPS); NUM_LEDS], 93 | } 94 | } 95 | } 96 | 97 | impl From<[RGB8; NUM_LEDS]> for LightData { 98 | fn from(data: [RGB8; NUM_LEDS]) -> Self { 99 | Self { leds: data } 100 | } 101 | } 102 | 103 | struct Strip { 104 | ws: Ws2812, Gpio15>, Gpio25>>, 105 | data: LightData, 106 | brightness: u8, 107 | } 108 | 109 | impl Strip { 110 | fn fade_into(&mut self, data: LightData) { 111 | while self.data != data { 112 | for i in 0..NUM_LEDS { 113 | let r_delta = self.data.get_led(i).r as i32 - data.get_led(i).r as i32; 114 | let g_delta = self.data.get_led(i).g as i32 - data.get_led(i).g as i32; 115 | let b_delta = self.data.get_led(i).b as i32 - data.get_led(i).b as i32; 116 | let mut r_step = (r_delta as f32 * 0.05) as u8; 117 | let mut g_step = (g_delta as f32 * 0.05) as u8; 118 | let mut b_step = (b_delta as f32 * 0.05) as u8; 119 | if r_step == 0 { 120 | r_step = 1; 121 | } 122 | if g_step == 0 { 123 | g_step = 1; 124 | } 125 | if b_step == 0 { 126 | b_step = 1; 127 | } 128 | if r_delta < 0 { 129 | self.data.set_red(i, self.data.get_led(i).r + r_step); 130 | } else if r_delta > 0 { 131 | self.data.set_red(i, self.data.get_led(i).r - r_step); 132 | } 133 | if g_delta < 0 { 134 | self.data.set_green(i, self.data.get_led(i).g + g_step); 135 | } else if g_delta > 0 { 136 | self.data.set_green(i, self.data.get_led(i).g - g_step); 137 | } 138 | if b_delta < 0 { 139 | self.data.set_blue(i, self.data.get_led(i).b + b_step); 140 | } else if b_delta > 0 { 141 | self.data.set_blue(i, self.data.get_led(i).b - b_step); 142 | } 143 | } 144 | self.write(); 145 | delay(BREATHING_MULTIPLIER * 1_000_000); 146 | } 147 | self.get_brightness(); 148 | } 149 | 150 | fn startup_animation(&mut self) { 151 | self.data = LightData::empty(); 152 | self.write(); 153 | for i in 0..TOP_ROW { 154 | self.set_color(RGB8::new(self.brightness, 0, 0), i); 155 | self.write(); 156 | delay(5_000_000); 157 | } 158 | for i in TOP_ROW..MID_ROW + TOP_ROW { 159 | self.set_color(RGB8::new(0, self.brightness, 0), i); 160 | self.write(); 161 | delay(5_000_000); 162 | } 163 | for i in MID_ROW + TOP_ROW..NUM_LEDS { 164 | self.set_color(RGB8::new(0, 0, self.brightness), i); 165 | self.write(); 166 | delay(5_000_000); 167 | } 168 | delay(40_000_000 * 10); 169 | } 170 | 171 | fn shutdown_animation(&mut self) { 172 | let mut i = NUM_LEDS; 173 | while i > 0 { 174 | i -= 1; 175 | self.set_color(RGB8::new(0, 0, 0), i); 176 | self.write(); 177 | delay(5_000_000); 178 | } 179 | } 180 | 181 | fn write(&mut self) { 182 | self.data.write_to_strip(&mut self.ws); 183 | } 184 | fn set_color(&mut self, color: RGB8, index: usize) { 185 | self.data.set_color(index, color); 186 | self.write(); 187 | } 188 | fn set_solid(&mut self, color: RGB8) { 189 | self.data.set_color_all(color); 190 | self.write(); 191 | } 192 | fn get_brightness(&mut self) { 193 | self.data.get_brightness(); 194 | } 195 | } 196 | 197 | #[entry] 198 | fn main() -> ! { 199 | let dp = target::Peripherals::take().unwrap(); 200 | let (_, dport_clock_control) = dp.DPORT.split(); 201 | 202 | let clkcntrl = ClockControl::new( 203 | dp.RTCCNTL, 204 | dp.APB_CTRL, 205 | dport_clock_control, 206 | XTAL_FREQUENCY_AUTO, 207 | ) 208 | .unwrap(); 209 | let (clkcntrl_config, _) = clkcntrl.freeze().unwrap(); 210 | let pins = dp.GPIO.split(); 211 | let data_out = pins.gpio15.into_push_pull_output(); 212 | let spi: SPI<_, _, _, _> = SPI::::new( 213 | dp.SPI2, 214 | spi::Pins { 215 | sclk: pins.gpio14, 216 | sdo: data_out, 217 | sdi: Some(pins.gpio25), 218 | cs: None, 219 | }, 220 | spi::config::Config { 221 | baudrate: 3.MHz().into(), 222 | bit_order: spi::config::BitOrder::MSBFirst, 223 | data_mode: spi::config::MODE_0, 224 | }, 225 | clkcntrl_config, 226 | ) 227 | .unwrap(); 228 | let ws = Ws2812::new(spi); 229 | 230 | let mut strip = Strip { 231 | ws, 232 | data: LightData::from_gradient(RGB8::new(40, 0, 0), RGB::new(0, 0, 40)), 233 | brightness: 10, 234 | }; 235 | loop { 236 | strip.startup_animation(); 237 | delay(1_000_000); 238 | strip.fade_into(LightData::from_gradient( 239 | RGB8::new(40, 0, 0), 240 | RGB::new(0, 0, 40), 241 | )); 242 | delay(DT * 40_000_000); 243 | strip.shutdown_animation(); 244 | } 245 | } 246 | 247 | #[panic_handler] 248 | fn panic(info: &PanicInfo) -> ! { 249 | dprintln!("\n\n*** {:?}", info); 250 | loop {} 251 | } 252 | -------------------------------------------------------------------------------- /src/clock_control/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! RTC Watchdog implementation 2 | //! 3 | //! # TODO: 4 | //! - Add convenience methods for configuration 5 | //! - Consider add default configuration for start with time only 6 | 7 | use crate::prelude::*; 8 | use crate::target; 9 | use crate::target::rtccntl::wdtconfig0::*; 10 | use crate::target::RTCCNTL; 11 | use embedded_hal::watchdog::{WatchdogDisable, WatchdogEnable}; 12 | 13 | pub type WatchdogAction = WDT_STG0_A; 14 | pub type WatchDogResetDuration = WDT_CPU_RESET_LENGTH_A; 15 | 16 | const WATCHDOG_UNBLOCK_KEY: u32 = 0x50D83AA1; 17 | 18 | const WATCHDOG_BLOCK_VALUE: u32 = 0x89ABCDEF; 19 | 20 | pub struct Watchdog { 21 | clock_control_config: super::ClockControlConfig, 22 | } 23 | 24 | /// Watchdog configuration 25 | /// 26 | /// The watchdog has four stages. 27 | /// Each of these stages can take a configurable action after expiry of the corresponding period. 28 | /// When this action is done, it will move to the next stage. 29 | /// The stage is reset to the first when the watchdog timer is fed. 30 | #[derive(Debug)] 31 | pub struct WatchdogConfig { 32 | // Delay before the first action to be taken 33 | pub period1: MicroSeconds, 34 | // First action 35 | pub action1: WatchdogAction, 36 | // Delay before the second action to be taken 37 | pub period2: MicroSeconds, 38 | // Second action 39 | pub action2: WatchdogAction, 40 | // Delay before the third action to be taken 41 | pub period3: MicroSeconds, 42 | // Third action 43 | pub action3: WatchdogAction, 44 | // Delay before the fourth action to be taken 45 | pub period4: MicroSeconds, 46 | // Fourth action 47 | pub action4: WatchdogAction, 48 | /// Duration of the cpu reset signal 49 | pub cpu_reset_duration: WatchDogResetDuration, 50 | /// Duration of the system reset signal 51 | pub sys_reset_duration: WatchDogResetDuration, 52 | /// Pause the watchdog timer when the system is in sleep mode 53 | pub pause_in_sleep: bool, 54 | /// Indicates which cpu(s) will be reset when action is RESETCPU 55 | pub reset_cpu: (bool, bool), 56 | } 57 | 58 | impl Watchdog { 59 | /// internal function to create new watchdog structure 60 | pub(crate) fn new(clock_control_config: super::ClockControlConfig) -> Self { 61 | Watchdog { 62 | clock_control_config, 63 | } 64 | } 65 | 66 | /// function to unlock the watchdog (write unblock key) and lock after use 67 | fn access_registers A>( 68 | &mut self, 69 | mut f: F, 70 | ) -> A { 71 | // Unprotect write access to registers 72 | let rtc_control = unsafe { &(*RTCCNTL::ptr()) }; 73 | 74 | rtc_control 75 | .wdtwprotect 76 | .write(|w| unsafe { w.bits(WATCHDOG_UNBLOCK_KEY) }); 77 | 78 | let a = f(rtc_control); 79 | 80 | // Protect again 81 | rtc_control 82 | .wdtwprotect 83 | .write(|w| unsafe { w.bits(WATCHDOG_BLOCK_VALUE) }); 84 | 85 | a 86 | } 87 | 88 | /// Calculate period from ref ticks 89 | fn calc_period(&self, value: u32) -> MicroSeconds { 90 | (((1000000u64 * value as u64) 91 | / (u32::from(self.clock_control_config.slow_rtc_frequency()) as u64)) as u32) 92 | .us() 93 | } 94 | 95 | /// Calculate ref ticks from period 96 | fn calc_ticks(&self, value: MicroSeconds) -> u32 { 97 | (u32::from(value) as u64 * u32::from(self.clock_control_config.slow_rtc_frequency()) as u64 98 | / 1000000u64) as u32 99 | } 100 | 101 | /// Get watchdog configuration 102 | pub fn config(&self) -> Result { 103 | let rtc_control = unsafe { &(*RTCCNTL::ptr()) }; 104 | let wdtconfig0 = rtc_control.wdtconfig0.read(); 105 | 106 | let stg0 = match wdtconfig0.wdt_stg0().variant() { 107 | Some(x) => x, 108 | _ => return Err(super::Error::UnsupportedWatchdogConfig), 109 | }; 110 | let stg1 = match wdtconfig0.wdt_stg1().variant() { 111 | Some(x) => x, 112 | _ => return Err(super::Error::UnsupportedWatchdogConfig), 113 | }; 114 | let stg2 = match wdtconfig0.wdt_stg2().variant() { 115 | Some(x) => x, 116 | _ => return Err(super::Error::UnsupportedWatchdogConfig), 117 | }; 118 | let stg3 = match wdtconfig0.wdt_stg3().variant() { 119 | Some(x) => x, 120 | _ => return Err(super::Error::UnsupportedWatchdogConfig), 121 | }; 122 | 123 | Ok(WatchdogConfig { 124 | period1: self.calc_period(rtc_control.wdtconfig1.read().bits()), 125 | action1: stg0, 126 | period2: self.calc_period(rtc_control.wdtconfig2.read().bits()), 127 | action2: stg1, 128 | period3: self.calc_period(rtc_control.wdtconfig3.read().bits()), 129 | action3: stg2, 130 | period4: self.calc_period(rtc_control.wdtconfig4.read().bits()), 131 | action4: stg3, 132 | cpu_reset_duration: wdtconfig0.wdt_cpu_reset_length().variant(), 133 | sys_reset_duration: wdtconfig0.wdt_sys_reset_length().variant(), 134 | pause_in_sleep: wdtconfig0.wdt_pause_in_slp().bit(), 135 | reset_cpu: ( 136 | wdtconfig0.wdt_procpu_reset_en().bit(), 137 | wdtconfig0.wdt_appcpu_reset_en().bit(), 138 | ), 139 | }) 140 | } 141 | 142 | /// Change watchdog timer configuration and start 143 | pub fn set_config(&mut self, config: &WatchdogConfig) { 144 | let per1 = self.calc_ticks(config.period1); 145 | let per2 = self.calc_ticks(config.period2); 146 | let per3 = self.calc_ticks(config.period3); 147 | let per4 = self.calc_ticks(config.period4); 148 | 149 | self.access_registers(|rtc_control| { 150 | rtc_control.wdtfeed.write(|w| w.wdt_feed().set_bit()); 151 | rtc_control.wdtconfig0.modify(|_, w| { 152 | w.wdt_stg0() 153 | .variant(config.action1) 154 | .wdt_stg1() 155 | .variant(config.action2) 156 | .wdt_stg2() 157 | .variant(config.action3) 158 | .wdt_stg3() 159 | .variant(config.action4) 160 | .wdt_en() 161 | .set_bit() 162 | .wdt_flashboot_mod_en() 163 | .clear_bit() 164 | .wdt_cpu_reset_length() 165 | .variant(config.cpu_reset_duration) 166 | .wdt_sys_reset_length() 167 | .variant(config.sys_reset_duration) 168 | .wdt_pause_in_slp() 169 | .bit(config.pause_in_sleep) 170 | .wdt_procpu_reset_en() 171 | .bit(config.reset_cpu.0) 172 | .wdt_appcpu_reset_en() 173 | .bit(config.reset_cpu.1) 174 | }); 175 | rtc_control.wdtconfig1.write(|w| unsafe { w.bits(per1) }); 176 | rtc_control.wdtconfig2.write(|w| unsafe { w.bits(per2) }); 177 | rtc_control.wdtconfig3.write(|w| unsafe { w.bits(per3) }); 178 | rtc_control.wdtconfig4.write(|w| unsafe { w.bits(per4) }); 179 | }); 180 | } 181 | } 182 | 183 | /// Enable watchdog timer, only change stage 1 period, don't change default action 184 | impl WatchdogEnable for Watchdog { 185 | type Time = MicroSeconds; 186 | 187 | fn start>(&mut self, period: T) { 188 | let per = self.calc_ticks(period.into()); 189 | 190 | self.access_registers(|rtc_control| { 191 | rtc_control.wdtfeed.write(|w| w.wdt_feed().set_bit()); 192 | rtc_control 193 | .wdtconfig1 194 | .write(|w| unsafe { w.wdt_stg0_hold().bits(per) }); 195 | rtc_control.wdtconfig0.modify(|_, w| { 196 | w.wdt_flashboot_mod_en() 197 | .clear_bit() 198 | .wdt_en() 199 | .set_bit() 200 | .wdt_pause_in_slp() 201 | .set_bit() 202 | .wdt_stg0() 203 | .variant(WatchdogAction::RESETRTC) 204 | }); 205 | }); 206 | } 207 | } 208 | 209 | /// Disable watchdog timer 210 | impl<'a> WatchdogDisable for Watchdog { 211 | fn disable(&mut self) { 212 | self.access_registers(|rtc_control| { 213 | rtc_control.wdtfeed.write(|w| w.wdt_feed().set_bit()); 214 | rtc_control 215 | .wdtconfig0 216 | .modify(|_, w| w.wdt_flashboot_mod_en().clear_bit().wdt_en().clear_bit()); 217 | }); 218 | } 219 | } 220 | 221 | /// Feed (=reset) the watchdog timer 222 | impl embedded_hal::watchdog::Watchdog for Watchdog { 223 | fn feed(&mut self) { 224 | self.access_registers(|rtc_control| { 225 | rtc_control.wdtfeed.write(|w| w.wdt_feed().set_bit()); 226 | }); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/timer/watchdog.rs: -------------------------------------------------------------------------------- 1 | //! RTC Watchdog implementation 2 | //! 3 | //! # TODO: 4 | //! - Add convenience methods for configuration 5 | //! - Consider add default configuration for start with time only 6 | 7 | use super::Error; 8 | use super::TimerGroup; 9 | use crate::prelude::*; 10 | use crate::target; 11 | use crate::target::timg::wdtconfig0::WDT_STG0_A; 12 | use crate::target::timg::wdtconfig0::*; 13 | use core::marker::PhantomData; 14 | use embedded_hal::watchdog::{WatchdogDisable, WatchdogEnable}; 15 | 16 | pub type WatchdogAction = WDT_STG0_A; 17 | pub type WatchDogResetDuration = WDT_CPU_RESET_LENGTH_A; 18 | 19 | const WATCHDOG_UNBLOCK_KEY: u32 = 0x50D83AA1; 20 | const WATCHDOG_BLOCK_VALUE: u32 = 0x89ABCDEF; 21 | 22 | pub struct Watchdog { 23 | clock_control_config: super::ClockControlConfig, 24 | timg: *const target::timg::RegisterBlock, 25 | _group: PhantomData, 26 | } 27 | 28 | unsafe impl Send for Watchdog {} 29 | 30 | /// Watchdog configuration 31 | /// 32 | /// The watchdog has four stages. 33 | /// Each of these stages can take a configurable action after expiry of the corresponding period. 34 | /// When this action is done, it will move to the next stage. 35 | /// The stage is reset to the first when the watchdog timer is fed. 36 | #[derive(Debug)] 37 | pub struct WatchdogConfig { 38 | // Delay before the first action to be taken 39 | pub period1: NanoSecondsU64, 40 | // First action 41 | pub action1: WatchdogAction, 42 | // Delay before the second action to be taken 43 | pub period2: NanoSecondsU64, 44 | // Second action 45 | pub action2: WatchdogAction, 46 | // Delay before the third action to be taken 47 | pub period3: NanoSecondsU64, 48 | // Third action 49 | pub action3: WatchdogAction, 50 | // Delay before the fourth action to be taken 51 | pub period4: NanoSecondsU64, 52 | // Fourth action 53 | pub action4: WatchdogAction, 54 | /// Duration of the cpu reset signal 55 | pub cpu_reset_duration: WatchDogResetDuration, 56 | /// Duration of the system reset signal 57 | pub sys_reset_duration: WatchDogResetDuration, 58 | /// Clock pre-scaling divider 59 | pub divider: u16, 60 | } 61 | 62 | impl Watchdog { 63 | /// internal function to create new watchdog structure 64 | pub(crate) fn new(timg: TIMG, clock_control_config: super::ClockControlConfig) -> Self { 65 | Watchdog { 66 | clock_control_config, 67 | timg: &*timg as *const _ as *const target::timg::RegisterBlock, 68 | _group: PhantomData, 69 | } 70 | } 71 | 72 | /// function to unlock the watchdog (write unblock key) and lock after use 73 | fn access_registers A>(&self, mut f: F) -> A { 74 | // Unprotect write access to registers 75 | 76 | let timg = unsafe { &*(self.timg) }; 77 | 78 | timg.wdtwprotect 79 | .write(|w| unsafe { w.bits(WATCHDOG_UNBLOCK_KEY) }); 80 | 81 | let a = f(&timg); 82 | 83 | // Protect again 84 | timg.wdtwprotect 85 | .write(|w| unsafe { w.bits(WATCHDOG_BLOCK_VALUE) }); 86 | 87 | a 88 | } 89 | 90 | /// Calculate period from ticks 91 | fn calc_period(&self, value: T) -> NanoSecondsU64 { 92 | let divider: u32 = unsafe { &*(self.timg) } 93 | .wdtconfig1 94 | .read() 95 | .wdt_clk_prescale() 96 | .bits() 97 | .into(); 98 | TicksU64::from(value.into()) / (self.clock_control_config.apb_frequency() / divider) 99 | } 100 | 101 | /// Calculate ticks from period 102 | fn calc_ticks_with_divider(&self, value: T, divider: u16) -> Ticks { 103 | let ticks = self.clock_control_config.apb_frequency() / u32::from(divider) * value.into(); 104 | use core::convert::TryInto; 105 | ticks.try_into().unwrap() 106 | } 107 | 108 | /// Calculate ticks from period 109 | fn calc_ticks(&self, value: T) -> Ticks { 110 | self.calc_ticks_with_divider( 111 | value, 112 | unsafe { &*(self.timg) } 113 | .wdtconfig1 114 | .read() 115 | .wdt_clk_prescale() 116 | .bits(), 117 | ) 118 | } 119 | 120 | /// Get watchdog configuration 121 | pub fn config(&self) -> Result { 122 | let timg = unsafe { &*(self.timg) }; 123 | let wdtconfig0 = timg.wdtconfig0.read(); 124 | 125 | let stg0 = wdtconfig0.wdt_stg0().variant(); 126 | let stg1 = wdtconfig0.wdt_stg1().variant(); 127 | let stg2 = wdtconfig0.wdt_stg2().variant(); 128 | let stg3 = wdtconfig0.wdt_stg3().variant(); 129 | 130 | Ok(WatchdogConfig { 131 | period1: self.calc_period(Ticks(timg.wdtconfig2.read().bits())), 132 | action1: stg0, 133 | period2: self.calc_period(Ticks(timg.wdtconfig3.read().bits())), 134 | action2: stg1, 135 | period3: self.calc_period(Ticks(timg.wdtconfig4.read().bits())), 136 | action3: stg2, 137 | period4: self.calc_period(Ticks(timg.wdtconfig5.read().bits())), 138 | action4: stg3, 139 | cpu_reset_duration: wdtconfig0.wdt_cpu_reset_length().variant(), 140 | sys_reset_duration: wdtconfig0.wdt_sys_reset_length().variant(), 141 | divider: timg.wdtconfig1.read().wdt_clk_prescale().bits(), 142 | }) 143 | } 144 | 145 | /// Change watchdog timer configuration and start 146 | pub fn set_config(&mut self, config: &WatchdogConfig) -> Result<(), Error> { 147 | self.access_registers(|timg| { 148 | timg.wdtconfig1 149 | .write(|w| unsafe { w.wdt_clk_prescale().bits(config.divider) }); 150 | 151 | let per1: u32 = self.calc_ticks(config.period1).into(); 152 | let per2: u32 = self.calc_ticks(config.period2).into(); 153 | let per3: u32 = self.calc_ticks(config.period3).into(); 154 | let per4: u32 = self.calc_ticks(config.period4).into(); 155 | 156 | unsafe { timg.wdtfeed.write(|w| w.wdt_feed().bits(0)) } 157 | timg.wdtconfig0.modify(|_, w| { 158 | w.wdt_stg0() 159 | .variant(config.action1) 160 | .wdt_stg1() 161 | .variant(config.action2) 162 | .wdt_stg2() 163 | .variant(config.action3) 164 | .wdt_stg3() 165 | .variant(config.action4) 166 | .wdt_en() 167 | .set_bit() 168 | .wdt_edge_int_en() 169 | .set_bit() 170 | .wdt_level_int_en() 171 | .set_bit() 172 | .wdt_flashboot_mod_en() 173 | .clear_bit() 174 | .wdt_cpu_reset_length() 175 | .variant(config.cpu_reset_duration) 176 | .wdt_sys_reset_length() 177 | .variant(config.sys_reset_duration) 178 | }); 179 | timg.wdtconfig2.write(|w| unsafe { w.bits(per1) }); 180 | timg.wdtconfig3.write(|w| unsafe { w.bits(per2) }); 181 | timg.wdtconfig4.write(|w| unsafe { w.bits(per3) }); 182 | timg.wdtconfig5.write(|w| unsafe { w.bits(per4) }); 183 | 184 | (&super::TIMER_MUTEX).lock(|_| { 185 | timg.int_ena_timers.modify(|_, w| w.wdt_int_ena().set_bit()); 186 | }); 187 | Ok(()) 188 | }) 189 | } 190 | 191 | pub fn clear_interrupt(&mut self) { 192 | unsafe { 193 | (*(self.timg)) 194 | .int_clr_timers 195 | .write(|w| w.wdt_int_clr().set_bit()); 196 | } 197 | } 198 | } 199 | 200 | /// Enable watchdog timer, only change stage 1 period, don't change default action 201 | impl WatchdogEnable for Watchdog { 202 | type Time = NanoSecondsU64; 203 | 204 | fn start>(&mut self, period: T) { 205 | let divider = 1; 206 | let ticks = self.calc_ticks_with_divider(period.into(), divider); 207 | self.access_registers(|timg| { 208 | timg.wdtfeed.write(|w| unsafe { w.wdt_feed().bits(0) }); 209 | timg.wdtconfig1 210 | .write(|w| unsafe { w.wdt_clk_prescale().bits(divider) }); 211 | timg.wdtconfig2.write(|w| unsafe { w.bits(ticks.into()) }); 212 | timg.wdtconfig0.modify(|_, w| { 213 | w.wdt_flashboot_mod_en() 214 | .clear_bit() 215 | .wdt_en() 216 | .set_bit() 217 | .wdt_stg0() 218 | .variant(WatchdogAction::RESETSYSTEM) 219 | }); 220 | }); 221 | } 222 | } 223 | 224 | /// Disable watchdog timer 225 | //#[allow(trivial_bounds)] 226 | impl<'a, TIMG: TimerGroup> WatchdogDisable for Watchdog { 227 | fn disable(&mut self) { 228 | self.access_registers(|timg| { 229 | unsafe { timg.wdtfeed.write(|w| w.wdt_feed().bits(0)) } 230 | timg.wdtconfig0 231 | .modify(|_, w| w.wdt_flashboot_mod_en().clear_bit().wdt_en().clear_bit()); 232 | }); 233 | } 234 | } 235 | 236 | /// Feed (=reset) the watchdog timer 237 | #[allow(trivial_bounds)] 238 | impl embedded_hal::watchdog::Watchdog for Watchdog { 239 | fn feed(&mut self) { 240 | self.access_registers(|timg| unsafe { timg.wdtfeed.write(|w| w.wdt_feed().bits(0)) }); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/analog/adc.rs: -------------------------------------------------------------------------------- 1 | //! Analog to digital (ADC) conversion support. 2 | //! 3 | //! This module provides functions for reading analog values from two 4 | //! analog to digital converters available on the ESP32: `ADC1` and `ADC2`. 5 | //! 6 | //! The following pins can be configured for analog readout: 7 | //! 8 | //! | Channel | ADC1 | ADC2 | 9 | //! |---------|----------------------|---------------| 10 | //! | 0 | GPIO36 (SENSOR_VP) | GPIO4 | 11 | //! | 1 | GPIO37 (SENSOR_CAPP) | GPIO0 | 12 | //! | 2 | GPIO38 (SENSOR_CAPN) | GPIO2 | 13 | //! | 3 | GPIO39 (SENSOR_VN) | GPIO15 (MTDO) | 14 | //! | 4 | GPIO33 (32K_XP) | GPIO13 (MTCK) | 15 | //! | 5 | GPIO32 (32K_XN) | GPIO12 (MTDI) | 16 | //! | 6 | GPIO34 (VDET_1) | GPIO14 (MTMS) | 17 | //! | 7 | GPIO35 (VDET_2) | GPIO27 | 18 | //! | 8 | | GPIO25 | 19 | //! | 9 | | GPIO26 | 20 | //! 21 | 22 | use core::marker::PhantomData; 23 | use embedded_hal::adc::{Channel, OneShot}; 24 | 25 | use crate::analog::config; 26 | use crate::analog::{ADC1, ADC2}; 27 | use crate::gpio::*; 28 | use crate::target::{RTCIO, SENS}; 29 | 30 | pub struct ADC { 31 | adc: PhantomData, 32 | attenuations: [Option; 10], 33 | active_channel: Option, 34 | } 35 | 36 | macro_rules! impl_adc_setup { 37 | ($config:expr, $bit_width:ident, $read_reg:ident, $sample_bit:ident, $atten_reg: ident, 38 | $atten_field:ident, $dig_force:ident, $meas_start_reg:ident, 39 | $start_force_field:ident, $en_pad_force:ident) => { 40 | let sensors = unsafe { &*SENS::ptr() }; 41 | 42 | /* Set reading and sampling resolution */ 43 | let resolution: u8 = $config.resolution as u8; 44 | 45 | sensors 46 | .sar_start_force 47 | .modify(|_, w| unsafe { w.$bit_width().bits(resolution) }); 48 | sensors 49 | .$read_reg 50 | .modify(|_, w| unsafe { w.$sample_bit().bits(resolution) }); 51 | 52 | /* Set attenuation for pins */ 53 | let attenuations = $config.attenuations; 54 | 55 | for channel in 0..attenuations.len() { 56 | if let Some(attenuation) = attenuations[channel] { 57 | sensors.$atten_reg.modify(|r, w| { 58 | let new_value = (r.bits() & !(0b11 << (channel * 2))) 59 | | (((attenuation as u8 & 0b11) as u32) << (channel * 2)); 60 | 61 | unsafe { w.$atten_field().bits(new_value) } 62 | }); 63 | } 64 | } 65 | 66 | /* Set controller to RTC */ 67 | sensors.$read_reg.modify(|_, w| w.$dig_force().clear_bit()); 68 | sensors 69 | .$meas_start_reg 70 | .modify(|_, w| w.$start_force_field().set_bit()); 71 | sensors 72 | .$meas_start_reg 73 | .modify(|_, w| w.$en_pad_force().set_bit()); 74 | sensors 75 | .sar_touch_ctrl1 76 | .modify(|_, w| w.xpd_hall_force().set_bit()); 77 | sensors 78 | .sar_touch_ctrl1 79 | .modify(|_, w| w.hall_phase_force().set_bit()); 80 | 81 | /* Set power to SW power on */ 82 | sensors 83 | .sar_meas_wait2 84 | .modify(|_, w| unsafe { w.force_xpd_sar().bits(0b11) }); 85 | 86 | /* disable AMP */ 87 | sensors 88 | .sar_meas_wait2 89 | .modify(|_, w| unsafe { w.force_xpd_amp().bits(0b10) }); 90 | sensors 91 | .sar_meas_ctrl 92 | .modify(|_, w| unsafe { w.amp_rst_fb_fsm().bits(0) }); 93 | sensors 94 | .sar_meas_ctrl 95 | .modify(|_, w| unsafe { w.amp_short_ref_fsm().bits(0) }); 96 | sensors 97 | .sar_meas_ctrl 98 | .modify(|_, w| unsafe { w.amp_short_ref_gnd_fsm().bits(0) }); 99 | sensors 100 | .sar_meas_wait1 101 | .modify(|_, w| unsafe { w.sar_amp_wait1().bits(1) }); 102 | sensors 103 | .sar_meas_wait1 104 | .modify(|_, w| unsafe { w.sar_amp_wait2().bits(1) }); 105 | sensors 106 | .sar_meas_wait2 107 | .modify(|_, w| unsafe { w.sar_amp_wait3().bits(1) }); 108 | }; 109 | } 110 | 111 | impl ADC { 112 | pub fn adc1(_adc_instance: ADC1, config: config::Adc1Config) -> Result { 113 | impl_adc_setup!( 114 | config, 115 | sar1_bit_width, 116 | sar_read_ctrl, 117 | sar1_sample_bit, 118 | sar_atten1, 119 | sar1_atten, 120 | sar1_dig_force, 121 | sar_meas_start1, 122 | meas1_start_force, 123 | sar1_en_pad_force 124 | ); 125 | 126 | /* Connect or disconnect hall sensor */ 127 | let rtcio = unsafe { &*RTCIO::ptr() }; 128 | 129 | if config.hall_sensor { 130 | rtcio.hall_sens.modify(|_, w| w.xpd_hall().set_bit()); 131 | } else { 132 | rtcio.hall_sens.modify(|_, w| w.xpd_hall().clear_bit()); 133 | } 134 | 135 | let adc = ADC { 136 | adc: PhantomData, 137 | attenuations: config.attenuations, 138 | active_channel: None, 139 | }; 140 | 141 | Ok(adc) 142 | } 143 | } 144 | 145 | impl ADC { 146 | pub fn adc2(_adc_instance: ADC2, config: config::Adc2Config) -> Result { 147 | impl_adc_setup!( 148 | config, 149 | sar2_bit_width, 150 | sar_read_ctrl2, 151 | sar2_sample_bit, 152 | sar_atten2, 153 | sar2_atten, 154 | sar2_dig_force, 155 | sar_meas_start2, 156 | meas2_start_force, 157 | sar2_en_pad_force 158 | ); 159 | 160 | let adc = ADC { 161 | adc: PhantomData, 162 | attenuations: config.attenuations, 163 | active_channel: None, 164 | }; 165 | 166 | Ok(adc) 167 | } 168 | } 169 | 170 | macro_rules! impl_adc_interface { 171 | ($adc:ident ($start_reg:ident, $en_pad:ident, $start:ident, $done:ident, $data:ident): [ 172 | $( ($pin:ident, $channel:expr) ,)+ 173 | ]) => { 174 | 175 | impl OneShot<$adc, WORD, PIN> for ADC<$adc> 176 | where 177 | WORD: From, 178 | PIN: Channel<$adc, ID=u8>, 179 | { 180 | type Error = (); 181 | 182 | fn read(&mut self, _pin: &mut PIN) -> nb::Result { 183 | let sensors = unsafe { &*SENS::ptr() }; 184 | 185 | if self.attenuations[PIN::channel() as usize] == None { 186 | panic!("Channel {} is not configured reading!", PIN::channel()); 187 | } 188 | 189 | if let Some(active_channel) = self.active_channel { 190 | // There is conversion in progress: 191 | // - if it's for a different channel try again later 192 | // - if it's for the given channel, go ahaid and check progress 193 | if active_channel != PIN::channel() { 194 | return Err(nb::Error::WouldBlock); 195 | } 196 | } 197 | else { 198 | // If no conversions are in progress, start a new one for given channel 199 | self.active_channel = Some(PIN::channel()); 200 | 201 | sensors.$start_reg.modify(|_, w| { 202 | unsafe { w.$en_pad().bits(1 << PIN::channel() as u8) } 203 | }); 204 | 205 | sensors.$start_reg.modify(|_,w| w.$start().clear_bit()); 206 | sensors.$start_reg.modify(|_,w| w.$start().set_bit()); 207 | } 208 | 209 | // Wait for ADC to finish conversion 210 | let conversion_finished = sensors.$start_reg.read().$done().bit_is_set(); 211 | if !conversion_finished { 212 | return Err(nb::Error::WouldBlock); 213 | } 214 | 215 | // Get converted value 216 | let converted_value = sensors.$start_reg.read().$data().bits() as u16; 217 | 218 | // Mark that no conversions are currently in progress 219 | self.active_channel = None; 220 | 221 | Ok(converted_value.into()) 222 | } 223 | } 224 | 225 | 226 | $( 227 | impl Channel<$adc> for $pin { 228 | type ID = u8; 229 | 230 | fn channel() -> u8 { $channel } 231 | } 232 | )+ 233 | } 234 | } 235 | 236 | impl_adc_interface! { 237 | ADC1 (sar_meas_start1, sar1_en_pad, meas1_start_sar, meas1_done_sar, meas1_data_sar): [ 238 | (Gpio36, 0), // Alt. name: SENSOR_VP 239 | (Gpio37, 1), // Alt. name: SENSOR_CAPP 240 | (Gpio38, 2), // Alt. name: SENSOR_CAPN 241 | (Gpio39, 3), // Alt. name: SENSOR_VN 242 | (Gpio33, 4), // Alt. name: 32K_XP 243 | (Gpio32, 5), // Alt. name: 32K_XN 244 | (Gpio34, 6), // Alt. name: VDET_1 245 | (Gpio35, 7), // Alt. name: VDET_2 246 | ] 247 | } 248 | 249 | impl_adc_interface! { 250 | ADC2 (sar_meas_start2, sar2_en_pad, meas2_start_sar, meas2_done_sar, meas2_data_sar): [ 251 | (Gpio4, 0), 252 | (Gpio0, 1), 253 | (Gpio2, 2), 254 | (Gpio15, 3), // Alt. name: MTDO 255 | (Gpio13, 4), // Alt. name: MTCK 256 | (Gpio12, 5), // Alt. name: MTDI 257 | (Gpio14, 6), // Alt. name: MTMS 258 | (Gpio27, 7), 259 | (Gpio25, 8), 260 | (Gpio26, 9), 261 | ] 262 | } 263 | -------------------------------------------------------------------------------- /src/dport.rs: -------------------------------------------------------------------------------- 1 | //! DPort peripheral configuration 2 | //! 3 | //! This peripheral contains many registers, which are used for various different functions. 4 | //! Registers needed in other blocks can be split off. 5 | //! 6 | use crate::target::{dport, DPORT}; 7 | use xtensa_lx::mutex::mutex_trait::Mutex; 8 | use xtensa_lx::mutex::CriticalSectionSpinLockMutex; 9 | 10 | /// Cpu Period Configuration Register 11 | pub struct ClockControl {} 12 | 13 | /// DPort registers related to clock control 14 | impl ClockControl { 15 | pub(crate) fn cpu_per_conf(&self) -> &dport::CPU_PER_CONF { 16 | // NOTE(unsafe) this proxy grants exclusive access to this register 17 | unsafe { &(*DPORT::ptr()).cpu_per_conf } 18 | } 19 | pub(crate) fn appcpu_ctrl_a(&self) -> &dport::APPCPU_CTRL_A { 20 | // NOTE(unsafe) this proxy grants exclusive access to this register 21 | unsafe { &(*DPORT::ptr()).appcpu_ctrl_a } 22 | } 23 | pub(crate) fn appcpu_ctrl_b(&self) -> &dport::APPCPU_CTRL_B { 24 | // NOTE(unsafe) this proxy grants exclusive access to this register 25 | unsafe { &(*DPORT::ptr()).appcpu_ctrl_b } 26 | } 27 | pub(crate) fn appcpu_ctrl_c(&self) -> &dport::APPCPU_CTRL_C { 28 | // NOTE(unsafe) this proxy grants exclusive access to this register 29 | unsafe { &(*DPORT::ptr()).appcpu_ctrl_c } 30 | } 31 | pub(crate) fn appcpu_ctrl_d(&self) -> &dport::APPCPU_CTRL_D { 32 | // NOTE(unsafe) this proxy grants exclusive access to this register 33 | unsafe { &(*DPORT::ptr()).appcpu_ctrl_d } 34 | } 35 | pub(crate) fn app_cache_ctrl(&self) -> &dport::APP_CACHE_CTRL { 36 | // NOTE(unsafe) this proxy grants exclusive access to this register 37 | unsafe { &(*DPORT::ptr()).app_cache_ctrl } 38 | } 39 | pub(crate) fn pro_cache_ctrl(&self) -> &dport::PRO_CACHE_CTRL { 40 | // NOTE(unsafe) this proxy grants exclusive access to this register 41 | unsafe { &(*DPORT::ptr()).pro_cache_ctrl } 42 | } 43 | } 44 | 45 | /// Trait to split the DPORT peripheral into subsets 46 | pub trait Split { 47 | fn split(self) -> (DPORT, ClockControl); 48 | } 49 | 50 | impl Split for DPORT { 51 | /// function to split the DPORT peripheral into subsets 52 | fn split(self) -> (DPORT, ClockControl) { 53 | (self, ClockControl {}) 54 | } 55 | } 56 | 57 | #[allow(non_camel_case_types)] 58 | #[derive(Copy, Clone)] 59 | pub enum Peripheral { 60 | TIMERS = 0, 61 | SPI0_SPI1 = 1, 62 | UART0 = 2, 63 | WDG = 3, 64 | I2S0 = 4, 65 | UART1 = 5, 66 | SPI2 = 6, 67 | I2C0 = 7, 68 | UHCI0 = 8, 69 | RMT = 9, 70 | PCNT = 10, 71 | LEDC = 11, 72 | UHCI1 = 12, 73 | TIMERGROUP0 = 13, 74 | EFUSE = 14, 75 | TIMERGROUP1 = 15, 76 | SPI3 = 16, 77 | PWM0 = 17, 78 | I2C1 = 18, 79 | CAN = 19, 80 | PWM1 = 20, 81 | I2S1 = 21, 82 | SPI_DMA = 22, 83 | UART2 = 23, 84 | UART_MEM = 24, 85 | PWM2 = 25, 86 | PWM3 = 26, 87 | AES = 32 + 0, 88 | SHA = 32 + 1, 89 | RSA = 32 + 2, 90 | SECUREBOOT = 32 + 3, 91 | SIGNATURE = 32 + 4, 92 | 93 | SDIO_SLAVE = 64 + 4, 94 | SDIO_HOST = 64 + 13, 95 | EMAC = 64 + 14, 96 | RNG = 64 + 15, 97 | 98 | WIFI = 96, 99 | BT = 97, 100 | WIFI_BT_COMMON = 98, 101 | BT_LC = 99, 102 | BT_BASEBAND = 100, 103 | } 104 | 105 | static PERIPHERAL_MUTEX: CriticalSectionSpinLockMutex<()> = CriticalSectionSpinLockMutex::new(()); 106 | 107 | const WIFI_CLK_MASK: u32 = 0x406; // bits 1, 2, 10 108 | const BT_CLK_MASK: u32 = 0x61; // bits 11, 16, 17 109 | const WIFI_BT_CLK_MASK: u32 = 0x3c9; // bits 0, 3, 6, 7, 8, 9 110 | const BT_LC_CLK_MASK: u32 = 0x3000; // bit 16, 17 111 | const BT_BASEBAND_CLK_MASK: u32 = 0x0800; // bit 11 112 | 113 | const EMAC_RST_MASK: u32 = 0x80; // bit 7 114 | const SDIO_HOST_RST_MASK: u32 = 0x40; // bit 6 115 | const SDIO_SLAVE_RST_MASK: u32 = 0x20; // bit 5 116 | 117 | const UART_MASK: u32 = 1 << (Peripheral::UART0 as u32) 118 | | 1 << (Peripheral::UART1 as u32) 119 | | 1 << (Peripheral::UART2 as u32); 120 | 121 | pub fn enable_peripheral(peripheral: Peripheral) { 122 | let bitnr = peripheral as u32; 123 | let dport = unsafe { &(*DPORT::ptr()) }; 124 | (&PERIPHERAL_MUTEX).lock(|_| unsafe { 125 | if bitnr < 32 { 126 | let mut mask = 1 << bitnr; 127 | match peripheral { 128 | Peripheral::UART0 | Peripheral::UART1 | Peripheral::UART2 => { 129 | mask |= 1 << (Peripheral::UART_MEM as u32) 130 | } 131 | _ => {} 132 | } 133 | 134 | dport.perip_clk_en.modify(|r, w| w.bits(r.bits() | mask)); 135 | dport.perip_rst_en.modify(|r, w| w.bits(r.bits() & !mask)); 136 | } else if bitnr < 64 { 137 | let mut mask = 1 << (bitnr - 32); 138 | match peripheral { 139 | Peripheral::AES => { 140 | mask |= 1 << (Peripheral::SIGNATURE as u32 - 32) 141 | | 1 << (Peripheral::SECUREBOOT as u32 - 32); 142 | } 143 | Peripheral::SHA => { 144 | mask |= 1 << (Peripheral::SECUREBOOT as u32 - 32); 145 | } 146 | Peripheral::RSA => { 147 | mask |= 1 << (Peripheral::SIGNATURE as u32 - 32); 148 | } 149 | _ => {} 150 | } 151 | 152 | dport.peri_clk_en.modify(|r, w| w.bits(r.bits() | mask)); 153 | dport.peri_rst_en.modify(|r, w| w.bits(r.bits() & !mask)); 154 | } else if bitnr < 96 { 155 | let mask = 1 << (bitnr - 64); 156 | dport.wifi_clk_en.modify(|r, w| w.bits(r.bits() | mask)); 157 | 158 | let rst_mask = match peripheral { 159 | Peripheral::EMAC => EMAC_RST_MASK, 160 | Peripheral::SDIO_HOST => SDIO_HOST_RST_MASK, 161 | Peripheral::SDIO_SLAVE => SDIO_SLAVE_RST_MASK, 162 | _ => 0, 163 | }; 164 | dport 165 | .core_rst_en 166 | .modify(|r, w| w.bits(r.bits() & !rst_mask)); 167 | } else { 168 | let mask = match peripheral { 169 | Peripheral::WIFI => WIFI_CLK_MASK, 170 | Peripheral::BT => BT_CLK_MASK, 171 | Peripheral::WIFI_BT_COMMON => WIFI_BT_CLK_MASK, 172 | Peripheral::BT_BASEBAND => BT_BASEBAND_CLK_MASK, 173 | Peripheral::BT_LC => BT_LC_CLK_MASK, 174 | _ => 0, 175 | }; 176 | dport.wifi_clk_en.modify(|r, w| w.bits(r.bits() | mask)); 177 | // no reset done 178 | } 179 | }); 180 | } 181 | 182 | pub fn disable_peripheral(peripheral: Peripheral) { 183 | let bitnr = peripheral as u32; 184 | let dport = unsafe { &(*DPORT::ptr()) }; 185 | (&PERIPHERAL_MUTEX).lock(|_| unsafe { 186 | if bitnr < 32 { 187 | let mut mask = 1 << bitnr; 188 | dport.perip_clk_en.modify(|r, w| { 189 | if r.bits() & !mask & UART_MASK == 0 { 190 | mask &= !(1 << (Peripheral::UART_MEM as u32)); 191 | } 192 | w.bits(r.bits() & !mask) 193 | }); 194 | dport.perip_rst_en.modify(|r, w| w.bits(r.bits() | mask)); 195 | } else if bitnr < 64 { 196 | let mask = 1 << (bitnr - 32); 197 | 198 | dport.peri_clk_en.modify(|r, w| w.bits(r.bits() & !mask)); 199 | dport.peri_rst_en.modify(|r, w| w.bits(r.bits() | mask)); 200 | } else if bitnr < 96 { 201 | let mask = 1 << (bitnr - 64); 202 | dport.wifi_clk_en.modify(|r, w| w.bits(r.bits() & !mask)); 203 | 204 | let rst_mask = match peripheral { 205 | Peripheral::EMAC => EMAC_RST_MASK, 206 | Peripheral::SDIO_HOST => SDIO_HOST_RST_MASK, 207 | Peripheral::SDIO_SLAVE => SDIO_SLAVE_RST_MASK, 208 | _ => 0, 209 | }; 210 | dport.core_rst_en.modify(|r, w| w.bits(r.bits() | rst_mask)); 211 | } else { 212 | let mask = match peripheral { 213 | Peripheral::WIFI => WIFI_CLK_MASK, 214 | Peripheral::BT => BT_CLK_MASK, 215 | Peripheral::WIFI_BT_COMMON => WIFI_BT_CLK_MASK, 216 | Peripheral::BT_BASEBAND => BT_BASEBAND_CLK_MASK, 217 | Peripheral::BT_LC => BT_LC_CLK_MASK, 218 | _ => 0, 219 | }; 220 | dport.wifi_clk_en.modify(|r, w| w.bits(r.bits() & !mask)); 221 | // no reset done 222 | } 223 | }); 224 | } 225 | 226 | pub fn reset_peripheral(peripheral: Peripheral) { 227 | let bitnr = peripheral as u32; 228 | let dport = unsafe { &(*DPORT::ptr()) }; 229 | (&PERIPHERAL_MUTEX).lock(|_| unsafe { 230 | if bitnr < 32 { 231 | let mask = 1 << bitnr; 232 | dport.perip_rst_en.modify(|r, w| w.bits(r.bits() | mask)); 233 | dport.perip_rst_en.modify(|r, w| w.bits(r.bits() & !mask)); 234 | } else if bitnr < 64 { 235 | let mask = 1 << (bitnr - 32); 236 | dport.peri_rst_en.modify(|r, w| w.bits(r.bits() | mask)); 237 | dport.peri_rst_en.modify(|r, w| w.bits(r.bits() & !mask)); 238 | } else if bitnr < 96 { 239 | let rst_mask = match peripheral { 240 | Peripheral::EMAC => EMAC_RST_MASK, 241 | Peripheral::SDIO_HOST => SDIO_HOST_RST_MASK, 242 | Peripheral::SDIO_SLAVE => SDIO_SLAVE_RST_MASK, 243 | _ => 0, 244 | }; 245 | dport.core_rst_en.modify(|r, w| w.bits(r.bits() | rst_mask)); 246 | dport 247 | .core_rst_en 248 | .modify(|r, w| w.bits(r.bits() & !rst_mask)); 249 | } else { 250 | // do nothing for WiFi / Bluetooth 251 | } 252 | }); 253 | } 254 | -------------------------------------------------------------------------------- /flash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # port esp32 is connected to (leave empty to autodetect) 4 | PORT= 5 | #PORT=/dev/ttyUSB1 6 | 7 | # baud rate for programming 8 | FLASH_BAUDRATE=921600 9 | 10 | # baud rate for terminal 11 | TERM_BAUDRATE=115200 12 | 13 | # Flash Mode 14 | FLASH_MODE="dio" 15 | 16 | # Flash Speed 17 | FLASH_SPEED="40m" 18 | 19 | # Use bootloader (needed for using irom, drom and psram) 20 | USE_BOOTLOADER=1 21 | 22 | # debug or release build 23 | TYPE=debug 24 | 25 | # address of partition table 26 | PARTION_ADDR=0x8000 27 | 28 | # address of application 29 | APP_ADDR=0x10000 30 | 31 | #source of the utility to generate the binary partition table 32 | GENPART_SOURCE=https://github.com/espressif/esp-idf/blob/v4.0/components/partition_table/gen_esp32part.py?raw=true 33 | 34 | # source of the bootloader 35 | # customized bootloader which initializes the external psram 36 | BOOTLOADER_SOURCE=https://github.com/arjanmels/esp32_bootloader_init_extram/blob/v1.0/build/bootloader/bootloader.bin?raw=true 37 | # default bootloader from the espressif arduino github 38 | #BOOTLOADER_SOURCE=https://github.com/espressif/arduino-esp32/blob/idf-release/v4.0/tools/sdk/bin/bootloader_dio_40m.bin?raw=true 39 | 40 | # color codes 41 | STAGE="\033[1;36m" 42 | SUCCESS="\033[1;32m" 43 | ERROR="\033[1;31m" 44 | RESET="\033[0m" 45 | 46 | 47 | 48 | showhelp() { 49 | cat << EOF 50 | Usage: flash -hrtbs [-p ] [-e ] 51 | 52 | -h, --help Display Help 53 | -r, --release Build in release mode 54 | -p, --port Set serial port (default: autodetect) 55 | -b, --baudrate Set baudrate for flasing (default: $FLASH_BAUDRATE) 56 | , --termbaudrate Set baudrate for monitoring (default: $TERM_BAUDRATE) 57 | -t, --terminal Open terminal program after flashing 58 | -e, --example Build the specified example 59 | -s, --skip Skip actual flashing 60 | EOF 61 | } 62 | 63 | # get command line options 64 | options=$(getopt -l "help,release,port:,terminal,baudrate:,termbaudrate:,example:,skip" -o "hrp:tb:e:s" -a -- "$@") 65 | 66 | if [[ $? -ne 0 ]] 67 | then 68 | echo 69 | showhelp 70 | exit 1 71 | fi 72 | 73 | eval set -- "$options" 74 | 75 | 76 | while true 77 | do 78 | case $1 in 79 | -h|--help) 80 | showhelp 81 | exit 0 82 | ;; 83 | -r|--release) 84 | export TYPE=release 85 | ;; 86 | -p|--port) 87 | shift 88 | export PORT=$1 89 | ;; 90 | -s|--skip) 91 | export SKIPFLASH=1 92 | ;; 93 | -t|--terminal) 94 | export TERMINAL=1 95 | ;; 96 | -b|--baudrate) 97 | shift 98 | export FLASH_BAUDRATE=$1 99 | ;; 100 | --termbaudrate) 101 | shift 102 | export TERM_BAUDRATE=$1 103 | ;; 104 | -e|--example) 105 | shift 106 | export EXAMPLE=$1 107 | ;; 108 | --) 109 | shift 110 | break;; 111 | esac 112 | shift 113 | done 114 | 115 | if [[ $# -ne 0 ]] 116 | then 117 | printf "${ERROR}*** Wrong number of arguments${RESET}\n\n" >&2 118 | showhelp 119 | exit 1 120 | fi 121 | 122 | if [[ ! -f Cargo.toml ]] 123 | then 124 | printf "${ERROR}*** Must be run in root of project where Cargo.toml file is located${RESET}\n" >&2 125 | exit 1 126 | fi 127 | 128 | if [[ -z "$EXAMPLE" ]] 129 | then 130 | FILE=$(awk 'BEGIN {FS="[ \t=\"]+";} /^\s*\[/ {section=$1} tolower(section)=="[package]" && tolower($0) ~ /^\s*name/ {print $2}' Cargo.toml) 131 | BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/$FILE 132 | EXAMPLE_ARG="" 133 | else 134 | BIN_PATH=target/xtensa-esp32-none-elf/$TYPE/examples/$EXAMPLE 135 | EXAMPLE_ARG="--example "$EXAMPLE 136 | fi 137 | 138 | # set the release flag 139 | if [ "$TYPE" = "release" ] 140 | then 141 | RELEASE="--release" 142 | else 143 | RELEASE="" 144 | fi 145 | 146 | CMD="cargo xbuild $RELEASE $EXAMPLE_ARG" 147 | 148 | printf "${STAGE}Building application with $CMD...${RESET}\n\n" 149 | 150 | rm target/current.elf 2> /dev/null 151 | rm target/current.bin 2> /dev/null 152 | 153 | 154 | # get error code of any step of the pipe 155 | set -o pipefail 156 | 157 | # run cargo & get missing features 158 | { FEATURES=$(script -efqc "$CMD 2>&1" /dev/null | tee /dev/stderr | egrep -o '\-\-features=".*"'; exit ${PIPESTATUS[0]}); } 159 | RES=$? 160 | 161 | # if cargo returned an error because features are missing to rerun 162 | if [[ $RES -ne 0 && -n $FEATURES ]] 163 | then 164 | CMD="cargo xbuild $RELEASE $EXAMPLE_ARG $FEATURES" 165 | printf "\n\n${STAGE}Building application with $CMD...${RESET}\n\n" 166 | eval $CMD 167 | RES=$? 168 | fi 169 | 170 | # if cargo returned an error exit 171 | if [[ $RES -ne 0 ]] 172 | then 173 | exit 1 174 | fi 175 | 176 | if [[ ! -f $BIN_PATH ]] 177 | then 178 | printf "${ERROR}Error: Output file ($BIN_PATH) not generated!${RESET}\n\n" 179 | exit 1 180 | fi 181 | 182 | #display section sizes 183 | echo 184 | xtensa-esp32-elf-readelf $BIN_PATH -S|egrep 'BIT|\[Nr\]' |awk 'BEGIN {FS="[ \t\[\]]+"} $9~/A|Flg/ {size=sprintf("%7d", "0x" $7)+0; printf("%-3s %-20s %-8s %-8s %-8s %8s %-3s %-3s\n",$2,$3,$4,$5,$7,((size>0)?size:$7),$9,$12); total+=size; } END { printf("\nTotal: %d bytes\n",total)}' 185 | echo 186 | 187 | # convert to bin 188 | rm $BIN_PATH.bin 2>/dev/null 189 | esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH > /dev/null 190 | if [ $? -ne 0 ] 191 | then 192 | printf "${ERROR}Error: Output file ($BIN_PATH).bin not generated!${RESET}\n\n" 193 | esptool.py --chip esp32 elf2image --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED -o $BIN_PATH.bin $BIN_PATH 194 | exit 1 195 | fi 196 | 197 | esptool.py --chip esp32 image_info $BIN_PATH.bin |egrep -v -i "esptool.py|Image version|Checksum|Validation Hash|Segments" 198 | echo 199 | 200 | if [ $? -ne 0 ] 201 | then 202 | exit 1 203 | fi 204 | 205 | # create links to output binaries for linking with debugger 206 | ln -sf $(pwd)/$BIN_PATH target/current.elf 207 | ln -sf $(pwd)/$BIN_PATH.bin target/current.bin 208 | 209 | if [[ $SKIPFLASH -ne 1 || $TERMINAL -eq 1 ]] 210 | then 211 | if [[ -z "$PORT" ]] 212 | then 213 | # kill terminal programs using any port 214 | ps -ef|grep "/dev/ttyUSB" |egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill 215 | printf "${STAGE}Detecting port...${RESET} " 216 | PORT=$(esptool.py --no-stub read_mac 2> /dev/null | awk '/^Serial port / {port=$3} END {print port}') 217 | if [[ -z $PORT ]] 218 | then 219 | printf "${ERROR}Error: cannot detect port!${RESET}\n\n" 220 | exit 1 221 | fi 222 | printf "$PORT\n\n" 223 | else 224 | # kill terminal programs using the same port 225 | ps -ef|grep $PORT|egrep -v "$0|grep" |awk '{print $2}'|xargs -r kill 226 | fi 227 | fi 228 | 229 | if [[ $SKIPFLASH -ne 1 ]] 230 | then 231 | flash() { 232 | echo -e "${STAGE}Flashing...${RESET} $@\n" 233 | esptool.py --chip esp32 --port $PORT --baud $FLASH_BAUDRATE --after hard_reset write_flash --flash_mode=$FLASH_MODE --flash_freq $FLASH_SPEED --flash_size detect ${@} |egrep -v -i "stub|baud rate|Changed.|Configuring flash size|Serial port|esptool.py|Leaving" 234 | } 235 | 236 | if [[ !USE_BOOTLOADER -eq 1 ]] 237 | then 238 | flash 0x1000 $BIN_PATH.bin 239 | else 240 | 241 | printf "${STAGE}Creating partition table... ${RESET}" 242 | if [[ target/partitions.bin -ot partitions.csv ]] 243 | then 244 | printf "\n\n" 245 | # get gen_esp32part.py and create binary partition table 246 | curl -s -S -f --max-time 5 -L $GENPART_SOURCE --output target/gen_esp32part.py_new 247 | 248 | if [ $? -ne 0 ]; then 249 | if [ -f target/gen_esp32part.py ]; then 250 | printf "\n${ERROR}Failed to get gen_esp32part.py${RESET} using old one\n\n" 251 | else 252 | printf "\n${ERROR}Failed to get gen_esp32part.py${RESET}\n\n" 253 | exit 1 254 | fi 255 | else 256 | mv target/gen_esp32part.py_new target/gen_esp32part.py 257 | fi 258 | 259 | rm target/partitions.bin 2> /dev/null 260 | python target/gen_esp32part.py partitions.csv target/partitions.bin 261 | 262 | echo 263 | else 264 | printf "skipping as it is up to date\n\n" 265 | fi 266 | 267 | 268 | printf "${STAGE}Getting bootloader... ${RESET}" 269 | 270 | # get bootloader.bin file 271 | # (different variants exist, but only difference is flash settings which are overriden by esptool) 272 | curl -s -S -f --max-time 5 -L $BOOTLOADER_SOURCE --output target/bootloader.bin_new 273 | 274 | if [ $? -ne 0 ]; then 275 | if [ -f target/bootloader.bin ]; then 276 | printf "\n${ERROR}Failed to get bootloader${RESET} using old one\n\n" 277 | else 278 | printf "\n${ERROR}Failed to get bootloader${RESET}\n\n" 279 | exit 1 280 | fi 281 | else 282 | mv target/bootloader.bin_new target/bootloader.bin 283 | printf "succesfully downloader bootloader\n\n" 284 | fi 285 | 286 | # check if bootloader.bin and paritions.bin are already correctly flashed (to prevent unnecessary writes) 287 | printf "${STAGE}Verify bootloader and partition table...${RESET} " 288 | esptool.py --no-stub --chip esp32 --port $PORT --baud $FLASH_BAUDRATE verify_flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin > /dev/null 289 | if [ $? -ne 0 ]; then 290 | printf "modified\n\n" 291 | # flash bootloader.bin, partitions.bin and application 292 | flash 0x1000 target/bootloader.bin $PARTION_ADDR target/partitions.bin $APP_ADDR $BIN_PATH.bin 293 | else 294 | printf "unchanged\n\n" 295 | # flash application only 296 | flash $APP_ADDR $BIN_PATH.bin 297 | fi 298 | fi 299 | 300 | if [[ $? -ne 0 ]] 301 | then 302 | printf "\n${ERROR}Error flashing application${RESET}\n\n" 303 | exit 1 304 | fi 305 | fi 306 | 307 | # start terminal program 308 | if [[ TERMINAL -eq 1 ]] 309 | then 310 | printf "\n${STAGE}Starting terminal.${RESET}\n" 311 | gnome-terminal --geometry 200x15+0+9999 -- picocom -b $TERM_BAUDRATE $PORT --imap lfcrlf 2>/dev/null 312 | fi 313 | 314 | 315 | if [[ $SKIPFLASH -ne 1 ]] 316 | then 317 | printf "\n${SUCCESS}Succesfully build and flashed application${RESET}\n\n" 318 | else 319 | printf "\n${SUCCESS}Succesfully build application${RESET}\n\n" 320 | fi -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. --------------------------------------------------------------------------------