From 603513e76e0cf727808033540598c6c7dd597133 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:01:33 +0200 Subject: [PATCH] Fix blocking I2C --- embassy-rp/src/i2c.rs | 196 +++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 69 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f7e6a6f6..b368c49c 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -25,22 +25,17 @@ pub enum Error { #[derive(Copy, Clone)] pub struct Config { pub frequency: u32, - pub sda_pullup: bool, - pub scl_pullup: bool, } impl Default for Config { fn default() -> Self { Self { frequency: 100_000, - sda_pullup: false, - scl_pullup: false, } } } -const TX_FIFO_SIZE: u8 = 16; -const RX_FIFO_SIZE: u8 = 16; +const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, @@ -64,7 +59,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed - p.ic_con().write(|w| { + p.ic_con().modify(|w| { // Always use "fast" mode (<= 400 kHz, works fine for standard // mode too) w.set_speed(i2c::vals::Speed::FAST); @@ -85,11 +80,17 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { scl.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.scl_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); sda.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.sda_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); // Configure baudrate @@ -97,7 +98,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // There are some subtleties to I2C 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 clk_base = crate::clocks::clk_sys_freq(); + let clk_base = crate::clocks::clk_peri_freq(); let period = (clk_base + config.frequency / 2) / config.frequency; let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low @@ -134,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_fs_spklen() .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); p.ic_sda_hold() - .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); // Enable I2C block p.ic_enable().write(|w| w.set_enable(true)); @@ -145,42 +146,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { - /// Number of bytes currently in the RX FIFO - #[inline] - pub fn rx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_rxflr().read().rxflr() } - } - - /// Remaining capacity in the RX FIFO - #[inline] - pub fn rx_fifo_free(&self) -> u8 { - RX_FIFO_SIZE - self.rx_fifo_used() - } - - /// RX FIFO is empty - #[inline] - pub fn rx_fifo_empty(&self) -> bool { - self.rx_fifo_used() == 0 - } - - /// Number of bytes currently in the TX FIFO - #[inline] - pub fn tx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_txflr().read().txflr() } - } - - /// Remaining capacity in the TX FIFO - #[inline] - pub fn tx_fifo_free(&self) -> u8 { - TX_FIFO_SIZE - self.tx_fifo_used() - } - - /// TX FIFO is at capacity - #[inline] - pub fn tx_fifo_full(&self) -> bool { - self.tx_fifo_free() == 0 - } - fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -229,22 +194,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while self.tx_fifo_full() {} + while p.ic_txflr().read().txflr() == FIFO_SIZE {} p.ic_data_cmd().write(|w| { - if restart && first { - w.set_restart(true); - } else { - w.set_restart(false); - } + w.set_restart(restart && first); + w.set_stop(send_stop && last); - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } - - w.cmd() + w.set_cmd(true); }); while p.ic_rxflr().read().rxflr() == 0 { @@ -273,11 +229,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { p.ic_data_cmd().write(|w| { - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } + w.set_stop(send_stop && last); w.set_dat(*byte); }); @@ -310,12 +262,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - // ========================= Blocking public API + // ========================= + // Blocking public API // ========================= pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.read_blocking_internal(buffer, false, true) + self.read_blocking_internal(buffer, true, true) // Automatic Stop } @@ -400,6 +353,107 @@ mod eh02 { } } +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl embedded_hal_1::i2c::Error for Error { + fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { + match *self { + _ => embedded_hal_1::i2c::ErrorKind::Bus, + // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + // Self::Nack => { + // embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) + // } + // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, + // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { + type Error = Error; + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + + fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, buffer) + } + + fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let mut peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + while let Some(tx) = peekable.next() { + self.write_blocking_internal(&[tx], peekable.peek().is_none())?; + } + Ok(()) + } + + fn write_iter_read(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + for tx in peekable { + self.write_blocking_internal(&[tx], false)? + } + self.read_blocking_internal(buffer, true, true) + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, wr_buffer, rd_buffer) + } + + fn transaction<'a>( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + ) -> Result<(), Self::Error> { + Self::setup(address.into())?; + for i in 0..operations.len() { + let last = i == operations.len() - 1; + match &mut operations[i] { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + + fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + Self::setup(address.into())?; + let mut peekable = operations.into_iter().peekable(); + while let Some(operation) = peekable.next() { + let last = peekable.peek().is_none(); + match operation { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } +} + fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 } @@ -428,6 +482,8 @@ impl_mode!(Blocking); impl_mode!(Async); pub trait Instance: sealed::Instance { + type Interrupt; + fn regs() -> pac::i2c::I2c; } @@ -435,6 +491,8 @@ macro_rules! impl_instance { ($type:ident, $irq:ident) => { impl sealed::Instance for peripherals::$type {} impl Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + fn regs() -> pac::i2c::I2c { pac::$type } @@ -442,8 +500,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2c0); -impl_instance!(I2C1, I2c1); +impl_instance!(I2C0, I2C0_IRQ); +impl_instance!(I2C1, I2C1_IRQ); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {}