diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 432c595..494dbc1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,12 +7,12 @@ name: Continuous integration jobs: ci-linux: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 continue-on-error: ${{ matrix.experimental || false }} strategy: matrix: - # All generated code should be running on stable now, MRSV is 1.60.0 - rust: [nightly, stable, 1.60.0] + # All generated code should be running on stable now, MRSV is 1.62.0 + rust: [nightly, stable, 1.62.0] include: # Nightly is only for reference and allowed to fail diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml index 0727384..849d298 100644 --- a/.github/workflows/rustfmt.yaml +++ b/.github/workflows/rustfmt.yaml @@ -9,7 +9,7 @@ name: Code formatting check jobs: fmt: name: Rustfmt - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index aa47fb6..a26b868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -- Use `portable-atomic` to allow builds on `riscv32imc-unknown-none-elf`` targets when needed. +### Added +- Added `embedded-io` dependency to fill in basic traits for `embedded-hal` version `1.0` + +### Changed +- Use `portable-atomic` to allow builds on `riscv32imc-unknown-none-elf`` targets when needed +- Update `embedded-hal` version to `1.0` changing `delay`, `spi`, `i2c`, `gpio` and `pwm` modules to match +- `I2c::free` renamed to `I2c::release` +- Update `riscv` to `0.11` +- Bumped MSRV to `1.62` + +### Removed +- Removed `nb` dependency ## [v0.10.0] - 2023-03-28 diff --git a/Cargo.toml b/Cargo.toml index ca7141c..76a8c5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,16 +8,16 @@ description = "HAL for the E310x family of microcontrollers." keywords = ["riscv", "e310", "hal"] license = "ISC" edition = "2018" -rust-version = "1.59" +rust-version = "1.62" [dependencies] -embedded-hal = { version = "0.2.6", features = ["unproven"] } -nb = "1.0.0" -riscv = { version = "0.10.1", features = ["critical-section-single-hart"] } +embedded-hal = "1.0.0" +embedded-io = "0.6.1" +riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } e310x = { version = "0.11.0", features = ["rt", "critical-section"] } [target.'cfg(not(target_has_atomic = "32"))'.dependencies] -portable-atomic = { version = "1.4", default-features = false, features = ["unsafe-assume-single-core"] } +portable-atomic = { version = "1.6", default-features = false, features = ["unsafe-assume-single-core"] } [features] g002 = ["e310x/g002"] diff --git a/README.md b/README.md index fe081e8..21fdc09 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This project is developed and maintained by the [RISC-V team][team]. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.62.0 and up. It *might* compile with older versions but that may change in any new patch release. ## License diff --git a/src/delay.rs b/src/delay.rs index 4778bf1..4920b1b 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -2,7 +2,7 @@ use crate::clock::Clocks; use crate::core::clint::{MTIME, MTIMECMP}; -use embedded_hal::blocking::delay::{DelayMs, DelayUs}; +use embedded_hal::delay::DelayNs; use riscv::register::{mie, mip}; /// Machine timer (mtime) as a busyloop delay provider @@ -17,9 +17,9 @@ impl Delay { } } -impl DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - let ticks = (us as u64) * TICKS_PER_SECOND / 1_000_000; +impl DelayNs for Delay { + fn delay_ns(&mut self, ns: u32) { + let ticks = (ns as u64) * TICKS_PER_SECOND / 1_000_000_000; let mtime = MTIME; let t = mtime.mtime() + ticks; @@ -27,58 +27,6 @@ impl DelayUs for Delay { } } -// This is a workaround to allow `delay_us(42)` construction without specifying a type. -impl DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: i32) { - assert!(us >= 0); - self.delay_us(us as u32); - } -} - -impl DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u16) { - self.delay_us(u32::from(us)); - } -} - -impl DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u8) { - self.delay_us(u32::from(us)); - } -} - -impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u32) { - self.delay_us(ms * 1000); - } -} - -// This is a workaround to allow `delay_ms(42)` construction without specifying a type. -impl DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: i32) { - assert!(ms >= 0); - self.delay_ms(ms as u32); - } -} - -impl DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: u16) { - self.delay_ms(u32::from(ms)); - } -} - -impl DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: u8) { - self.delay_ms(u32::from(ms)); - } -} - /// Machine timer (mtime) as a sleep delay provider using mtimecmp pub struct Sleep { clock_freq: u32, @@ -95,9 +43,9 @@ impl Sleep { } } -impl DelayMs for Sleep { - fn delay_ms(&mut self, ms: u32) { - let ticks = (ms as u64) * (self.clock_freq as u64) / 1000; +impl DelayNs for Sleep { + fn delay_ns(&mut self, ns: u32) { + let ticks = (ns as u64) * (self.clock_freq as u64) / 1_000_000_000; let t = MTIME.mtime() + ticks; self.mtimecmp.set_mtimecmp(t); @@ -112,9 +60,7 @@ impl DelayMs for Sleep { // after which empty handler gets called and we go into the // next iteration of this loop loop { - unsafe { - riscv::asm::wfi(); - } + riscv::asm::wfi(); // check if we got the right interrupt cause, otherwise just loop back to wfi if mip::read().mtimer() { @@ -128,26 +74,3 @@ impl DelayMs for Sleep { } } } - -// This is a workaround to allow `delay_ms(42)` construction without specifying a type. -impl DelayMs for Sleep { - #[inline(always)] - fn delay_ms(&mut self, ms: i32) { - assert!(ms >= 0); - self.delay_ms(ms as u32); - } -} - -impl DelayMs for Sleep { - #[inline(always)] - fn delay_ms(&mut self, ms: u16) { - self.delay_ms(u32::from(ms)); - } -} - -impl DelayMs for Sleep { - #[inline(always)] - fn delay_ms(&mut self, ms: u8) { - self.delay_ms(u32::from(ms)); - } -} diff --git a/src/gpio.rs b/src/gpio.rs index 1061884..d2776ae 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -146,8 +146,7 @@ macro_rules! gpio { use core::marker::PhantomData; use core::convert::Infallible; - use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, - ToggleableOutputPin}; + use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin}; use e310x::$GPIOX; use super::{Unknown, IOF0, IOF1, Drive, Floating, GpioExt, Input, Invert, NoInvert, Output, PullUp, Regular, PinIndex, PeripheralAccess}; @@ -189,6 +188,10 @@ macro_rules! gpio { const INDEX: usize = $i; } + impl embedded_hal::digital::ErrorType for $PXi { + type Error = Infallible; + } + impl $PXi { /// Configures the pin to serve as alternate function 0 (AF0) pub fn into_iof0(self) -> $PXi> { @@ -280,31 +283,27 @@ macro_rules! gpio { } impl InputPin for $PXi> { - type Error = Infallible; - - fn is_high(&self) -> Result { + fn is_high(&mut self) -> Result { Ok($GPIOX::input_value(Self::INDEX)) } - fn is_low(&self) -> Result { + fn is_low(&mut self) -> Result { Ok(!self.is_high()?) } } impl StatefulOutputPin for $PXi> { - fn is_set_high(&self) -> Result { + fn is_set_high(&mut self) -> Result { Ok($GPIOX::input_value(Self::INDEX)) } - fn is_set_low(&self) -> Result { + fn is_set_low(&mut self) -> Result { Ok(!self.is_set_high()?) } } impl OutputPin for $PXi> { - type Error = Infallible; - fn set_high(&mut self) -> Result<(), Infallible> { $GPIOX::set_output_value(Self::INDEX, true); Ok(()) @@ -315,16 +314,6 @@ macro_rules! gpio { Ok(()) } } - - impl ToggleableOutputPin for $PXi> { - type Error = Infallible; - - /// Toggles the pin state. - fn toggle(&mut self) -> Result<(), Infallible> { - $GPIOX::toggle_pin(Self::INDEX); - Ok(()) - } - } )+ } } diff --git a/src/i2c.rs b/src/i2c.rs index d969add..e5f0a91 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -16,7 +16,7 @@ use crate::time::Bps; use core::mem; use core::ops::Deref; use e310x::{i2c0, I2C0}; -use embedded_hal::blocking::i2c::{Read, Write, WriteRead}; +use embedded_hal::i2c::{ErrorKind, ErrorType, I2c as I2cTrait, Operation}; /// SDA pin - DO NOT IMPLEMENT THIS TRAIT pub unsafe trait SdaPin {} @@ -27,7 +27,7 @@ unsafe impl SdaPin for gpio0::Pin12> {} unsafe impl SclPin for gpio0::Pin13> {} /// I2C error -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Error { /// Invalid peripheral state InvalidState, @@ -98,7 +98,7 @@ impl I2c { impl I2c { /// Releases the I2C peripheral and associated pins - pub fn free(self) -> (I2C, PINS) { + pub fn release(self) -> (I2C, PINS) { (self.i2c, self.pins) } } @@ -179,9 +179,23 @@ impl, PINS> I2c { const FLAG_READ: u8 = 1; const FLAG_WRITE: u8 = 0; -impl, PINS> Read for I2c { +// e-h 1.0 + +impl embedded_hal::i2c::Error for Error { + fn kind(&self) -> ErrorKind { + match *self { + Error::ArbitrationLost => ErrorKind::ArbitrationLoss, + Error::NoAck => ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Data), + Error::InvalidState => ErrorKind::Other, + } + } +} + +impl, PINS> ErrorType for I2c { type Error = Error; +} +impl, PINS> I2cTrait for I2c { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.reset(); @@ -212,10 +226,6 @@ impl, PINS> Read for I2c { } Ok(()) } -} - -impl, PINS> Write for I2c { - type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { self.reset(); @@ -244,10 +254,6 @@ impl, PINS> Write for I2c { } Ok(()) } -} - -impl, PINS> WriteRead for I2c { - type Error = Error; fn write_read( &mut self, @@ -262,9 +268,9 @@ impl, PINS> WriteRead for I2c, PINS> WriteRead for I2c], + ) -> Result<(), Self::Error> { + for op in operations { + match op { + Operation::Read(buf) => I2cTrait::read(self, address, buf)?, + Operation::Write(buf) => I2cTrait::write(self, address, buf)?, + } + } + + Ok(()) + } } diff --git a/src/prelude.rs b/src/prelude.rs index c6ccd12..9925108 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,9 +7,3 @@ pub use crate::rtc::RtcExt as _e310x_hal_rtc_RtcExt; pub use crate::stdout::Write as _e310x_hal_stdout_Write; pub use crate::time::U32Ext as _e310x_hal_time_U32Ext; pub use crate::wdog::WdogExt as _e310x_hal_wdog_WdogExt; -pub use embedded_hal::digital::v2::{ - InputPin as _embedded_hal_digital_v2_InputPin, OutputPin as _embedded_hal_digital_v2_OutputPin, - StatefulOutputPin as _embedded_hal_digital_v2_StatefulOutputPin, - ToggleableOutputPin as _embedded_hal_digital_v2_ToggleableOutputPin, -}; -pub use embedded_hal::prelude::*; diff --git a/src/pwm.rs b/src/pwm.rs index 7db8bf5..009edcf 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -175,14 +175,9 @@ impl Pwm { } } -impl embedded_hal::Pwm for Pwm { - type Channel = Channel; - - type Time = PWM::CmpWidth; - - type Duty = PWM::CmpWidth; - - fn enable(&mut self, channel: Self::Channel) { +impl Pwm { + /// Enables a PWM channel + pub fn enable(&mut self, channel: Channel) { match channel.cmp_index { CmpIndex::Cmp1 => self.pwm.cmp1.write(|w| unsafe { w.bits(u32::MAX) }), CmpIndex::Cmp2 => self.pwm.cmp2.write(|w| unsafe { w.bits(u32::MAX) }), @@ -190,7 +185,8 @@ impl embedded_hal::Pwm for Pwm { } } - fn disable(&mut self, channel: Self::Channel) { + /// Disables a PWM channel + pub fn disable(&mut self, channel: Channel) { match channel.cmp_index { CmpIndex::Cmp1 => self.pwm.cmp1.reset(), CmpIndex::Cmp2 => self.pwm.cmp2.reset(), @@ -198,11 +194,13 @@ impl embedded_hal::Pwm for Pwm { } } - fn get_period(&self) -> Self::Time { + /// Returns the current PWM period + pub fn get_period(&self) -> PWM::CmpWidth { PWM::bits_into_cmp_width(self.pwm.cmp0.read().bits()) } - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { + /// Returns the current duty cycle + pub fn get_duty(&self, channel: Channel) -> PWM::CmpWidth { let duty = match channel.cmp_index { CmpIndex::Cmp1 => self.pwm.cmp1.read().bits(), CmpIndex::Cmp2 => self.pwm.cmp2.read().bits(), @@ -211,11 +209,13 @@ impl embedded_hal::Pwm for Pwm { PWM::bits_into_cmp_width(duty) } - fn get_max_duty(&self) -> Self::Duty { + /// Returns the maximum duty cycle value + pub fn get_max_duty(&self) -> PWM::CmpWidth { self.get_period() } - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { + /// Sets a new duty cycle + pub fn set_duty(&mut self, channel: Channel, duty: PWM::CmpWidth) { let duty = PWM::bits_from_cmp_width(duty.min(self.get_max_duty())); match channel.cmp_index { CmpIndex::Cmp1 => self.pwm.cmp1.write(|w| unsafe { w.bits(duty) }), @@ -224,9 +224,10 @@ impl embedded_hal::Pwm for Pwm { } } - fn set_period

