From 820e6462b6a48a6a59f082bccd5d7a3035937b2f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 11:51:42 +0200 Subject: [PATCH 1/8] Add preliminary I2C implementation for RP2040 --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 221 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 4 + embassy-rp/src/uart/mod.rs | 5 + 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/i2c.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1c446f389..875c129c0 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn _clk_sys_freq() -> u32 { +pub(crate) fn clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs new file mode 100644 index 000000000..4a27ee8df --- /dev/null +++ b/embassy-rp/src/i2c.rs @@ -0,0 +1,221 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use pac::i2c; + +use crate::{pac, peripherals, Peripheral}; + +/// I2C error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// I2C abort with error + Abort(u32), + /// User passed in a read buffer that was 0 length + InvalidReadBufferLength, + /// User passed in a write buffer that was 0 length + InvalidWriteBufferLength, + /// Target i2c address is out of range + AddressOutOfRange(u16), + /// Target i2c address is reserved + AddressReserved(u16), +} + +#[non_exhaustive] +#[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, + } + } +} + +pub struct I2c<'d, T: Instance, M: Mode> { + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance> I2c<'d, T, Master> { + pub fn new_master( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(_peri, scl, sda); + + assert!(config.frequency <= 1_000_000); + assert!(config.frequency > 0); + + let p = T::regs(); + + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + + // select controller mode & speed + p.ic_con().write(|w| { + // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + w.set_speed(i2c::vals::Speed::FAST); + w.set_master_mode(true); + w.set_ic_slave_disable(true); + w.set_ic_restart_en(true); + w.set_tx_empty_ctrl(true); + }); + + // Clear FIFO threshold + p.ic_tx_tl().write(|w| w.set_tx_tl(0)); + p.ic_rx_tl().write(|w| w.set_rx_tl(0)); + + // Configure SCL & SDA pins + scl.io().ctrl().write(|w| w.set_funcsel(3)); + sda.io().ctrl().write(|w| w.set_funcsel(3)); + + scl.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.scl_pullup); + }); + sda.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.sda_pullup); + }); + + // Configure baudrate + + // 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 period = (clk_base + config.frequency / 2) / config.frequency; + let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low + let hcnt = period - lcnt; // and 2/5 (40%) of the period high + + // Check for out-of-range divisors: + assert!(hcnt <= 0xffff); + assert!(lcnt <= 0xffff); + assert!(hcnt >= 8); + assert!(lcnt >= 8); + + // Per I2C-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. + let sda_tx_hold_count = if config.frequency < 1_000_000 { + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) + // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 10_000_000) + 1 + } else { + // fast mode plus requires a clk_base > 32MHz + assert!(clk_base >= 32_000_000); + + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) + // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 25_000_000) + 1 + }; + assert!(sda_tx_hold_count <= lcnt - 2); + + p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); + p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); + 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)); + + // Enable I2C block + p.ic_enable().write(|w| w.set_enable(true)); + } + + Self { phantom: PhantomData } + } +} + +mod sealed { + pub trait Instance {} + pub trait Mode {} + + pub trait SdaPin {} + pub trait SclPin {} +} + +pub trait Mode: sealed::Mode {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Master; +pub struct Slave; + +impl_mode!(Master); +impl_mode!(Slave); + +pub trait Instance: sealed::Instance { + fn regs() -> pac::i2c::I2c; +} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type {} + impl Instance for peripherals::$type { + fn regs() -> pac::i2c::I2c { + pac::$type + } + } + }; +} + +impl_instance!(I2C0, I2c0); +impl_instance!(I2C1, I2c1); + +pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} +pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $instance:ident, $function:ident) => { + impl sealed::$function for peripherals::$pin {} + impl $function for peripherals::$pin {} + }; +} + +impl_pin!(PIN_0, I2C0, SdaPin); +impl_pin!(PIN_1, I2C0, SclPin); +impl_pin!(PIN_2, I2C1, SdaPin); +impl_pin!(PIN_3, I2C1, SclPin); +impl_pin!(PIN_4, I2C0, SdaPin); +impl_pin!(PIN_5, I2C0, SclPin); +impl_pin!(PIN_6, I2C1, SdaPin); +impl_pin!(PIN_7, I2C1, SclPin); +impl_pin!(PIN_8, I2C0, SdaPin); +impl_pin!(PIN_9, I2C0, SclPin); +impl_pin!(PIN_10, I2C1, SdaPin); +impl_pin!(PIN_11, I2C1, SclPin); +impl_pin!(PIN_12, I2C0, SdaPin); +impl_pin!(PIN_13, I2C0, SclPin); +impl_pin!(PIN_14, I2C1, SdaPin); +impl_pin!(PIN_15, I2C1, SclPin); +impl_pin!(PIN_16, I2C0, SdaPin); +impl_pin!(PIN_17, I2C0, SclPin); +impl_pin!(PIN_18, I2C1, SdaPin); +impl_pin!(PIN_19, I2C1, SclPin); +impl_pin!(PIN_20, I2C0, SdaPin); +impl_pin!(PIN_21, I2C0, SclPin); +impl_pin!(PIN_22, I2C1, SdaPin); +impl_pin!(PIN_23, I2C1, SclPin); +impl_pin!(PIN_24, I2C0, SdaPin); +impl_pin!(PIN_25, I2C0, SclPin); +impl_pin!(PIN_26, I2C1, SdaPin); +impl_pin!(PIN_27, I2C1, SclPin); +impl_pin!(PIN_28, I2C0, SdaPin); +impl_pin!(PIN_29, I2C0, SclPin); diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ac98d226..e784399d4 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ mod intrinsics; pub mod dma; pub mod gpio; +pub mod i2c; pub mod interrupt; pub mod rom_data; pub mod rtc; @@ -75,6 +76,9 @@ embassy_hal_common::peripherals! { SPI0, SPI1, + I2C0, + I2C1, + DMA_CH0, DMA_CH1, DMA_CH2, diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index d9285ee51..567c79db3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -428,9 +428,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } @@ -438,6 +440,7 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; + fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } @@ -445,9 +448,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } From bcd3ab4ba1541a098a74b5abffdb30a293c97f64 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 14:15:43 +0200 Subject: [PATCH 2/8] Add blocking read & write for I2C --- embassy-rp/src/i2c.rs | 306 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 286 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 4a27ee8df..f7e6a6f6b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -39,12 +39,15 @@ impl Default for Config { } } +const TX_FIFO_SIZE: u8 = 16; +const RX_FIFO_SIZE: u8 = 16; + pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Master> { - pub fn new_master( +impl<'d, T: Instance> I2c<'d, T, Blocking> { + pub fn new_blocking( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, @@ -60,9 +63,10 @@ impl<'d, T: Instance> I2c<'d, T, Master> { unsafe { p.ic_enable().write(|w| w.set_enable(false)); - // select controller mode & speed + // Select controller mode & speed p.ic_con().write(|w| { - // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + // Always use "fast" mode (<= 400 kHz, works fine for standard + // mode too) w.set_speed(i2c::vals::Speed::FAST); w.set_master_mode(true); w.set_ic_slave_disable(true); @@ -70,7 +74,8 @@ impl<'d, T: Instance> I2c<'d, T, Master> { w.set_tx_empty_ctrl(true); }); - // Clear FIFO threshold + // Set FIFO watermarks to 1 to make things simpler. This is encoded + // by a register value of 0. p.ic_tx_tl().write(|w| w.set_tx_tl(0)); p.ic_rx_tl().write(|w| w.set_rx_tl(0)); @@ -89,8 +94,9 @@ impl<'d, T: Instance> I2c<'d, T, Master> { // Configure baudrate - // 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 + // 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 period = (clk_base + config.frequency / 2) / config.frequency; @@ -104,21 +110,21 @@ impl<'d, T: Instance> I2c<'d, T, Master> { assert!(lcnt >= 8); // Per I2C-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. + // 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. let sda_tx_hold_count = if config.frequency < 1_000_000 { - // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) - // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / + // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 10_000_000) + 1 } else { // fast mode plus requires a clk_base > 32MHz assert!(clk_base >= 32_000_000); - // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) - // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / + // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 25_000_000) + 1 }; assert!(sda_tx_hold_count <= lcnt - 2); @@ -138,6 +144,266 @@ impl<'d, T: Instance> I2c<'d, T, Master> { } } +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)); + } + + if i2c_reserved_addr(addr) { + return Err(Error::AddressReserved(addr)); + } + + let p = T::regs(); + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + p.ic_tar().write(|w| w.set_ic_tar(addr)); + p.ic_enable().write(|w| w.set_enable(true)); + } + Ok(()) + } + + fn read_and_clear_abort_reason(&mut self) -> Option { + let p = T::regs(); + unsafe { + let abort_reason = p.ic_tx_abrt_source().read().0; + if abort_reason != 0 { + // Note clearing the abort flag also clears the reason, and this + // instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p.ic_clr_tx_abrt().read(); + Some(abort_reason) + } else { + None + } + } + } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // 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() {} + + p.ic_data_cmd().write(|w| { + if restart && first { + w.set_restart(true); + } else { + w.set_restart(false); + } + + if send_stop && last { + w.set_stop(true); + } else { + w.set_stop(false); + } + + w.cmd() + }); + + while p.ic_rxflr().read().rxflr() == 0 { + if let Some(abort_reason) = self.read_and_clear_abort_reason() { + return Err(Error::Abort(abort_reason)); + } + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // 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_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_some() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + if let Some(abort_reason) = abort_reason { + return Err(Error::Abort(abort_reason)); + } + } + } + Ok(()) + } + + // ========================= 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) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } +} + +// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // +// Async public API // ========================= + +// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), +// Error> { if bytes.is_empty() { self.write_blocking_internal(address, +// bytes, true) } else { self.write_dma_internal(address, bytes, +// true, true).await } } + +// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> +// Result<(), Error> { if bytes.is_empty() { return +// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); + +// let mut first = true; let mut current = iter.next(); while let +// Some(c) = current { let next = iter.next(); let is_last = +// next.is_none(); + +// self.write_dma_internal(address, c, first, is_last).await?; +// first = false; +// current = next; +// } Ok(()) +// } + +// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> +// Result<(), Error> { if buffer.is_empty() { +// self.read_blocking_internal(address, buffer, false) } else { +// self.read_dma_internal(address, buffer, false).await } } + +// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: +// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { +// self.write_blocking_internal(address, bytes, false)?; } else { +// self.write_dma_internal(address, bytes, true, true).await?; } + +// if buffer.is_empty() { self.read_blocking_internal(address, buffer, +// true)?; } else { self.read_dma_internal(address, buffer, +// true).await?; } + +// Ok(()) +// } +// } + +mod eh02 { + use super::*; + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + type Error = Error; + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, bytes) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + type Error = Error; + + fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, bytes, buffer) + } + } +} + +fn i2c_reserved_addr(addr: u16) -> bool { + (addr & 0x78) == 0 || (addr & 0x78) == 0x78 +} + mod sealed { pub trait Instance {} pub trait Mode {} @@ -155,11 +421,11 @@ macro_rules! impl_mode { }; } -pub struct Master; -pub struct Slave; +pub struct Blocking; +pub struct Async; -impl_mode!(Master); -impl_mode!(Slave); +impl_mode!(Blocking); +impl_mode!(Async); pub trait Instance: sealed::Instance { fn regs() -> pac::i2c::I2c; From 603513e76e0cf727808033540598c6c7dd597133 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:01:33 +0200 Subject: [PATCH 3/8] 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 f7e6a6f6b..b368c49cf 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 {} From be68d8ebb773fcf6d0cb94f3bc580d6661f6779b Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 14:24:49 +0200 Subject: [PATCH 4/8] Add further i2c error types --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 69 +++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 875c129c0..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn clk_sys_freq() -> u32 { +pub(crate) fn _clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b368c49cf..20616cd62 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -5,12 +5,25 @@ use pac::i2c; use crate::{pac, peripherals, Peripheral}; +/// I2C error abort reason +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AbortReason { + /// A bus operation was not acknowledged, e.g. due to the addressed device + /// not being available on the bus or the device not being ready to process + /// requests at the moment + NoAcknowledge, + /// The arbitration was lost, e.g. electrical problems with the clock signal + ArbitrationLoss, + Other(u32), +} + /// I2C error #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// I2C abort with error - Abort(u32), + Abort(AbortReason), /// User passed in a read buffer that was 0 length InvalidReadBufferLength, /// User passed in a write buffer that was 0 length @@ -29,9 +42,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { - frequency: 100_000, - } + Self { frequency: 100_000 } } } @@ -164,18 +175,30 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - fn read_and_clear_abort_reason(&mut self) -> Option { + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { - let abort_reason = p.ic_tx_abrt_source().read().0; - if abort_reason != 0 { + let abort_reason = p.ic_tx_abrt_source().read(); + if abort_reason.0 != 0 { // Note clearing the abort flag also clears the reason, and this // instance of flag is clear-on-read! Note also the // IC_CLR_TX_ABRT register always reads as 0. p.ic_clr_tx_abrt().read(); - Some(abort_reason) + + let reason = if abort_reason.abrt_7b_addr_noack() + | abort_reason.abrt_10addr1_noack() + | abort_reason.abrt_10addr2_noack() + { + AbortReason::NoAcknowledge + } else if abort_reason.arb_lost() { + AbortReason::ArbitrationLoss + } else { + AbortReason::Other(abort_reason.0) + }; + + Err(Error::Abort(reason)) } else { - None + Ok(()) } } } @@ -204,9 +227,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { }); while p.ic_rxflr().read().rxflr() == 0 { - if let Some(abort_reason) = self.read_and_clear_abort_reason() { - return Err(Error::Abort(abort_reason)); - } + self.read_and_clear_abort_reason()?; } *byte = p.ic_data_cmd().read().dat(); @@ -241,7 +262,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let abort_reason = self.read_and_clear_abort_reason(); - if abort_reason.is_some() || (send_stop && last) { + if abort_reason.is_err() || (send_stop && last) { // If the transaction was aborted or if it completed // successfully wait until the STOP condition has occured. @@ -254,9 +275,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // condition. Note also the hardware clears RX FIFO as well as // TX on abort, ecause we set hwparam // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - if let Some(abort_reason) = abort_reason { - return Err(Error::Abort(abort_reason)); - } + abort_reason?; } } Ok(()) @@ -360,15 +379,15 @@ mod eh1 { 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, + Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + Self::Abort(AbortReason::NoAcknowledge) => { + embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) + } + Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other, } } } From 53c34ccc399c7bec98880657e52842477af09f49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 29 Aug 2022 13:31:17 +0200 Subject: [PATCH 5/8] Add async API for I2C --- embassy-rp/src/i2c.rs | 281 +++++++++++++++++++++++++++++++++--------- 1 file changed, 225 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 20616cd62..19cdef133 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,8 +1,13 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use atomic_polyfill::Ordering; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; +use crate::dma::{AnyChannel, Channel}; +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; /// I2C error abort reason @@ -49,9 +54,165 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { + tx_dma: Option>, + rx_dma: Option>, + dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq, tx_dma, rx_dma); + + // Enable interrupts + unsafe { + T::regs().ic_intr_mask().modify(|w| { + w.set_m_rx_done(true); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self::new_inner( + _peri, + scl.map_into(), + sda.map_into(), + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), + config, + ) + } + + unsafe fn on_interrupt(_: *mut ()) { + let status = T::regs().ic_intr_stat().read(); + + // FIXME: + if status.tcr() || status.tc() { + let state = T::state(); + state.chunks_transferred.fetch_add(1, Ordering::Relaxed); + 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)); + // }); + } + + async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + let len = bytes.len(); + for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { + let first = idx == 0; + let last = idx * self.dma_buf.len() + chunk.len() == len; + + for (i, byte) in chunk.iter().enumerate() { + let mut b = i2c::regs::IcDataCmd::default(); + b.set_dat(*byte); + b.set_stop(send_stop && last); + + self.dma_buf[i] = b.0 as u16; + } + + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.tx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_dma_cr().modify(|w| { + w.set_tdmae(true); + }); + + crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) + }; + + transfer.await; + } + + Ok(()) + } + + async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let len = buffer.len(); + self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; + + if len > 2 { + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.rx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_data_cmd().modify(|w| { + w.set_cmd(true); + }); + + T::regs().ic_dma_cr().modify(|reg| { + reg.set_rdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read( + ch, + T::regs().ic_data_cmd().ptr() as *const _, + &mut buffer[1..len - 1], + T::RX_DREQ, + ) + }; + transfer.await; + } + + if len > 2 { + self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; + } + + Ok(()) + } + + // ========================= + // Async public API + // ========================= + + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, true) + } else { + self.write_internal(bytes, true).await + } + } + + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } + + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, false)?; + } else { + self.write_internal(bytes, false).await?; + } + + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } +} + impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -59,7 +220,21 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { sda: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(_peri, scl, sda); + into_ref!(scl, sda); + Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + scl: PeripheralRef<'d, AnyPin>, + sda: PeripheralRef<'d, AnyPin>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Self { + into_ref!(_peri); assert!(config.frequency <= 1_000_000); assert!(config.frequency > 0); @@ -152,11 +327,14 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { phantom: PhantomData } + Self { + tx_dma, + rx_dma, + dma_buf: [0; 256], + phantom: PhantomData, + } } -} -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -304,46 +482,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } -// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // -// Async public API // ========================= - -// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), -// Error> { if bytes.is_empty() { self.write_blocking_internal(address, -// bytes, true) } else { self.write_dma_internal(address, bytes, -// true, true).await } } - -// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> -// Result<(), Error> { if bytes.is_empty() { return -// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); - -// let mut first = true; let mut current = iter.next(); while let -// Some(c) = current { let next = iter.next(); let is_last = -// next.is_none(); - -// self.write_dma_internal(address, c, first, is_last).await?; -// first = false; -// current = next; -// } Ok(()) -// } - -// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> -// Result<(), Error> { if buffer.is_empty() { -// self.read_blocking_internal(address, buffer, false) } else { -// self.read_dma_internal(address, buffer, false).await } } - -// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: -// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { -// self.write_blocking_internal(address, bytes, false)?; } else { -// self.write_dma_internal(address, bytes, true, true).await?; } - -// if buffer.is_empty() { self.read_blocking_internal(address, buffer, -// true)?; } else { self.read_dma_internal(address, buffer, -// true).await?; } - -// Ok(()) -// } -// } - mod eh02 { use super::*; @@ -478,7 +616,34 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - pub trait Instance {} + use atomic_polyfill::AtomicUsize; + use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; + + pub(crate) struct State { + pub(crate) waker: AtomicWaker, + pub(crate) chunks_transferred: AtomicUsize, + } + + impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + chunks_transferred: AtomicUsize::new(0), + } + } + } + + pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + + type Interrupt: Interrupt; + + fn regs() -> crate::pac::i2c::I2c; + fn state() -> &'static State; + } + pub trait Mode {} pub trait SdaPin {} @@ -500,27 +665,31 @@ pub struct Async; impl_mode!(Blocking); impl_mode!(Async); -pub trait Instance: sealed::Instance { - type Interrupt; - - fn regs() -> pac::i2c::I2c; -} +pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$type {} - impl Instance for peripherals::$type { + ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + impl sealed::Instance for peripherals::$type { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::i2c::I2c { pac::$type } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } } + impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ); -impl_instance!(I2C1, I2C1_IRQ); +impl_instance!(I2C0, I2C0_IRQ, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From b0d91e9f310f86b4eb9d75c92471831f1656ed1b Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Thu, 15 Sep 2022 05:38:55 +0200 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Jacob Gonzalez --- embassy-rp/src/i2c.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 19cdef133..12fae3b7b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -144,7 +144,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let len = buffer.len(); self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - if len > 2 { + if len >= 2 { // Note(safety): Unwrap should be safe, as this can only be called // when `Mode == Async`, where we have dma channels. let ch = self.rx_dma.as_mut().unwrap(); @@ -168,7 +168,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { transfer.await; } - if len > 2 { + if len >= 2 { self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; } @@ -202,7 +202,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if bytes.is_empty() { self.write_blocking_internal(bytes, false)?; } else { - self.write_internal(bytes, false).await?; + self.write_internal(bytes, true).await?; } if buffer.is_empty() { From 44c46e3c93ae0718110a8805cab4c6c8a9b5df55 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 07:55:28 +0200 Subject: [PATCH 7/8] Move async i2c implementation to new PR, to merge working blocking implementation faster --- embassy-rp/src/i2c.rs | 179 +----------------------------------------- 1 file changed, 1 insertion(+), 178 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 12fae3b7b..d1ec77d33 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,11 +1,9 @@ use core::marker::PhantomData; -use atomic_polyfill::Ordering; -use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::{AnyChannel, Channel}; +use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -60,159 +58,6 @@ pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Async> { - pub fn new( - _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(scl, sda, irq, tx_dma, rx_dma); - - // Enable interrupts - unsafe { - T::regs().ic_intr_mask().modify(|w| { - w.set_m_rx_done(true); - }); - } - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner( - _peri, - scl.map_into(), - sda.map_into(), - Some(tx_dma.map_into()), - Some(rx_dma.map_into()), - config, - ) - } - - unsafe fn on_interrupt(_: *mut ()) { - let status = T::regs().ic_intr_stat().read(); - - // FIXME: - if status.tcr() || status.tc() { - let state = T::state(); - state.chunks_transferred.fetch_add(1, Ordering::Relaxed); - 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)); - // }); - } - - async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - let len = bytes.len(); - for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { - let first = idx == 0; - let last = idx * self.dma_buf.len() + chunk.len() == len; - - for (i, byte) in chunk.iter().enumerate() { - let mut b = i2c::regs::IcDataCmd::default(); - b.set_dat(*byte); - b.set_stop(send_stop && last); - - self.dma_buf[i] = b.0 as u16; - } - - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_dma_cr().modify(|w| { - w.set_tdmae(true); - }); - - crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) - }; - - transfer.await; - } - - Ok(()) - } - - async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - - if len >= 2 { - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_data_cmd().modify(|w| { - w.set_cmd(true); - }); - - T::regs().ic_dma_cr().modify(|reg| { - reg.set_rdmae(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read( - ch, - T::regs().ic_data_cmd().ptr() as *const _, - &mut buffer[1..len - 1], - T::RX_DREQ, - ) - }; - transfer.await; - } - - if len >= 2 { - self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; - } - - Ok(()) - } - - // ========================= - // Async public API - // ========================= - - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, true) - } else { - self.write_internal(bytes, true).await - } - } - - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } - - pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, false)?; - } else { - self.write_internal(bytes, true).await?; - } - - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } -} - impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -616,23 +461,7 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Interrupt; - use embassy_sync::waitqueue::AtomicWaker; - - pub(crate) struct State { - pub(crate) waker: AtomicWaker, - pub(crate) chunks_transferred: AtomicUsize, - } - - impl State { - pub(crate) const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - chunks_transferred: AtomicUsize::new(0), - } - } - } pub trait Instance { const TX_DREQ: u8; @@ -641,7 +470,6 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; - fn state() -> &'static State; } pub trait Mode {} @@ -678,11 +506,6 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } - - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); - &STATE - } } impl Instance for peripherals::$type {} }; From bf1da0497ce1a591865f2bd8d0a29489079e710b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 08:03:49 +0200 Subject: [PATCH 8/8] Allow unused fields temporarily in i2c --- embassy-rp/src/i2c.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d1ec77d33..9596d661d 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -52,9 +52,9 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { - tx_dma: Option>, - rx_dma: Option>, - dma_buf: [u16; 256], + _tx_dma: Option>, + _rx_dma: Option>, + _dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } @@ -75,8 +75,8 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, sda: PeripheralRef<'d, AnyPin>, - tx_dma: Option>, - rx_dma: Option>, + _tx_dma: Option>, + _rx_dma: Option>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +173,9 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - tx_dma, - rx_dma, - dma_buf: [0; 256], + _tx_dma, + _rx_dma, + _dma_buf: [0; 256], phantom: PhantomData, } }