diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs new file mode 100644 index 0000000000..daae43bcdd --- /dev/null +++ b/embassy-stm32/src/i2c/config.rs @@ -0,0 +1,170 @@ +#[cfg(gpio_v2)] +use crate::gpio::Pull; +use crate::gpio::{AfType, OutputType, Speed}; + +#[repr(u8)] +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Bits of the I2C OA2 register to mask out. +pub enum AddrMask { + /// No mask + NOMASK, + /// OA2\[1\] is masked and don’t care. Only OA2\[7:2\] are compared. + MASK1, + /// OA2\[2:1\] are masked and don’t care. Only OA2\[7:3\] are compared. + MASK2, + /// OA2\[3:1\] are masked and don’t care. Only OA2\[7:4\] are compared. + MASK3, + /// OA2\[4:1\] are masked and don’t care. Only OA2\[7:5\] are compared. + MASK4, + /// OA2\[5:1\] are masked and don’t care. Only OA2\[7:6\] are compared. + MASK5, + /// OA2\[6:1\] are masked and don’t care. Only OA2\[7:6\] are compared. + MASK6, + /// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged + MASK7, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// An I2C address. Either 7 or 10 bit. +pub enum Address { + /// A 7 bit address + SevenBit(u8), + /// A 10 bit address. + /// + /// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address. + TenBit(u16), +} +impl From for Address { + fn from(value: u8) -> Self { + Address::SevenBit(value) + } +} +impl From for Address { + fn from(value: u16) -> Self { + assert!(value < 0x400, "Ten bit address must be less than 0x400"); + Address::TenBit(value) + } +} +impl Address { + /// Get the inner address as a u16. + /// + /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16. + pub fn addr(&self) -> u16 { + match self { + Address::SevenBit(addr) => *addr as u16, + Address::TenBit(addr) => *addr, + } + } +} + +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The second Own Address register. +pub struct OA2 { + /// The address. + pub addr: u8, + /// The bit mask that will affect how the own address 2 register is compared. + pub mask: AddrMask, +} + +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The Own Address(es) of the I2C peripheral. +pub enum OwnAddresses { + /// Configuration for only the OA1 register. + OA1(Address), + /// Configuration for only the OA2 register. + OA2(OA2), + /// Configuration for both the OA1 and OA2 registers. + Both { + /// The [Address] for the OA1 register. + oa1: Address, + /// The [OA2] configuration. + oa2: OA2, + }, +} + +/// Slave Configuration +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SlaveAddrConfig { + /// Target Address(es) + pub addr: OwnAddresses, + /// Control if the peripheral should respond to the general call address + pub general_call: bool, +} +impl SlaveAddrConfig { + /// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled. + pub fn basic(addr: u8) -> Self { + Self { + addr: OwnAddresses::OA1(Address::SevenBit(addr)), + general_call: false, + } + } +} + +/// I2C config +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + /// Enable internal pullup on SDA. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + #[cfg(gpio_v2)] + pub sda_pullup: bool, + /// Enable internal pullup on SCL. + /// + /// Using external pullup resistors is recommended for I2C. If you do + /// have external pullups you should not enable this. + #[cfg(gpio_v2)] + pub scl_pullup: bool, + /// Timeout. + #[cfg(feature = "time")] + pub timeout: embassy_time::Duration, +} + +impl Default for Config { + fn default() -> Self { + Self { + #[cfg(gpio_v2)] + sda_pullup: false, + #[cfg(gpio_v2)] + scl_pullup: false, + #[cfg(feature = "time")] + timeout: embassy_time::Duration::from_millis(1000), + } + } +} + +impl Config { + pub(super) fn scl_af(&self) -> AfType { + #[cfg(gpio_v1)] + return AfType::output(OutputType::OpenDrain, Speed::Medium); + #[cfg(gpio_v2)] + return AfType::output_pull( + OutputType::OpenDrain, + Speed::Medium, + match self.scl_pullup { + true => Pull::Up, + false => Pull::Down, + }, + ); + } + + pub(super) fn sda_af(&self) -> AfType { + #[cfg(gpio_v1)] + return AfType::output(OutputType::OpenDrain, Speed::Medium); + #[cfg(gpio_v2)] + return AfType::output_pull( + OutputType::OpenDrain, + Speed::Medium, + match self.sda_pullup { + true => Pull::Up, + false => Pull::Down, + }, + ); + } +} diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 739e960b9c..2ff21702ba 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -5,19 +5,22 @@ #[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] mod _version; +mod config; + use core::future::Future; use core::iter; use core::marker::PhantomData; +pub use config::*; use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; +use mode::MasterMode; +pub use mode::{Master, MultiMaster}; use crate::dma::ChannelAndRequest; -#[cfg(gpio_v2)] -use crate::gpio::Pull; -use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; +use crate::gpio::{AnyPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; use crate::rcc::{RccInfo, SealedRccPeripheral}; @@ -44,85 +47,89 @@ pub enum Error { ZeroLengthTransfer, } -/// I2C config -#[non_exhaustive] -#[derive(Copy, Clone)] -pub struct Config { - /// Enable internal pullup on SDA. - /// - /// Using external pullup resistors is recommended for I2C. If you do - /// have external pullups you should not enable this. - #[cfg(gpio_v2)] - pub sda_pullup: bool, - /// Enable internal pullup on SCL. - /// - /// Using external pullup resistors is recommended for I2C. If you do - /// have external pullups you should not enable this. - #[cfg(gpio_v2)] - pub scl_pullup: bool, - /// Timeout. - #[cfg(feature = "time")] - pub timeout: embassy_time::Duration, +/// I2C modes +pub mod mode { + trait SealedMode {} + + /// Trait for I2C master operations. + #[allow(private_bounds)] + pub trait MasterMode: SealedMode {} + + /// Mode allowing for I2C master operations. + pub struct Master; + /// Mode allowing for I2C master and slave operations. + pub struct MultiMaster; + + impl SealedMode for Master {} + impl MasterMode for Master {} + + impl SealedMode for MultiMaster {} + impl MasterMode for MultiMaster {} } -impl Default for Config { - fn default() -> Self { - Self { - #[cfg(gpio_v2)] - sda_pullup: false, - #[cfg(gpio_v2)] - scl_pullup: false, - #[cfg(feature = "time")] - timeout: embassy_time::Duration::from_millis(1000), - } - } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The command kind to the slave from the master +pub enum SlaveCommandKind { + /// Write to the slave + Write, + /// Read from the slave + Read, } -impl Config { - fn scl_af(&self) -> AfType { - #[cfg(gpio_v1)] - return AfType::output(OutputType::OpenDrain, Speed::Medium); - #[cfg(gpio_v2)] - return AfType::output_pull( - OutputType::OpenDrain, - Speed::Medium, - match self.scl_pullup { - true => Pull::Up, - false => Pull::Down, - }, - ); - } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The command kind to the slave from the master and the address that the slave matched +pub struct SlaveCommand { + /// The kind of command + pub kind: SlaveCommandKind, + /// The address that the slave matched + pub address: Address, +} - fn sda_af(&self) -> AfType { - #[cfg(gpio_v1)] - return AfType::output(OutputType::OpenDrain, Speed::Medium); - #[cfg(gpio_v2)] - return AfType::output_pull( - OutputType::OpenDrain, - Speed::Medium, - match self.sda_pullup { - true => Pull::Up, - false => Pull::Down, - }, - ); +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The status of the slave send operation +pub enum SendStatus { + /// The slave send operation is done, all bytes have been sent and the master is not requesting more + Done, + /// The slave send operation is done, but there are leftover bytes that the master did not read + LeftoverBytes(usize), +} + +struct I2CDropGuard<'d> { + info: &'static Info, + scl: Option>, + sda: Option>, +} +impl<'d> Drop for I2CDropGuard<'d> { + fn drop(&mut self) { + if let Some(x) = self.scl.as_ref() { + x.set_as_disconnected() + } + if let Some(x) = self.sda.as_ref() { + x.set_as_disconnected() + } + + self.info.rcc.disable(); } } /// I2C driver. -pub struct I2c<'d, M: Mode> { +pub struct I2c<'d, M: Mode, IM: MasterMode> { info: &'static Info, state: &'static State, kernel_clock: Hertz, - scl: Option>, - sda: Option>, tx_dma: Option>, rx_dma: Option>, #[cfg(feature = "time")] timeout: Duration, _phantom: PhantomData, + _phantom2: PhantomData, + _drop_guard: I2CDropGuard<'d>, } -impl<'d> I2c<'d, Async> { +impl<'d> I2c<'d, Async, Master> { /// Create a new I2C driver. pub fn new( peri: impl Peripheral

