From f6be3bf23ecac4f4225f53a7ddd9ac3472a580b6 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Fri, 22 Mar 2024 10:37:09 -0700 Subject: [PATCH 01/15] split embedded-hal dependency --- Cargo.toml | 3 ++- src/delay.rs | 2 +- src/gpio.rs | 2 +- src/i2c.rs | 2 +- src/prelude.rs | 4 ++-- src/pwm.rs | 2 +- src/serial.rs | 2 +- src/spi/bus.rs | 6 +++--- src/spi/config.rs | 2 +- src/spi/exclusive_device.rs | 2 +- src/spi/shared_bus.rs | 2 +- src/spi/shared_device.rs | 2 +- src/stdout.rs | 2 +- 13 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca7141c..4885224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ edition = "2018" rust-version = "1.59" [dependencies] -embedded-hal = { version = "0.2.6", features = ["unproven"] } +embedded-hal-0 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0" } nb = "1.0.0" riscv = { version = "0.10.1", features = ["critical-section-single-hart"] } e310x = { version = "0.11.0", features = ["rt", "critical-section"] } diff --git a/src/delay.rs b/src/delay.rs index 4778bf1..1a33aa8 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_0::blocking::delay::{DelayMs, DelayUs}; use riscv::register::{mie, mip}; /// Machine timer (mtime) as a busyloop delay provider diff --git a/src/gpio.rs b/src/gpio.rs index 1061884..2d73383 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -146,7 +146,7 @@ macro_rules! gpio { use core::marker::PhantomData; use core::convert::Infallible; - use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, + use embedded_hal_0::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; use e310x::$GPIOX; use super::{Unknown, IOF0, IOF1, Drive, Floating, GpioExt, Input, Invert, diff --git a/src/i2c.rs b/src/i2c.rs index d969add..e53cb6c 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_0::blocking::i2c::{Read, Write, WriteRead}; /// SDA pin - DO NOT IMPLEMENT THIS TRAIT pub unsafe trait SdaPin {} diff --git a/src/prelude.rs b/src/prelude.rs index c6ccd12..c7791f5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,9 +7,9 @@ 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::{ +pub use embedded_hal_0::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::*; +pub use embedded_hal_0::prelude::*; diff --git a/src/pwm.rs b/src/pwm.rs index 7db8bf5..8f7a15a 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -175,7 +175,7 @@ impl Pwm { } } -impl embedded_hal::Pwm for Pwm { +impl embedded_hal_0::Pwm for Pwm { type Channel = Channel; type Time = PWM::CmpWidth; diff --git a/src/serial.rs b/src/serial.rs index 0184679..431ec49 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -16,7 +16,7 @@ use core::convert::Infallible; use core::ops::Deref; -use embedded_hal::serial; +use embedded_hal_0::serial; use nb; use crate::clock::Clocks; diff --git a/src/spi/bus.rs b/src/spi/bus.rs index 94ea72b..7e5cec6 100644 --- a/src/spi/bus.rs +++ b/src/spi/bus.rs @@ -1,7 +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 embedded_hal_0::blocking::spi::Operation; +pub use embedded_hal_0::blocking::spi::{Transfer, Write, WriteIter}; +pub use embedded_hal_0::spi::{FullDuplex, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use nb; diff --git a/src/spi/config.rs b/src/spi/config.rs index 6b30e72..3c6aacc 100644 --- a/src/spi/config.rs +++ b/src/spi/config.rs @@ -1,5 +1,5 @@ use e310x::qspi0::csmode::MODE_A; -use embedded_hal::spi::Mode; +use embedded_hal_0::spi::Mode; use crate::{clock::Clocks, time::Hertz}; diff --git a/src/spi/exclusive_device.rs b/src/spi/exclusive_device.rs index d9e808e..1776bbf 100644 --- a/src/spi/exclusive_device.rs +++ b/src/spi/exclusive_device.rs @@ -1,6 +1,6 @@ use core::convert::Infallible; -use embedded_hal::{ +use embedded_hal_0::{ blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, spi::FullDuplex, }; diff --git a/src/spi/shared_bus.rs b/src/spi/shared_bus.rs index 67b9e06..f920f23 100644 --- a/src/spi/shared_bus.rs +++ b/src/spi/shared_bus.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::ops::Deref; -pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +// pub use embedded_hal_0::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use riscv::interrupt; use super::{PinCS, PinsNoCS, SpiBus, SpiConfig, SpiSharedDevice, SpiX}; diff --git a/src/spi/shared_device.rs b/src/spi/shared_device.rs index cbedea9..15c6dd6 100644 --- a/src/spi/shared_device.rs +++ b/src/spi/shared_device.rs @@ -1,6 +1,6 @@ use core::convert::Infallible; -use embedded_hal::{ +use embedded_hal_0::{ blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, spi::FullDuplex, }; diff --git a/src/stdout.rs b/src/stdout.rs index 004bcc8..5123767 100644 --- a/src/stdout.rs +++ b/src/stdout.rs @@ -10,7 +10,7 @@ where impl<'p, T> Write for Stdout<'p, T> where - T: embedded_hal::serial::Write, + T: embedded_hal_0::serial::Write, { fn write_str(&mut self, s: &str) -> ::core::fmt::Result { for byte in s.as_bytes() { From ccd17989027dd4af2abf09d5eca207679a39ec10 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Fri, 22 Mar 2024 10:56:28 -0700 Subject: [PATCH 02/15] implement e-h1 delay --- src/delay.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 1a33aa8..868b3a5 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -3,6 +3,7 @@ use crate::clock::Clocks; use crate::core::clint::{MTIME, MTIMECMP}; use embedded_hal_0::blocking::delay::{DelayMs, DelayUs}; +use embedded_hal_1::delay::DelayNs; use riscv::register::{mie, mip}; /// Machine timer (mtime) as a busyloop delay provider @@ -17,6 +18,16 @@ impl Delay { } } +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; + while mtime.mtime() < t {} + } +} + impl DelayUs for Delay { fn delay_us(&mut self, us: u32) { let ticks = (us as u64) * TICKS_PER_SECOND / 1_000_000; @@ -32,27 +43,27 @@ impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: i32) { assert!(us >= 0); - self.delay_us(us as u32); + DelayUs::delay_us(self, us as u32); } } impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u16) { - self.delay_us(u32::from(us)); + DelayUs::delay_us(self, u32::from(us)); } } impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u8) { - self.delay_us(u32::from(us)); + DelayUs::delay_us(self, u32::from(us)); } } impl DelayMs for Delay { fn delay_ms(&mut self, ms: u32) { - self.delay_us(ms * 1000); + DelayUs::delay_us(self, ms * 1000); } } @@ -61,21 +72,21 @@ impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: i32) { assert!(ms >= 0); - self.delay_ms(ms as u32); + DelayMs::delay_ms(self, ms as u32); } } impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u16) { - self.delay_ms(u32::from(ms)); + DelayMs::delay_ms(self, u32::from(ms)); } } impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u8) { - self.delay_ms(u32::from(ms)); + DelayMs::delay_ms(self, u32::from(ms)); } } @@ -95,6 +106,40 @@ impl Sleep { } } +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); + + // Enable timer interrupt + unsafe { + mie::set_mtimer(); + } + + // Wait For Interrupt will put CPU to sleep until an interrupt hits + // in our case when internal timer mtime value >= mtimecmp value + // after which empty handler gets called and we go into the + // next iteration of this loop + loop { + unsafe { + riscv::asm::wfi(); + } + + // check if we got the right interrupt cause, otherwise just loop back to wfi + if mip::read().mtimer() { + break; + } + } + + // Clear timer interrupt + unsafe { + mie::clear_mtimer(); + } + } +} + impl DelayMs for Sleep { fn delay_ms(&mut self, ms: u32) { let ticks = (ms as u64) * (self.clock_freq as u64) / 1000; @@ -134,20 +179,20 @@ impl DelayMs for Sleep { #[inline(always)] fn delay_ms(&mut self, ms: i32) { assert!(ms >= 0); - self.delay_ms(ms as u32); + DelayMs::delay_ms(self, ms as u32); } } impl DelayMs for Sleep { #[inline(always)] fn delay_ms(&mut self, ms: u16) { - self.delay_ms(u32::from(ms)); + DelayMs::delay_ms(self, u32::from(ms)); } } impl DelayMs for Sleep { #[inline(always)] fn delay_ms(&mut self, ms: u8) { - self.delay_ms(u32::from(ms)); + DelayMs::delay_ms(self, u32::from(ms)); } } From 3595546a6006f3e7578c6b7012ef60dc091bc131 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 07:42:12 -0700 Subject: [PATCH 03/15] implement e-h 1.0 for serial --- Cargo.toml | 1 + src/serial.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4885224..56f65f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.59" [dependencies] embedded-hal-0 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0" } +embedded-io = "0.6.1" nb = "1.0.0" riscv = { version = "0.10.1", features = ["critical-section-single-hart"] } e310x = { version = "0.11.0", features = ["rt", "critical-section"] } diff --git a/src/serial.rs b/src/serial.rs index 431ec49..64f8a1e 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -114,6 +114,72 @@ impl Serial { } } +// e-h 1.0.0 + +impl embedded_io::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_io::ErrorType for Tx { + type Error = Infallible; +} + +impl embedded_io::ReadReady for Rx { + fn read_ready(&mut self) -> Result { + let rxdata = self.uart.rxdata.read(); + + Ok(rxdata.empty().bit_is_clear()) + } +} + +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; + } + + Ok(count) + } +} + +impl embedded_io::WriteReady for Tx { + fn write_ready(&mut self) -> Result { + let txdata = self.uart.txdata.read(); + + 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)); + } + + count += 1; + } + + Ok(count) + } + + fn flush(&mut self) -> Result<(), Infallible> { + while !self.uart.ip.read().txwm().bit_is_set() { + // busy wait + } + + Ok(()) + } +} + +// e-h 0.2 + impl serial::Read for Rx { type Error = Infallible; From 36f32c0f18e75979856142760cc93a9633949f13 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 07:50:08 -0700 Subject: [PATCH 04/15] implement e-h 1.0 for gpio --- src/gpio.rs | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 4 deletions(-) diff --git a/src/gpio.rs b/src/gpio.rs index 2d73383..e8641de 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -137,7 +137,7 @@ trait PeripheralAccess { } } -macro_rules! gpio { +macro_rules! gpio_eh_1 { ($GPIOX:ident, $gpiox:ident, [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ ]) => { @@ -146,8 +146,7 @@ macro_rules! gpio { use core::marker::PhantomData; use core::convert::Infallible; - use embedded_hal_0::digital::v2::{InputPin, OutputPin, StatefulOutputPin, - ToggleableOutputPin}; + use embedded_hal_1::digital::{InputPin, OutputPin, StatefulOutputPin}; use e310x::$GPIOX; use super::{Unknown, IOF0, IOF1, Drive, Floating, GpioExt, Input, Invert, NoInvert, Output, PullUp, Regular, PinIndex, PeripheralAccess}; @@ -179,6 +178,208 @@ macro_rules! gpio { } } + $( + /// Pin + pub struct $PXi { + _mode: PhantomData, + } + + impl PinIndex for $PXi { + const INDEX: usize = $i; + } + + impl embedded_hal_1::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> { + $GPIOX::set_out_xor(Self::INDEX, false); + $GPIOX::set_iof_sel(Self::INDEX, false); + $GPIOX::set_iof_en(Self::INDEX, true); + $PXi { _mode: PhantomData } + } + + + /// Configures the pin to serve as alternate function 1 (AF1) + pub fn into_iof1(self) -> $PXi> { + $GPIOX::set_out_xor(Self::INDEX, false); + $GPIOX::set_iof_sel(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, true); + $PXi { _mode: PhantomData } + } + + /// Configures the pin to serve as inverted alternate function 0 (AF0) + pub fn into_inverted_iof0(self) -> $PXi> { + $GPIOX::set_out_xor(Self::INDEX, true); + $GPIOX::set_iof_sel(Self::INDEX, false); + $GPIOX::set_iof_en(Self::INDEX, true); + $PXi { _mode: PhantomData } + } + + + /// Configures the pin to serve as inverted alternate function 1 (AF1) + pub fn into_inverted_iof1(self) -> $PXi> { + $GPIOX::set_out_xor(Self::INDEX, true); + $GPIOX::set_iof_sel(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, true); + $PXi { _mode: PhantomData } + } + + /// Configures the pin to serve as a floating input pin + pub fn into_floating_input(self) -> $PXi> { + $GPIOX::set_pullup(Self::INDEX, false); + $GPIOX::set_input_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + + /// Configures the pin to operate as a pulled down input pin + pub fn into_pull_up_input(self) -> $PXi> { + $GPIOX::set_pullup(Self::INDEX, true); + $GPIOX::set_input_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + + /// Configures the pin to operate as an output pin + pub fn into_output(self) -> $PXi>> { + $GPIOX::set_drive(Self::INDEX, false); + $GPIOX::set_out_xor(Self::INDEX, false); + $GPIOX::set_output_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + + /// Configures the pin to operate as an inverted output pin + pub fn into_inverted_output(self) -> $PXi>> { + $GPIOX::set_drive(Self::INDEX, false); + $GPIOX::set_out_xor(Self::INDEX, true); + $GPIOX::set_output_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + + /// Configure the pin to operate as an output pin with high + /// current drive + pub fn into_output_drive(self) -> $PXi>> { + $GPIOX::set_drive(Self::INDEX, true); + $GPIOX::set_out_xor(Self::INDEX, false); + $GPIOX::set_output_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + + /// Configure the pin to operate as an inverted output pin with + /// high current drive + pub fn into_inverted_output_drive(self) -> $PXi>> { + $GPIOX::set_drive(Self::INDEX, true); + $GPIOX::set_out_xor(Self::INDEX, true); + $GPIOX::set_output_en(Self::INDEX, true); + $GPIOX::set_iof_en(Self::INDEX, false); + $PXi { _mode: PhantomData } + } + } + + impl InputPin for $PXi> { + fn is_high(&mut self) -> Result { + Ok($GPIOX::input_value(Self::INDEX)) + + } + + fn is_low(&mut self) -> Result { + Ok(!self.is_high()?) + } + } + + impl StatefulOutputPin for $PXi> { + fn is_set_high(&mut self) -> Result { + Ok($GPIOX::input_value(Self::INDEX)) + } + + fn is_set_low(&mut self) -> Result { + Ok(!self.is_set_high()?) + } + } + + impl OutputPin for $PXi> { + fn set_high(&mut self) -> Result<(), Infallible> { + $GPIOX::set_output_value(Self::INDEX, true); + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Infallible> { + $GPIOX::set_output_value(Self::INDEX, false); + Ok(()) + } + } + )+ + } + } +} + +// By default, all GPIOs are in the Unknown state for two reasons: +// * bootloader may reconfigure some GPIOs +// * we do not enforce any specific state in `split()` +gpio_eh_1!(GPIO0, gpio0, [ + Pin0: (pin0, 0, Unknown), + Pin1: (pin1, 1, Unknown), + Pin2: (pin2, 2, Unknown), + Pin3: (pin3, 3, Unknown), + Pin4: (pin4, 4, Unknown), + Pin5: (pin5, 5, Unknown), + Pin6: (pin6, 6, Unknown), + Pin7: (pin7, 7, Unknown), + Pin8: (pin8, 8, Unknown), + Pin9: (pin9, 9, Unknown), + Pin10: (pin10, 10, Unknown), + Pin11: (pin11, 11, Unknown), + Pin12: (pin12, 12, Unknown), + Pin13: (pin13, 13, Unknown), + Pin14: (pin14, 14, Unknown), + Pin15: (pin15, 15, Unknown), + Pin16: (pin16, 16, Unknown), + Pin17: (pin17, 17, Unknown), + Pin18: (pin18, 18, Unknown), + Pin19: (pin19, 19, Unknown), + Pin20: (pin20, 20, Unknown), + Pin21: (pin21, 21, Unknown), + Pin22: (pin22, 22, Unknown), + Pin23: (pin23, 23, Unknown), + Pin24: (pin24, 24, Unknown), + Pin25: (pin25, 25, Unknown), + Pin26: (pin26, 26, Unknown), + Pin27: (pin27, 27, Unknown), + Pin28: (pin28, 28, Unknown), + Pin29: (pin29, 29, Unknown), + Pin30: (pin30, 30, Unknown), + Pin31: (pin31, 31, Unknown), +]); + +macro_rules! gpio_eh_0 { + ($GPIOX:ident, $gpiox:ident, [ + $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ + ]) => { + /// GPIO + pub mod $gpiox { + use core::marker::PhantomData; + use core::convert::Infallible; + + use embedded_hal_0::digital::v2::{InputPin, OutputPin, StatefulOutputPin, + ToggleableOutputPin}; + use e310x::$GPIOX; + use super::{Unknown, IOF0, IOF1, Drive, Floating, Input, Invert, + NoInvert, Output, PullUp, Regular, PinIndex, PeripheralAccess}; + + /// GPIO parts for fine grained permission control. + pub struct Parts { + $( + /// Pin + pub $pxi: $PXi<$MODE>, + )+ + } + $( /// Pin pub struct $PXi { @@ -333,7 +534,7 @@ macro_rules! gpio { // By default, all GPIOs are in the Unknown state for two reasons: // * bootloader may reconfigure some GPIOs // * we do not enforce any specific state in `split()` -gpio!(GPIO0, gpio0, [ +gpio_eh_0!(GPIO0, eh0_gpio0, [ Pin0: (pin0, 0, Unknown), Pin1: (pin1, 1, Unknown), Pin2: (pin2, 2, Unknown), From bc3ff79d990c2bfade228352a755b97725ba3fae Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 08:11:04 -0700 Subject: [PATCH 05/15] implement e-h 1.0 for i2c --- CHANGELOG.md | 5 +- src/i2c.rs | 165 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 165 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa47fb6..79f90ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,10 @@ 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. +### Changed +- Use `portable-atomic` to allow builds on `riscv32imc-unknown-none-elf`` targets when needed +- Implement `embedded-hal` version `1.0` traits alongside version `0.2` +- `I2c::free` renamed to `I2c::release` ## [v0.10.0] - 2023-03-28 diff --git a/src/i2c.rs b/src/i2c.rs index e53cb6c..b86d9ad 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -17,6 +17,7 @@ use core::mem; use core::ops::Deref; use e310x::{i2c0, I2C0}; use embedded_hal_0::blocking::i2c::{Read, Write, WriteRead}; +use embedded_hal_1::i2c::{ErrorKind, ErrorType, I2c as I2cTrait, Operation}; /// SDA pin - DO NOT IMPLEMENT THIS TRAIT pub unsafe trait SdaPin {} @@ -27,7 +28,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 +99,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,6 +180,162 @@ impl, PINS> I2c { const FLAG_READ: u8 = 1; const FLAG_WRITE: u8 = 0; +// e-h 1.0 + +impl embedded_hal_1::i2c::Error for Error { + fn kind(&self) -> ErrorKind { + match *self { + Error::ArbitrationLost => ErrorKind::ArbitrationLoss, + Error::NoAck => { + ErrorKind::NoAcknowledge(embedded_hal_1::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(); + + if self.read_sr().busy().bit_is_set() { + return Err(Error::InvalidState); + } + + // Write address + R + self.write_byte((address << 1) + FLAG_READ); + + // Generate start condition and write command + self.write_cr(|w| w.sta().set_bit().wr().set_bit()); + self.wait_for_write()?; + + // Read bytes + let buffer_len = buffer.len(); + for (i, byte) in buffer.iter_mut().enumerate() { + if i != buffer_len - 1 { + // R + ACK + self.write_cr(|w| w.rd().set_bit().ack().clear_bit()); + } else { + // R + NACK + STOP + self.write_cr(|w| w.rd().set_bit().ack().set_bit().sto().set_bit()); + } + self.wait_for_read()?; + + *byte = self.read_byte(); + } + Ok(()) + } + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.reset(); + + if self.read_sr().busy().bit_is_set() { + return Err(Error::InvalidState); + } + + // Write address + W + self.write_byte((address << 1) + FLAG_WRITE); + + // Generate start condition and write command + self.write_cr(|w| w.sta().set_bit().wr().set_bit()); + self.wait_for_write()?; + + // Write bytes + for (i, byte) in bytes.iter().enumerate() { + self.write_byte(*byte); + + if i != bytes.len() - 1 { + self.write_cr(|w| w.wr().set_bit()); + } else { + self.write_cr(|w| w.wr().set_bit().sto().set_bit()); + } + self.wait_for_write()?; + } + Ok(()) + } + + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.reset(); + + if self.read_sr().busy().bit_is_set() { + return Err(Error::InvalidState); + } + + if !bytes.is_empty() && buffer.is_empty() { + I2cTrait::write(self, address, bytes) + } else if !buffer.is_empty() && bytes.is_empty() { + I2cTrait::read(self, address, buffer) + } else if bytes.is_empty() && buffer.is_empty() { + Ok(()) + } else { + // Write address + W + self.write_byte((address << 1) + FLAG_WRITE); + + // Generate start condition and write command + self.write_cr(|w| w.sta().set_bit().wr().set_bit()); + self.wait_for_write()?; + + // Write bytes + for byte in bytes { + self.write_byte(*byte); + + self.write_cr(|w| w.wr().set_bit()); + self.wait_for_write()?; + } + + // Write address + R + self.write_byte((address << 1) + FLAG_READ); + + // Generate repeated start condition and write command + self.write_cr(|w| w.sta().set_bit().wr().set_bit()); + self.wait_for_write()?; + + // Read bytes + let buffer_len = buffer.len(); + for (i, byte) in buffer.iter_mut().enumerate() { + if i != buffer_len - 1 { + // W + ACK + self.write_cr(|w| w.rd().set_bit().ack().clear_bit()); + } else { + // W + NACK + STOP + self.write_cr(|w| w.rd().set_bit().ack().set_bit().sto().set_bit()); + } + self.wait_for_read()?; + + *byte = self.read_byte(); + } + + Ok(()) + } + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [Operation<'_>], + ) -> 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(()) + } +} + +// e-h 0.2 + impl, PINS> Read for I2c { type Error = Error; @@ -262,9 +419,9 @@ impl, PINS> WriteRead for I2c Date: Mon, 1 Apr 2024 09:43:17 -0700 Subject: [PATCH 06/15] remove e-h 0.2 from all except spi --- src/delay.rs | 120 ---------------------------- src/gpio.rs | 212 ------------------------------------------------- src/i2c.rs | 134 ------------------------------- src/prelude.rs | 6 -- src/pwm.rs | 31 ++++---- src/serial.rs | 45 ----------- 6 files changed, 16 insertions(+), 532 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 868b3a5..8494759 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -2,7 +2,6 @@ use crate::clock::Clocks; use crate::core::clint::{MTIME, MTIMECMP}; -use embedded_hal_0::blocking::delay::{DelayMs, DelayUs}; use embedded_hal_1::delay::DelayNs; use riscv::register::{mie, mip}; @@ -28,68 +27,6 @@ impl DelayNs for Delay { } } -impl DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - let ticks = (us as u64) * TICKS_PER_SECOND / 1_000_000; - - let mtime = MTIME; - let t = mtime.mtime() + ticks; - while mtime.mtime() < t {} - } -} - -// 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); - DelayUs::delay_us(self, us as u32); - } -} - -impl DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u16) { - DelayUs::delay_us(self, u32::from(us)); - } -} - -impl DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u8) { - DelayUs::delay_us(self, u32::from(us)); - } -} - -impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u32) { - DelayUs::delay_us(self, 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); - DelayMs::delay_ms(self, ms as u32); - } -} - -impl DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: u16) { - DelayMs::delay_ms(self, u32::from(ms)); - } -} - -impl DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: u8) { - DelayMs::delay_ms(self, u32::from(ms)); - } -} - /// Machine timer (mtime) as a sleep delay provider using mtimecmp pub struct Sleep { clock_freq: u32, @@ -139,60 +76,3 @@ impl DelayNs for Sleep { } } } - -impl DelayMs for Sleep { - fn delay_ms(&mut self, ms: u32) { - let ticks = (ms as u64) * (self.clock_freq as u64) / 1000; - let t = MTIME.mtime() + ticks; - - self.mtimecmp.set_mtimecmp(t); - - // Enable timer interrupt - unsafe { - mie::set_mtimer(); - } - - // Wait For Interrupt will put CPU to sleep until an interrupt hits - // in our case when internal timer mtime value >= mtimecmp value - // after which empty handler gets called and we go into the - // next iteration of this loop - loop { - unsafe { - riscv::asm::wfi(); - } - - // check if we got the right interrupt cause, otherwise just loop back to wfi - if mip::read().mtimer() { - break; - } - } - - // Clear timer interrupt - unsafe { - mie::clear_mtimer(); - } - } -} - -// 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); - DelayMs::delay_ms(self, ms as u32); - } -} - -impl DelayMs for Sleep { - #[inline(always)] - fn delay_ms(&mut self, ms: u16) { - DelayMs::delay_ms(self, u32::from(ms)); - } -} - -impl DelayMs for Sleep { - #[inline(always)] - fn delay_ms(&mut self, ms: u8) { - DelayMs::delay_ms(self, u32::from(ms)); - } -} diff --git a/src/gpio.rs b/src/gpio.rs index e8641de..c34985e 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -356,215 +356,3 @@ gpio_eh_1!(GPIO0, gpio0, [ Pin30: (pin30, 30, Unknown), Pin31: (pin31, 31, Unknown), ]); - -macro_rules! gpio_eh_0 { - ($GPIOX:ident, $gpiox:ident, [ - $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ - ]) => { - /// GPIO - pub mod $gpiox { - use core::marker::PhantomData; - use core::convert::Infallible; - - use embedded_hal_0::digital::v2::{InputPin, OutputPin, StatefulOutputPin, - ToggleableOutputPin}; - use e310x::$GPIOX; - use super::{Unknown, IOF0, IOF1, Drive, Floating, Input, Invert, - NoInvert, Output, PullUp, Regular, PinIndex, PeripheralAccess}; - - /// GPIO parts for fine grained permission control. - pub struct Parts { - $( - /// Pin - pub $pxi: $PXi<$MODE>, - )+ - } - - $( - /// Pin - pub struct $PXi { - _mode: PhantomData, - } - - impl PinIndex for $PXi { - const INDEX: usize = $i; - } - - impl $PXi { - /// Configures the pin to serve as alternate function 0 (AF0) - pub fn into_iof0(self) -> $PXi> { - $GPIOX::set_out_xor(Self::INDEX, false); - $GPIOX::set_iof_sel(Self::INDEX, false); - $GPIOX::set_iof_en(Self::INDEX, true); - $PXi { _mode: PhantomData } - } - - - /// Configures the pin to serve as alternate function 1 (AF1) - pub fn into_iof1(self) -> $PXi> { - $GPIOX::set_out_xor(Self::INDEX, false); - $GPIOX::set_iof_sel(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, true); - $PXi { _mode: PhantomData } - } - - /// Configures the pin to serve as inverted alternate function 0 (AF0) - pub fn into_inverted_iof0(self) -> $PXi> { - $GPIOX::set_out_xor(Self::INDEX, true); - $GPIOX::set_iof_sel(Self::INDEX, false); - $GPIOX::set_iof_en(Self::INDEX, true); - $PXi { _mode: PhantomData } - } - - - /// Configures the pin to serve as inverted alternate function 1 (AF1) - pub fn into_inverted_iof1(self) -> $PXi> { - $GPIOX::set_out_xor(Self::INDEX, true); - $GPIOX::set_iof_sel(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, true); - $PXi { _mode: PhantomData } - } - - /// Configures the pin to serve as a floating input pin - pub fn into_floating_input(self) -> $PXi> { - $GPIOX::set_pullup(Self::INDEX, false); - $GPIOX::set_input_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a pulled down input pin - pub fn into_pull_up_input(self) -> $PXi> { - $GPIOX::set_pullup(Self::INDEX, true); - $GPIOX::set_input_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an output pin - pub fn into_output(self) -> $PXi>> { - $GPIOX::set_drive(Self::INDEX, false); - $GPIOX::set_out_xor(Self::INDEX, false); - $GPIOX::set_output_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an inverted output pin - pub fn into_inverted_output(self) -> $PXi>> { - $GPIOX::set_drive(Self::INDEX, false); - $GPIOX::set_out_xor(Self::INDEX, true); - $GPIOX::set_output_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - - /// Configure the pin to operate as an output pin with high - /// current drive - pub fn into_output_drive(self) -> $PXi>> { - $GPIOX::set_drive(Self::INDEX, true); - $GPIOX::set_out_xor(Self::INDEX, false); - $GPIOX::set_output_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - - /// Configure the pin to operate as an inverted output pin with - /// high current drive - pub fn into_inverted_output_drive(self) -> $PXi>> { - $GPIOX::set_drive(Self::INDEX, true); - $GPIOX::set_out_xor(Self::INDEX, true); - $GPIOX::set_output_en(Self::INDEX, true); - $GPIOX::set_iof_en(Self::INDEX, false); - $PXi { _mode: PhantomData } - } - } - - impl InputPin for $PXi> { - type Error = Infallible; - - fn is_high(&self) -> Result { - Ok($GPIOX::input_value(Self::INDEX)) - - } - - fn is_low(&self) -> Result { - Ok(!self.is_high()?) - } - } - - impl StatefulOutputPin for $PXi> { - fn is_set_high(&self) -> Result { - Ok($GPIOX::input_value(Self::INDEX)) - } - - fn is_set_low(&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(()) - } - - fn set_low(&mut self) -> Result<(), Infallible> { - $GPIOX::set_output_value(Self::INDEX, false); - Ok(()) - } - } - - impl ToggleableOutputPin for $PXi> { - type Error = Infallible; - - /// Toggles the pin state. - fn toggle(&mut self) -> Result<(), Infallible> { - $GPIOX::toggle_pin(Self::INDEX); - Ok(()) - } - } - )+ - } - } -} - -// By default, all GPIOs are in the Unknown state for two reasons: -// * bootloader may reconfigure some GPIOs -// * we do not enforce any specific state in `split()` -gpio_eh_0!(GPIO0, eh0_gpio0, [ - Pin0: (pin0, 0, Unknown), - Pin1: (pin1, 1, Unknown), - Pin2: (pin2, 2, Unknown), - Pin3: (pin3, 3, Unknown), - Pin4: (pin4, 4, Unknown), - Pin5: (pin5, 5, Unknown), - Pin6: (pin6, 6, Unknown), - Pin7: (pin7, 7, Unknown), - Pin8: (pin8, 8, Unknown), - Pin9: (pin9, 9, Unknown), - Pin10: (pin10, 10, Unknown), - Pin11: (pin11, 11, Unknown), - Pin12: (pin12, 12, Unknown), - Pin13: (pin13, 13, Unknown), - Pin14: (pin14, 14, Unknown), - Pin15: (pin15, 15, Unknown), - Pin16: (pin16, 16, Unknown), - Pin17: (pin17, 17, Unknown), - Pin18: (pin18, 18, Unknown), - Pin19: (pin19, 19, Unknown), - Pin20: (pin20, 20, Unknown), - Pin21: (pin21, 21, Unknown), - Pin22: (pin22, 22, Unknown), - Pin23: (pin23, 23, Unknown), - Pin24: (pin24, 24, Unknown), - Pin25: (pin25, 25, Unknown), - Pin26: (pin26, 26, Unknown), - Pin27: (pin27, 27, Unknown), - Pin28: (pin28, 28, Unknown), - Pin29: (pin29, 29, Unknown), - Pin30: (pin30, 30, Unknown), - Pin31: (pin31, 31, Unknown), -]); diff --git a/src/i2c.rs b/src/i2c.rs index b86d9ad..467429f 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -16,7 +16,6 @@ use crate::time::Bps; use core::mem; use core::ops::Deref; use e310x::{i2c0, I2C0}; -use embedded_hal_0::blocking::i2c::{Read, Write, WriteRead}; use embedded_hal_1::i2c::{ErrorKind, ErrorType, I2c as I2cTrait, Operation}; /// SDA pin - DO NOT IMPLEMENT THIS TRAIT @@ -333,136 +332,3 @@ impl, PINS> I2cTrait for I2c Ok(()) } } - -// e-h 0.2 - -impl, PINS> Read for I2c { - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.reset(); - - if self.read_sr().busy().bit_is_set() { - return Err(Error::InvalidState); - } - - // Write address + R - self.write_byte((address << 1) + FLAG_READ); - - // Generate start condition and write command - self.write_cr(|w| w.sta().set_bit().wr().set_bit()); - self.wait_for_write()?; - - // Read bytes - let buffer_len = buffer.len(); - for (i, byte) in buffer.iter_mut().enumerate() { - if i != buffer_len - 1 { - // R + ACK - self.write_cr(|w| w.rd().set_bit().ack().clear_bit()); - } else { - // R + NACK + STOP - self.write_cr(|w| w.rd().set_bit().ack().set_bit().sto().set_bit()); - } - self.wait_for_read()?; - - *byte = self.read_byte(); - } - Ok(()) - } -} - -impl, PINS> Write for I2c { - type Error = Error; - - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.reset(); - - if self.read_sr().busy().bit_is_set() { - return Err(Error::InvalidState); - } - - // Write address + W - self.write_byte((address << 1) + FLAG_WRITE); - - // Generate start condition and write command - self.write_cr(|w| w.sta().set_bit().wr().set_bit()); - self.wait_for_write()?; - - // Write bytes - for (i, byte) in bytes.iter().enumerate() { - self.write_byte(*byte); - - if i != bytes.len() - 1 { - self.write_cr(|w| w.wr().set_bit()); - } else { - self.write_cr(|w| w.wr().set_bit().sto().set_bit()); - } - self.wait_for_write()?; - } - Ok(()) - } -} - -impl, PINS> WriteRead for I2c { - type Error = Error; - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.reset(); - - if self.read_sr().busy().bit_is_set() { - return Err(Error::InvalidState); - } - - if !bytes.is_empty() && buffer.is_empty() { - Write::write(self, address, bytes) - } else if !buffer.is_empty() && bytes.is_empty() { - Read::read(self, address, buffer) - } else if bytes.is_empty() && buffer.is_empty() { - Ok(()) - } else { - // Write address + W - self.write_byte((address << 1) + FLAG_WRITE); - - // Generate start condition and write command - self.write_cr(|w| w.sta().set_bit().wr().set_bit()); - self.wait_for_write()?; - - // Write bytes - for byte in bytes { - self.write_byte(*byte); - - self.write_cr(|w| w.wr().set_bit()); - self.wait_for_write()?; - } - - // Write address + R - self.write_byte((address << 1) + FLAG_READ); - - // Generate repeated start condition and write command - self.write_cr(|w| w.sta().set_bit().wr().set_bit()); - self.wait_for_write()?; - - // Read bytes - let buffer_len = buffer.len(); - for (i, byte) in buffer.iter_mut().enumerate() { - if i != buffer_len - 1 { - // W + ACK - self.write_cr(|w| w.rd().set_bit().ack().clear_bit()); - } else { - // W + NACK + STOP - self.write_cr(|w| w.rd().set_bit().ack().set_bit().sto().set_bit()); - } - self.wait_for_read()?; - - *byte = self.read_byte(); - } - - Ok(()) - } - } -} diff --git a/src/prelude.rs b/src/prelude.rs index c7791f5..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_0::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_0::prelude::*; diff --git a/src/pwm.rs b/src/pwm.rs index 8f7a15a..009edcf 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -175,14 +175,9 @@ impl Pwm { } } -impl embedded_hal_0::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_0::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_0::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_0::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_0::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 64f8a1e..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_0::serial; -use nb; - use crate::clock::Clocks; use crate::gpio::{gpio0, IOF0}; use crate::time::Bps; @@ -178,48 +175,6 @@ impl embedded_io::Write for Tx { } } -// e-h 0.2 - -impl serial::Read for Rx { - type Error = Infallible; - - fn read(&mut self) -> nb::Result { - let rxdata = self.uart.rxdata.read(); - - if rxdata.empty().bit_is_set() { - Err(::nb::Error::WouldBlock) - } else { - Ok(rxdata.data().bits() as u8) - } - } -} - -impl serial::Write for Tx { - type Error = Infallible; - - fn write(&mut self, byte: u8) -> nb::Result<(), Infallible> { - let txdata = self.uart.txdata.read(); - - if txdata.full().bit_is_set() { - Err(::nb::Error::WouldBlock) - } else { - unsafe { - self.uart.txdata.write(|w| w.data().bits(byte)); - } - Ok(()) - } - } - - 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) - } - } -} - // Backward compatibility impl Serial { /// Configures a UART peripheral to provide serial communication From 1e25ece5a43fe8e2e77951d1fe74c49fbeac51ef Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 10:56:13 -0700 Subject: [PATCH 07/15] use new spi setup --- Cargo.toml | 10 +-- src/delay.rs | 6 +- src/gpio.rs | 4 +- src/i2c.rs | 8 +- src/spi.rs | 6 +- src/spi/bus.rs | 143 ++++++++++++++++++++---------------- src/spi/config.rs | 2 +- src/spi/exclusive_device.rs | 83 +++++---------------- src/spi/shared_bus.rs | 3 +- src/spi/shared_device.rs | 111 +++++----------------------- src/stdout.rs | 7 +- 11 files changed, 138 insertions(+), 245 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56f65f1..76a8c5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,18 +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-0 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0" } +embedded-hal = "1.0.0" embedded-io = "0.6.1" -nb = "1.0.0" -riscv = { version = "0.10.1", features = ["critical-section-single-hart"] } +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/src/delay.rs b/src/delay.rs index 8494759..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_1::delay::DelayNs; +use embedded_hal::delay::DelayNs; use riscv::register::{mie, mip}; /// Machine timer (mtime) as a busyloop delay provider @@ -60,9 +60,7 @@ impl DelayNs 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() { diff --git a/src/gpio.rs b/src/gpio.rs index c34985e..9825f9d 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -146,7 +146,7 @@ macro_rules! gpio_eh_1 { use core::marker::PhantomData; use core::convert::Infallible; - use embedded_hal_1::digital::{InputPin, OutputPin, StatefulOutputPin}; + 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}; @@ -188,7 +188,7 @@ macro_rules! gpio_eh_1 { const INDEX: usize = $i; } - impl embedded_hal_1::digital::ErrorType for $PXi { + impl embedded_hal::digital::ErrorType for $PXi { type Error = Infallible; } diff --git a/src/i2c.rs b/src/i2c.rs index 467429f..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_1::i2c::{ErrorKind, ErrorType, I2c as I2cTrait, Operation}; +use embedded_hal::i2c::{ErrorKind, ErrorType, I2c as I2cTrait, Operation}; /// SDA pin - DO NOT IMPLEMENT THIS TRAIT pub unsafe trait SdaPin {} @@ -181,13 +181,11 @@ const FLAG_WRITE: u8 = 0; // e-h 1.0 -impl embedded_hal_1::i2c::Error for Error { +impl embedded_hal::i2c::Error for Error { fn kind(&self) -> ErrorKind { match *self { Error::ArbitrationLost => ErrorKind::ArbitrationLoss, - Error::NoAck => { - ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Data) - } + Error::NoAck => ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Data), Error::InvalidState => ErrorKind::Other, } } 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 7e5cec6..c55154d 100644 --- a/src/spi/bus.rs +++ b/src/spi/bus.rs @@ -1,9 +1,6 @@ use core::convert::Infallible; -use embedded_hal_0::blocking::spi::Operation; -pub use embedded_hal_0::blocking::spi::{Transfer, Write, WriteIter}; -pub use embedded_hal_0::spi::{FullDuplex, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; - -use nb; +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 +98,67 @@ 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(); + + let mut read_count = 0; + let mut has_data = true; - pub(crate) fn read(&mut self) -> nb::Result { - let rxdata = self.spi.rxdata.read(); + // Ensure that RX FIFO is empty + self.wait_for_rxfifo(); - if rxdata.empty().bit_is_set() { - Err(nb::Error::WouldBlock) - } else { - Ok(rxdata.data().bits()) + 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 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 send(&mut self, byte: u8) -> nb::Result<(), Infallible> { - let txdata = self.spi.txdata.read(); +impl embedded_hal::spi::ErrorType for SpiBus +where + SPI: SpiX, +{ + type Error = Infallible; +} - if txdata.full().bit_is_set() { - Err(nb::Error::WouldBlock) - } else { - self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); - Ok(()) +impl embedded_hal::spi::SpiBus for SpiBus +where + SPI: SpiX, +{ + fn read(&mut self, words: &mut [u8]) -> Result<(), Infallible> { + let rxdata = self.spi.rxdata.read(); + // TODO: send 00 ?? + + for word in words { + if rxdata.empty().bit_is_set() { + break; + } + + *word = rxdata.data().bits(); } + + Ok(()) } - pub(crate) fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Infallible> { + fn write(&mut self, words: &[u8]) -> Result<(), Infallible> { let mut iwrite = 0; let mut iread = 0; @@ -139,34 +173,34 @@ where } if iread < iwrite { - let data = self.spi.rxdata.read(); - if data.empty().bit_is_clear() { - unsafe { *words.get_unchecked_mut(iread) = data.data().bits() }; + // Read and discard byte, if any + if self.spi.rxdata.read().empty().bit_is_clear() { iread += 1; } } } - Ok(words) + Ok(()) } - pub(crate) fn write(&mut self, words: &[u8]) -> Result<(), Infallible> { + 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 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) }; + 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 iread < iwrite { - // Read and discard byte, if any - if self.spi.rxdata.read().empty().bit_is_clear() { + let data = self.spi.rxdata.read(); + if data.empty().bit_is_clear() { + unsafe { *read.get_unchecked_mut(iread) = data.data().bits() }; iread += 1; } } @@ -175,32 +209,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_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 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 < 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 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 { *words.get_unchecked_mut(iread) = data.data().bits() }; + iread += 1; } } } @@ -208,20 +235,8 @@ 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 flush(&mut self) -> Result<(), Self::Error> { + self.wait_for_rxfifo(); Ok(()) } diff --git a/src/spi/config.rs b/src/spi/config.rs index 3c6aacc..6b30e72 100644 --- a/src/spi/config.rs +++ b/src/spi/config.rs @@ -1,5 +1,5 @@ use e310x::qspi0::csmode::MODE_A; -use embedded_hal_0::spi::Mode; +use embedded_hal::spi::Mode; use crate::{clock::Clocks, time::Hertz}; diff --git a/src/spi/exclusive_device.rs b/src/spi/exclusive_device.rs index 1776bbf..145659e 100644 --- a/src/spi/exclusive_device.rs +++ b/src/spi/exclusive_device.rs @@ -1,9 +1,6 @@ use core::convert::Infallible; -use embedded_hal_0::{ - blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, - spi::FullDuplex, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus as HalBus, SpiDevice}; use crate::spi::SpiConfig; @@ -34,87 +31,47 @@ where 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, { - 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) => Ok(()), // TODO: NOOP? + }?; + } self.bus.end_frame(); - result + Ok(()) } } diff --git a/src/spi/shared_bus.rs b/src/spi/shared_bus.rs index f920f23..bdef51f 100644 --- a/src/spi/shared_bus.rs +++ b/src/spi/shared_bus.rs @@ -1,11 +1,10 @@ use core::cell::RefCell; use core::ops::Deref; -// pub use embedded_hal_0::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; 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>); diff --git a/src/spi/shared_device.rs b/src/spi/shared_device.rs index 15c6dd6..b8459b7 100644 --- a/src/spi/shared_device.rs +++ b/src/spi/shared_device.rs @@ -1,9 +1,6 @@ use core::convert::Infallible; -use embedded_hal_0::{ - blocking::spi::{Operation, Transactional, Transfer, Write, WriteIter}, - spi::FullDuplex, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use riscv::interrupt; use super::{PinCS, Pins, PinsNoCS, SharedBus, SpiConfig, SpiX}; @@ -38,76 +35,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 +55,40 @@ where } } -impl WriteIter for SpiSharedDevice<'_, SPI, PINS, CS> +impl ErrorType for SpiSharedDevice<'_, SPI, PINS, CS> where SPI: SpiX, PINS: Pins, CS: PinCS, { 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> where SPI: SpiX, PINS: Pins, CS: PinCS, { - 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) => Ok(()), // TODO: NOOP? + }?; + } bus.end_frame(); - result + Ok(()) }) } } diff --git a/src/stdout.rs b/src/stdout.rs index 5123767..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_0::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); From 6347e5a98a347a5ae10e095a72c85af73d7951fa Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 10:56:24 -0700 Subject: [PATCH 08/15] bump MSRV to 1.62 --- .github/workflows/ci.yaml | 6 +++--- .github/workflows/rustfmt.yaml | 2 +- README.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) 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/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 From 067de9a358aeb2e17e15d2b3c29087c340c571df Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 10:58:04 -0700 Subject: [PATCH 09/15] bump changelog and rename gpio macro back --- CHANGELOG.md | 2 +- src/gpio.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f90ca..6cc31d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Use `portable-atomic` to allow builds on `riscv32imc-unknown-none-elf`` targets when needed -- Implement `embedded-hal` version `1.0` traits alongside version `0.2` +- Update `embedded-hal` version to `1.0` changing `delay`, `spi`, `i2c`, `gpio` and `pwm` modules to match - `I2c::free` renamed to `I2c::release` ## [v0.10.0] - 2023-03-28 diff --git a/src/gpio.rs b/src/gpio.rs index 9825f9d..d2776ae 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -137,7 +137,7 @@ trait PeripheralAccess { } } -macro_rules! gpio_eh_1 { +macro_rules! gpio { ($GPIOX:ident, $gpiox:ident, [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ ]) => { @@ -322,7 +322,7 @@ macro_rules! gpio_eh_1 { // By default, all GPIOs are in the Unknown state for two reasons: // * bootloader may reconfigure some GPIOs // * we do not enforce any specific state in `split()` -gpio_eh_1!(GPIO0, gpio0, [ +gpio!(GPIO0, gpio0, [ Pin0: (pin0, 0, Unknown), Pin1: (pin1, 1, Unknown), Pin2: (pin2, 2, Unknown), From 81cf577303ee08597eb5cfc98a34cf65685bbde2 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 10:58:47 -0700 Subject: [PATCH 10/15] add MSRV changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc31d3..63fc02e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - 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` +- Bumped MSRV to `1.62` ## [v0.10.0] - 2023-03-28 From a379fe01c046a56c5077a1613a46ea4fe28e5dc9 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 10:59:10 -0700 Subject: [PATCH 11/15] add changelog entry for removal of nb --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63fc02e..28629d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `I2c::free` renamed to `I2c::release` - Bumped MSRV to `1.62` +### Removed +- Removed `nb` dependency + ## [v0.10.0] - 2023-03-28 ### Added From df110d28b479d5700ab55c60ce0a66832cd7cf86 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 11:00:09 -0700 Subject: [PATCH 12/15] add embedded-io changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28629d5..3123c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### 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 From 9b78d21c4ba6042b5b523c1dfbfdec1e406dfcce Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 11:00:50 -0700 Subject: [PATCH 13/15] add riscv dependency change entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3123c4d..a26b868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - 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 From 7ad7d1326f6a1a9f7a60ce643f97226fa41502b7 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 14:43:31 -0700 Subject: [PATCH 14/15] add DelayNs providers to spi devices --- src/spi/bus.rs | 8 ++++++-- src/spi/exclusive_device.rs | 32 +++++++++++++++++++------------- src/spi/shared_bus.rs | 9 ++++++--- src/spi/shared_device.rs | 32 ++++++++++++++++++++------------ 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/spi/bus.rs b/src/spi/bus.rs index c55154d..d23fa69 100644 --- a/src/spi/bus.rs +++ b/src/spi/bus.rs @@ -1,4 +1,5 @@ use core::convert::Infallible; +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; @@ -248,8 +249,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 145659e..f753834 100644 --- a/src/spi/exclusive_device.rs +++ b/src/spi/exclusive_device.rs @@ -1,30 +1,35 @@ use core::convert::Infallible; -use embedded_hal::spi::{ErrorType, Operation, SpiBus as HalBus, SpiDevice}; +use embedded_hal::{ + delay::DelayNs, + spi::{ErrorType, Operation, SpiBus as HalBus, SpiDevice}, +}; 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 @@ -45,7 +50,7 @@ where } } -impl ErrorType for SpiExclusiveDevice +impl ErrorType for SpiExclusiveDevice where SPI: SpiX, PINS: Pins, @@ -53,22 +58,23 @@ where type Error = Infallible; } -impl SpiDevice for SpiExclusiveDevice +impl SpiDevice for SpiExclusiveDevice where SPI: SpiX, PINS: Pins, + D: DelayNs, { fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { self.bus.start_frame(); 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) => Ok(()), // TODO: NOOP? - }?; + 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(); diff --git a/src/spi/shared_bus.rs b/src/spi/shared_bus.rs index bdef51f..e285b02 100644 --- a/src/spi/shared_bus.rs +++ b/src/spi/shared_bus.rs @@ -1,5 +1,6 @@ use core::cell::RefCell; use core::ops::Deref; +use embedded_hal::delay::DelayNs; use riscv::interrupt; use super::{PinCS, PinsNoCS, SpiBus, SpiConfig, SpiSharedDevice, SpiX}; @@ -18,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 b8459b7..5319f08 100644 --- a/src/spi/shared_device.rs +++ b/src/spi/shared_device.rs @@ -1,26 +1,31 @@ use core::convert::Infallible; -use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; +use embedded_hal::{ + 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, { @@ -28,6 +33,7 @@ where bus, cs, config: config.clone(), + delay, } } @@ -55,20 +61,22 @@ where } } -impl ErrorType 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; } -impl SpiDevice for SpiSharedDevice<'_, SPI, PINS, CS> +impl SpiDevice for SpiSharedDevice<'_, SPI, PINS, CS, D> where SPI: SpiX, PINS: Pins, CS: PinCS, + D: DelayNs, { fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Infallible> { interrupt::free(|| { @@ -79,12 +87,12 @@ where bus.start_frame(); 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) => Ok(()), // TODO: NOOP? - }?; + 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(); From 70bc9402f480582c314dd55219dfe0c0d03aa611 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 1 Apr 2024 15:06:37 -0700 Subject: [PATCH 15/15] fix spi read --- src/spi/bus.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/spi/bus.rs b/src/spi/bus.rs index d23fa69..ae7520d 100644 --- a/src/spi/bus.rs +++ b/src/spi/bus.rs @@ -145,15 +145,25 @@ where SPI: SpiX, { fn read(&mut self, words: &mut [u8]) -> Result<(), Infallible> { - let rxdata = self.spi.rxdata.read(); - // TODO: send 00 ?? + let mut iwrite = 0; + let mut iread = 0; + + // Ensure that RX FIFO is empty + self.wait_for_rxfifo(); - for word in words { - if rxdata.empty().bit_is_set() { - break; + while iwrite < words.len() || iread < words.len() { + if iwrite < words.len() && self.spi.txdata.read().full().bit_is_clear() { + iwrite += 1; + self.spi.txdata.write(|w| unsafe { w.data().bits(0x00) }); } - *word = rxdata.data().bits(); + 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(())