Implement DelayMs and DelayUs traits from embedded-hal

This commit is contained in:
Jesse Braham 2022-01-10 13:40:16 -08:00
parent a9cf53a001
commit 9a030cbf78
10 changed files with 129 additions and 21 deletions

View File

@ -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?

View File

@ -0,0 +1,95 @@
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
pub use self::delay::Delay;
impl<T> DelayMs<T> for Delay
where
T: Into<u32>,
{
fn delay_ms(&mut self, ms: T) {
for _ in 0..ms.into() {
self.delay_us(1000u32);
}
}
}
impl<T> DelayUs<T> for Delay
where
T: Into<u32>,
{
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);
}
}
}

View File

@ -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;

View File

@ -1,4 +1,5 @@
pub use embedded_hal::{
blocking::delay::{DelayMs as _, DelayUs as _},
digital::v2::{
InputPin as _,
OutputPin as _,

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {}

View File

@ -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};