From 9a030cbf78024f39e8386d8d7dcce64a162351ba Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Mon, 10 Jan 2022 13:40:16 -0800 Subject: [PATCH] Implement DelayMs and DelayUs traits from embedded-hal --- README.md | 2 +- esp-hal-common/src/delay.rs | 95 ++++++++++++++++++++++++++++++++++ esp-hal-common/src/lib.rs | 2 + esp-hal-common/src/prelude.rs | 1 + esp32-hal/examples/blinky.rs | 18 ++++--- esp32-hal/src/lib.rs | 7 +-- esp32c3-hal/examples/blinky.rs | 19 ++++--- esp32c3-hal/src/lib.rs | 2 +- esp32s2-hal/src/lib.rs | 2 +- esp32s3-hal/src/lib.rs | 2 +- 10 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 esp-hal-common/src/delay.rs diff --git a/README.md b/README.md index a45a39858..3b25125d1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The various packages in this repository may or may not build at any given time. ## What is working? -For the **ESP32** and **ESP32-C3**, the `GPIO`, `TIMG` and `UART` peripherals have (probably incomplete) implementations which are nonetheless functional. These packages include examples to demonstrate these peripherals. +For the **ESP32** and **ESP32-C3**, the `GPIO`, `TIMG` and `UART` peripherals have (probably incomplete) implementations which are nonetheless functional. Both of the aforementioned chips also have implmented the `DelayUs` and `DelayMs` traits from `embedded-hal`. These packages include examples to demonstrate these peripherals. ## What is NOT working? diff --git a/esp-hal-common/src/delay.rs b/esp-hal-common/src/delay.rs new file mode 100644 index 000000000..1173237a6 --- /dev/null +++ b/esp-hal-common/src/delay.rs @@ -0,0 +1,95 @@ +use embedded_hal::blocking::delay::{DelayMs, DelayUs}; + +pub use self::delay::Delay; + +impl DelayMs for Delay +where + T: Into, +{ + fn delay_ms(&mut self, ms: T) { + for _ in 0..ms.into() { + self.delay_us(1000u32); + } + } +} + +impl DelayUs for Delay +where + T: Into, +{ + fn delay_us(&mut self, us: T) { + self.delay(us.into()); + } +} + +// The delay implementation for RISC-V devices (ESP32-C3) uses the `SYSTIMER` +// peripheral, as unfortunately this device does NOT implement the `mcycle` CSR. +#[cfg(feature = "esp32c3")] +mod delay { + use crate::pac::SYSTIMER; + + // The counters and comparators are driven using `XTAL_CLK`. The average clock + // frequency is fXTAL_CLK/2.5, which is 16 MHz. The timer counting is + // incremented by 1/16 μs on each `CNT_CLK` cycle. + const CLK_FREQ_HZ: u64 = 16_000_000; + + pub struct Delay { + systimer: SYSTIMER, + } + + impl Delay { + pub fn new(systimer: SYSTIMER) -> Self { + Self { systimer } + } + + pub fn delay(&self, us: u32) { + let t0 = self.unit0_value(); + let clocks = (us as u64 * CLK_FREQ_HZ) / 1_000_000; + + while self.unit0_value().wrapping_sub(t0) <= clocks {} + } + + #[inline(always)] + fn unit0_value(&self) -> u64 { + self.systimer + .unit0_op + .write(|w| w.timer_unit0_update().set_bit()); + + while !self + .systimer + .unit0_op + .read() + .timer_unit0_value_valid() + .bit_is_set() + {} + + let value_lo = self.systimer.unit0_value_lo.read().bits(); + let value_hi = self.systimer.unit0_value_hi.read().bits(); + + ((value_hi as u64) << 32) | value_lo as u64 + } + } +} + +// The delay implementation for Xtensa devices (ESP32, ESP32-S2, ESP32-S3) uses +// the built-in timer from the `xtensa_lx` crate. +#[cfg(not(feature = "esp32c3"))] +mod delay { + // FIXME: The ESP32-S2 and ESP32-S3 have fixed crystal frequencies of 40MHz. + // This will not always be the case when using the ESP32. + const CLK_FREQ_HZ: u64 = 40_000_000; + + #[derive(Default)] + pub struct Delay; + + impl Delay { + pub fn new() -> Self { + Self + } + + pub fn delay(&self, us: u32) { + let clocks = (us as u64 * CLK_FREQ_HZ) / 1_000_000; + xtensa_lx::timer::delay(clocks as u32); + } + } +} diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 12718b11b..5ddd96838 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -9,11 +9,13 @@ pub use esp32s2_pac as pac; #[cfg(feature = "esp32s3")] pub use esp32s3_pac as pac; +pub mod delay; pub mod gpio; pub mod prelude; pub mod serial; pub mod timer; +pub use delay::Delay; pub use gpio::*; pub use serial::Serial; pub use timer::Timer; diff --git a/esp-hal-common/src/prelude.rs b/esp-hal-common/src/prelude.rs index 8a9e87a91..9380bdf1d 100644 --- a/esp-hal-common/src/prelude.rs +++ b/esp-hal-common/src/prelude.rs @@ -1,4 +1,5 @@ pub use embedded_hal::{ + blocking::delay::{DelayMs as _, DelayUs as _}, digital::v2::{ InputPin as _, OutputPin as _, diff --git a/esp32-hal/examples/blinky.rs b/esp32-hal/examples/blinky.rs index 04a0fac92..fd75b6e01 100644 --- a/esp32-hal/examples/blinky.rs +++ b/esp32-hal/examples/blinky.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] -use esp32_hal::{gpio::IO, pac::Peripherals, prelude::*, Timer}; -use nb::block; +use esp32_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, Timer}; use panic_halt as _; use xtensa_lx_rt::entry; @@ -10,18 +9,23 @@ use xtensa_lx_rt::entry; fn main() -> ! { let peripherals = Peripherals::take().unwrap(); - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = io.pins.gpio15.into_push_pull_output(); + // Disable the TIMG watchdog timer. let mut timer0 = Timer::new(peripherals.TIMG0); - // Disable watchdog timer timer0.disable(); + // Set GPIO15 as an output, and set its state high initially. + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio15.into_push_pull_output(); + led.set_high().unwrap(); - timer0.start(10_000_000u64); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(); loop { led.toggle().unwrap(); - block!(timer0.wait()).unwrap(); + delay.delay_ms(500u32); } } diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index d87d17d67..1043af72b 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{pac, prelude, Serial, Timer}; +pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer}; pub use self::gpio::IO; @@ -45,8 +45,9 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { xtensa_lx_rt::Reset(); } -/// The esp32 has a first stage bootloader that handles loading program data into the right place -/// therefore we skip loading it again. +/// The ESP32 has a first stage bootloader that handles loading program data +/// into the right place therefore we skip loading it again. +#[doc(hidden)] #[no_mangle] #[rustfmt::skip] pub extern "Rust" fn __init_data() -> bool { diff --git a/esp32c3-hal/examples/blinky.rs b/esp32c3-hal/examples/blinky.rs index f48cd7ec9..200275968 100644 --- a/esp32c3-hal/examples/blinky.rs +++ b/esp32c3-hal/examples/blinky.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] -use esp32c3_hal::{gpio::IO, pac::Peripherals, prelude::*, RtcCntl, Timer}; -use nb::block; +use esp32c3_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, RtcCntl, Timer}; use panic_halt as _; use riscv_rt::entry; @@ -10,23 +9,29 @@ use riscv_rt::entry; fn main() -> ! { let peripherals = Peripherals::take().unwrap(); - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = io.pins.gpio5.into_push_pull_output(); + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. let rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); let mut timer0 = Timer::new(peripherals.TIMG0); let mut timer1 = Timer::new(peripherals.TIMG1); - // Disable watchdog timers rtc_cntl.set_super_wdt_enable(false); rtc_cntl.set_wdt_enable(false); timer0.disable(); timer1.disable(); + // Set GPIO5 as an output, and set its state high initially. + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio5.into_push_pull_output(); + led.set_high().unwrap(); - timer0.start(10_000_000u64); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(peripherals.SYSTIMER); loop { led.toggle().unwrap(); - block!(timer0.wait()).unwrap(); + delay.delay_ms(500u32); } } diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 159df9618..c17ea3023 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{pac, prelude, Serial, Timer}; +pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer}; pub mod gpio; pub mod rtc_cntl; diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 4e5d793e4..0ad0b5e3b 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{pac, prelude, Serial, Timer}; +pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer}; #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index 1cfcbdb32..f56ba41af 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -1,4 +1,4 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{pac, prelude, Serial, Timer}; +pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer};