diff --git a/Cargo.toml b/Cargo.toml index 56fa9a9..b42732a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,3 +91,11 @@ required-features = ["stm32h503"] [[example]] name = "i2c" required-features = ["stm32h503"] + +[[example]] +name = "i2c_target" +required-features = ["stm32h503"] + +[[example]] +name = "i2c_target_manual_ack" +required-features = ["stm32h503"] diff --git a/examples/i2c_target.rs b/examples/i2c_target.rs new file mode 100644 index 0000000..60d4498 --- /dev/null +++ b/examples/i2c_target.rs @@ -0,0 +1,65 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use] +mod utilities; +use stm32h5xx_hal::{ + i2c::{TargetConfig, TargetListenEvent, Targetable}, + pac, + prelude::*, +}; + +use cortex_m_rt::entry; + +use log::info; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + log::set_max_level(log::LevelFilter::Debug); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + + let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); + + // Configure the SCL and the SDA pin for our I2C bus + let scl = gpiob.pb5.into_alternate_open_drain(); + let sda = gpiob.pb3.into_alternate_open_drain(); + + info!(""); + info!("stm32h5xx-hal example - I2C Target"); + info!(""); + + let own_addr: u16 = 0x18; + let bus_freq_hz: u32 = 100_000; + let mut i2c = dp.I2C2.i2c_target_only( + (scl, sda), + TargetConfig::new(own_addr, bus_freq_hz), + ccdr.peripheral.I2C2, + &ccdr.clocks, + ); + + i2c.enable_listen_event(TargetListenEvent::PrimaryAddress); + let mut buf = [0u8; 2]; + + loop { + let count = i2c.wait_for_target_read(&mut buf).unwrap(); + info!("Read {count} bytes: {:X?}", &buf[..count]); + + let count = i2c.wait_for_target_write(b"Hello").unwrap(); + info!("Wrote {count} bytes"); + + i2c.wait_for_stop().unwrap(); + info!("=========="); + } +} diff --git a/examples/i2c_target_manual_ack.rs b/examples/i2c_target_manual_ack.rs new file mode 100644 index 0000000..9085df5 --- /dev/null +++ b/examples/i2c_target_manual_ack.rs @@ -0,0 +1,80 @@ +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use] +mod utilities; +use stm32h5xx_hal::{ + i2c::{TargetConfig, TargetEvent, TargetListenEvent, Targetable}, + pac, + prelude::*, +}; + +use cortex_m_rt::entry; + +use log::info; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + log::set_max_level(log::LevelFilter::Info); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + + let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); + + // Configure the SCL and the SDA pin for our I2C bus + let scl = gpiob.pb5.into_alternate_open_drain(); + let sda = gpiob.pb3.into_alternate_open_drain(); + + info!(""); + info!("stm32h5xx-hal example - I2C Target"); + info!(""); + + let own_addr: u16 = 0x18; + let bus_freq_hz: u32 = 100_000; + let mut i2c = dp + .I2C2 + .i2c_target_only( + (scl, sda), + TargetConfig::new(own_addr, bus_freq_hz), + ccdr.peripheral.I2C2, + &ccdr.clocks, + ) + .with_manual_ack_control(); + + i2c.enable_listen_event(TargetListenEvent::PrimaryAddress); + let mut buf = [0u8; 2]; + + loop { + let event = i2c.wait_for_event().unwrap(); + let result = match event { + TargetEvent::Read { address: _ } => i2c.write(b"Hello"), + TargetEvent::Write { address: _ } => { + let result = i2c.read(&mut buf); + // An operation can be performed here while the clock is stretched low (ie. + // calculating or fetching data) + i2c.ack_transfer(); + result + } + TargetEvent::Stop => Ok(0), + }; + + match result { + Err(error) => info!("Error: {event:?} - {error:?}"), + Ok(count) => match event { + TargetEvent::Stop => info!("=========="), + _ => info!("{event:?} ({count})"), + }, + }; + } +} diff --git a/src/i2c.rs b/src/i2c.rs index 2b236d7..514b00e 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -1,5 +1,11 @@ //! Inter Integrated Circuit (I2C) //! +//! This module provides I2C functionality as both a Controller and Target, and +//! supports multi-controller topologies through role-switching between +//! Controller and Target. The Controller implementation is exposed via the I2c +//! struct, while the Target implementation is exposed via the I2cTarget +//! struct. +//! //! # Terminology //! This uses the updated Controller/Target terminology that replaces Master/Slave, as of v7 of the //! I2C Spec. See https://www.nxp.com/docs/en/user-guide/UM10204.pdf @@ -43,10 +49,104 @@ //! i2c.write_read(0x18, &write, &mut read)?; //! ``` //! +//! ## Target +//! In the simplest case, the I2cTarget can be initialized from the device peripheral +//! and GPIO pins: +//! +//! ``` +//! let dp = ...; // Device peripherals +//! let (scl, sda) = ...; // GPIO pins +//! let own_addr = ...; // Primary address for target operation +//! +//! let mut i2c_target = dp.I2C1.i2c_target_only( +//! (scl, sda), +//! own_addr, +//! ccdr.peripheral.I2C1, +//! ); +//! ``` +//! +//! The target (or controller when role switching is allowed) must be +//! instructed to respond to specific TargetListenEvents using the +//! `enable_target_event` function: +//! +//! ``` +//! i2c_target.enable_target_event(TargetListenEvent::PrimaryAddress); +//! ``` +//! +//! Use the `get_target_event_nb` (non-blocking) or `wait_for_event` (blocking) +//! functions to get target events to which to respond. Then use the provided +//! `read`/`write`` (blocking) or `read_nb`/`write_nb`(non-blocking) functions +//! to respond to the controller: +//! +//! ``` +//! let mut buffer = [0u8; 10]; +//! match i2c.wait_for_event().unwrap() { +//! TargetEvent::Read { address: _ } => i2c.write(...), +//! TargetEvent::Write { address: _ } => i2c.read(&mut buffer), +//! TargetEvent::Stop => Ok(0), +//! }; +//! ``` +//! +//! ### Manual ACK control in receive mode +//! +//! To gain control over ACK'ing received data when operating as a target, convert to +//! manual ACK control with: +//! +//! ``` +//! let mut i2c_target_manual_ack = i2c_target.with_manual_ack_control(); +//! ``` +//! +//! This provides the methods `I2cTarget::ack_transfer` and +//! `I2cTarget::nack_transfer` to end a transfer of an expected number of bytes +//! with an ACK or NACK, respectively: +//! +//! ``` +//! let mut buf = [0u8; 10]; +//! i2c_target_manual_ack.read(&mut buf)?; // Read 10 bytes, ACK each byte except the last. +//! // Check something +//! if good { +//! i2c_target_manual_ack.ack_transfer() +//! } else { +//! i2c_target_manual_ack.nack_transfer() +//! } +//! ``` +//! +//! ## Switching between target and controller +//! +//! Use the provided initialization functions to create an I2C driver that can switch +//! between controller and target operation: +//! +//! ``` +//! let dp = ...; // Device peripherals +//! let (scl, sda) = ...; // GPIO pins +//! let own_addr = ...; // Primary address for target operation +//! +//! let mut i2c = dp.I2C1.i2c_controller_target( +//! (scl, sda), +//! own_addr, +//! ccdr.peripheral.I2C1, +//! ); +//! ``` +//! +//! To switch operating modes, use the provided functions: +//! +//! ``` +//! let i2c_target = i2c.to_target(); +//! ... +//! let i2c = i2c_target.to_controller(); +//! ``` +//! +//! These functions are only available on driver instances created with the +//! above initialization function. +//! //! # Examples //! //! - [I2C controller simple example](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/i2c.rs) +//! - [I2C Target simple example](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/i2c_target.rs) +//! - [I2C Target with manual ACK control](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/i2c_target_manual_ack.rs) +use core::iter; +use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use crate::rcc::{CoreClocks, ResetEnable}; @@ -57,6 +157,9 @@ type Isr = stm32h5::R; use crate::time::Hertz; +pub mod config; +pub use config::TargetConfig; + mod hal; mod i2c_def; @@ -90,7 +193,7 @@ enum Direction { /// Addressing mode #[derive(Copy, Clone, PartialEq, Eq)] -enum AddressMode { +pub enum AddressMode { /// 7-bit addressing mode AddressMode7bit, /// 10-bit addressing mode @@ -108,6 +211,55 @@ pub enum Error { Arbitration, /// NACK received NotAcknowledge, + /// Target operation only: + /// Indicates that a stop or repeat start was received while reading, or + /// while explicitly waiting for a controller read or write event. + TransferStopped, + /// Target operation only: + /// While waiting for a controller read event, a write event was received. + ControllerExpectedWrite, + /// Target operation only: + /// While waiting for a controller write event, a read event was received. + ControllerExpectedRead, +} + +/// Target Event. +/// +/// This encapsulates a transaction event that occurs when listening in Target +/// operation. A Read event indicates that the Controller wants to read +/// data from the Target and the target must write to the bus. +/// A Write event indicates that the Controller wants to write data to the +/// Target and the target must read data from the bus. +/// A Stop event indicates that a Stop condition was received and the transaction has been completed. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TargetEvent { + /// The controller initiated an I2C Read operation, requiring that the Target must write to the + /// bus. + Read { address: u16 }, + /// The controller initiated an I2C Write operation, requiring that the Target must read from + /// the bus. + Write { address: u16 }, + /// A Stop condition was received, ending the transaction. + Stop, +} + +/// Target Listen Event +/// +/// Indicates what listen events are responded to. A target can respond to one +/// of or all of a general call address, the primary address configured or a +/// secondary address. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum TargetListenEvent { + /// The General Call address (0x0) is reserved for specific broadcasts in the I2C + /// standard. Enabling the general call event will enable the target to receive + /// General Call broadcasts from the bus controller + GeneralCall, + /// Primary address of the target. A single address specific to this target. Can + /// be 7- or 10-bit. + PrimaryAddress, + /// The Secondary address event is a match against a 7-bit address range governed + /// by an associated mask. See the `Config` for more. + SecondaryAddress, } /// A trait to represent the SCL Pin of an I2C Port @@ -147,13 +299,26 @@ pub struct Inner { i2c: I2C, } +/// Marker struct for an I2c/I2cTarget that can switch between Controller and Target operation +pub struct SwitchRole; + +/// Marker struct for an I2c/I2cTarget that cannot switch between Controller and Target operation +pub struct SingleRole; + +/// Marker struct for I2cTarget implementation to indicate that it allows for manual ACK control +pub struct ManualAck; + +/// Marker struct for I2cTarget implementation to indicate that it automatically handles all ACK'ing +pub struct AutoAck; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct I2c { +pub struct I2c { inner: Inner, + _role: PhantomData, } -impl Deref for I2c { +impl Deref for I2c { type Target = Inner; fn deref(&self) -> &Self::Target { @@ -161,7 +326,27 @@ impl Deref for I2c { } } -impl DerefMut for I2c { +impl DerefMut for I2c { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +pub struct I2cTarget { + inner: Inner, + _role: PhantomData, + _ack: PhantomData, +} + +impl Deref for I2cTarget { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for I2cTarget { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } @@ -181,7 +366,7 @@ pub trait I2cExt: Sized { frequency: Hertz, rec: I2C::Rec, clocks: &CoreClocks, - ) -> I2c; + ) -> I2c; /// Create a I2c instance that is capable of Controller operation only. /// This will not check that the pins are properly configured. @@ -190,7 +375,47 @@ pub trait I2cExt: Sized { frequency: Hertz, rec: I2C::Rec, clocks: &CoreClocks, - ) -> I2c; + ) -> I2c; + + /// Create an I2cTarget instance capable of Target operation only + fn i2c_target_only>( + self, + _pins: P, + target_config: impl Into, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2cTarget; + + /// Create an I2cTarget instance capable of Target operation only. + /// This will not check that the pins are properly configured. + fn i2c_target_only_unchecked>( + self, + target_config: impl Into, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2cTarget; + + /// Create an I2c instance that can switch roles to a I2cTarget to perform + /// Target operations + fn i2c_controller_target>( + self, + _pins: P, + frequency: Hertz, + target_config: TargetConfig, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2c; + + /// Create an I2c instance that can switch roles to a I2cTarget to perform + /// Target operations. + /// This will not check that the pins are properly configured. + fn i2c_controller_target_unchecked( + self, + frequency: Hertz, + target_config: TargetConfig, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2c; } impl I2cExt for I2C { @@ -200,8 +425,8 @@ impl I2cExt for I2C { frequency: Hertz, rec: I2C::Rec, clocks: &CoreClocks, - ) -> I2c { - I2c::new(self, frequency, rec, clocks) + ) -> I2c { + I2c::new(self, frequency, None::, rec, clocks) } fn i2c_unchecked( @@ -209,8 +434,48 @@ impl I2cExt for I2C { frequency: Hertz, rec: I2C::Rec, clocks: &CoreClocks, - ) -> I2c { - I2c::new(self, frequency, rec, clocks) + ) -> I2c { + I2c::new(self, frequency, None::, rec, clocks) + } + + fn i2c_target_only>( + self, + _pins: P, + config: impl Into, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2cTarget { + I2cTarget::new(self, config, rec, clocks) + } + + fn i2c_target_only_unchecked>( + self, + target_config: impl Into, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2cTarget { + I2cTarget::new(self, target_config, rec, clocks) + } + + fn i2c_controller_target>( + self, + _pins: P, + frequency: Hertz, + target_config: TargetConfig, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2c { + I2c::new(self, frequency, Some(target_config), rec, clocks) + } + + fn i2c_controller_target_unchecked( + self, + frequency: Hertz, + target_config: TargetConfig, + rec: I2C::Rec, + clocks: &CoreClocks, + ) -> I2c { + I2c::new(self, frequency, Some(target_config), rec, clocks) } } @@ -256,7 +521,7 @@ fn calc_timing_params(ker_ck: u32, target_freq: u32) -> (u8, u8, u8, u8, u8) { let (sdadel, scldel) = if target_freq > 400_000 { // Fast-mode Plus (Fm+) - assert!(ker_ck >= 17_000_000); // See table in datsheet + assert!(ker_ck >= 17_000_000); // See table in datasheet let sdadel = ker_ck / 8_000_000 / presc; let scldel = ker_ck / 4_000_000 / presc - 1; @@ -264,7 +529,7 @@ fn calc_timing_params(ker_ck: u32, target_freq: u32) -> (u8, u8, u8, u8, u8) { (sdadel, scldel) } else { // Fast-mode (Fm) - assert!(ker_ck >= 8_000_000); // See table in datsheet + assert!(ker_ck >= 8_000_000); // See table in datasheet let sdadel = ker_ck / 3_000_000 / presc; let scldel = ker_ck / 1_000_000 / presc - 1; @@ -333,7 +598,28 @@ fn calc_timing_params(ker_ck: u32, target_freq: u32) -> (u8, u8, u8, u8, u8) { (presc_reg, scll, sclh, sdadel, scldel) } -impl I2c { +fn configure_target_addresses(i2c: &I2C, config: TargetConfig) { + i2c.oar1().write(|w| match config.own_address_mode { + AddressMode::AddressMode7bit => { + w.oa1().set(config.own_address << 1).oa1mode().bit7() + } + AddressMode::AddressMode10bit => { + w.oa1().set(config.own_address).oa1mode().bit10() + } + }); + + if let Some(secondary_address) = config.secondary_address { + i2c.oar2().write(|w| { + w.oa2().set(secondary_address); + if let Some(mask) = config.secondary_address_mask_bits { + w.oa2msk().set(mask + 1); // The address is shifted up by one, so increment the mask bits too + } + w + }); + } +} + +impl I2c { /// Create and initialise a new I2C peripheral. /// /// The frequency of the I2C bus clock is specified by `frequency`. @@ -347,6 +633,7 @@ impl I2c { pub fn new( i2c: I2C, frequency: Hertz, + config: Option>, rec: I2C::Rec, clocks: &CoreClocks, ) -> Self { @@ -378,24 +665,70 @@ impl I2c { .set(scldel) }); + if let Some(config) = config { + let config = config.into(); + configure_target_addresses(&i2c, config); + } + // Enable the peripheral and analog filter i2c.cr1().write(|w| w.pe().enabled().anfoff().enabled()); I2c { inner: Inner::new(i2c), + _role: PhantomData, } } - /// Reset the peripheral - pub fn reset(&mut self) { - self.i2c.cr1().modify(|_, w| w.pe().disabled()); - interrupt_clear_clock_sync_delay!(self.i2c.cr1()); - while self.i2c.cr1().read().pe().is_enabled() {} - self.i2c.cr1().modify(|_, w| w.pe().enabled()); + pub fn free(self) -> I2C { + let _ = I2C::rec().reset().disable(); + self.inner.i2c } +} + +/// Target implementation +impl I2cTarget { + fn new( + i2c: I2C, + target_config: impl Into, + rec: ::Rec, + clocks: &CoreClocks, + ) -> Self { + let config = target_config.into(); + + let _ = rec.enable().reset(); + + // Clear PE bit in I2C_CR1 + i2c.cr1().modify(|_, w| w.pe().disabled()); + + let i2c_ker_ck: u32 = I2C::clock(clocks).raw(); + + // Configure timing parameters for target mode + let (presc_reg, _, _, sdadel, scldel) = + calc_timing_params(i2c_ker_ck, config.bus_frequency_hz); + i2c.timingr().write(|w| { + w.presc() + .set(presc_reg) + .sdadel() + .set(sdadel) + .scldel() + .set(scldel) + }); - pub fn free(mut self) -> I2C { - self.reset(); + configure_target_addresses(&i2c, config); + + // Enable the peripheral and Analog Noise Filter + i2c.cr1().modify(|_, w| w.pe().enabled().anfoff().enabled()); + + Self { + inner: Inner::new(i2c), + _role: PhantomData, + _ack: PhantomData, + } + } + + /// Releases the I2C peripheral + pub fn free(self) -> I2C { + let _ = I2C::rec().reset().disable(); self.inner.i2c } } @@ -443,7 +776,7 @@ impl Inner { /// Ok(false) is returned. /// /// If a bus error occurs it will be returned and a write will not be - /// attempted. If a previous byte was Nack'd by the receiver, that is + /// attempted. If a previous byte was NACK'd by the receiver, that is /// indicated by a NotAcknowledge error being returned. #[inline(always)] fn write_byte_if_ready(&self, data: u8) -> Result { @@ -461,7 +794,7 @@ impl Inner { /// Blocks until data can be written and writes it. /// /// If a bus error occurs it will be returned and a write will not be - /// attempted. If a previous byte was Nack'd by the receiver, that is + /// attempted. If a previous byte was NACK'd by the receiver, that is /// indicated by a NotAcknowledge error being returned. fn write_byte(&mut self, data: u8) -> Result<(), Error> { while !self.write_byte_if_ready(data)? {} @@ -482,6 +815,10 @@ impl Inner { if isr.rxne().is_not_empty() { *data = self.i2c.rxdr().read().rxdata().bits(); Ok(true) + } else if isr.stopf().is_stop() || isr.addr().is_match() { + // This is only relevant to Target operation, when the controller stops the read + // operation with a Stop or Restart condition. + Err(Error::TransferStopped) } else { Ok(false) } @@ -497,6 +834,42 @@ impl Inner { while !self.read_byte_if_ready(&mut data)? {} Ok(data) } + + fn enable_target_event(&mut self, event: TargetListenEvent) { + match event { + TargetListenEvent::GeneralCall => { + self.i2c.cr1().modify(|_, w| w.gcen().enabled()); + } + TargetListenEvent::PrimaryAddress => { + self.i2c.oar1().modify(|_, w| w.oa1en().enabled()); + } + TargetListenEvent::SecondaryAddress => { + self.i2c.oar2().modify(|_, w| w.oa2en().enabled()); + } + } + } + + fn disable_target_event(&mut self, event: TargetListenEvent) { + match event { + TargetListenEvent::GeneralCall => { + self.i2c.cr1().modify(|_, w| w.gcen().disabled()); + } + TargetListenEvent::PrimaryAddress => { + self.i2c.oar1().modify(|_, w| w.oa1en().disabled()); + } + TargetListenEvent::SecondaryAddress => { + self.i2c.oar2().modify(|_, w| w.oa2en().disabled()); + } + } + } + + /// Reset the peripheral + pub fn reset(&mut self) { + self.i2c.cr1().modify(|_, w| w.pe().disabled()); + interrupt_clear_clock_sync_delay!(self.i2c.cr1()); + while self.i2c.cr1().read().pe().is_enabled() {} + self.i2c.cr1().modify(|_, w| w.pe().enabled()); + } } /// Controller methods @@ -512,7 +885,7 @@ impl Inner { /// previous transaction can still be "in progress" up to 50% of a /// bus cycle after a ACK/NACK event. Otherwise these methods return /// immediately. -impl I2c { +impl I2c { /// Start read transaction /// /// Perform an I2C start prepare peripheral to perform subsequent operation defined by @@ -535,7 +908,11 @@ impl I2c { direction: Direction, stop: Stop, ) { - assert!(length < 256 && length > 0); + assert!( + length <= u8::MAX as usize, + "I2C max transaction size = {} bytes", + u8::MAX + ); // Set START condition and self.i2c.cr2().write(|w| { @@ -576,6 +953,12 @@ impl I2c { direction: Direction, stop: Stop, ) { + assert!( + length <= u8::MAX as usize, + "I2C max transaction size = {} bytes", + u8::MAX + ); + self.i2c.cr2().write(|w| { w.rd_wrn() .bit(direction == Direction::Read) @@ -606,6 +989,12 @@ impl I2c { /// /// See the sequence diagrams in Figures 390 & 393 of RM0492 Rev 2 for more. fn reload(&self, length: usize, direction: Direction, stop: Stop) { + assert!( + length <= u8::MAX as usize, + "I2C max transfer size per reload = {}", + u8::MAX + ); + self.i2c.cr2().write(|w| { w.rd_wrn() .bit(direction == Direction::Read) @@ -619,7 +1008,7 @@ impl I2c { } } -impl I2c { +impl I2c { /// Check whether start sequence has completed. #[inline(always)] fn is_start_sequence_complete(&self) -> Result { @@ -718,6 +1107,501 @@ impl I2c { } } +/// I2C Target common blocking operations +impl I2cTarget { + /// While operating with automatic ACK control, this will indicate to the + /// peripheral that the next byte received should be NACK'd (the last + /// received was already ACK'd). The peripheral only NACKs if a byte is + /// received. If a control signal is received, the peripheral will respond + /// correctly. + /// + /// With manual ACK control, this will NACK the last received byte. + fn nack(&mut self) { + self.i2c.cr2().modify(|_, w| w.nack().nack()); + } + + fn read_all(&mut self, buffer: &mut [u8]) -> Result { + assert!(!buffer.is_empty()); + + for (i, byte) in buffer.iter_mut().enumerate() { + match self.read_byte() { + Ok(data) => { + *byte = data; + } + Err(Error::TransferStopped) => return Ok(i), + Err(error) => return Err(error), + }; + } + Ok(buffer.len()) + } + + fn write_buf(&mut self, bytes: &[u8]) -> Result { + assert!(!bytes.is_empty()); + + for (i, data) in bytes.iter().enumerate() { + match self.write_byte(*data) { + Ok(()) => {} + // If we receive a NACK it will be on the FIFO write subsequent to the last byte + // actually written to the bus + Err(Error::NotAcknowledge) => return Ok(i - 1), + Err(error) => return Err(error), + } + } + Ok(bytes.len()) + } + + fn write_buf_fill_zeroes(&mut self, bytes: &[u8]) -> Result { + assert!(!bytes.is_empty()); + + // The controller can try to read more data than the target has to write, so we write + // zeroes until the controller stops the transaction. + // This creates an iterator that will return zeroes when the buffer is exhausted. + for (i, data) in bytes.iter().chain(iter::repeat(&0)).enumerate() { + match self.write_byte(*data) { + Ok(()) => {} + Err(Error::NotAcknowledge) => { + // If we receive a NACK it will be on the FIFO write subsequent to the last byte + // actually written to the bus. If we start writing zeroes out, we only want to + // indicate how many bytes from the buffer we wrote. + return Ok(core::cmp::min(i - 1, bytes.len())); + } + Err(error) => return Err(error), + } + } + unreachable!(); + } + + fn get_address(&self, addcode: u8) -> u16 { + const I2C_RESERVED_ADDR_MASK: u8 = 0x7C; + const I2C_10BIT_HEADER_CODE: u8 = 0x78; + + if (addcode & I2C_RESERVED_ADDR_MASK) == I2C_10BIT_HEADER_CODE { + // No need to check if we're using 7 bits to perform a shift because we wouldn't + // have received a 10-bit address unless we were listening for it + self.i2c.oar1().read().oa1().bits() + } else { + addcode as u16 + } + } +} + +trait TargetAckMode { + fn get_target_event(&mut self) -> Result, Error>; + fn read(&mut self, buffer: &mut [u8]) -> Result; + fn write(&mut self, bytes: &[u8]) -> Result; +} + +impl TargetAckMode for I2cTarget { + fn get_target_event(&mut self) -> Result, Error> { + let isr = self.read_isr_and_check_errors()?; + + if isr.addr().is_match() { + if isr.txe().is_not_empty() { + // Flush the contents of TXDR without writing it to the bus. Doing so ensures that + // spurious data is not written to the bus and that the clock remains stretched when + // the ADDR flag is cleared and until data is written to TXDR (in the case of a + // target write operation) + self.i2c.isr().write(|w| w.txe().set_bit()); + } + self.i2c.icr().write(|w| w.addrcf().clear()); + + let address = self.get_address(isr.addcode().bits()); + + if isr.dir().is_read() { + Ok(Some(TargetEvent::Read { address })) + } else { + Ok(Some(TargetEvent::Write { address })) + } + } else if isr.stopf().is_stop() { + self.i2c.icr().write(|w| w.stopcf().clear()); + Ok(Some(TargetEvent::Stop)) + } else { + Ok(None) + } + } + + fn read(&mut self, buffer: &mut [u8]) -> Result { + let size = self.read_all(buffer)?; + + // NACK any subsequent bytes + self.nack(); + + Ok(size) + } + + fn write(&mut self, bytes: &[u8]) -> Result { + self.write_buf_fill_zeroes(bytes) + } +} + +impl TargetAckMode for I2cTarget { + fn get_target_event(&mut self) -> Result, Error> { + let isr = self.read_isr_and_check_errors()?; + + if isr.addr().is_match() { + if isr.txe().is_not_empty() { + // Flush the contents of TXDR without writing it to the bus. Doing so ensures that + // spurious data is not written to the bus and that the clock remains stretched when + // the ADDR flag is cleared and until data is written to TXDR (in the case of a + // target write operation) + self.i2c.isr().write(|w| w.txe().set_bit()); + } + // Reset SBC, reload, and nbytes. They're used differently for a read or write, but + // make sure they're in a known state before starting the next operation. + self.i2c.cr1().modify(|_, w| w.sbc().disabled()); + self.i2c + .cr2() + .modify(|_, w| w.reload().completed().nbytes().set(0)); + + let address = self.get_address(isr.addcode().bits()); + + if isr.dir().is_read() { + self.i2c.icr().write(|w| w.addrcf().clear()); + Ok(Some(TargetEvent::Read { address })) + } else { + // Manual ACK control uses slave byte control mode when reading data from the + // controller, so set it up here. + self.i2c.cr1().modify(|_, w| w.sbc().enabled()); + self.i2c.cr2().modify(|_, w| w.reload().not_completed()); + Ok(Some(TargetEvent::Write { address })) + } + } else if isr.stopf().is_stop() { + self.i2c.icr().write(|w| w.stopcf().clear()); + Ok(Some(TargetEvent::Stop)) + } else { + Ok(None) + } + } + + fn read(&mut self, buffer: &mut [u8]) -> Result { + let mut bytes_read = 0; + // In manual ACK'ing mode we use Slave Byte Control (SBC) which requires using the NBYTES + // field to indicate how many bytes to ACK before a manual ACK is required. The NBYTES field + // is 8 bits wide, so if the transaction is larger than 255 bytes, we need to manually + // restart the transfer by setting NBYTES again. + for (i, buf) in buffer.chunks_mut(u8::MAX as usize).enumerate() { + if i == 0 { + self.start_transfer(buf.len()); + } else { + self.restart_transfer(buf.len()) + } + let count = self.read_all(buf)?; + bytes_read += count; + if count < buf.len() { + break; + } + } + + Ok(bytes_read) + } + + fn write(&mut self, bytes: &[u8]) -> Result { + let mut bytes_written = 0; + for (i, buf) in bytes.chunks(u8::MAX as usize).enumerate() { + // In manual ACK'ing mode we use Slave Byte Control (SBC) which requires using the + // NBYTES field to indicate how many TXIS events are generated. We need to set this + // up for every 255 bytes because the NBYTES field is only 8 bits wide. + if i == 0 { + self.start_transfer(buf.len()); + } else { + self.restart_transfer(buf.len()) + } + let count = if bytes_written + buf.len() == bytes.len() { + // This is the last chunk so write out zeroes if we reach the end of the buffer + self.write_buf_fill_zeroes(buf)? + } else { + // Only write the contents of the buffer and move on to the next chunk + self.write_buf(buf)? + }; + bytes_written += count; + if count < buf.len() { + break; + } + } + Ok(bytes_written) + } +} + +impl I2cTarget { + /// This will start a transfer operation in manual ACK'ing mode. + /// + /// When performing a read operation the caller must pass the number + /// of bytes that will be read before a manual ACK will be performed. All + /// bytes prior to that will be ACK'd automatically. + /// + /// When performing a write operation this determines how many TXIS events + /// will be generated for event handling (typically via interrupts). + fn start_transfer(&mut self, expected_bytes: usize) { + self.i2c + .cr2() + .modify(|_, w| w.nbytes().set(expected_bytes as u8)); + self.i2c.icr().write(|w| w.addrcf().clear()); + } + + /// End a transfer in manual ACK'ing mode. This should only be called after + /// the expected number of bytes have been read (as set up with + /// I2cTarget::start_transfer), otherwise the bus might hang. + fn end_transfer(&mut self) { + self.i2c.cr2().modify(|_, w| w.reload().completed()); + } + + /// Restart a transfer in manual ACK'ing mode. + /// + /// When performing a read operation, this allows partial reads of a + /// transaction with manual ACK'ing together with I2cTarget::start_transfer. + /// To handle a single byte between each ACK, set expected_bytes to 1 + /// before each read. + /// + /// When performing a write operation this determines how many TXIS events + /// will be generated for event handling (typically via interrupts) + fn restart_transfer(&mut self, expected_bytes: usize) { + self.i2c + .cr2() + .modify(|_, w| w.nbytes().set(expected_bytes as u8)); + } + + /// End the transfer by ACK'ing the last byte in a read transfer after + /// using the I2cTarget::read function in manual ACK'ing mode. This + /// should only be called when the controller is not expected to send any + /// more bytes, otherwise the bus will hang. + pub fn ack_transfer(&mut self) { + self.end_transfer(); + } + + /// End the transfer by NACK'ing the last received byte + pub fn nack_transfer(&mut self) { + self.nack(); + self.end_transfer(); + } +} + +// We don't need to to expose TargetAckMode publically. All the public methods that +// wrap the allowable implementations are exposed below. +#[allow(private_bounds)] +impl I2cTarget +where + Self: TargetAckMode, +{ + /// Blocks until this device to be addressed or for a transaction to be + /// stopped. When the device is addressed, a `TargetEvent::Read` or + /// `TargetEvent::Write` event will be returned. If the transaction was + /// stopped, `TargetEvent::Stop` will be returned. + /// + /// If an error occurs while waiting for an event, this will be returned. + pub fn wait_for_event(&mut self) -> Result { + loop { + if let Some(event) = self.get_target_event()? { + return Ok(event); + } + } + } + + /// Perform a blocking read. + /// + /// If no error occurs, this will read until the buffer is full, or until + /// the transfer is stopped by the controller, whichever comes first. + /// + /// The function will return the number of bytes received wrapped in the + /// Ok result, or an error if one occurred. + /// + /// ## Auto-ACK'ing mode + /// When the buffer is full the controller will be NACK'd for any + /// subsequent byte received. + /// + /// ## Manual ACK'ing mode: + /// If the buffer is filled before the controller stops the transaction, + /// the final byte will not be ACK'd, but the function will return. The + /// I2cTarget::ack_transfer function must be called in this case in order + /// to complete the transaction. + /// + /// Note: this function should not be called to read less than the expected + /// number of bytes in the total transaction. This could potentially leave + /// the bus in a bad state. + pub fn read(&mut self, buffer: &mut [u8]) -> Result { + TargetAckMode::read(self, buffer) + } + + /// Perform a blocking write to the bus. This will write the + /// contents of the buffer, followed by zeroes if the controller keeps + /// clocking the bus. If the controller NACKs at any point the function + /// will return with an Ok result. + /// + /// The function will return the number of bytes written wrapped in the + /// Ok result, or an error if one occurred. + pub fn write(&mut self, bytes: &[u8]) -> Result { + TargetAckMode::write(self, bytes) + } + + /// Waits for a controller write event and reads the data written by the + /// controller to the buffer provided. This will read until the buffer is + /// full, or until the transfer is stopped by the controller, whichever + /// comes first. + /// + /// If the a bus error occurs or a stop condition is received, an error + /// will be returned, otherwise the number of bytes received wrapped in an + /// Ok result will be returned + /// + /// ## Auto ACK'ing mode + /// This will read until the buffer is full, at which point the controller + /// will be NACK'd for any subsequent bytes received. + /// + /// ## Manual ACK'ing mode + /// If the buffer is filled before the controller stops the transaction, + /// the final byte will not be ACK'd. The I2cTarget::ack_transfer function + /// must be called in this case in order to complete the transaction. + /// + /// ## Note: + /// Any one of the configured primary or secondary addresses will be + /// matched against. This method is not be suitable for use when + /// different operations must be performed depending upon the received + /// address. + pub fn wait_for_target_read( + &mut self, + buffer: &mut [u8], + ) -> Result { + match self.wait_for_event()? { + TargetEvent::Read { address: _ } => { + Err(Error::ControllerExpectedWrite) + } + TargetEvent::Write { address: _ } => self.read(buffer), + TargetEvent::Stop => Err(Error::TransferStopped), + } + } + + /// Waits for a controller read event and writes the contents of the + /// buffer, followed by zeroes if the controller keeps clocking the bus. If + /// the controller NACKs at any point the write will be terminated and the + /// function will return with an Ok result indicating the number of bytes + /// written before the NACK occurred. + /// + /// If a bus error occurs or a stop condition is received, the function + /// will return an error, otherwise it will return the number of bytes + /// written wrapped in an Ok result. + pub fn wait_for_target_write( + &mut self, + bytes: &[u8], + ) -> Result { + match self.wait_for_event()? { + TargetEvent::Read { address: _ } => self.write(bytes), + TargetEvent::Write { address: _ } => { + Err(Error::ControllerExpectedRead) + } + TargetEvent::Stop => Err(Error::TransferStopped), + } + } + + /// Waits for a transaction to be stopped + /// + /// If a controller write or read event is received, an error is returned, + /// otherwise an Ok result is returned when the stop condition is received. + pub fn wait_for_stop(&mut self) -> Result<(), Error> { + match self.wait_for_event()? { + TargetEvent::Read { address: _ } => { + Err(Error::ControllerExpectedWrite) + } + TargetEvent::Write { address: _ } => { + Err(Error::ControllerExpectedRead) + } + TargetEvent::Stop => Ok(()), + } + } +} + +impl I2cTarget { + /// Convert this target to manual ACK control mode + pub fn with_manual_ack_control(self) -> I2cTarget { + I2cTarget { + inner: self.inner, + _role: PhantomData, + _ack: PhantomData, + } + } +} + +impl I2cTarget { + /// Convert this target to automatic ACK control mode + pub fn with_automatic_ack_control(self) -> I2cTarget { + I2cTarget { + inner: self.inner, + _role: PhantomData, + _ack: PhantomData, + } + } +} + +pub trait Targetable { + /// Enable the specified TargetListenEvent. This is required to start + /// listening for transactions addressed to the device. It will not Ack + /// any transactions addressed to it until one of these events is enabled. + /// ie. to listen for transactions addressed to the primary address + /// provided during Target configuration do: + /// ``` + /// i2c.enable_target_event(TargetListenEvent::PrimaryAddress) + /// ``` + fn enable_listen_event(&mut self, event: TargetListenEvent); + + /// Disable the specified TargetListenEvent. This can be used to stop + /// listening for transactions. The peripheral will not Ack any messages + /// addressed to it if all events are disabled. + fn disable_listen_event(&mut self, event: TargetListenEvent); + + /// Configure target after initialization: allows the target addresses to be + /// dynamically configured. This will disable any target events previously + /// enabled. + fn configure_target(&mut self, config: impl Into); +} + +impl Targetable for I2c { + fn enable_listen_event(&mut self, event: TargetListenEvent) { + self.inner.enable_target_event(event) + } + + fn disable_listen_event(&mut self, event: TargetListenEvent) { + self.inner.disable_target_event(event) + } + + fn configure_target(&mut self, config: impl Into) { + configure_target_addresses(&self.i2c, config.into()) + } +} + +impl Targetable for I2cTarget { + fn enable_listen_event(&mut self, event: TargetListenEvent) { + self.inner.enable_target_event(event) + } + + fn disable_listen_event(&mut self, event: TargetListenEvent) { + self.inner.disable_target_event(event) + } + + fn configure_target(&mut self, config: impl Into) { + configure_target_addresses(&self.i2c, config.into()) + } +} + +impl I2c { + /// Convert a controller implementation to a target. This is only possible + /// if the I2c was created with a target configuration. + pub fn to_target(self) -> I2cTarget { + I2cTarget { + inner: self.inner, + _role: PhantomData, + _ack: PhantomData, + } + } +} + +impl I2cTarget { + /// Convert a target implementation to a controller. This is only possible + /// for a I2cTarget created from an I2c instance. + pub fn to_controller(self) -> I2c { + I2c { + inner: self.inner, + _role: PhantomData, + } + } +} + #[cfg(test)] mod tests { use crate::i2c::calc_timing_params; diff --git a/src/i2c/config.rs b/src/i2c/config.rs new file mode 100644 index 0000000..c4ca746 --- /dev/null +++ b/src/i2c/config.rs @@ -0,0 +1,71 @@ +use super::AddressMode; + +/// A structure for specifying the I2C Target configuration +/// +/// This structure uses the builder pattern to generate the configuration: +/// +/// ``` +/// let config = Config::new().secondary_address(0x10); +/// ``` +#[derive(Copy, Clone)] +pub struct TargetConfig { + /// Address mode of the MCU acting as a target + pub(crate) own_address_mode: AddressMode, + /// Target address for MCU + pub(crate) own_address: u16, + /// Secondary target address for MCU (7-bit only) + pub(crate) secondary_address: Option, + // Address mask for secondary mask + pub(crate) secondary_address_mask_bits: Option, + /// Frequency at which bus is expected to run. + pub(crate) bus_frequency_hz: u32, +} + +impl TargetConfig { + /// Create a default configuration with the address of the MCU and the expected bus frequency. + /// + /// The address should be specified unshifted. 7-bit addressing mode is used by default. + /// + /// If this is not known, use a lower bound in order to ensure that the correct timing + /// parameters are used. + pub const fn new(own_address: u16, bus_frequency_hz: u32) -> Self { + TargetConfig { + own_address_mode: AddressMode::AddressMode7bit, + own_address, + secondary_address: None, + secondary_address_mask_bits: None, + bus_frequency_hz, + } + } + + /// Set the primary address that the target will listen to. The address should be specified + /// unshifted. For 7-bit addresses the driver will shift it left by 1 to match the peripheral's + /// expectation. 10 bit addresses are not shifted. + pub const fn own_address(mut self, own_address: u16) -> Self { + self.own_address = own_address; + self + } + + /// Set the addressing mode for the primary (own) address. Secondary addresses can only be + /// specified as 7 bit addresses. + pub const fn own_address_mode(mut self, address_mode: AddressMode) -> Self { + self.own_address_mode = address_mode; + self + } + + /// Optional secondary 7-bit address for which the MCU can listen. This can also be masked + /// (using secondary_address_mask()) to enable the MCU to listen to a range of addresses. The + /// address should be specified unshifted. For 7-bit addresses the driver will shift it left by + /// 1 to match the peripheral's expectation. + pub const fn secondary_address(mut self, secondary_address: u8) -> Self { + self.secondary_address = Some(secondary_address); + self + } + + /// Mask bits for secondary address. This allows the MCU to listen to a range of addresses. The + /// lower `mask_bits` bits will be masked. + pub const fn secondary_address_mask_bits(mut self, mask_bits: u8) -> Self { + self.secondary_address_mask_bits = Some(mask_bits); + self + } +} diff --git a/src/i2c/hal.rs b/src/i2c/hal.rs index 92ef128..9f57d0a 100644 --- a/src/i2c/hal.rs +++ b/src/i2c/hal.rs @@ -11,11 +11,12 @@ impl i2c::Error for Error { Error::NotAcknowledge => { i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Unknown) } + _ => i2c::ErrorKind::Other, } } } -impl i2c::ErrorType for I2c { +impl i2c::ErrorType for I2c { type Error = Error; } @@ -40,7 +41,7 @@ impl OperationExt for i2c::Operation<'_> { } } -impl I2c { +impl I2c { fn process_operation( &mut self, stop: Stop, @@ -123,7 +124,7 @@ impl I2c { } } -impl i2c::I2c for I2c { +impl i2c::I2c for I2c { fn transaction( &mut self, address: i2c::SevenBitAddress, @@ -138,7 +139,7 @@ impl i2c::I2c for I2c { } } -impl i2c::I2c for I2c { +impl i2c::I2c for I2c { fn transaction( &mut self, address: i2c::TenBitAddress,