(&mut self, period: P) + /// Sets a new PWM period + pub fn set_period

(&mut self, period: P) where - P: Into, + P: Into, { let period = PWM::bits_from_cmp_width(period.into()); self.pwm.count.reset(); diff --git a/src/serial.rs b/src/serial.rs index 0184679..d53a844 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -16,9 +16,6 @@ use core::convert::Infallible; use core::ops::Deref; -use embedded_hal::serial; -use nb; - use crate::clock::Clocks; use crate::gpio::{gpio0, IOF0}; use crate::time::Bps; @@ -114,43 +111,67 @@ impl Serial { } } -impl serial::Read for Rx { +// e-h 1.0.0 + +impl embedded_io::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_io::ErrorType for Tx { type Error = Infallible; +} - fn read(&mut self) -> nb::Result { +impl embedded_io::ReadReady for Rx { + fn read_ready(&mut self) -> Result { let rxdata = self.uart.rxdata.read(); - if rxdata.empty().bit_is_set() { - Err(::nb::Error::WouldBlock) - } else { - Ok(rxdata.data().bits() as u8) - } + Ok(rxdata.empty().bit_is_clear()) } } -impl serial::Write for Tx { - type Error = Infallible; +impl embedded_io::Read for Rx { + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut count = 0; + + for byte in buf { + let rxdata = self.uart.rxdata.read(); + + *byte = rxdata.data().bits() as u8; + count += 1; + } - fn write(&mut self, byte: u8) -> nb::Result<(), Infallible> { + Ok(count) + } +} + +impl embedded_io::WriteReady for Tx { + fn write_ready(&mut self) -> Result { let txdata = self.uart.txdata.read(); - if txdata.full().bit_is_set() { - Err(::nb::Error::WouldBlock) - } else { + Ok(txdata.full().bit_is_clear()) + } +} + +impl embedded_io::Write for Tx { + fn write(&mut self, buf: &[u8]) -> Result { + let mut count = 0; + for byte in buf { unsafe { - self.uart.txdata.write(|w| w.data().bits(byte)); + self.uart.txdata.write(|w| w.data().bits(*byte)); } - Ok(()) + + count += 1; } + + Ok(count) } - fn flush(&mut self) -> nb::Result<(), Infallible> { - if self.uart.ip.read().txwm().bit_is_set() { - // FIFO count is below the receive watermark (1) - Ok(()) - } else { - Err(nb::Error::WouldBlock) + fn flush(&mut self) -> Result<(), Infallible> { + while !self.uart.ip.read().txwm().bit_is_set() { + // busy wait } + + Ok(()) } } diff --git a/src/spi.rs b/src/spi.rs index d508849..057e6bd 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -51,9 +51,9 @@ mod bus; // contains the SPI Bus abstraction mod config; -mod exclusive_device; // contains the exclusive SPI device abstraction -mod shared_bus; // shared bus newtype -mod shared_device; // contains the shared SPI device abstraction +mod exclusive_device; +mod shared_bus; +mod shared_device; mod traits; // contains SPI device abstraction pub use bus::*; diff --git a/src/spi/bus.rs b/src/spi/bus.rs index 94ea72b..ae7520d 100644 --- a/src/spi/bus.rs +++ b/src/spi/bus.rs @@ -1,9 +1,7 @@ use core::convert::Infallible; -use embedded_hal::blocking::spi::Operation; -pub use embedded_hal::blocking::spi::{Transfer, Write, WriteIter}; -pub use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; - -use nb; +use embedded_hal::delay::DelayNs; +pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +pub use embedded_io::Write; use super::{Pins, PinsNoCS, SharedBus, SpiConfig, SpiExclusiveDevice, SpiX}; @@ -101,30 +99,52 @@ where } } - // ex-traits now only accessible via devices + pub(crate) fn write_iter(&mut self, words: WI) -> Result<(), Infallible> + where + WI: IntoIterator, + { + let mut iter = words.into_iter(); - pub(crate) fn read(&mut self) -> nb::Result { - let rxdata = self.spi.rxdata.read(); + let mut read_count = 0; + let mut has_data = true; - if rxdata.empty().bit_is_set() { - Err(nb::Error::WouldBlock) - } else { - Ok(rxdata.data().bits()) - } - } + // Ensure that RX FIFO is empty + self.wait_for_rxfifo(); - pub(crate) fn send(&mut self, byte: u8) -> nb::Result<(), Infallible> { - let txdata = self.spi.txdata.read(); + while has_data || read_count > 0 { + if has_data && self.spi.txdata.read().full().bit_is_clear() { + if let Some(byte) = iter.next() { + self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); + read_count += 1; + } else { + has_data = false; + } + } - if txdata.full().bit_is_set() { - Err(nb::Error::WouldBlock) - } else { - self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); - Ok(()) + if read_count > 0 { + // Read and discard byte, if any + if self.spi.rxdata.read().empty().bit_is_clear() { + read_count -= 1; + } + } } + + Ok(()) } +} - pub(crate) fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Infallible> { +impl embedded_hal::spi::ErrorType for SpiBus +where + SPI: SpiX, +{ + type Error = Infallible; +} + +impl embedded_hal::spi::SpiBus for SpiBus +where + SPI: SpiX, +{ + fn read(&mut self, words: &mut [u8]) -> Result<(), Infallible> { let mut iwrite = 0; let mut iread = 0; @@ -133,9 +153,8 @@ where while iwrite < words.len() || iread < words.len() { if iwrite < words.len() && self.spi.txdata.read().full().bit_is_clear() { - let byte = unsafe { words.get_unchecked(iwrite) }; iwrite += 1; - self.spi.txdata.write(|w| unsafe { w.data().bits(*byte) }); + self.spi.txdata.write(|w| unsafe { w.data().bits(0x00) }); } if iread < iwrite { @@ -147,10 +166,10 @@ where } } - Ok(words) + Ok(()) } - pub(crate) fn write(&mut self, words: &[u8]) -> Result<(), Infallible> { + fn write(&mut self, words: &[u8]) -> Result<(), Infallible> { let mut iwrite = 0; let mut iread = 0; @@ -175,32 +194,25 @@ where Ok(()) } - pub(crate) fn write_iter(&mut self, words: WI) -> Result<(), Infallible> - where - WI: IntoIterator, - { - let mut iter = words.into_iter(); - - let mut read_count = 0; - let mut has_data = true; + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Infallible> { + let mut iwrite = 0; + let mut iread = 0; // Ensure that RX FIFO is empty self.wait_for_rxfifo(); - while has_data || read_count > 0 { - if has_data && self.spi.txdata.read().full().bit_is_clear() { - if let Some(byte) = iter.next() { - self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); - read_count += 1; - } else { - has_data = false; - } + while iwrite < read.len() || iread < read.len() { + if iwrite < write.len() && self.spi.txdata.read().full().bit_is_clear() { + let byte = unsafe { write.get_unchecked(iwrite) }; + iwrite += 1; + self.spi.txdata.write(|w| unsafe { w.data().bits(*byte) }); } - if read_count > 0 { - // Read and discard byte, if any - if self.spi.rxdata.read().empty().bit_is_clear() { - read_count -= 1; + if iread < iwrite { + let data = self.spi.rxdata.read(); + if data.empty().bit_is_clear() { + unsafe { *read.get_unchecked_mut(iread) = data.data().bits() }; + iread += 1; } } } @@ -208,23 +220,37 @@ where Ok(()) } - pub(crate) fn exec<'op>( - &mut self, - operations: &mut [Operation<'op, u8>], - ) -> Result<(), Infallible> { - for op in operations { - match op { - Operation::Transfer(words) => { - self.transfer(words)?; - } - Operation::Write(words) => { - self.write(words)?; + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Infallible> { + let mut iwrite = 0; + let mut iread = 0; + + // Ensure that RX FIFO is empty + self.wait_for_rxfifo(); + + while iwrite < words.len() || iread < words.len() { + if iwrite < words.len() && self.spi.txdata.read().full().bit_is_clear() { + let byte = unsafe { words.get_unchecked(iwrite) }; + iwrite += 1; + self.spi.txdata.write(|w| unsafe { w.data().bits(*byte) }); + } + + if iread < iwrite { + let data = self.spi.rxdata.read(); + if data.empty().bit_is_clear() { + unsafe { *words.get_unchecked_mut(iread) = data.data().bits() }; + iread += 1; } } } Ok(()) } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.wait_for_rxfifo(); + + Ok(()) + } } impl SpiBus @@ -233,8 +259,11 @@ where PINS: Pins, { /// Create a new [SpiExclusiveDevice] for exclusive use on this bus - pub fn new_device(self, config: &SpiConfig) -> SpiExclusiveDevice { - SpiExclusiveDevice::new(self, config) + pub fn new_device(self, config: &SpiConfig, delay: D) -> SpiExclusiveDevice + where + D: DelayNs, + { + SpiExclusiveDevice::new(self, config, delay) } } diff --git a/src/spi/exclusive_device.rs b/src/spi/exclusive_device.rs index d9e808e..f753834 100644 --- a/src/spi/exclusive_device.rs +++ b/src/spi/exclusive_device.rs @@ -1,8 +1,8 @@ use core::convert::Infallible; use embedded_hal::{ - blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, - spi::FullDuplex, + delay::DelayNs, + spi::{ErrorType, Operation, SpiBus as HalBus, SpiDevice}, }; use crate::spi::SpiConfig; @@ -10,111 +10,74 @@ use crate::spi::SpiConfig; use super::{Pins, SpiBus, SpiX}; /// SPI exclusive device abstraction -pub struct SpiExclusiveDevice { +pub struct SpiExclusiveDevice { bus: SpiBus, + delay: D, } -impl SpiExclusiveDevice +impl SpiExclusiveDevice where SPI: SpiX, PINS: Pins, + D: DelayNs, { /// Create [SpiExclusiveDevice] using the existing [SpiBus](super::SpiBus) /// with the given [SpiConfig] - pub fn new(mut bus: SpiBus, config: &SpiConfig) -> Self + pub fn new(mut bus: SpiBus, config: &SpiConfig, delay: D) -> Self where PINS: Pins, { bus.configure(config, PINS::CS_INDEX); - Self { bus } + Self { bus, delay } } /// Releases the Bus back deconstructing it pub fn release(self) -> (SPI, PINS) { self.bus.release() } -} - -impl FullDuplex for SpiExclusiveDevice -where - SPI: SpiX, - PINS: Pins, -{ - type Error = Infallible; - - fn read(&mut self) -> nb::Result { - self.bus.read() - } - - fn send(&mut self, byte: u8) -> nb::Result<(), Infallible> { - self.bus.send(byte) - } -} -impl Transfer for SpiExclusiveDevice -where - SPI: SpiX, - PINS: Pins, -{ - type Error = Infallible; - - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + /// Writes words from an iterator + pub fn write_iter(&mut self, words: WI) -> Result<(), Infallible> + where + WI: IntoIterator, + { self.bus.start_frame(); - let result = self.bus.transfer(words); + let result = self.bus.write_iter(words); self.bus.end_frame(); result } } -impl Write for SpiExclusiveDevice +impl ErrorType for SpiExclusiveDevice where SPI: SpiX, PINS: Pins, { type Error = Infallible; - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.bus.start_frame(); - let result = self.bus.write(words); - self.bus.end_frame(); - - result - } } -impl WriteIter for SpiExclusiveDevice +impl SpiDevice for SpiExclusiveDevice where SPI: SpiX, PINS: Pins, + D: DelayNs, { - type Error = Infallible; - - fn write_iter(&mut self, words: WI) -> Result<(), Self::Error> - where - WI: IntoIterator, - { + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { self.bus.start_frame(); - let result = self.bus.write_iter(words); - self.bus.end_frame(); - result - } -} - -impl Transactional for SpiExclusiveDevice -where - SPI: SpiX, - PINS: Pins, -{ - type Error = Infallible; - - fn exec<'op>(&mut self, operations: &mut [Operation<'op, u8>]) -> Result<(), Infallible> { - self.bus.start_frame(); - let result = self.bus.exec(operations); + for operation in operations { + match operation { + Operation::Read(words) => self.bus.read(words)?, + Operation::Write(words) => self.bus.write(words)?, + Operation::Transfer(read, write) => self.bus.transfer(read, write)?, + Operation::TransferInPlace(words) => self.bus.transfer_in_place(words)?, + Operation::DelayNs(ns) => self.delay.delay_ns(*ns), + } + } self.bus.end_frame(); - result + Ok(()) } } diff --git a/src/spi/shared_bus.rs b/src/spi/shared_bus.rs index 67b9e06..e285b02 100644 --- a/src/spi/shared_bus.rs +++ b/src/spi/shared_bus.rs @@ -1,11 +1,11 @@ use core::cell::RefCell; use core::ops::Deref; -pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +use embedded_hal::delay::DelayNs; use riscv::interrupt; use super::{PinCS, PinsNoCS, SpiBus, SpiConfig, SpiSharedDevice, SpiX}; -/// Newtype for RefCell locked behind a Mutex. +/// Newtype for RefCell locked behind a interrupt::free (single-hart) /// Used to hold the [SpiBus] instance so it can be used for multiple [SpiSharedDevice] instances. pub struct SharedBus(RefCell>); @@ -19,15 +19,17 @@ where } /// Create a new shared device on this SPI bus. - pub fn new_device<'bus, CS>( + pub fn new_device<'bus, CS, D>( &'bus self, cs: CS, config: &SpiConfig, - ) -> SpiSharedDevice<'bus, SPI, PINS, CS> + delay: D, + ) -> SpiSharedDevice<'bus, SPI, PINS, CS, D> where CS: PinCS, + D: DelayNs, { - SpiSharedDevice::new(self, cs, config) + SpiSharedDevice::new(self, cs, config, delay) } } diff --git a/src/spi/shared_device.rs b/src/spi/shared_device.rs index cbedea9..5319f08 100644 --- a/src/spi/shared_device.rs +++ b/src/spi/shared_device.rs @@ -1,29 +1,31 @@ use core::convert::Infallible; use embedded_hal::{ - blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, - spi::FullDuplex, + delay::DelayNs, + spi::{ErrorType, Operation, SpiBus, SpiDevice}, }; use riscv::interrupt; use super::{PinCS, Pins, PinsNoCS, SharedBus, SpiConfig, SpiX}; /// SPI shared device abstraction -pub struct SpiSharedDevice<'bus, SPI, PINS, CS> { +pub struct SpiSharedDevice<'bus, SPI, PINS, CS, D> { bus: &'bus SharedBus, cs: CS, config: SpiConfig, + delay: D, } -impl<'bus, SPI, PINS, CS> SpiSharedDevice<'bus, SPI, PINS, CS> +impl<'bus, SPI, PINS, CS, D> SpiSharedDevice<'bus, SPI, PINS, CS, D> where SPI: SpiX, PINS: PinsNoCS, CS: PinCS, + D: DelayNs, { /// Create shared [SpiSharedDevice] using the existing [SharedBus] /// and given [SpiConfig]. The config gets cloned. - pub fn new(bus: &'bus SharedBus, cs: CS, config: &SpiConfig) -> Self + pub fn new(bus: &'bus SharedBus, cs: CS, config: &SpiConfig, delay: D) -> Self where PINS: PinsNoCS, { @@ -31,6 +33,7 @@ where bus, cs, config: config.clone(), + delay, } } @@ -38,76 +41,19 @@ where pub fn release(self) -> CS { self.cs } -} - -impl FullDuplex for SpiSharedDevice<'_, SPI, PINS, CS> -where - SPI: SpiX, - PINS: Pins, - CS: PinCS, -{ - type Error = Infallible; - - fn read(&mut self) -> nb::Result { - interrupt::free(|| { - let mut bus = self.bus.borrow_mut(); - - bus.configure(&self.config, Some(CS::CS_INDEX)); - - bus.read() - }) - } - - fn send(&mut self, byte: u8) -> nb::Result<(), Infallible> { - interrupt::free(|| { - let mut bus = self.bus.borrow_mut(); - - bus.configure(&self.config, Some(CS::CS_INDEX)); - - bus.send(byte) - }) - } -} - -impl Transfer for SpiSharedDevice<'_, SPI, PINS, CS> -where - SPI: SpiX, - PINS: Pins, - CS: PinCS, -{ - type Error = Infallible; - - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - interrupt::free(move || { - let mut bus = self.bus.borrow_mut(); - - bus.configure(&self.config, Some(CS::CS_INDEX)); - - bus.start_frame(); - let result = bus.transfer(words); - bus.end_frame(); - - result - }) - } -} -impl Write for SpiSharedDevice<'_, SPI, PINS, CS> -where - SPI: SpiX, - PINS: Pins, - CS: PinCS, -{ - type Error = Infallible; - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + /// Writes words from iterator + pub fn write_iter(&mut self, words: WI) -> Result<(), Infallible> + where + WI: IntoIterator, + { interrupt::free(|| { let mut bus = self.bus.borrow_mut(); bus.configure(&self.config, Some(CS::CS_INDEX)); bus.start_frame(); - let result = bus.write(words); + let result = bus.write_iter(words); bus.end_frame(); result @@ -115,51 +61,42 @@ where } } -impl WriteIter for SpiSharedDevice<'_, SPI, PINS, CS> +impl ErrorType for SpiSharedDevice<'_, SPI, PINS, CS, D> where SPI: SpiX, PINS: Pins, CS: PinCS, + D: DelayNs, { type Error = Infallible; - - fn write_iter(&mut self, words: WI) -> Result<(), Self::Error> - where - WI: IntoIterator, - { - interrupt::free(|| { - let mut bus = self.bus.borrow_mut(); - - bus.configure(&self.config, Some(CS::CS_INDEX)); - - bus.start_frame(); - let result = bus.write_iter(words); - bus.end_frame(); - - result - }) - } } -impl Transactional for SpiSharedDevice<'_, SPI, PINS, CS> +impl SpiDevice for SpiSharedDevice<'_, SPI, PINS, CS, D> where SPI: SpiX, PINS: Pins, CS: PinCS, + D: DelayNs, { - type Error = Infallible; - - fn exec<'op>(&mut self, operations: &mut [Operation<'op, u8>]) -> Result<(), Infallible> { + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Infallible> { interrupt::free(|| { let mut bus = self.bus.borrow_mut(); bus.configure(&self.config, Some(CS::CS_INDEX)); bus.start_frame(); - let result = bus.exec(operations); + for operation in operations { + match operation { + Operation::Read(words) => bus.read(words)?, + Operation::Write(words) => bus.write(words)?, + Operation::Transfer(read, write) => bus.transfer(read, write)?, + Operation::TransferInPlace(words) => bus.transfer_in_place(words)?, + Operation::DelayNs(ns) => self.delay.delay_ns(*ns), + }; + } bus.end_frame(); - result + Ok(()) }) } } diff --git a/src/stdout.rs b/src/stdout.rs index 004bcc8..efb0f4a 100644 --- a/src/stdout.rs +++ b/src/stdout.rs @@ -1,6 +1,5 @@ //! Stdout pub use core::fmt::Write; -use nb::block; /// Stdout implements the core::fmt::Write trait for hal::serial::Write /// implementations. @@ -10,19 +9,19 @@ where impl<'p, T> Write for Stdout<'p, T> where - T: embedded_hal::serial::Write, + T: embedded_io::Write, { fn write_str(&mut self, s: &str) -> ::core::fmt::Result { for byte in s.as_bytes() { if *byte == b'\n' { - let res = block!(self.0.write(b'\r')); + let res = self.0.write(&[b'\r']); if res.is_err() { return Err(::core::fmt::Error); } } - let res = block!(self.0.write(*byte)); + let res = self.0.write(&[*byte]); if res.is_err() { return Err(::core::fmt::Error);