From f30ff9cadcf575100a4e08c972d2f161172a3fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Fri, 8 Jul 2022 15:47:47 +0200 Subject: [PATCH] Shared buses with SetConfig --- embassy-embedded-hal/src/lib.rs | 4 + .../src/shared_bus/blocking/i2c.rs | 84 +++++++++++++++++++ .../src/shared_bus/blocking/spi.rs | 50 +++++++++++ embassy-embedded-hal/src/shared_bus/i2c.rs | 78 +++++++++++++++++ embassy-embedded-hal/src/shared_bus/spi.rs | 61 ++++++++++++++ embassy-nrf/Cargo.toml | 1 + embassy-nrf/src/spim.rs | 43 ++++++++++ embassy-nrf/src/twim.rs | 10 +++ examples/nrf/src/bin/shared_bus.rs | 21 +++++ 9 files changed, 352 insertions(+) create mode 100644 examples/nrf/src/bin/shared_bus.rs diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 27ffa7421..aae719925 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -4,3 +4,7 @@ pub mod adapter; pub mod shared_bus; + +pub trait SetConfig { + fn set_config(&mut self, config: &C); +} diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index 2c762fe14..6f5f07051 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -7,6 +7,7 @@ use embedded_hal_1::i2c::blocking::{I2c, Operation}; use embedded_hal_1::i2c::ErrorType; use crate::shared_bus::i2c::I2cBusDeviceError; +use crate::SetConfig; pub struct I2cBusDevice<'a, M: RawMutex, BUS> { bus: &'a Mutex>, @@ -82,3 +83,86 @@ where todo!() } } + +pub struct I2cBusDeviceWithConfig<'a, M: RawMutex, BUS, C> { + bus: &'a Mutex>, + config: C, +} + +impl<'a, M: RawMutex, BUS, C> I2cBusDeviceWithConfig<'a, M, BUS, C> { + pub fn new(bus: &'a Mutex>, config: C) -> Self { + Self { bus, config } + } +} + +impl<'a, M: RawMutex, BUS, C> ErrorType for I2cBusDeviceWithConfig<'a, M, BUS, C> +where + BUS: ErrorType, +{ + type Error = I2cBusDeviceError; +} + +impl I2c for I2cBusDeviceWithConfig<'_, M, BUS, C> +where + M: RawMutex, + BUS: I2c + SetConfig, +{ + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + bus.read(address, buffer).map_err(I2cBusDeviceError::I2c) + }) + } + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + bus.write(address, bytes).map_err(I2cBusDeviceError::I2c) + }) + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + bus.write_read(address, wr_buffer, rd_buffer) + .map_err(I2cBusDeviceError::I2c) + }) + } + + fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> { + let _ = address; + let _ = operations; + todo!() + } + + fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { + let _ = addr; + let _ = bytes; + todo!() + } + + fn write_iter_read>( + &mut self, + addr: u8, + bytes: B, + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + let _ = addr; + let _ = bytes; + let _ = buffer; + todo!() + } + + fn transaction_iter<'a, O: IntoIterator>>( + &mut self, + address: u8, + operations: O, + ) -> Result<(), Self::Error> { + let _ = address; + let _ = operations; + todo!() + } +} diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index c08bcbf62..d54ca6bfa 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -8,6 +8,7 @@ use embedded_hal_1::spi; use embedded_hal_1::spi::blocking::{SpiBusFlush, SpiDevice}; use crate::shared_bus::spi::SpiBusDeviceError; +use crate::SetConfig; pub struct SpiBusDevice<'a, M: RawMutex, BUS, CS> { bus: &'a Mutex>, @@ -55,3 +56,52 @@ where }) } } + +pub struct SpiBusDeviceWithConfig<'a, M: RawMutex, BUS, CS, C> { + bus: &'a Mutex>, + cs: CS, + config: C, +} + +impl<'a, M: RawMutex, BUS, CS, C> SpiBusDeviceWithConfig<'a, M, BUS, CS, C> { + pub fn new(bus: &'a Mutex>, cs: CS, config: C) -> Self { + Self { bus, cs, config } + } +} + +impl<'a, M: RawMutex, BUS, CS, C> spi::ErrorType for SpiBusDeviceWithConfig<'a, M, BUS, CS, C> +where + BUS: spi::ErrorType, + CS: OutputPin, +{ + type Error = SpiBusDeviceError; +} + +impl SpiDevice for SpiBusDeviceWithConfig<'_, M, BUS, CS, C> +where + M: RawMutex, + BUS: SpiBusFlush + SetConfig, + CS: OutputPin, +{ + type Bus = BUS; + + fn transaction(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result) -> Result { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiBusDeviceError::Cs)?; + + let f_res = f(&mut bus); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let f_res = f_res.map_err(SpiBusDeviceError::Spi)?; + flush_res.map_err(SpiBusDeviceError::Spi)?; + cs_res.map_err(SpiBusDeviceError::Cs)?; + + Ok(f_res) + }) + } +} diff --git a/embassy-embedded-hal/src/shared_bus/i2c.rs b/embassy-embedded-hal/src/shared_bus/i2c.rs index e8131288a..0e964773c 100644 --- a/embassy-embedded-hal/src/shared_bus/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/i2c.rs @@ -29,6 +29,8 @@ use embassy::blocking_mutex::raw::RawMutex; use embassy::mutex::Mutex; use embedded_hal_async::i2c; +use crate::SetConfig; + #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum I2cBusDeviceError { I2c(BUS), @@ -116,3 +118,79 @@ where async move { todo!() } } } + +pub struct I2cBusDeviceWithConfig<'a, M: RawMutex, BUS, C> { + bus: &'a Mutex, + config: C, +} + +impl<'a, M: RawMutex, BUS, C> I2cBusDeviceWithConfig<'a, M, BUS, C> { + pub fn new(bus: &'a Mutex, config: C) -> Self { + Self { bus, config } + } +} + +impl<'a, M: RawMutex, BUS, C> i2c::ErrorType for I2cBusDeviceWithConfig<'a, M, BUS, C> +where + BUS: i2c::ErrorType, +{ + type Error = I2cBusDeviceError; +} + +impl i2c::I2c for I2cBusDeviceWithConfig<'_, M, BUS, C> +where + M: RawMutex + 'static, + BUS: i2c::I2c + SetConfig + 'static, +{ + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.read(address, buffer).await.map_err(I2cBusDeviceError::I2c)?; + Ok(()) + } + } + + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + async move { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write(address, bytes).await.map_err(I2cBusDeviceError::I2c)?; + Ok(()) + } + } + + type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write_read<'a>( + &'a mut self, + address: u8, + wr_buffer: &'a [u8], + rd_buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + async move { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write_read(address, wr_buffer, rd_buffer) + .await + .map_err(I2cBusDeviceError::I2c)?; + Ok(()) + } + } + + type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; + + fn transaction<'a, 'b>( + &'a mut self, + address: u8, + operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let _ = address; + let _ = operations; + async move { todo!() } + } +} diff --git a/embassy-embedded-hal/src/shared_bus/spi.rs b/embassy-embedded-hal/src/shared_bus/spi.rs index fd4b6d565..04378c330 100644 --- a/embassy-embedded-hal/src/shared_bus/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/spi.rs @@ -34,6 +34,8 @@ use embedded_hal_1::digital::blocking::OutputPin; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi; +use crate::SetConfig; + #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SpiBusDeviceError { Spi(BUS), @@ -109,3 +111,62 @@ where } } } + +pub struct SpiBusDeviceWithConfig<'a, M: RawMutex, BUS, CS, C> { + bus: &'a Mutex, + cs: CS, + config: C, +} + +impl<'a, M: RawMutex, BUS, CS, C> SpiBusDeviceWithConfig<'a, M, BUS, CS, C> { + pub fn new(bus: &'a Mutex, cs: CS, config: C) -> Self { + Self { bus, cs, config } + } +} + +impl<'a, M: RawMutex, BUS, CS, C> spi::ErrorType for SpiBusDeviceWithConfig<'a, M, BUS, CS, C> +where + BUS: spi::ErrorType, + CS: OutputPin, +{ + type Error = SpiBusDeviceError; +} + +impl spi::SpiDevice for SpiBusDeviceWithConfig<'_, M, BUS, CS, C> +where + M: RawMutex + 'static, + BUS: spi::SpiBusFlush + SetConfig + 'static, + CS: OutputPin, +{ + type Bus = BUS; + + type TransactionFuture<'a, R, F, Fut> = impl Future> + 'a + where + Self: 'a, R: 'a, F: FnOnce(*mut Self::Bus) -> Fut + 'a, + Fut: Future::Error>> + 'a; + + fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut> + where + R: 'a, + F: FnOnce(*mut Self::Bus) -> Fut + 'a, + Fut: Future::Error>> + 'a, + { + async move { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiBusDeviceError::Cs)?; + + let f_res = f(&mut *bus).await; + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); + + let f_res = f_res.map_err(SpiBusDeviceError::Spi)?; + flush_res.map_err(SpiBusDeviceError::Spi)?; + cs_res.map_err(SpiBusDeviceError::Cs)?; + + Ok(f_res) + } + } +} diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 887ea1bd2..3f9dabbb5 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -68,6 +68,7 @@ embassy = { version = "0.1.0", path = "../embassy" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index efccfeca3..882bf4b47 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -4,6 +4,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_embedded_hal::SetConfig; use embassy_hal_common::unborrow; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use futures::future::poll_fn; @@ -521,3 +522,45 @@ cfg_if::cfg_if! { } } } + +impl<'d, T: Instance> SetConfig for Spim<'d, T> { + fn set_config(&mut self, config: &Config) { + let r = T::regs(); + // Configure mode. + let mode = config.mode; + r.config.write(|w| { + match mode { + MODE_0 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().leading(); + } + MODE_1 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().trailing(); + } + MODE_2 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().leading(); + } + MODE_3 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().trailing(); + } + } + + w + }); + + // Configure frequency. + let frequency = config.frequency; + r.frequency.write(|w| w.frequency().variant(frequency)); + + // Set over-read character + let orc = config.orc; + r.orc.write(|w| unsafe { w.orc().bits(orc) }); + } +} diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index c3921104e..8189d3e71 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -15,6 +15,7 @@ use core::task::Poll; #[cfg(feature = "time")] use embassy::time::{Duration, Instant}; use embassy::waitqueue::AtomicWaker; +use embassy_embedded_hal::SetConfig; use embassy_hal_common::unborrow; use futures::future::poll_fn; @@ -24,6 +25,7 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Unborrow}; +#[derive(Clone, Copy)] pub enum Frequency { #[doc = "26738688: 100 kbps"] K100 = 26738688, @@ -877,3 +879,11 @@ cfg_if::cfg_if! { } } } + +impl<'d, T: Instance> SetConfig for Twim<'d, T> { + fn set_config(&mut self, config: &Config) { + let r = T::regs(); + r.frequency + .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); + } +} diff --git a/examples/nrf/src/bin/shared_bus.rs b/examples/nrf/src/bin/shared_bus.rs new file mode 100644 index 000000000..23d16f796 --- /dev/null +++ b/examples/nrf/src/bin/shared_bus.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::Peripherals; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +}