From f81cdbdccda6e9842ae60a0ae167536962f641f3 Mon Sep 17 00:00:00 2001 From: Benjamin Koch <bbbsnowball@gmail.com> Date: Sat, 13 May 2023 03:12:20 +0200 Subject: [PATCH] make a copy of embassy-rs' i2c implementation --- firmware/rust1/src/lib/i2c.rs | 876 ++++++++++++++++++++++++++++++++++ 1 file changed, 876 insertions(+) create mode 100644 firmware/rust1/src/lib/i2c.rs diff --git a/firmware/rust1/src/lib/i2c.rs b/firmware/rust1/src/lib/i2c.rs new file mode 100644 index 0000000..3e9946f --- /dev/null +++ b/firmware/rust1/src/lib/i2c.rs @@ -0,0 +1,876 @@ +// This is a modified copy of embassy/embassy-rp/src/i2c.rs. +// We have changed: +// - allow empty buffer for write (for I2C scan) +// - don't block access to reserved addresses (also for I2C scan) + +use core::future; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use pac::i2c; + +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; +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(AbortReason), + /// 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, +} + +impl Default for Config { + fn default() -> Self { + Self { frequency: 100_000 } + } +} + +const 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, Blocking> { + pub fn new_blocking( + peri: impl Peripheral<P = T> + 'd, + scl: impl Peripheral<P = impl SclPin<T>> + 'd, + sda: impl Peripheral<P = impl SdaPin<T>> + 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda); + Self::new_inner(peri, scl.map_into(), sda.map_into(), config) + } +} + +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new_async( + peri: impl Peripheral<P = T> + 'd, + scl: impl Peripheral<P = impl SclPin<T>> + 'd, + sda: impl Peripheral<P = impl SdaPin<T>> + 'd, + irq: impl Peripheral<P = T::Interrupt> + 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq); + + let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); + + irq.set_handler(Self::on_interrupt); + unsafe { + let i2c = T::regs(); + + // mask everything initially + i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + } + irq.unpend(); + debug_assert!(!irq.is_pending()); + irq.enable(); + + i2c + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll<U>, + G: FnMut(&mut Self), + { + future::poll_fn(|cx| { + let r = f(self); + + if r.is_pending() { + T::waker().register(cx.waker()); + g(self); + } + r + }) + .await + } + + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt(_: *mut ()) { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + T::waker().wake(); + } + + async fn read_async_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 mut remaining = buffer.len(); + let mut remaining_queue = buffer.len(); + + let mut abort_reason = Ok(()); + + while remaining > 0 { + // Waggle SCK - basically the same as write + let tx_fifo_space = Self::tx_fifo_capacity(); + let mut batch = 0; + + debug_assert!(remaining_queue > 0); + + for _ in 0..remaining_queue.min(tx_fifo_space as usize) { + remaining_queue -= 1; + let last = remaining_queue == 0; + batch += 1; + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_restart(restart && remaining_queue == buffer.len() - 1); + w.set_stop(last && send_stop); + w.set_cmd(true); + }); + } + } + + // We've either run out of txfifo or just plain finished setting up + // the clocks for the message - either way we need to wait for rx + // data. + + debug_assert!(batch > 0); + let res = self + .wait_on( + |me| { + let rxfifo = Self::rx_fifo_len(); + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if rxfifo >= batch { + Poll::Ready(Ok(rxfifo)) + } else { + Poll::Pending + } + }, + |_me| unsafe { + // Set the read threshold to the number of bytes we're + // expecting so we don't get spurious interrupts. + p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); + + p.ic_intr_mask().modify(|w| { + w.set_m_rx_full(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + match res { + Err(reason) => { + abort_reason = Err(reason); + break; + } + Ok(rxfifo) => { + // Fetch things from rx fifo. We're assuming we're the only + // rxfifo reader, so nothing else can take things from it. + let rxbytes = (rxfifo as usize).min(remaining); + let received = buffer.len() - remaining; + for b in &mut buffer[received..received + rxbytes] { + *b = unsafe { p.ic_data_cmd().read().dat() }; + } + remaining -= rxbytes; + } + }; + } + + self.wait_stop_det(abort_reason, send_stop).await + } + + async fn write_async_internal( + &mut self, + bytes: impl IntoIterator<Item = u8>, + send_stop: bool, + ) -> Result<(), Error> { + let p = T::regs(); + + let mut bytes = bytes.into_iter().peekable(); + + let res = 'xmit: loop { + let tx_fifo_space = Self::tx_fifo_capacity(); + + for _ in 0..tx_fifo_space { + if let Some(byte) = bytes.next() { + let last = bytes.peek().is_none(); + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(last && send_stop); + w.set_cmd(false); + w.set_dat(byte); + }); + } + } else { + break 'xmit Ok(()); + } + } + + let res = self + .wait_on( + |me| { + if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { + Poll::Ready(abort_reason) + } else if !Self::tx_fifo_full() { + // resume if there's any space free in the tx fifo + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + // Set tx "free" threshold a little high so that we get + // woken before the fifo completely drains to minimize + // transfer stalls. + p.ic_tx_tl().write(|w| w.set_tx_tl(1)); + + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await; + if res.is_err() { + break res; + } + }; + + self.wait_stop_det(res, send_stop).await + } + + /// Helper to wait for a stop bit, for both tx and rx. If we had an abort, + /// then we'll get a hardware-generated stop, otherwise wait for a stop if + /// we're expecting it. + /// + /// Also handles an abort which arises while processing the tx fifo. + async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> { + if had_abort.is_err() || do_stop { + let p = T::regs(); + + let had_abort2 = self + .wait_on( + |me| unsafe { + // We could see an abort while processing fifo backlog, + // so handle it here. + let abort = me.read_and_clear_abort_reason(); + if had_abort.is_ok() && abort.is_err() { + Poll::Ready(abort) + } else if p.ic_raw_intr_stat().read().stop_det() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { + p.ic_clr_stop_det().read(); + } + + had_abort.and(had_abort2) + } else { + had_abort + } + } + + pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + + pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> { + Self::setup(addr)?; + self.write_async_internal(bytes, true).await + } +} + +impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: impl Peripheral<P = T> + 'd, + scl: PeripheralRef<'d, AnyPin>, + sda: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(_peri); + + assert!(config.frequency <= 1_000_000); + assert!(config.frequency > 0); + + let p = T::regs(); + + unsafe { + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + + p.ic_enable().write(|w| w.set_enable(false)); + + // Select controller mode & speed + p.ic_con().modify(|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); + }); + + // 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)); + + // 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_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_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); + }); + + // 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_peri_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() + .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)); + } + + Self { phantom: PhantomData } + } + + 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(()) + } + + #[inline] + fn tx_fifo_full() -> bool { + Self::tx_fifo_capacity() == 0 + } + + #[inline] + fn tx_fifo_capacity() -> u8 { + let p = T::regs(); + unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } + } + + #[inline] + fn rx_fifo_len() -> u8 { + let p = T::regs(); + unsafe { p.ic_rxflr().read().rxflr() } + } + + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { + let p = T::regs(); + unsafe { + 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(); + + 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 { + Ok(()) + } + } + } + + fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if read.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = read.len() - 1; + for (i, byte) in read.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| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while Self::rx_fifo_len() == 0 { + self.read_and_clear_abort_reason()?; + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> { + if write.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in write.iter().enumerate() { + let last = i == write.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + 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_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occurred. + + 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. + abort_reason?; + } + } + Ok(()) + } + + // ========================= + // Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(read, true, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(write, true) + } + + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(write, false)?; + self.read_blocking_internal(read, true, true) + // Automatic Stop + } +} + +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) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> { + type Error = Error; + + fn exec( + &mut self, + address: u8, + operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>], + ) -> 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_02::blocking::i2c::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_02::blocking::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } +} + +#[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 { + 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, + } + } + } + + 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::I2c for I2c<'d, T, M> { + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> 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::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } +} +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod nightly { + use embedded_hal_1::i2c::Operation; + use embedded_hal_async::i2c::AddressMode; + + use super::*; + + impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async> + where + A: AddressMode + Into<u16> + 'static, + T: Instance + 'd, + { + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + let addr: u16 = address.into(); + + Self::setup(addr)?; + self.read_async_internal(read, false, true).await + } + + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + let addr: u16 = address.into(); + + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await + } + + async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + let addr: u16 = address.into(); + + Self::setup(addr)?; + self.write_async_internal(write.iter().cloned(), false).await?; + self.read_async_internal(read, false, true).await + } + + async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { + let addr: u16 = address.into(); + + let mut iterator = operations.iter_mut(); + + while let Some(op) = iterator.next() { + let last = iterator.len() == 0; + + match op { + Operation::Read(buffer) => { + Self::setup(addr)?; + self.read_async_internal(buffer, false, last).await?; + } + Operation::Write(buffer) => { + Self::setup(addr)?; + self.write_async_internal(buffer.into_iter().cloned(), last).await?; + } + } + } + Ok(()) + } + } +} + +fn i2c_reserved_addr(addr: u16) -> bool { + (addr & 0x78) == 0 || (addr & 0x78) == 0x78 +} + +mod sealed { + use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; + + pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + + type Interrupt: Interrupt; + + fn regs() -> crate::pac::i2c::I2c; + fn reset() -> crate::pac::resets::regs::Peripherals; + fn waker() -> &'static AtomicWaker; + } + + pub trait Mode {} + + pub trait SdaPin<T: Instance> {} + pub trait SclPin<T: Instance> {} +} + +pub trait Mode: sealed::Mode {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); + +pub trait Instance: sealed::Instance {} + +macro_rules! impl_instance { + ($type:ident, $irq:ident, $reset: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; + + #[inline] + fn regs() -> pac::i2c::I2c { + pac::$type + } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.$reset(true); + ret + } + + #[inline] + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + + &WAKER + } + } + impl Instance for peripherals::$type {} + }; +} + +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); + +pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} +pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $instance:ident, $function:ident) => { + impl sealed::$function<peripherals::$instance> for peripherals::$pin {} + impl $function<peripherals::$instance> 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); -- GitLab