use alloc::boxed::Box;
use core::future::Future;
use core::pin::Pin;
use embedded_hal::i2c::{ErrorKind, Operation, SevenBitAddress};
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub enum I2cError {
Abort,
ArbitrationLoss,
InvalidBufferLength,
AddressOutOfRange,
UnsupportedTransaction,
Other,
}
impl core::fmt::Display for I2cError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self)
}
}
impl core::error::Error for I2cError {}
impl embedded_hal::i2c::Error for I2cError {
fn kind(&self) -> ErrorKind {
match self {
I2cError::Abort => {
ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Unknown)
}
I2cError::ArbitrationLoss => ErrorKind::ArbitrationLoss,
I2cError::InvalidBufferLength => ErrorKind::Other,
I2cError::AddressOutOfRange => ErrorKind::Other,
I2cError::UnsupportedTransaction => ErrorKind::Other,
I2cError::Other => ErrorKind::Other,
}
}
}
impl From<embassy_rp::i2c::Error> for I2cError {
fn from(e: embassy_rp::i2c::Error) -> Self {
match e {
embassy_rp::i2c::Error::Abort(embassy_rp::i2c::AbortReason::ArbitrationLoss) => {
I2cError::ArbitrationLoss
}
embassy_rp::i2c::Error::Abort(_) => I2cError::Abort,
embassy_rp::i2c::Error::InvalidReadBufferLength
| embassy_rp::i2c::Error::InvalidWriteBufferLength => I2cError::InvalidBufferLength,
embassy_rp::i2c::Error::AddressOutOfRange(_) => I2cError::AddressOutOfRange,
_ => I2cError::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub enum I2cBusVersion {
Hardware,
BitBang,
}
pub trait DynI2cBus: Send {
fn read<'a>(
&'a mut self,
address: u8,
read: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>>;
fn write<'a>(
&'a mut self,
address: u8,
write: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>>;
fn write_read<'a>(
&'a mut self,
address: u8,
write: &'a [u8],
read: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>>;
fn transaction<'a, 'op>(
&'a mut self,
address: u8,
operations: &'a mut [Operation<'op>],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>>
where
'op: 'a;
}
#[must_use = "dropping a bus handle does not return its startup resources"]
pub struct I2cBusHandle {
inner: Box<dyn DynI2cBus>,
version: I2cBusVersion,
}
impl I2cBusHandle {
pub fn new(inner: Box<dyn DynI2cBus>, version: I2cBusVersion) -> Self {
Self { inner, version }
}
pub fn version(&self) -> I2cBusVersion {
self.version
}
pub async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cError> {
self.inner.read(address, read).await
}
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cError> {
self.inner.write(address, write).await
}
pub async fn write_read(
&mut self,
address: u8,
write: &[u8],
read: &mut [u8],
) -> Result<(), I2cError> {
self.inner.write_read(address, write, read).await
}
pub async fn transaction(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), I2cError> {
self.inner.transaction(address, operations).await
}
}
impl embedded_hal::i2c::ErrorType for I2cBusHandle {
type Error = I2cError;
}
impl embedded_hal_async::i2c::I2c<SevenBitAddress> for I2cBusHandle {
async fn read(&mut self, address: SevenBitAddress, read: &mut [u8]) -> Result<(), I2cError> {
self.inner.read(address, read).await
}
async fn write(&mut self, address: SevenBitAddress, write: &[u8]) -> Result<(), I2cError> {
self.inner.write(address, write).await
}
async fn write_read(
&mut self,
address: SevenBitAddress,
write: &[u8],
read: &mut [u8],
) -> Result<(), I2cError> {
self.inner.write_read(address, write, read).await
}
async fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), I2cError> {
self.inner.transaction(address, operations).await
}
}
impl<T> DynI2cBus for T
where
T: embedded_hal_async::i2c::I2c<SevenBitAddress>,
T::Error: Into<I2cError>,
T: Send,
{
fn read<'a>(
&'a mut self,
address: u8,
read: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>> {
Box::pin(async move {
if address > 0x7f {
return Err(I2cError::AddressOutOfRange);
}
embedded_hal_async::i2c::I2c::read(self, address, read)
.await
.map_err(Into::into)
})
}
fn write<'a>(
&'a mut self,
address: u8,
write: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>> {
Box::pin(async move {
if address > 0x7f {
return Err(I2cError::AddressOutOfRange);
}
embedded_hal_async::i2c::I2c::write(self, address, write)
.await
.map_err(Into::into)
})
}
fn write_read<'a>(
&'a mut self,
address: u8,
write: &'a [u8],
read: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>> {
Box::pin(async move {
if address > 0x7f {
return Err(I2cError::AddressOutOfRange);
}
embedded_hal_async::i2c::I2c::write_read(self, address, write, read)
.await
.map_err(Into::into)
})
}
fn transaction<'a, 'op>(
&'a mut self,
address: u8,
operations: &'a mut [Operation<'op>],
) -> Pin<Box<dyn Future<Output = Result<(), I2cError>> + 'a>>
where
'op: 'a,
{
Box::pin(async move {
if address > 0x7f {
return Err(I2cError::AddressOutOfRange);
}
embedded_hal_async::i2c::I2c::transaction(self, address, operations)
.await
.map_err(Into::into)
})
}
}