From 2b6de3a3c9e5666d97a3baa7a280921a19b5b810 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Sat, 2 Oct 2021 07:36:40 +0200 Subject: [PATCH] implement embedded-hal 1.0.0-alpha.5 (#131) * implement embedded-hal 1.0.0-alpha.5 * Depend on specific alpha version of embedded-hal. * enable feature eh1_0_alpha for CI check --- .github/workflows/check.yml | 2 +- rp2040-hal/Cargo.toml | 1 + rp2040-hal/README.md | 12 +++++ rp2040-hal/src/adc.rs | 49 ++++++++++++++++++++ rp2040-hal/src/gpio/dynpin.rs | 49 ++++++++++++++++++++ rp2040-hal/src/gpio/pin.rs | 84 +++++++++++++++++++++++++++++++++++ rp2040-hal/src/i2c.rs | 26 +++++++++++ rp2040-hal/src/pwm/mod.rs | 65 +++++++++++++++++++++++++++ rp2040-hal/src/spi.rs | 28 ++++++++++++ rp2040-hal/src/timer.rs | 43 ++++++++++++++++++ rp2040-hal/src/uart.rs | 37 +++++++++++++++ rp2040-hal/src/watchdog.rs | 43 ++++++++++++++++++ 12 files changed, 438 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 29f3217..aea48a6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -19,7 +19,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check - args: --workspace --examples + args: --workspace --examples --features eh1_0_alpha - uses: actions-rs/cargo@v1 with: command: test diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 6598c61..e64919a 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -12,6 +12,7 @@ license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7.2" embedded-hal = { version = "0.2.5", features = ["unproven"] } +eh1_0_alpha = { version = "=1.0.0-alpha.5", package="embedded-hal", optional=true } embedded-time = "0.12.0" itertools = { version = "0.10.1", default-features = false } nb = "1.0" diff --git a/rp2040-hal/README.md b/rp2040-hal/README.md index bdc4e66..84d411f 100644 --- a/rp2040-hal/README.md +++ b/rp2040-hal/README.md @@ -90,6 +90,18 @@ volatile until a 1.0.0 release. See the [open issues](https://github.com/rp-rs/rp-hal/issues) for a list of proposed features (and known issues). +### Support for embedded-hal 1.0 + +We plan to support embedded-hal 1.0 soon after it is released. + +For now, there is preliminary support for alpha versions of embedded-hal, which can +be enabled with the feature `eh1_0_alpha`. Please note that this support does not +provide any semver compatibility guarantees: With that feature activated, there +will be breaking changes even in minor versions of rp2040-hal. + +Support for embedded-hal 1.0(-alpha) exists in parallel to support for +embedded-hal 0.2: Traits of both versions are implemented and can be used +at the same time. ## Contributing diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index eac89fd..5f8afb3 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -103,6 +103,15 @@ macro_rules! channel { $channel } } + + #[cfg(feature = "eh1_0_alpha")] + impl eh1_0_alpha::adc::nb::Channel for Pin<$pin, FloatingInput> { + type ID = u8; // ADC channels are identified numerically + + fn channel(&self) -> u8 { + $channel + } + } }; } @@ -124,6 +133,15 @@ impl Channel for TempSense { } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::adc::nb::Channel for TempSense { + type ID = u8; // ADC channels are identified numerically + + fn channel(&self) -> u8 { + TEMPERATURE_SENSOR_CHANNEL + } +} + impl OneShot for Adc where WORD: From, @@ -153,3 +171,34 @@ where Ok(self.device.result.read().result().bits().into()) } } + +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::adc::nb::OneShot for Adc +where + WORD: From, + PIN: eh1_0_alpha::adc::nb::Channel, +{ + type Error = (); + + fn read(&mut self, pin: &mut PIN) -> nb::Result { + let chan = PIN::channel(pin); + + if chan == 4 { + self.device.cs.modify(|_, w| w.ts_en().set_bit()) + } + + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + self.device + .cs + .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + Ok(self.device.result.read().result().bits().into()) + } +} diff --git a/rp2040-hal/src/gpio/dynpin.rs b/rp2040-hal/src/gpio/dynpin.rs index 4cb275f..dd4c455 100644 --- a/rp2040-hal/src/gpio/dynpin.rs +++ b/rp2040-hal/src/gpio/dynpin.rs @@ -78,6 +78,8 @@ use super::pin::{Pin, PinId, PinMode, ValidPinMode}; use super::reg::RegisterInterface; use core::convert::TryFrom; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::digital::blocking as eh1; use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; //============================================================================== @@ -541,3 +543,50 @@ impl StatefulOutputPin for DynPin { self._is_set_low() } } + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::OutputPin for DynPin { + type Error = Error; + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high() + } + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low() + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::InputPin for DynPin { + type Error = Error; + #[inline] + fn is_high(&self) -> Result { + self._is_high() + } + #[inline] + fn is_low(&self) -> Result { + self._is_low() + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::ToggleableOutputPin for DynPin { + type Error = Error; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle() + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::StatefulOutputPin for DynPin { + #[inline] + fn is_set_high(&self) -> Result { + self._is_set_high() + } + #[inline] + fn is_set_low(&self) -> Result { + self._is_set_low() + } +} diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index edf6054..03bce9b 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -103,6 +103,8 @@ use core::convert::Infallible; use core::marker::PhantomData; use crate::gpio::dynpin::DynFunction; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::digital::blocking as eh1; use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; use core::mem::transmute; @@ -792,6 +794,88 @@ where } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::OutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + type Error = Infallible; + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::InputPin for Pin +where + I: PinId, +{ + type Error = Infallible; + #[inline] + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::InputPin for Pin> +where + I: PinId, + C: InputConfig, +{ + type Error = Infallible; + #[inline] + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::ToggleableOutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +impl eh1::StatefulOutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + #[inline] + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + #[inline] + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } +} + //============================================================================== // Pin definitions //============================================================================== diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index d38f635..eadbd59 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -52,6 +52,8 @@ use crate::{ resets::SubsystemReset, typelevel::Sealed, }; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::i2c::blocking as eh1; use embedded_time::rate::Hertz; use hal::blocking::i2c::{Read, Write, WriteRead}; use rp2040_pac::{I2C0, I2C1, RESETS}; @@ -490,6 +492,30 @@ macro_rules! hal { } } + #[cfg(feature = "eh1_0_alpha")] + impl eh1::Write for I2C<$I2CX, PINS> { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + Write::write(self, addr, bytes) + } + } + #[cfg(feature = "eh1_0_alpha")] + impl eh1::WriteRead for I2C<$I2CX, PINS> { + type Error = Error; + fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + WriteRead::write_read(self, addr, bytes, buffer) + } + } + #[cfg(feature = "eh1_0_alpha")] + impl eh1::Read for I2C<$I2CX, PINS> { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + Read::read(self, addr, buffer) + } + } + )+ } } diff --git a/rp2040-hal/src/pwm/mod.rs b/rp2040-hal/src/pwm/mod.rs index d0ae990..15beaa8 100644 --- a/rp2040-hal/src/pwm/mod.rs +++ b/rp2040-hal/src/pwm/mod.rs @@ -87,6 +87,8 @@ use crate::{ resets::SubsystemReset, typelevel::Sealed, }; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::pwm::blocking as eh1; use embedded_hal::PwmPin; use pac::PWM; @@ -551,6 +553,38 @@ impl PwmPin for Channel { } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::PwmPin for Channel { + type Duty = u16; + type Error = core::convert::Infallible; + + /// We cant disable the channel without disturbing the other channel. + /// So this just sets the duty cycle to zero + fn disable(&mut self) -> Result<(), Self::Error> { + self.duty_cycle = self.regs.read_cc_a(); + self.regs.write_cc_a(0); + Ok(()) + } + + fn enable(&mut self) -> Result<(), Self::Error> { + self.regs.write_cc_a(self.duty_cycle); + Ok(()) + } + + fn get_duty(&self) -> Result { + Ok(self.regs.read_cc_a()) + } + + fn get_max_duty(&self) -> Result { + Ok(self.regs.read_top()) + } + + fn set_duty(&mut self, duty: Self::Duty) -> Result<(), Self::Error> { + self.regs.write_cc_a(duty); + Ok(()) + } +} + impl PwmPin for Channel { type Duty = u16; @@ -577,6 +611,37 @@ impl PwmPin for Channel { self.regs.write_cc_b(duty) } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::PwmPin for Channel { + type Duty = u16; + type Error = core::convert::Infallible; + + /// We cant disable the channel without disturbing the other channel. + /// So this just sets the duty cycle to zero + fn disable(&mut self) -> Result<(), Self::Error> { + self.duty_cycle = self.regs.read_cc_b(); + self.regs.write_cc_b(0); + Ok(()) + } + + fn enable(&mut self) -> Result<(), Self::Error> { + self.regs.write_cc_b(self.duty_cycle); + Ok(()) + } + + fn get_duty(&self) -> Result { + Ok(self.regs.read_cc_b()) + } + + fn get_max_duty(&self) -> Result { + Ok(self.regs.read_top()) + } + + fn set_duty(&mut self, duty: Self::Duty) -> Result<(), Self::Error> { + self.regs.write_cc_b(duty); + Ok(()) + } +} impl> Channel { /// Capture a gpio pin and use it as pwm output for channel A diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index 3d4ee52..91e46ad 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -21,6 +21,8 @@ use crate::resets::SubsystemReset; use core::{convert::Infallible, marker::PhantomData, ops::Deref}; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::spi as eh1; use embedded_hal::blocking::spi; use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity}; use embedded_time::rate::*; @@ -217,6 +219,32 @@ macro_rules! impl_write { impl spi::transfer::Default<$type> for Spi {} impl spi::write_iter::Default<$type> for Spi {} + #[cfg(feature = "eh1_0_alpha")] + impl eh1::nb::FullDuplex<$type> for Spi { + type Error = Infallible; + + fn read(&mut self) -> Result<$type, nb::Error> { + if !self.is_readable() { + return Err(nb::Error::WouldBlock); + } + + Ok(self.device.sspdr.read().data().bits() as $type) + } + fn write(&mut self, word: $type) -> Result<(), nb::Error> { + // Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX + // is full, PL022 inhibits RX pushes, and sets a sticky flag on + // push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set. + if !self.is_writable() { + return Err(nb::Error::WouldBlock); + } + + self.device + .sspdr + .write(|w| unsafe { w.data().bits(word as u16) }); + Ok(()) + } + } + )+ }; diff --git a/rp2040-hal/src/timer.rs b/rp2040-hal/src/timer.rs index 8e0dd51..89a2d11 100644 --- a/rp2040-hal/src/timer.rs +++ b/rp2040-hal/src/timer.rs @@ -77,7 +77,39 @@ impl embedded_hal::timer::CountDown for CountDown<'_> { } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::timer::nb::CountDown for CountDown<'_> { + type Time = embedded_time::duration::Microseconds; + type Error = &'static str; + + fn start(&mut self, count: T) -> Result<(), Self::Error> + where + T: Into, + { + self.period = count.into(); + self.next_end = Some(self.timer.get_counter().wrapping_add(self.period.0)); + Ok(()) + } + + fn wait(&mut self) -> nb::Result<(), Self::Error> { + if let Some(end) = self.next_end { + let ts = self.timer.get_counter(); + if ts >= end { + self.next_end = Some(end.wrapping_add(self.period.0)); + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } else { + panic!("CountDown is not running!"); + } + } +} + impl embedded_hal::timer::Periodic for CountDown<'_> {} +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::timer::Periodic for CountDown<'_> {} + impl embedded_hal::timer::Cancel for CountDown<'_> { type Error = &'static str; @@ -90,3 +122,14 @@ impl embedded_hal::timer::Cancel for CountDown<'_> { } } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1_0_alpha::timer::nb::Cancel for CountDown<'_> { + fn cancel(&mut self) -> Result<(), Self::Error> { + if self.next_end.is_none() { + Err("CountDown is not running.") + } else { + self.next_end = None; + Ok(()) + } + } +} diff --git a/rp2040-hal/src/uart.rs b/rp2040-hal/src/uart.rs index 174c651..20c55bc 100644 --- a/rp2040-hal/src/uart.rs +++ b/rp2040-hal/src/uart.rs @@ -37,6 +37,8 @@ use embedded_time::fixed_point::FixedPoint; use embedded_time::rate::Baud; use embedded_time::rate::Hertz; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::serial::nb as eh1; use embedded_hal::serial::{Read, Write}; use nb::Error::{Other, WouldBlock}; @@ -65,6 +67,7 @@ pub struct ReadError<'err> { } /// Possible types of read errors. See Chapter 4, Section 2 ยง8 - Table 436: "UARTDR Register" +#[cfg_attr(feature = "eh1_0_alpha", derive(Debug))] pub enum ReadErrorType { /// Triggered when the FIFO (or shift-register) is overflowed. Overrun, @@ -495,6 +498,23 @@ impl Read for UartPeripheral { } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::Read for UartPeripheral { + type Error = ReadErrorType; + + fn read(&mut self) -> nb::Result { + let byte: &mut [u8] = &mut [0; 1]; + + match self.read_raw(byte) { + Ok(_) => Ok(byte[0]), + Err(e) => match e { + Other(inner) => Err(Other(inner.err_type)), + WouldBlock => Err(WouldBlock), + }, + } + } +} + impl Write for UartPeripheral { type Error = Infallible; @@ -511,6 +531,23 @@ impl Write for UartPeripheral { } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::Write for UartPeripheral { + type Error = Infallible; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + if self.write_raw(&[word]).is_err() { + Err(WouldBlock) + } else { + Ok(()) + } + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.transmit_flushed() + } +} + impl fmt::Write for UartPeripheral { fn write_str(&mut self, s: &str) -> fmt::Result { s.bytes() diff --git a/rp2040-hal/src/watchdog.rs b/rp2040-hal/src/watchdog.rs index 8b300a5..a355268 100644 --- a/rp2040-hal/src/watchdog.rs +++ b/rp2040-hal/src/watchdog.rs @@ -35,6 +35,8 @@ //! See [examples/watchdog.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/watchdog.rs) for a more complete example use crate::pac::WATCHDOG; +#[cfg(feature = "eh1_0_alpha")] +use eh1_0_alpha::watchdog::blocking as eh1; use embedded_hal::watchdog; use embedded_time::{duration, fixed_point::FixedPoint}; @@ -97,6 +99,15 @@ impl watchdog::Watchdog for Watchdog { self.load_counter(self.delay_ms) } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::Watchdog for Watchdog { + type Error = core::convert::Infallible; + + fn feed(&mut self) -> Result<(), Self::Error> { + self.load_counter(self.delay_ms); + Ok(()) + } +} impl watchdog::WatchdogEnable for Watchdog { type Time = duration::Microseconds; @@ -117,9 +128,41 @@ impl watchdog::WatchdogEnable for Watchdog { self.enable(true); } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::Enable for Watchdog { + type Error = core::convert::Infallible; + type Target = Self; + type Time = duration::Microseconds; + + fn start>(mut self, period: T) -> Result { + const MAX_PERIOD: u32 = 0xFFFFFF; + + // Due to a logic error, the watchdog decrements by 2 and + // the load value must be compensated; see RP2040-E1 + self.delay_ms = period.into().integer() * 2; + + if self.delay_ms > MAX_PERIOD { + panic!("Period cannot exceed maximum load value of {}", MAX_PERIOD); + } + + self.enable(false); + self.load_counter(self.delay_ms); + self.enable(true); + Ok(self) + } +} impl watchdog::WatchdogDisable for Watchdog { fn disable(&mut self) { self.enable(false) } } +#[cfg(feature = "eh1_0_alpha")] +impl eh1::Disable for Watchdog { + type Error = core::convert::Infallible; + type Target = Self; + fn disable(self) -> Result { + self.enable(false); + Ok(self) + } +}