diff --git a/rp2040-hal/examples/i2c.rs b/rp2040-hal/examples/i2c.rs index ef6480912..8df1a7241 100644 --- a/rp2040-hal/examples/i2c.rs +++ b/rp2040-hal/examples/i2c.rs @@ -17,7 +17,6 @@ use panic_halt as _; use rp2040_hal as hal; // Some traits we need -use embedded_hal_0_2::blocking::i2c::Write; use hal::fugit::RateExtU32; // A shorter alias for the Peripheral Access Crate, which provides low-level @@ -81,7 +80,7 @@ fn main() -> ! { let scl_pin: Pin<_, FunctionI2C, _> = pins.gpio19.reconfigure(); // let not_an_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure(); - // Create the I²C drive, using the two pre-configured pins. This will fail + // Create the I²C driver, using the two pre-configured pins. This will fail // at compile time if the pins are in the wrong mode, or if this I²C // peripheral isn't available on these pins! let mut i2c = hal::I2C::i2c1( diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index a01fccdbb..1bcb6d98a 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -20,7 +20,6 @@ //! ); //! //! // Scan for devices on the bus by attempting to read from them -//! use embedded_hal_0_2::prelude::_embedded_hal_blocking_i2c_Read; //! for i in 0..=127 { //! let mut readbuf: [u8; 1] = [0; 1]; //! let result = i2c.read(i, &mut readbuf); @@ -31,11 +30,9 @@ //! } //! //! // Write some data to a device at 0x2c -//! use embedded_hal_0_2::prelude::_embedded_hal_blocking_i2c_Write; //! i2c.write(0x2c, &[1, 2, 3]).unwrap(); //! //! // Write and then read from a device at 0x3a -//! use embedded_hal_0_2::prelude::_embedded_hal_blocking_i2c_WriteRead; //! let mut readbuf: [u8; 1] = [0; 1]; //! i2c.write_read(0x2c, &[1, 2, 3], &mut readbuf).unwrap(); //! ``` @@ -70,26 +67,26 @@ impl I2cDevice for pac::I2C1 { const ID: usize = 1; } -/// I2C error +/// I²C Error #[non_exhaustive] pub enum Error { - /// I2C abort with error + /// I²C abort with error Abort(u32), /// User passed in a read buffer that was 0 length /// - /// This is a limitation of the RP2040 I2C peripheral. - /// If the slave ACKs its address, the I2C peripheral must read + /// This is a limitation of the RP2040 I²C peripheral. + /// If the slave ACKs its address, the I²C peripheral must read /// at least one byte before sending the STOP condition. InvalidReadBufferLength, /// User passed in a write buffer that was 0 length /// - /// This is a limitation of the RP2040 I2C peripheral. - /// If the slave ACKs its address, the I2C peripheral must write + /// This is a limitation of the RP2040 I²C peripheral. + /// If the slave ACKs its address, the I²C peripheral must write /// at least one byte before sending the STOP condition. InvalidWriteBufferLength, - /// Target i2c address is out of range + /// Target I²C address is out of range AddressOutOfRange(u16), - /// Target i2c address is reserved + /// Target I²C address is reserved AddressReserved(u16), } @@ -99,8 +96,8 @@ impl core::fmt::Debug for Error { match self { Error::InvalidReadBufferLength => write!(fmt, "InvalidReadBufferLength"), Error::InvalidWriteBufferLength => write!(fmt, "InvalidWriteBufferLength"), - Error::AddressOutOfRange(addr) => write!(fmt, "AddressOutOfRange({:x})", addr), - Error::AddressReserved(addr) => write!(fmt, "AddressReserved({:x})", addr), + Error::AddressOutOfRange(addr) => write!(fmt, "AddressOutOfRange({:?})", addr), + Error::AddressReserved(addr) => write!(fmt, "AddressReserved({:?})", addr), Error::Abort(_) => { write!(fmt, "{:?}", self.kind()) } diff --git a/rp2040-hal/src/i2c/controller.rs b/rp2040-hal/src/i2c/controller.rs index 3fef2f609..d45f05a68 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,8 +1,16 @@ +//! # I²C Controller-mode code +//! +//! This is for when the RP2040 is actively reading from or writing to other I²C +//! devices on the bus. +//! +//! We implement both the Embedded HAL 1.0 and legacy Embedded HAL 0.2 traits. +//! Currently we only support 7-bit addresses, not 10-bit addresses. + use core::{marker::PhantomData, ops::Deref}; -use embedded_hal_0_2::blocking::i2c::{Read, Write, WriteIter, WriteIterRead, WriteRead}; use fugit::HertzU32; -use embedded_hal::i2c as eh1; +use embedded_hal::i2c; +use embedded_hal_0_2::blocking::i2c as i2c02; use super::{i2c_reserved_addr, Controller, Error, ValidPinScl, ValidPinSda, I2C}; use crate::{ @@ -10,13 +18,19 @@ use crate::{ resets::SubsystemReset, }; +// ============================================================================ +// +// Inherent Methods +// +// ============================================================================ + impl I2C where T: SubsystemReset + Deref, Sda: ValidPinSda, Scl: ValidPinScl, { - /// Configures the I2C peripheral to work in controller mode + /// Configures the I²C peripheral to work in controller mode pub fn new_controller( i2c: T, sda_pin: Sda, @@ -38,6 +52,8 @@ where i2c.ic_con.modify(|_, w| { w.speed().fast(); w.master_mode().enabled(); + w.ic_10bitaddr_master().addr_7bits(); + w.ic_10bitaddr_slave().addr_7bits(); w.ic_slave_disable().slave_disabled(); w.ic_restart_en().enabled(); w.tx_empty_ctrl().enabled() @@ -49,7 +65,7 @@ where let freq_in = system_clock.to_Hz(); - // There are some subtleties to I2C timing which we are completely ignoring here + // There are some subtleties to I²C timing which we are completely ignoring here // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 let period = (freq_in + freq / 2) / freq; let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low @@ -61,7 +77,7 @@ where assert!(hcnt >= 8); assert!(lcnt >= 8); - // Per I2C-bus specification a device in standard or fast mode must + // Per I²C-bus specification a device in standard or fast mode must // internally provide a hold time of at least 300ns for the SDA signal to // bridge the undefined region of the falling edge of SCL. A smaller hold // time of 120ns is used for fast mode plus. @@ -94,7 +110,7 @@ where .modify(|_r, w| w.ic_sda_tx_hold().bits(sda_tx_hold_count as u16)); } - // Enable I2C block + // Enable I²C block i2c.ic_enable.write(|w| w.enable().enabled()); Self { @@ -104,9 +120,19 @@ where } } } + impl, PINS> I2C { + /// Validate user-supplied arguments + /// + /// If the arguments are not valid, an Error is returned. + /// + /// Checks that: + /// + /// * The address is a valid 7-bit I²C address + /// * The `opt_tx_empty` arg is not `Some(true)` + /// * The `opt_rx_empty` arg is not `Some(true)` fn validate( - addr: u16, + address: u8, opt_tx_empty: Option, opt_rx_empty: Option, ) -> Result<(), Error> { @@ -121,18 +147,20 @@ impl, PINS> I2C { } // validate address - if addr >= 0x80 { - Err(Error::AddressOutOfRange(addr)) - } else if i2c_reserved_addr(addr) { - Err(Error::AddressReserved(addr)) + if address >= 0x80 { + Err(Error::AddressOutOfRange(address as u16)) + } else if i2c_reserved_addr(address as u16) { + Err(Error::AddressReserved(address as u16)) } else { Ok(()) } } - fn setup(&mut self, addr: u16) { + fn setup(&mut self, address: u8) { self.i2c.ic_enable.write(|w| w.enable().disabled()); - self.i2c.ic_tar.write(|w| unsafe { w.ic_tar().bits(addr) }); + self.i2c + .ic_tar + .write(|w| unsafe { w.ic_tar().bits(address as u16) }); self.i2c.ic_enable.write(|w| w.enable().enabled()); } @@ -231,9 +259,38 @@ impl, PINS> I2C { Ok(()) } + /// Write to an I²C device on the bus. + /// + /// The address is given as a 7-bit value, right-aligned in a `u8`. + pub fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::validate(address, Some(bytes.is_empty()), None)?; + self.setup(address); + + self.write_internal(bytes, true) + } + + /// Read from an I²C device on the bus. + /// + /// The address is given as a 7-bit value, right-aligned in a `u8`. + pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::validate(address, None, Some(buffer.is_empty()))?; + self.setup(address); + + self.read_internal(buffer, true, true) + } + + /// Does a write to and then a read from an I²C device on the bus. + pub fn write_read(&mut self, addr: u8, tx: &[u8], rx: &mut [u8]) -> Result<(), Error> { + Self::validate(addr, Some(tx.is_empty()), Some(rx.is_empty()))?; + self.setup(addr); + + self.write_internal(tx, false)?; + self.read_internal(rx, true, true) + } + /// Writes bytes to slave with address `address` /// - /// # I2C Events (contract) + /// # I²C Events (contract) /// /// Same as the `write` method pub fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Error> @@ -241,9 +298,8 @@ impl, PINS> I2C { B: IntoIterator, { let mut peekable = bytes.into_iter().peekable(); - let addr: u16 = address.into(); - Self::validate(addr, Some(peekable.peek().is_none()), None)?; - self.setup(addr); + Self::validate(address, Some(peekable.peek().is_none()), None)?; + self.setup(address); while let Some(tx) = peekable.next() { self.write_internal(&[tx], peekable.peek().is_none())? @@ -254,7 +310,7 @@ impl, PINS> I2C { /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a /// single transaction* /// - /// # I2C Events (contract) + /// # I²C Events (contract) /// /// Same as the `write_read` method pub fn write_iter_read( @@ -267,9 +323,8 @@ impl, PINS> I2C { B: IntoIterator, { let mut peekable = bytes.into_iter().peekable(); - let addr: u16 = address.into(); - Self::validate(addr, Some(peekable.peek().is_none()), None)?; - self.setup(addr); + Self::validate(address, Some(peekable.peek().is_none()), None)?; + self.setup(address); for tx in peekable { self.write_internal(&[tx], false)? @@ -277,7 +332,7 @@ impl, PINS> I2C { self.read_internal(buffer, true, true) } - /// Execute the provided operations on the I2C bus (iterator version). + /// Execute the provided operations on the I²C bus (iterator version). /// /// Transaction contract: /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. @@ -292,72 +347,65 @@ impl, PINS> I2C { /// - `SP` = stop condition pub fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Error> where - O: IntoIterator>, + O: IntoIterator>, { - let addr: u16 = address.into(); - self.setup(addr); + self.setup(address); let mut peekable = operations.into_iter().peekable(); while let Some(operation) = peekable.next() { let last = peekable.peek().is_none(); match operation { - eh1::Operation::Read(buf) => self.read_internal(buf, false, last)?, - eh1::Operation::Write(buf) => self.write_internal(buf, last)?, + i2c::Operation::Read(buf) => self.read_internal(buf, false, last)?, + i2c::Operation::Write(buf) => self.write_internal(buf, last)?, } } Ok(()) } } -impl, PINS> Read for I2C { +// ============================================================================ +// +// Embedded HAL 0.2 +// +// ============================================================================ + +impl, PINS> i2c02::Read for I2C { type Error = Error; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - let addr: u16 = addr.into(); - - Self::validate(addr, None, Some(buffer.is_empty()))?; - - self.setup(addr); - self.read_internal(buffer, true, true) + // Defer to the inherent implementation + Self::read(self, addr, buffer) } } -impl, PINS> WriteRead for I2C { +impl, PINS> i2c02::WriteRead for I2C { type Error = Error; fn write_read(&mut self, addr: u8, tx: &[u8], rx: &mut [u8]) -> Result<(), Error> { - let addr: u16 = addr.into(); - - Self::validate(addr, Some(tx.is_empty()), Some(rx.is_empty()))?; - self.setup(addr); - - self.write_internal(tx, false)?; - self.read_internal(rx, true, true) + Self::write_read(self, addr, tx, rx) } } -impl, PINS> Write for I2C { +impl, PINS> i2c02::Write for I2C { type Error = Error; fn write(&mut self, addr: u8, tx: &[u8]) -> Result<(), Error> { - let addr: u16 = addr.into(); - Self::validate(addr, Some(tx.is_empty()), None)?; - self.setup(addr); - - self.write_internal(tx, true) + // Defer to the inherent implementation + Self::write(self, addr, tx) } } -impl, PINS> WriteIter for I2C { +impl, PINS> i2c02::WriteIter for I2C { type Error = Error; fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> where B: IntoIterator, { - self.write_iter(address, bytes) + // Defer to the inherent implementation + Self::write_iter(self, address, bytes) } } -impl, PINS> WriteIterRead for I2C { +impl, PINS> i2c02::WriteIterRead for I2C { type Error = Error; fn write_iter_read( @@ -369,35 +417,41 @@ impl, PINS> WriteIterRead for I2C where B: IntoIterator, { - self.write_iter_read(address, bytes, buffer) + // Defer to the inherent implementation + Self::write_iter_read(self, address, bytes, buffer) } } -impl, PINS> eh1::ErrorType for I2C { +// ============================================================================ +// +// Embedded HAL 1.0 +// +// ============================================================================ + +impl, PINS> i2c::ErrorType for I2C { type Error = Error; } -impl, PINS> eh1::I2c for I2C { - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - Write::write(self, addr, bytes) +impl, PINS> i2c::I2c for I2C { + fn write(&mut self, addr: i2c::SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> { + Self::write(self, addr, bytes) } - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - Read::read(self, addr, buffer) + fn read(&mut self, addr: i2c::SevenBitAddress, buffer: &mut [u8]) -> Result<(), Error> { + Self::read(self, addr, buffer) } fn transaction( &mut self, - address: u8, - operations: &mut [eh1::Operation<'_>], + address: i2c::SevenBitAddress, + operations: &mut [i2c::Operation<'_>], ) -> Result<(), Self::Error> { - let addr: u16 = address.into(); - self.setup(addr); + self.setup(address); for i in 0..operations.len() { let last = i == operations.len() - 1; match &mut operations[i] { - eh1::Operation::Read(buf) => self.read_internal(buf, false, last)?, - eh1::Operation::Write(buf) => self.write_internal(buf, last)?, + i2c::Operation::Read(buf) => self.read_internal(buf, false, last)?, + i2c::Operation::Write(buf) => self.write_internal(buf, last)?, } } Ok(())