use core::{marker::PhantomData, ops::Deref};
use fugit::HertzU32;
use rp235x_pac::i2c0::ic_con::IC_10BITADDR_SLAVE_A;
use crate::{
gpio::{bank0::*, pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionI2c, PullUp},
pac::{
i2c0::{ic_con::IC_10BITADDR_MASTER_A, RegisterBlock},
I2C0, I2C1, RESETS,
},
resets::SubsystemReset,
typelevel::Sealed,
};
mod controller;
pub mod peripheral;
pub trait I2cDevice: Deref<Target = RegisterBlock> + SubsystemReset + Sealed {
const ID: usize;
}
impl Sealed for I2C0 {}
impl I2cDevice for I2C0 {
const ID: usize = 0;
}
impl Sealed for I2C1 {}
impl I2cDevice for I2C1 {
const ID: usize = 1;
}
pub trait ValidAddress:
Into<u16> + embedded_hal::i2c::AddressMode + embedded_hal_0_2::blocking::i2c::AddressMode + Copy
{
const BIT_ADDR_M: IC_10BITADDR_MASTER_A;
const BIT_ADDR_S: IC_10BITADDR_SLAVE_A;
fn is_valid(self) -> Result<(), Error>;
}
impl ValidAddress for u8 {
const BIT_ADDR_M: IC_10BITADDR_MASTER_A = IC_10BITADDR_MASTER_A::ADDR_7BITS;
const BIT_ADDR_S: IC_10BITADDR_SLAVE_A = IC_10BITADDR_SLAVE_A::ADDR_7BITS;
fn is_valid(self) -> Result<(), Error> {
if self >= 0x80 {
Err(Error::AddressOutOfRange(self.into()))
} else {
Ok(())
}
}
}
impl ValidAddress for u16 {
const BIT_ADDR_M: IC_10BITADDR_MASTER_A = IC_10BITADDR_MASTER_A::ADDR_10BITS;
const BIT_ADDR_S: IC_10BITADDR_SLAVE_A = IC_10BITADDR_SLAVE_A::ADDR_10BITS;
fn is_valid(self) -> Result<(), Error> {
Ok(())
}
}
#[non_exhaustive]
pub enum Error {
Abort(u32),
InvalidReadBufferLength,
InvalidWriteBufferLength,
AddressOutOfRange(u16),
AddressReserved(u16),
}
impl core::fmt::Debug for Error {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use embedded_hal::i2c::Error as _;
match self {
Error::InvalidReadBufferLength => write!(fmt, "InvalidReadBufferLength"),
Error::InvalidWriteBufferLength => write!(fmt, "InvalidWriteBufferLength"),
Error::AddressOutOfRange(addr) => write!(fmt, "AddressOutOfRange({addr:x})"),
Error::AddressReserved(addr) => write!(fmt, "AddressReserved({addr:x})"),
Error::Abort(_) => {
write!(fmt, "{:?}", self.kind())
}
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Error {
fn format(&self, fmt: defmt::Formatter) {
use embedded_hal::i2c::Error as _;
match self {
Error::InvalidReadBufferLength => defmt::write!(fmt, "InvalidReadBufferLength"),
Error::InvalidWriteBufferLength => defmt::write!(fmt, "InvalidWriteBufferLength"),
Error::AddressOutOfRange(addr) => defmt::write!(fmt, "AddressOutOfRange({:x})", addr),
Error::AddressReserved(addr) => defmt::write!(fmt, "AddressReserved({:x})", addr),
Error::Abort(_) => {
defmt::write!(fmt, "{}", defmt::Debug2Format(&self.kind()))
}
}
}
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
match &self {
Error::Abort(v) if v & (1<<12) != 0 => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
Error::Abort(v) if v & (1<<7) != 0 => embedded_hal::i2c::ErrorKind::Bus,
Error::Abort(v) if v & (1<<6) != 0 => embedded_hal::i2c::ErrorKind::Bus,
Error::Abort(v) if v & (1<<4) != 0 => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Address),
Error::Abort(v) if v & (1<<3) != 0 => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Data),
Error::Abort(v) if v & (1<<2) != 0 => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Address),
Error::Abort(v) if v & (1<<1) != 0 => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Address),
Error::Abort(v) if v & (1<<0) != 0 => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Address),
_ => embedded_hal::i2c::ErrorKind::Other,
}
}
}
macro_rules! pin_validation {
($p:ident) => {
paste::paste!{
#[doc = "Marker for PinId that can serve as " $p]
pub trait [<ValidPinId $p>]<I2C>: Sealed {}
#[doc = "Valid " $p]
pub trait [<ValidPin $p>]<I2C>: Sealed {}
impl<T, U: I2cDevice> [<ValidPin $p>]<U> for T
where
T: AnyPin<Function = FunctionI2c>,
T::Id: [<ValidPinId $p>]<U>,
{
}
#[doc = "A runtime validated " $p " pin for I2C."]
pub struct [<ValidatedPin $p>]<P, I2C>(P, PhantomData<I2C>);
impl<P, I2C: I2cDevice> Sealed for [<ValidatedPin $p>]<P, I2C> {}
impl<P, I2C: I2cDevice> [<ValidPin $p>]<I2C> for [<ValidatedPin $p>]<P, I2C> {}
impl<P, S> [<ValidatedPin $p>]<P, S>
where
P: AnyPin<Function = FunctionI2c>,
S: I2cDevice,
{
#[doc = "Will err if the pin cannot be used as a " $p " pin for that I2C."]
pub fn validate(p: P, _u: &S) -> Result<Self, P> {
if [<$p:upper>].contains(&(p.borrow().id().num, S::ID)) &&
p.borrow().id().bank == crate::gpio::DynBankId::Bank0 {
Ok(Self(p, PhantomData))
} else {
Err(p)
}
}
}
}
};
($($p:ident),*) => {
$(
pin_validation!($p);
)*
};
}
pin_validation!(Scl, Sda);
macro_rules! valid_pins {
($($i2c:ident: {
sda: [$($sda:ident),*],
scl: [$($scl:ident),*]
}),*) => {
$(
$(impl ValidPinIdSda<$i2c> for $sda {})*
$(impl ValidPinIdScl<$i2c> for $scl {})*
)*
const SDA: &[(u8, usize)] = &[$($(($sda::ID.num, $i2c::ID)),*),*];
const SCL: &[(u8, usize)] = &[$($(($scl::ID.num, $i2c::ID)),*),*];
};
}
valid_pins! {
I2C0: {
sda: [Gpio0, Gpio4, Gpio8, Gpio12, Gpio16, Gpio20, Gpio24, Gpio28, Gpio32, Gpio36, Gpio40, Gpio44],
scl: [Gpio1, Gpio5, Gpio9, Gpio13, Gpio17, Gpio21, Gpio25, Gpio29, Gpio33, Gpio37, Gpio41, Gpio45]
},
I2C1: {
sda: [Gpio2, Gpio6, Gpio10, Gpio14, Gpio18, Gpio22, Gpio26, Gpio30, Gpio34, Gpio38, Gpio42, Gpio46],
scl: [Gpio3, Gpio7, Gpio11, Gpio15, Gpio19, Gpio23, Gpio27, Gpio31, Gpio35, Gpio39, Gpio43, Gpio47]
}
}
pub trait I2CMode: Sealed {
const IS_CONTROLLER: bool;
}
pub struct Controller {}
impl Sealed for Controller {}
impl I2CMode for Controller {
const IS_CONTROLLER: bool = true;
}
pub struct Peripheral {
state: peripheral::State,
}
impl Sealed for Peripheral {}
impl I2CMode for Peripheral {
const IS_CONTROLLER: bool = false;
}
pub struct I2C<I2C, Pins, Mode = Controller> {
i2c: I2C,
pins: Pins,
mode: Mode,
}
impl<Block, Sda, Scl, Mode> I2C<Block, (Sda, Scl), Mode>
where
Block: SubsystemReset + Deref<Target = RegisterBlock>,
{
#[allow(clippy::type_complexity)]
pub fn free(self, resets: &mut RESETS) -> (Block, (Sda, Scl)) {
self.i2c.reset_bring_down(resets);
(self.i2c, self.pins)
}
}
impl<Block: Deref<Target = RegisterBlock>, PINS, Mode> I2C<Block, PINS, Mode> {
pub const TX_FIFO_DEPTH: u8 = 16;
pub const RX_FIFO_DEPTH: u8 = 16;
#[inline]
pub fn rx_fifo_used(&self) -> u8 {
self.i2c.ic_rxflr().read().rxflr().bits()
}
#[inline]
pub fn rx_fifo_available(&self) -> u8 {
Self::RX_FIFO_DEPTH - self.rx_fifo_used()
}
#[inline]
pub fn rx_fifo_empty(&self) -> bool {
self.i2c.ic_status().read().rfne().bit_is_clear()
}
#[inline]
pub fn tx_fifo_used(&self) -> u8 {
self.i2c.ic_txflr().read().txflr().bits()
}
#[inline]
pub fn tx_fifo_available(&self) -> u8 {
Self::TX_FIFO_DEPTH - self.tx_fifo_used()
}
#[inline]
pub fn tx_fifo_full(&self) -> bool {
self.i2c.ic_status().read().tfnf().bit_is_clear()
}
}
macro_rules! hal {
($($I2CX:ident: ($i2cX:ident),)+) => {
$(
impl<Sda, Scl> I2C<$I2CX, (Sda, Scl)> {
pub fn $i2cX<F, SystemF>(
i2c: $I2CX,
sda_pin: Sda,
scl_pin: Scl,
freq: F,
resets: &mut RESETS,
system_clock: SystemF) -> Self
where
F: Into<HertzU32>,
Sda: ValidPinSda<$I2CX> + AnyPin<Pull = PullUp>,
Scl: ValidPinScl<$I2CX> + AnyPin<Pull = PullUp>,
SystemF: Into<HertzU32>,
{
Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into())
}
$crate::paste::paste! {
pub fn [<$i2cX _with_external_pull_up>]<F, SystemF>(
i2c: $I2CX,
sda_pin: Sda,
scl_pin: Scl,
freq: F,
resets: &mut RESETS,
system_clock: SystemF) -> Self
where
F: Into<HertzU32>,
Sda: ValidPinSda<$I2CX>,
Scl: ValidPinScl<$I2CX>,
SystemF: Into<HertzU32>,
{
Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into())
}
}
}
impl<P, M> $crate::async_utils::sealed::Wakeable for I2C<$I2CX, P, M> {
fn waker() -> &'static $crate::async_utils::sealed::IrqWaker {
static WAKER: $crate::async_utils::sealed::IrqWaker =
$crate::async_utils::sealed::IrqWaker::new();
&WAKER
}
}
)+
}
}
hal! {
I2C0: (i2c0),
I2C1: (i2c1),
}