From 1efaaa4025120413ec17de90106244445208804a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Nov 2025 11:32:37 -0800 Subject: I2c Controller Blocking Driver (#27) * I2c Master Signed-off-by: Felipe Balbi * Fix review comments Signed-off-by: Felipe Balbi * More review comments Signed-off-by: Felipe Balbi --------- Signed-off-by: Felipe Balbi Co-authored-by: Felipe Balbi --- src/clocks/mod.rs | 9 +- src/clocks/periph_helpers.rs | 113 +++++++++++ src/i2c/controller.rs | 455 +++++++++++++++++++++++++++++++++++++++++++ src/i2c/mod.rs | 171 ++++++++++++++++ src/interrupt.rs | 4 +- src/lib.rs | 1 + 6 files changed, 750 insertions(+), 3 deletions(-) create mode 100644 src/i2c/controller.rs create mode 100644 src/i2c/mod.rs (limited to 'src') diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs index cd6318c4b..9c9e6ef3d 100644 --- a/src/clocks/mod.rs +++ b/src/clocks/mod.rs @@ -553,7 +553,7 @@ impl Clocks { return Err(ClockError::BadConfig { clock: "main_clk", reason: "not low power active", - }) + }); } } @@ -904,7 +904,7 @@ macro_rules! impl_cc_gate { pub(crate) mod gate { #[cfg(not(feature = "time"))] use super::periph_helpers::OsTimerConfig; - use super::periph_helpers::{AdcConfig, LpuartConfig, NoConfig}; + use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig}; use super::*; // These peripherals have no additional upstream clocks or configuration required @@ -928,6 +928,11 @@ pub(crate) mod gate { #[cfg(not(feature = "time"))] impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig); + impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig); + impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig); + impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig); + impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig); + impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig); impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig); impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig); diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs index eac3ef8dd..24d074e8a 100644 --- a/src/clocks/periph_helpers.rs +++ b/src/clocks/periph_helpers.rs @@ -123,6 +123,119 @@ impl SPConfHelper for NoConfig { } } +// +// LPI2c +// + +/// Selectable clocks for `Lpi2c` peripherals +#[derive(Debug, Clone, Copy)] +pub enum Lpi2cClockSel { + /// FRO12M/FRO_LF/SIRC clock source, passed through divider + /// "fro_lf_div" + FroLfDiv, + /// FRO180M/FRO_HF/FIRC clock source, passed through divider + /// "fro_hf_div" + FroHfDiv, + /// SOSC/XTAL/EXTAL clock source + ClkIn, + /// clk_1m/FRO_LF divided by 12 + Clk1M, + /// Output of PLL1, passed through clock divider, + /// "pll1_clk_div", maybe "pll1_lf_div"? + Pll1ClkDiv, + /// Disabled + None, +} + +/// Which instance of the `Lpi2c` is this? +/// +/// Should not be directly selectable by end-users. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Lpi2cInstance { + /// Instance 0 + Lpi2c0, + /// Instance 1 + Lpi2c1, + /// Instance 2 + Lpi2c2, + /// Instance 3 + Lpi2c3, +} + +/// Top level configuration for `Lpi2c` instances. +pub struct Lpi2cConfig { + /// Power state required for this peripheral + pub power: PoweredClock, + /// Clock source + pub source: Lpi2cClockSel, + /// Clock divisor + pub div: Div4, + /// Which instance is this? + // NOTE: should not be user settable + pub(crate) instance: Lpi2cInstance, +} + +impl SPConfHelper for Lpi2cConfig { + fn post_enable_config(&self, clocks: &Clocks) -> Result { + // check that source is suitable + let mrcc0 = unsafe { pac::Mrcc0::steal() }; + use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux; + + let (clkdiv, clksel) = match self.instance { + Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()), + Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()), + Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()), + Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()), + }; + + let (freq, variant) = match self.source { + Lpi2cClockSel::FroLfDiv => { + let freq = clocks.ensure_fro_lf_div_active(&self.power)?; + (freq, Mux::ClkrootFunc0) + } + Lpi2cClockSel::FroHfDiv => { + let freq = clocks.ensure_fro_hf_div_active(&self.power)?; + (freq, Mux::ClkrootFunc2) + } + Lpi2cClockSel::ClkIn => { + let freq = clocks.ensure_clk_in_active(&self.power)?; + (freq, Mux::ClkrootFunc3) + } + Lpi2cClockSel::Clk1M => { + let freq = clocks.ensure_clk_1m_active(&self.power)?; + (freq, Mux::ClkrootFunc5) + } + Lpi2cClockSel::Pll1ClkDiv => { + let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; + (freq, Mux::ClkrootFunc6) + } + Lpi2cClockSel::None => unsafe { + // no ClkrootFunc7, just write manually for now + clksel.write(|w| w.bits(0b111)); + clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted()); + return Ok(0); + }, + }; + + // set clksel + clksel.modify(|_r, w| w.mux().variant(variant)); + + // Set up clkdiv + clkdiv.modify(|_r, w| { + unsafe { w.div().bits(self.div.into_bits()) } + .halt() + .asserted() + .reset() + .asserted() + }); + clkdiv.modify(|_r, w| w.halt().deasserted().reset().deasserted()); + + while clkdiv.read().unstab().is_unstable() {} + + Ok(freq / self.div.into_divisor()) + } +} + // // LPUart // diff --git a/src/i2c/controller.rs b/src/i2c/controller.rs new file mode 100644 index 000000000..41bbc821d --- /dev/null +++ b/src/i2c/controller.rs @@ -0,0 +1,455 @@ +//! LPI2C controller driver + +use core::marker::PhantomData; + +use embassy_hal_internal::Peri; +use mcxa_pac::lpi2c0::mtdr::Cmd; + +use super::{Blocking, Error, Instance, Mode, Result, SclPin, SdaPin}; +use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; +use crate::clocks::{enable_and_reset, PoweredClock}; +use crate::AnyPin; + +/// Bus speed (nominal SCL, no clock stretching) +#[derive(Clone, Copy, Default, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Speed { + #[default] + /// 100 kbit/sec + Standard, + /// 400 kbit/sec + Fast, + /// 1 Mbit/sec + FastPlus, + /// 3.4 Mbit/sec + UltraFast, +} + +impl From for (u8, u8, u8, u8) { + fn from(value: Speed) -> (u8, u8, u8, u8) { + match value { + Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d), + Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06), + Speed::FastPlus => (0x04, 0x03, 0x03, 0x02), + + // UltraFast is "special". Leaving it unimplemented until + // the driver and the clock API is further stabilized. + Speed::UltraFast => todo!(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum SendStop { + No, + Yes, +} + +/// I2C controller configuration +#[derive(Clone, Copy, Default)] +#[non_exhaustive] +pub struct Config { + /// Bus speed + pub speed: Speed, +} + +/// I2C Controller Driver. +pub struct I2c<'d, T: Instance, M: Mode> { + _peri: Peri<'d, T>, + _scl: Peri<'d, AnyPin>, + _sda: Peri<'d, AnyPin>, + _phantom: PhantomData, + is_hs: bool, +} + +impl<'d, T: Instance> I2c<'d, T, Blocking> { + /// Create a new blocking instance of the I2C Controller bus driver. + pub fn new_blocking( + peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + config: Config, + ) -> Result { + Self::new_inner(peri, scl, sda, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + config: Config, + ) -> Result { + let (power, source, div) = Self::clock_config(config.speed); + + // Enable clocks + let conf = Lpi2cConfig { + power, + source, + div, + instance: T::CLOCK_INSTANCE, + }; + + _ = unsafe { enable_and_reset::(&conf).map_err(Error::ClockSetup)? }; + + scl.mux(); + sda.mux(); + + let _scl = scl.into(); + let _sda = sda.into(); + + Self::set_config(&config)?; + + Ok(Self { + _peri, + _scl, + _sda, + _phantom: PhantomData, + is_hs: config.speed == Speed::UltraFast, + }) + } + + fn set_config(config: &Config) -> Result<()> { + // Disable the controller. + critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled())); + + // Soft-reset the controller, read and write FIFOs. + critical_section::with(|_| { + T::regs() + .mcr() + .modify(|_, w| w.rst().reset().rtf().reset().rrf().reset()); + // According to Reference Manual section 40.7.1.4, "There + // is no minimum delay required before clearing the + // software reset", therefore we clear it immediately. + T::regs().mcr().modify(|_, w| w.rst().not_reset()); + + T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit()); + }); + + let (clklo, clkhi, sethold, datavd) = config.speed.into(); + + critical_section::with(|_| { + T::regs().mccr0().modify(|_, w| unsafe { + w.clklo() + .bits(clklo) + .clkhi() + .bits(clkhi) + .sethold() + .bits(sethold) + .datavd() + .bits(datavd) + }) + }); + + // Enable the controller. + critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled())); + + // Clear all flags + T::regs().msr().write(|w| { + w.epf() + .clear_bit_by_one() + .sdf() + .clear_bit_by_one() + .ndf() + .clear_bit_by_one() + .alf() + .clear_bit_by_one() + .fef() + .clear_bit_by_one() + .pltf() + .clear_bit_by_one() + .dmf() + .clear_bit_by_one() + .stf() + .clear_bit_by_one() + }); + + Ok(()) + } + + // REVISIT: turn this into a function of the speed parameter + fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) { + match speed { + Speed::Standard | Speed::Fast | Speed::FastPlus => ( + PoweredClock::NormalEnabledDeepSleepDisabled, + Lpi2cClockSel::FroLfDiv, + const { Div4::no_div() }, + ), + Speed::UltraFast => ( + PoweredClock::NormalEnabledDeepSleepDisabled, + Lpi2cClockSel::FroHfDiv, + const { Div4::no_div() }, + ), + } + } + + fn is_tx_fifo_full(&mut self) -> bool { + let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits(); + T::regs().mfsr().read().txcount().bits() == txfifo_size + } + + fn is_tx_fifo_empty(&mut self) -> bool { + T::regs().mfsr().read().txcount() == 0 + } + + fn is_rx_fifo_empty(&mut self) -> bool { + T::regs().mfsr().read().rxcount() == 0 + } + + fn status(&mut self) -> Result<()> { + // Wait for TxFIFO to be drained + while !self.is_tx_fifo_empty() {} + + let msr = T::regs().msr().read(); + T::regs().msr().write(|w| { + w.epf() + .clear_bit_by_one() + .sdf() + .clear_bit_by_one() + .ndf() + .clear_bit_by_one() + .alf() + .clear_bit_by_one() + .fef() + .clear_bit_by_one() + .fef() + .clear_bit_by_one() + .pltf() + .clear_bit_by_one() + .dmf() + .clear_bit_by_one() + .stf() + .clear_bit_by_one() + }); + + if msr.ndf().bit_is_set() { + Err(Error::AddressNack) + } else if msr.alf().bit_is_set() { + Err(Error::ArbitrationLoss) + } else { + Ok(()) + } + } + + fn send_cmd(&mut self, cmd: Cmd, data: u8) { + #[cfg(feature = "defmt")] + defmt::trace!( + "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}", + cmd, + cmd as u8, + data, + T::regs().msr().read().bits() + ); + + T::regs() + .mtdr() + .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd)); + } + + fn start(&mut self, address: u8, read: bool) -> Result<()> { + if address >= 0x80 { + return Err(Error::AddressOutOfRange(address)); + } + + // Wait until we have space in the TxFIFO + while self.is_tx_fifo_full() {} + + let addr_rw = address << 1 | if read { 1 } else { 0 }; + self.send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw); + + // Check controller status + self.status() + } + + fn stop(&mut self) -> Result<()> { + // Wait until we have space in the TxFIFO + while self.is_tx_fifo_full() {} + + self.send_cmd(Cmd::Stop, 0); + self.status() + } + + fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> { + self.start(address, true)?; + + if read.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + for chunk in read.chunks_mut(256) { + // Wait until we have space in the TxFIFO + while self.is_tx_fifo_full() {} + + self.send_cmd(Cmd::Receive, (chunk.len() - 1) as u8); + + for byte in chunk.iter_mut() { + // Wait until there's data in the RxFIFO + while self.is_rx_fifo_empty() {} + + *byte = T::regs().mrdr().read().data().bits(); + } + + if send_stop == SendStop::Yes { + self.stop()?; + } + } + + Ok(()) + } + + fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> { + self.start(address, false)?; + + // Usually, embassy HALs error out with an empty write, + // however empty writes are useful for writing I2C scanning + // logic through write probing. That is, we send a start with + // R/w bit cleared, but instead of writing any data, just send + // the stop onto the bus. This has the effect of checking if + // the resulting address got an ACK but causing no + // side-effects to the device on the other end. + // + // Because of this, we are not going to error out in case of + // empty writes. + #[cfg(feature = "defmt")] + if write.is_empty() { + defmt::trace!("Empty write, write probing?"); + } + + for byte in write { + // Wait until we have space in the TxFIFO + while self.is_tx_fifo_full() {} + + self.send_cmd(Cmd::Transmit, *byte); + } + + if send_stop == SendStop::Yes { + self.stop()?; + } + + Ok(()) + } + + // Public API: Blocking + + /// Read from address into buffer blocking caller until done. + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> { + self.blocking_read_internal(address, read, SendStop::Yes) + // Automatic Stop + } + + /// Write to address from buffer blocking caller until done. + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> { + self.blocking_write_internal(address, write, SendStop::Yes) + } + + /// Write to address from bytes and read from address into buffer blocking caller until done. + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> { + self.blocking_write_internal(address, write, SendStop::No)?; + self.blocking_read_internal(address, read, SendStop::Yes) + } +} + +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.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.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.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<()> { + if let Some((last, rest)) = operations.split_last_mut() { + for op in rest { + match op { + embedded_hal_02::blocking::i2c::Operation::Read(buf) => { + self.blocking_read_internal(address, buf, SendStop::No)? + } + embedded_hal_02::blocking::i2c::Operation::Write(buf) => { + self.blocking_write_internal(address, buf, SendStop::No)? + } + } + } + + match last { + embedded_hal_02::blocking::i2c::Operation::Read(buf) => { + self.blocking_read_internal(address, buf, SendStop::Yes) + } + embedded_hal_02::blocking::i2c::Operation::Write(buf) => { + self.blocking_write_internal(address, buf, SendStop::Yes) + } + } + } else { + Ok(()) + } + } +} + +impl embedded_hal_1::i2c::Error for Error { + fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { + match *self { + Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + Self::AddressNack => { + embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) + } + _ => 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 transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> { + if let Some((last, rest)) = operations.split_last_mut() { + for op in rest { + match op { + embedded_hal_1::i2c::Operation::Read(buf) => { + self.blocking_read_internal(address, buf, SendStop::No)? + } + embedded_hal_1::i2c::Operation::Write(buf) => { + self.blocking_write_internal(address, buf, SendStop::No)? + } + } + } + + match last { + embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes), + embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes), + } + } else { + Ok(()) + } + } +} + +impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> { + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<()> { + Self::set_config(config) + } +} diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs new file mode 100644 index 000000000..a1f842029 --- /dev/null +++ b/src/i2c/mod.rs @@ -0,0 +1,171 @@ +//! I2C Support + +use core::marker::PhantomData; + +use embassy_hal_internal::PeripheralType; +use embassy_sync::waitqueue::AtomicWaker; +use paste::paste; + +use crate::clocks::periph_helpers::Lpi2cConfig; +use crate::clocks::{ClockError, Gate}; +use crate::gpio::{GpioPin, SealedPin}; +use crate::{interrupt, pac}; + +/// Shorthand for `Result`. +pub type Result = core::result::Result; + +pub mod controller; + +/// Error information type +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Clock configuration error. + ClockSetup(ClockError), + /// Reading for I2C failed. + ReadFail, + /// Writing to I2C failed. + WriteFail, + /// I2C address NAK condition. + AddressNack, + /// Bus level arbitration loss. + ArbitrationLoss, + /// Address out of range. + AddressOutOfRange(u8), + /// Invalid write buffer length. + InvalidWriteBufferLength, + /// Invalid read buffer length. + InvalidReadBufferLength, + /// Other internal errors or unexpected state. + Other, +} + +/// I2C interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let waker = T::waker(); + + waker.wake(); + + todo!() + } +} + +mod sealed { + /// Seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for T {} + +trait SealedInstance { + fn regs() -> &'static pac::lpi2c0::RegisterBlock; + fn waker() -> &'static AtomicWaker; +} + +/// I2C Instance +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate { + /// Interrupt for this I2C instance. + type Interrupt: interrupt::typelevel::Interrupt; + /// Clock instance + const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + fn regs() -> &'static pac::lpi2c0::RegisterBlock { + unsafe { &*pac::[]::ptr() } + } + + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance + = crate::clocks::periph_helpers::Lpi2cInstance::[]; + } + } + )* + }; +} + +impl_instance!(0, 1, 2, 3); + +/// SCL pin trait. +pub trait SclPin: GpioPin + sealed::Sealed + PeripheralType { + fn mux(&self); +} + +/// SDA pin trait. +pub trait SdaPin: GpioPin + sealed::Sealed + PeripheralType { + fn mux(&self); +} + +/// Driver mode. +#[allow(private_bounds)] +pub trait Mode: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl sealed::Sealed for Blocking {} +impl Mode for Blocking {} + +/// Async mode. +pub struct Async; +impl sealed::Sealed for Async {} +impl Mode for Async {} + +macro_rules! impl_pin { + ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => { + impl $trait for crate::peripherals::$pin { + fn mux(&self) { + self.set_pull(crate::gpio::Pull::Disabled); + self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); + self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); + self.set_function(crate::pac::port0::pcr0::Mux::$fn); + self.set_enable_input_buffer(); + } + } + }; +} + +impl_pin!(P0_16, LPI2C0, Mux2, SdaPin); +impl_pin!(P0_17, LPI2C0, Mux2, SclPin); +impl_pin!(P0_18, LPI2C0, Mux2, SclPin); +impl_pin!(P0_19, LPI2C0, Mux2, SdaPin); +impl_pin!(P1_0, LPI2C1, Mux3, SdaPin); +impl_pin!(P1_1, LPI2C1, Mux3, SclPin); +impl_pin!(P1_2, LPI2C1, Mux3, SdaPin); +impl_pin!(P1_3, LPI2C1, Mux3, SclPin); +impl_pin!(P1_8, LPI2C2, Mux3, SdaPin); +impl_pin!(P1_9, LPI2C2, Mux3, SclPin); +impl_pin!(P1_10, LPI2C2, Mux3, SdaPin); +impl_pin!(P1_11, LPI2C2, Mux3, SclPin); +impl_pin!(P1_12, LPI2C1, Mux2, SdaPin); +impl_pin!(P1_13, LPI2C1, Mux2, SclPin); +impl_pin!(P1_14, LPI2C1, Mux2, SclPin); +impl_pin!(P1_15, LPI2C1, Mux2, SdaPin); +impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); +impl_pin!(P1_31, LPI2C0, Mux3, SclPin); +impl_pin!(P3_27, LPI2C3, Mux2, SclPin); +impl_pin!(P3_28, LPI2C3, Mux2, SdaPin); +// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin? +impl_pin!(P3_30, LPI2C3, Mux2, SclPin); +impl_pin!(P3_31, LPI2C3, Mux2, SdaPin); +impl_pin!(P4_2, LPI2C2, Mux2, SdaPin); +impl_pin!(P4_3, LPI2C0, Mux2, SclPin); +impl_pin!(P4_4, LPI2C2, Mux2, SdaPin); +impl_pin!(P4_5, LPI2C0, Mux2, SclPin); +// impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin? diff --git a/src/interrupt.rs b/src/interrupt.rs index 4d409067a..f2f1cccac 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -7,7 +7,9 @@ #![allow(clippy::missing_safety_doc)] mod generated { - embassy_hal_internal::interrupt_mod!(OS_EVENT, LPUART0, LPUART1, LPUART2, LPUART3, LPUART4, LPUART5, RTC, ADC1,); + embassy_hal_internal::interrupt_mod!( + OS_EVENT, LPUART0, LPI2C0, LPI2C1, LPI2C2, LPI2C3, LPUART1, LPUART2, LPUART3, LPUART4, LPUART5, RTC, ADC1, + ); } use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; diff --git a/src/lib.rs b/src/lib.rs index f9dda67d9..7fccc86c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod pins; // pin mux helpers pub mod adc; pub mod clkout; pub mod config; +pub mod i2c; pub mod interrupt; pub mod lpuart; pub mod ostimer; -- cgit