+ 'd, @@ -148,7 +155,7 @@ impl<'d> I2c<'d, Async> { } } -impl<'d> I2c<'d, Blocking> { +impl<'d> I2c<'d, Blocking, Master> { /// Create a new blocking I2C driver. pub fn new_blocking( peri: impl Peripheral

+ 'd, @@ -169,7 +176,7 @@ impl<'d> I2c<'d, Blocking> { } } -impl<'d, M: Mode> I2c<'d, M> { +impl<'d, M: Mode> I2c<'d, M, Master> { /// Create a new I2C driver. fn new_inner( _peri: impl Peripheral

+ 'd, @@ -187,15 +194,20 @@ impl<'d, M: Mode> I2c<'d, M> { info: T::info(), state: T::state(), kernel_clock: T::frequency(), - scl, - sda, tx_dma, rx_dma, #[cfg(feature = "time")] timeout: config.timeout, _phantom: PhantomData, + _phantom2: PhantomData, + _drop_guard: I2CDropGuard { + info: T::info(), + scl, + sda, + }, }; this.enable_and_init(freq, config); + this } @@ -203,7 +215,9 @@ impl<'d, M: Mode> I2c<'d, M> { self.info.rcc.enable_and_reset(); self.init(freq, config); } +} +impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn timeout(&self) -> Timeout { Timeout { #[cfg(feature = "time")] @@ -212,15 +226,6 @@ impl<'d, M: Mode> I2c<'d, M> { } } -impl<'d, M: Mode> Drop for I2c<'d, M> { - fn drop(&mut self) { - self.scl.as_ref().map(|x| x.set_as_disconnected()); - self.sda.as_ref().map(|x| x.set_as_disconnected()); - - self.info.rcc.disable() - } -} - #[derive(Copy, Clone)] struct Timeout { #[cfg(feature = "time")] @@ -329,7 +334,7 @@ foreach_peripheral!( }; ); -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -337,7 +342,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { } } -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -345,7 +350,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { } } -impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { type Error = Error; fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -369,11 +374,11 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> { type Error = Error; } -impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } @@ -395,7 +400,7 @@ impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { } } -impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { +impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } @@ -511,9 +516,7 @@ fn operation_frames<'a, 'b: 'a>( let mut next_first_frame = true; Ok(iter::from_fn(move || { - let Some(op) = operations.next() else { - return None; - }; + let op = operations.next()?; // Is `op` first frame of its type? let first_frame = next_first_frame; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 28026f83c6..35f13ab46e 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -11,6 +11,7 @@ use embassy_embedded_hal::SetConfig; use embassy_futures::select::{select, Either}; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; +use mode::Master; use super::*; use crate::mode::Mode as PeriMode; @@ -41,7 +42,7 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, M: PeriMode> I2c<'d, M> { +impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -354,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M> { } } -impl<'d> I2c<'d, Async> { +impl<'d, IM: MasterMode> I2c<'d, Async, IM> { async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for @@ -800,7 +801,7 @@ impl Timings { } } -impl<'d, M: PeriMode> SetConfig for I2c<'d, M> { +impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 8c8df79dd8..64ccd24c7e 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -2,28 +2,61 @@ use core::cmp; use core::future::poll_fn; use core::task::Poll; +use config::{Address, OwnAddresses, OA2}; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; +use mode::{Master, MultiMaster}; +use stm32_metapac::i2c::vals::{Addmode, Oamsk}; use super::*; use crate::pac::i2c; +impl From for Oamsk { + fn from(value: AddrMask) -> Self { + match value { + AddrMask::NOMASK => Oamsk::NOMASK, + AddrMask::MASK1 => Oamsk::MASK1, + AddrMask::MASK2 => Oamsk::MASK2, + AddrMask::MASK3 => Oamsk::MASK3, + AddrMask::MASK4 => Oamsk::MASK4, + AddrMask::MASK5 => Oamsk::MASK5, + AddrMask::MASK6 => Oamsk::MASK6, + AddrMask::MASK7 => Oamsk::MASK7, + } + } +} + +impl Address { + pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode { + match self { + Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7, + Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, + } + } +} + pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); - if isr.tcr() || isr.tc() { + if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() { T::state().waker.wake(); } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt + critical_section::with(|_| { - regs.cr1().modify(|w| w.set_tcie(false)); + regs.cr1().modify(|w| { + w.set_addrie(false); + w.set_stopie(false); + w.set_tcie(false); + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + w.set_tcie(false); + }); }); } -impl<'d, M: Mode> I2c<'d, M> { +impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -51,7 +84,7 @@ impl<'d, M: Mode> I2c<'d, M> { fn master_read( info: &'static Info, - address: u8, + address: Address, length: usize, stop: Stop, reload: bool, @@ -80,8 +113,8 @@ impl<'d, M: Mode> I2c<'d, M> { }; info.regs.cr2().modify(|w| { - w.set_sadd((address << 1 | 0) as u16); - w.set_add10(i2c::vals::Addmode::BIT7); + w.set_sadd(address.addr() << 1); + w.set_add10(address.add_mode()); w.set_dir(i2c::vals::Dir::READ); w.set_nbytes(length as u8); w.set_start(true); @@ -94,7 +127,7 @@ impl<'d, M: Mode> I2c<'d, M> { fn master_write( info: &'static Info, - address: u8, + address: Address, length: usize, stop: Stop, reload: bool, @@ -124,8 +157,8 @@ impl<'d, M: Mode> I2c<'d, M> { // START bit can be set even if the bus is BUSY or // I2C is in slave mode. info.regs.cr2().modify(|w| { - w.set_sadd((address << 1 | 0) as u16); - w.set_add10(i2c::vals::Addmode::BIT7); + w.set_sadd(address.addr() << 1); + w.set_add10(address.add_mode()); w.set_dir(i2c::vals::Dir::WRITE); w.set_nbytes(length as u8); w.set_start(true); @@ -136,14 +169,14 @@ impl<'d, M: Mode> I2c<'d, M> { Ok(()) } - fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { + fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); while !info.regs.isr().read().tcr() { timeout.check()?; } - let reload = if reload { + let will_reload = if will_reload { i2c::vals::Reload::NOTCOMPLETED } else { i2c::vals::Reload::COMPLETED @@ -151,7 +184,7 @@ impl<'d, M: Mode> I2c<'d, M> { info.regs.cr2().modify(|w| { w.set_nbytes(length as u8); - w.set_reload(reload); + w.set_reload(will_reload); }); Ok(()) @@ -229,7 +262,13 @@ impl<'d, M: Mode> I2c<'d, M> { } } - fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> { + fn read_internal( + &mut self, + address: Address, + read: &mut [u8], + restart: bool, + timeout: Timeout, + ) -> Result<(), Error> { let completed_chunks = read.len() / 255; let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks @@ -250,7 +289,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { @@ -263,7 +302,13 @@ impl<'d, M: Mode> I2c<'d, M> { Ok(()) } - fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> { + fn write_internal( + &mut self, + address: Address, + write: &[u8], + send_stop: bool, + timeout: Timeout, + ) -> Result<(), Error> { let completed_chunks = write.len() / 255; let total_chunks = if completed_chunks * 255 == write.len() { completed_chunks @@ -291,7 +336,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { @@ -321,20 +366,20 @@ impl<'d, M: Mode> I2c<'d, M> { /// Blocking read. pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { - self.read_internal(address, read, false, self.timeout()) + self.read_internal(address.into(), read, false, self.timeout()) // Automatic Stop } /// Blocking write. pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { - self.write_internal(address, write, true, self.timeout()) + self.write_internal(address.into(), write, true, self.timeout()) } /// Blocking write, restart, read. pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { let timeout = self.timeout(); - self.write_internal(address, write, false, timeout)?; - self.read_internal(address, read, true, timeout) + self.write_internal(address.into(), write, false, timeout)?; + self.read_internal(address.into(), read, true, timeout) // Automatic Stop } @@ -364,7 +409,7 @@ impl<'d, M: Mode> I2c<'d, M> { if let Err(err) = Self::master_write( self.info, - address, + address.into(), first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), @@ -385,7 +430,7 @@ impl<'d, M: Mode> I2c<'d, M> { let last_chunk_idx = total_chunks.saturating_sub(1); if idx != 0 { - if let Err(err) = Self::master_continue( + if let Err(err) = Self::reload( self.info, slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), @@ -398,7 +443,7 @@ impl<'d, M: Mode> I2c<'d, M> { for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { - if let Err(err) = Self::master_continue( + if let Err(err) = Self::reload( self.info, chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), @@ -431,10 +476,10 @@ impl<'d, M: Mode> I2c<'d, M> { } } -impl<'d> I2c<'d, Async> { +impl<'d, IM: MasterMode> I2c<'d, Async, IM> { async fn write_dma_internal( &mut self, - address: u8, + address: Address, write: &[u8], first_slice: bool, last_slice: bool, @@ -482,7 +527,7 @@ impl<'d> I2c<'d, Async> { timeout, )?; } else { - Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -493,7 +538,7 @@ impl<'d> I2c<'d, Async> { } else { let last_piece = (remaining_len <= 255) && last_slice; - if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -519,7 +564,7 @@ impl<'d> I2c<'d, Async> { async fn read_dma_internal( &mut self, - address: u8, + address: Address, buffer: &mut [u8], restart: bool, timeout: Timeout, @@ -569,7 +614,7 @@ impl<'d> I2c<'d, Async> { } else { let last_piece = remaining_len <= 255; - if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -590,7 +635,6 @@ impl<'d> I2c<'d, Async> { Ok(()) } - // ========================= // Async public API @@ -598,10 +642,10 @@ impl<'d> I2c<'d, Async> { pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { - self.write_internal(address, write, true, timeout) + self.write_internal(address.into(), write, true, timeout) } else { timeout - .with(self.write_dma_internal(address, write, true, true, timeout)) + .with(self.write_dma_internal(address.into(), write, true, true, timeout)) .await } } @@ -609,7 +653,7 @@ impl<'d> I2c<'d, Async> { /// Write multiple buffers. /// /// The buffers are concatenated in a single write transaction. - pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { let timeout = self.timeout(); if write.is_empty() { @@ -636,9 +680,9 @@ impl<'d> I2c<'d, Async> { let timeout = self.timeout(); if buffer.is_empty() { - self.read_internal(address, buffer, false, timeout) + self.read_internal(address.into(), buffer, false, timeout) } else { - let fut = self.read_dma_internal(address, buffer, false, timeout); + let fut = self.read_dma_internal(address.into(), buffer, false, timeout); timeout.with(fut).await } } @@ -648,16 +692,16 @@ impl<'d> I2c<'d, Async> { let timeout = self.timeout(); if write.is_empty() { - self.write_internal(address, write, false, timeout)?; + self.write_internal(address.into(), write, false, timeout)?; } else { - let fut = self.write_dma_internal(address, write, true, true, timeout); + let fut = self.write_dma_internal(address.into(), write, true, true, timeout); timeout.with(fut).await?; } if read.is_empty() { - self.read_internal(address, read, true, timeout)?; + self.read_internal(address.into(), read, true, timeout)?; } else { - let fut = self.read_dma_internal(address, read, true, timeout); + let fut = self.read_dma_internal(address.into(), read, true, timeout); timeout.with(fut).await?; } @@ -676,6 +720,360 @@ impl<'d> I2c<'d, Async> { } } +impl<'d, M: Mode> I2c<'d, M, Master> { + /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) + pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { + let mut slave = I2c { + info: self.info, + state: self.state, + kernel_clock: self.kernel_clock, + tx_dma: self.tx_dma.take(), + rx_dma: self.rx_dma.take(), + #[cfg(feature = "time")] + timeout: self.timeout, + _phantom: PhantomData, + _phantom2: PhantomData, + _drop_guard: self._drop_guard, + }; + slave.init_slave(slave_addr_config); + slave + } +} + +impl<'d, M: Mode> I2c<'d, M, MultiMaster> { + pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { + self.info.regs.cr1().modify(|reg| { + reg.set_pe(false); + }); + + self.info.regs.cr1().modify(|reg| { + reg.set_nostretch(false); + reg.set_gcen(config.general_call); + reg.set_sbc(true); + reg.set_pe(true); + }); + + self.reconfigure_addresses(config.addr); + } + + /// Configure the slave address. + pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) { + match addresses { + OwnAddresses::OA1(oa1) => self.configure_oa1(oa1), + OwnAddresses::OA2(oa2) => self.configure_oa2(oa2), + OwnAddresses::Both { oa1, oa2 } => { + self.configure_oa1(oa1); + self.configure_oa2(oa2); + } + } + } + + fn configure_oa1(&mut self, oa1: Address) { + match oa1 { + Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| { + reg.set_oa1en(false); + reg.set_oa1((addr << 1) as u16); + reg.set_oa1mode(Addmode::BIT7); + reg.set_oa1en(true); + }), + Address::TenBit(addr) => self.info.regs.oar1().write(|reg| { + reg.set_oa1en(false); + reg.set_oa1(addr); + reg.set_oa1mode(Addmode::BIT10); + reg.set_oa1en(true); + }), + } + } + + fn configure_oa2(&mut self, oa2: OA2) { + self.info.regs.oar2().write(|reg| { + reg.set_oa2en(false); + reg.set_oa2msk(oa2.mask.into()); + reg.set_oa2(oa2.addr << 1); + reg.set_oa2en(true); + }); + } + + fn determine_matched_address(&self) -> Result { + let matched = self.info.regs.isr().read().addcode(); + + if matched >> 3 == 0b11110 { + // is 10-bit address and we need to get the other 8 bits from the rxdr + // we do this by doing a blocking read of 1 byte + let mut buffer = [0]; + self.slave_read_internal(&mut buffer, self.timeout())?; + Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16)) + } else { + Ok(Address::SevenBit(matched)) + } + } +} + +impl<'d, M: Mode> I2c<'d, M, MultiMaster> { + /// # Safety + /// This function will clear the address flag which will stop the clock stretching. + /// This should only be done after the dma transfer has been set up. + fn slave_start(info: &'static Info, length: usize, reload: bool) { + assert!(length < 256); + + let reload = if reload { + i2c::vals::Reload::NOTCOMPLETED + } else { + i2c::vals::Reload::COMPLETED + }; + + info.regs.cr2().modify(|w| { + w.set_nbytes(length as u8); + w.set_reload(reload); + }); + + // clear the address flag, will stop the clock stretching. + // this should only be done after the dma transfer has been set up. + info.regs.icr().modify(|reg| reg.set_addrcf(true)); + } + + // A blocking read operation + fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<(), Error> { + let completed_chunks = read.len() / 255; + let total_chunks = if completed_chunks * 255 == read.len() { + completed_chunks + } else { + completed_chunks + 1 + }; + let last_chunk_idx = total_chunks.saturating_sub(1); + for (number, chunk) in read.chunks_mut(255).enumerate() { + if number != 0 { + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + } + + for byte in chunk { + // Wait until we have received something + self.wait_rxne(timeout)?; + + *byte = self.info.regs.rxdr().read().rxdata(); + } + } + + Ok(()) + } + + // A blocking write operation + fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<(), Error> { + let completed_chunks = write.len() / 255; + let total_chunks = if completed_chunks * 255 == write.len() { + completed_chunks + } else { + completed_chunks + 1 + }; + let last_chunk_idx = total_chunks.saturating_sub(1); + + for (number, chunk) in write.chunks(255).enumerate() { + if number != 0 { + Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + } + + for byte in chunk { + // Wait until we are allowed to send data + // (START has been ACKed or last byte when + // through) + self.wait_txe(timeout)?; + + self.info.regs.txdr().write(|w| w.set_txdata(*byte)); + } + } + Ok(()) + } + + /// Listen for incoming I2C messages. + /// + /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. + pub async fn listen(&mut self) -> Result { + let state = self.state; + self.info.regs.cr1().modify(|reg| { + reg.set_addrie(true); + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + let isr = self.info.regs.isr().read(); + if !isr.addr() { + Poll::Pending + } else { + // we do not clear the address flag here as it will be cleared by the dma read/write + // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it + match isr.dir() { + i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Write, + address: self.determine_matched_address()?, + })), + i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand { + kind: SlaveCommandKind::Read, + address: self.determine_matched_address()?, + })), + } + } + }) + .await + } + + /// Respond to a write command. + pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + self.slave_read_internal(read, timeout) + } + + /// Respond to a read command. + pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> { + let timeout = self.timeout(); + self.slave_write_internal(write, timeout) + } +} + +impl<'d> I2c<'d, Async, MultiMaster> { + /// Respond to a write command. + /// + /// Returns the total number of bytes received. + pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { + let timeout = self.timeout(); + timeout.with(self.read_dma_internal_slave(buffer, timeout)).await + } + + /// Respond to a read request from an I2C master. + pub async fn respond_to_read(&mut self, write: &[u8]) -> Result { + let timeout = self.timeout(); + timeout.with(self.write_dma_internal_slave(write, timeout)).await + } + + // for data reception in slave mode + // + // returns the total number of bytes received + async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result { + let total_len = buffer.len(); + let mut remaining_len = total_len; + + let regs = self.info.regs; + + let dma_transfer = unsafe { + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_stopie(true); + w.set_tcie(true); + }); + let src = regs.rxdr().as_ptr() as *mut u8; + + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) + }; + + let state = self.state; + + let on_drop = OnDrop::new(|| { + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_stopie(false); + w.set_tcie(false); + }) + }); + + let total_received = poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = regs.isr().read(); + + if remaining_len == total_len { + Self::slave_start(self.info, total_len.min(255), total_len > 255); + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + } else if isr.tcr() { + let is_last_slice = remaining_len <= 255; + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + return Poll::Ready(Err(e)); + } + remaining_len = remaining_len.saturating_sub(255); + regs.cr1().modify(|w| w.set_tcie(true)); + Poll::Pending + } else if isr.stopf() { + regs.icr().write(|reg| reg.set_stopcf(true)); + let poll = Poll::Ready(Ok(total_len - remaining_len)); + poll + } else { + Poll::Pending + } + }) + .await?; + + dma_transfer.await; + + drop(on_drop); + + Ok(total_received) + } + + async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result { + let total_len = buffer.len(); + let mut remaining_len = total_len; + + let mut dma_transfer = unsafe { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_txdmaen(true); + w.set_stopie(true); + w.set_tcie(true); + }); + let dst = regs.txdr().as_ptr() as *mut u8; + + self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default()) + }; + + let on_drop = OnDrop::new(|| { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_txdmaen(false); + w.set_stopie(false); + w.set_tcie(false); + }) + }); + + let state = self.state; + + let size = poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = self.info.regs.isr().read(); + + if remaining_len == total_len { + Self::slave_start(self.info, total_len.min(255), total_len > 255); + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + } else if isr.tcr() { + let is_last_slice = remaining_len <= 255; + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + return Poll::Ready(Err(e)); + } + remaining_len = remaining_len.saturating_sub(255); + self.info.regs.cr1().modify(|w| w.set_tcie(true)); + Poll::Pending + } else if isr.stopf() { + self.info.regs.icr().write(|reg| reg.set_stopcf(true)); + if remaining_len > 0 { + dma_transfer.request_stop(); + Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) + } else { + Poll::Ready(Ok(SendStatus::Done)) + } + } else { + Poll::Pending + } + }) + .await?; + + dma_transfer.await; + + drop(on_drop); + + Ok(size) + } +} + /// I2C Stop Configuration /// /// Peripheral options for generating the STOP condition @@ -800,7 +1198,7 @@ impl Timings { } } -impl<'d, M: Mode> SetConfig for I2c<'d, M> { +impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { @@ -816,3 +1214,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> { Ok(()) } } + +impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { + type Config = (Hertz, SlaveAddrConfig); + type ConfigError = (); + fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> { + let timings = Timings::new(self.kernel_clock, *config); + self.info.regs.timingr().write(|reg| { + reg.set_presc(timings.prescale); + reg.set_scll(timings.scll); + reg.set_sclh(timings.sclh); + reg.set_sdadel(timings.sdadel); + reg.set_scldel(timings.scldel); + }); + self.init_slave(*addr_config); + + Ok(()) + } +} diff --git a/examples/stm32g4/src/bin/i2c_slave.rs b/examples/stm32g4/src/bin/i2c_slave.rs new file mode 100644 index 0000000000..a723a0e186 --- /dev/null +++ b/examples/stm32g4/src/bin/i2c_slave.rs @@ -0,0 +1,149 @@ +//! This example shows how to use an stm32 as both a master and a slave. +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind}; +use embassy_stm32::mode::Async; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C1_ER => i2c::ErrorInterruptHandler; + I2C1_EV => i2c::EventInterruptHandler; + I2C2_ER => i2c::ErrorInterruptHandler; + I2C2_EV => i2c::EventInterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! { + info!("Device start"); + + let mut state = 0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen().await { + Ok(i2c::SlaveCommand { + kind: SlaveCommandKind::Read, + address: Address::SevenBit(DEV_ADDR), + }) => match dev.respond_to_read(&[state]).await { + Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x), + Ok(i2c::SendStatus::Done) => {} + Err(e) => error!("error while responding {}", e), + }, + Ok(i2c::SlaveCommand { + kind: SlaveCommandKind::Write, + address: Address::SevenBit(DEV_ADDR), + }) => match dev.respond_to_write(&mut buf).await { + Ok(len) => { + info!("Device received write: {}", buf[..len]); + + if match buf[0] { + // Set the state + 0xC2 => { + state = buf[1]; + true + } + // Reset State + 0xC8 => { + state = 0; + true + } + x => { + error!("Invalid Write Read {:x}", x); + false + } + } { + match dev.respond_to_read(&[state]).await { + Ok(read_status) => info!( + "This read is part of a write/read transaction. The response read status {}", + read_status + ), + Err(i2c::Error::Timeout) => { + info!("The device only performed a write and it not also do a read") + } + Err(e) => error!("error while responding {}", e), + } + } + } + Err(e) => error!("error while receiving {}", e), + }, + Ok(i2c::SlaveCommand { address, .. }) => { + defmt::unreachable!( + "The slave matched address: {}, which it was not configured for", + address + ); + } + Err(e) => error!("{}", e), + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) { + info!("Controller start"); + + loop { + let mut resp_buff = [0u8; 1]; + for i in 0..10 { + match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { + Ok(_) => { + info!("write_read response: {}", resp_buff); + defmt::assert_eq!(resp_buff[0], i); + } + Err(e) => error!("Error writing {}", e), + } + + Timer::after_millis(100).await; + } + match con.read(DEV_ADDR, &mut resp_buff).await { + Ok(_) => { + info!("read response: {}", resp_buff); + // assert that the state is the last index that was written + defmt::assert_eq!(resp_buff[0], 9); + } + Err(e) => error!("Error writing {}", e), + } + match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { + Ok(_) => { + info!("write_read response: {}", resp_buff); + // assert that the state has been reset + defmt::assert_eq!(resp_buff[0], 0); + } + Err(e) => error!("Error writing {}", e), + } + Timer::after_millis(100).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let speed = Hertz::khz(400); + let config = i2c::Config::default(); + + let d_addr_config = i2c::SlaveAddrConfig { + addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)), + general_call: false, + }; + let d_sda = p.PA8; + let d_scl = p.PA9; + let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config) + .into_slave_multimaster(d_addr_config); + + unwrap!(spawner.spawn(device_task(device))); + + let c_sda = p.PB8; + let c_scl = p.PB7; + let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config); + + unwrap!(spawner.spawn(controller_task(controller))); +} diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 6f4815582b..321b35a3e5 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -23,7 +23,7 @@ const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17]; const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; -static I2C_BUS: StaticCell>>> = StaticCell::new(); +static I2C_BUS: StaticCell>>> = StaticCell::new(); bind_interrupts!(struct Irqs { I2C1_EV => i2c::EventInterruptHandler; diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index bd633cecb5..8f23e40831 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -60,7 +60,7 @@ pub type SpeSpiCs = ExclusiveDevice, Delay>; pub type SpeInt = exti::ExtiInput<'static>; pub type SpeRst = Output<'static>; pub type Adin1110T = ADIN1110; -pub type TempSensI2c = I2c<'static, Async>; +pub type TempSensI2c = I2c<'static, Async, i2c::Master>; static TEMP: AtomicI32 = AtomicI32::new(0);