e4ad1aa542
Change embassy-rp i2c.rs impl of embedded_hal_async::i2c::I2c::transaction to only do the call to setup() for address once per call to transactions. Calling setup multiple times results in I2C transactions being skipped on the bus, even across calls to transaction() or devices.
858 lines
28 KiB
Rust
858 lines
28 KiB
Rust
use core::future;
|
|
use core::marker::PhantomData;
|
|
use core::task::Poll;
|
|
|
|
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::interrupt::typelevel::{Binding, Interrupt};
|
|
use crate::{interrupt, 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 Binding<T::Interrupt, InterruptHandler<T>>,
|
|
config: Config,
|
|
) -> Self {
|
|
into_ref!(scl, sda);
|
|
|
|
let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
|
|
|
|
let r = T::regs();
|
|
|
|
// mask everything initially
|
|
r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
|
|
T::Interrupt::unpend();
|
|
unsafe { T::Interrupt::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
|
|
}
|
|
|
|
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;
|
|
|
|
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| {
|
|
// 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 = 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();
|
|
|
|
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| {
|
|
// 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| {
|
|
// 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| {
|
|
p.ic_intr_mask().modify(|w| {
|
|
w.set_m_stop_det(true);
|
|
w.set_m_tx_abrt(true);
|
|
});
|
|
},
|
|
)
|
|
.await;
|
|
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
|
|
}
|
|
}
|
|
|
|
pub struct InterruptHandler<T: Instance> {
|
|
_uart: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
|
// Mask interrupts and wake any task waiting for this interrupt
|
|
unsafe fn on_interrupt() {
|
|
let i2c = T::regs();
|
|
i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
|
|
|
|
T::waker().wake();
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
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();
|
|
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();
|
|
FIFO_SIZE - p.ic_txflr().read().txflr()
|
|
}
|
|
|
|
#[inline]
|
|
fn rx_fifo_len() -> u8 {
|
|
let p = T::regs();
|
|
p.ic_rxflr().read().rxflr()
|
|
}
|
|
|
|
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
|
|
let p = T::regs();
|
|
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;
|
|
|
|
// 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;
|
|
|
|
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();
|
|
|
|
if operations.len() > 0 {
|
|
Self::setup(addr)?;
|
|
}
|
|
let mut iterator = operations.iter_mut();
|
|
|
|
while let Some(op) = iterator.next() {
|
|
let last = iterator.len() == 0;
|
|
|
|
match op {
|
|
Operation::Read(buffer) => {
|
|
self.read_async_internal(buffer, false, last).await?;
|
|
}
|
|
Operation::Write(buffer) => {
|
|
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_sync::waitqueue::AtomicWaker;
|
|
|
|
use crate::interrupt;
|
|
|
|
pub trait Instance {
|
|
const TX_DREQ: u8;
|
|
const RX_DREQ: u8;
|
|
|
|
type Interrupt: interrupt::typelevel::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::typelevel::$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);
|