#![macro_use]
use embedded_hal as embedded_hal_1;
use crate::csdk;
use crate::mode::{Async, Blocking, Mode};
use core::future::Future;
use core::marker::PhantomData;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Bus,
Arbitration,
Nack,
Timeout,
Crc,
Overrun,
#[cfg(feature = "peri-dma")]
Dma,
#[cfg(feature = "peri-dma")]
DmaParam,
Size,
Csdk,
Other,
}
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
pub init: csdk::I2C_InitTypeDef,
#[cfg(feature = "time")]
pub timeout: embassy_time::Duration,
#[cfg(not(feature = "time"))]
pub timeout_tick: u32,
}
impl Default for Config {
fn default() -> Self {
Self {
init: csdk::I2C_InitTypeDef {
ClockSpeed: 100000,
DutyCycle: csdk::I2C_DUTYCYCLE_16_9,
OwnAddress1: 0xA0,
GeneralCallMode: csdk::I2C_GENERALCALL_DISABLE,
NoStretchMode: csdk::I2C_NOSTRETCH_DISABLE,
},
#[cfg(feature = "time")]
timeout: embassy_time::Duration::from_secs(2),
#[cfg(not(feature = "time"))]
timeout_tick: 2000,
}
}
}
pub struct I2c<M: Mode> {
pub handle: csdk::I2C_HandleTypeDef,
#[cfg(feature = "time")]
pub timeout: embassy_time::Duration,
#[cfg(not(feature = "time"))]
timeout_tick: u32,
_phantom: PhantomData<M>,
}
impl I2c<Blocking> {
pub fn new_blocking_from_csdk(instance: *mut csdk::I2C_TypeDef, config: Config) -> Result<Self, Error> {
Self::new_from_csdk(instance, config)
}
#[cfg(feature = "peri-i2c0")]
pub fn new_blocking(config: Config) -> Result<Self, Error> {
let instance = csdk::I2C;
Self::new_from_csdk(instance, config)
}
#[cfg(not(feature = "peri-i2c0"))]
pub fn new_blocking(instance_num: u8, config: Config) -> Result<Self, Error> {
let instance = match instance_num {
#[cfg(feature = "peri-i2c1")]
1 => csdk::I2C1,
#[cfg(feature = "peri-i2c2")]
2 => csdk::I2C2,
_ => panic("unknown i2c id"),
};
let instance = csdk::I2C;
Self::new_from_csdk(instance, config)
}
}
impl<M: Mode> I2c<M> {
fn new_from_csdk(instance: *mut csdk::I2C_TypeDef, config: Config) -> Result<Self, Error> {
let handle = csdk::I2C_HandleTypeDef {
Instance: instance,
Init: config.init,
pBuffPtr: core::ptr::null_mut(),
XferSize: 0,
XferCount: 0,
XferOptions: 0,
PreviousState: 0,
hdmatx: core::ptr::null_mut(),
hdmarx: core::ptr::null_mut(),
Lock: 0,
State: 0,
Mode: 0,
ErrorCode: 0,
Devaddress: 0,
Memaddress: 0,
MemaddSize: 0,
EventCount: 0,
};
let mut this = Self {
handle,
#[cfg(feature = "time")]
timeout: config.timeout,
#[cfg(not(feature = "time"))]
timeout_tick: config.timeout_tick,
_phantom: Default::default(),
};
this.enable_and_init()?;
Ok(this)
}
fn enable_and_init(&mut self) -> Result<(), Error> {
unsafe{
match self.handle.Instance {
#[cfg(feature = "peri-i2c0")]
csdk::I2C => {
csdk::HAL_RCC_I2C_CLK_ENABLE();
csdk::HAL_RCC_I2C_FORCE_RESET();
csdk::HAL_RCC_I2C_RELEASE_RESET();
Ok(())
},
#[cfg(feature = "peri-i2c1")]
csdk::I2C1 => {
csdk::HAL_RCC_I2C1_CLK_ENABLE();
csdk::HAL_RCC_I2C1_FORCE_RESET();
csdk::HAL_RCC_I2C1_RELEASE_RESET();
Ok(())
},
#[cfg(feature = "peri-i2c2")]
csdk::I2C2 => {
csdk::HAL_RCC_I2C2_CLK_ENABLE();
csdk::HAL_RCC_I2C2_FORCE_RESET();
csdk::HAL_RCC_I2C2_RELEASE_RESET();
Ok(())
},
_ => Err(Error::Csdk),
}?;
match csdk::HAL_I2C_Init(&mut self.handle) {
csdk::HAL_StatusTypeDef_HAL_OK => Ok(()),
_ => Err(Error::Csdk),
}
}
}
fn timeout(&self) -> Timeout {
Timeout {
#[cfg(feature = "time")]
deadline: Instant::now() + self.timeout,
}
}
}
impl<M: Mode> I2c<M> {
fn get_error_code(&self, result: u32) -> Result<(), Error> {
if result == csdk::HAL_StatusTypeDef_HAL_OK {
Ok(())
} else {
let err = match self.handle.ErrorCode {
csdk::HAL_I2C_ERROR_NONE => Error::Csdk,
csdk::HAL_I2C_ERROR_BERR => Error::Bus,
csdk::HAL_I2C_ERROR_ARLO => Error::Arbitration,
csdk::HAL_I2C_ERROR_AF => Error::Nack,
csdk::HAL_I2C_ERROR_OVR => Error::Overrun,
#[cfg(feature = "peri-dma")]
csdk::HAL_I2C_ERROR_DMA => Error::Dma,
#[cfg(feature = "peri-dma")]
csdk::HAL_I2C_ERROR_DMA_PARAM => Error::DmaParam,
csdk::HAL_I2C_ERROR_TIMEOUT => Error::Timeout,
csdk::HAL_I2C_ERROR_SIZE => Error::Size,
_ => Error::Other,
};
Err(err)
}
}
fn get_timeout_tick(&self) -> u32 {
#[cfg(feature = "time")]
let timout_tick = self.timeout.as_ticks() as u32;
#[cfg(not(feature = "time"))]
let timout_tick = self.timeout_tick;
timout_tick
}
fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
let result = unsafe {
csdk::HAL_I2C_Master_Receive(
&mut self.handle,
(address as u16) << 1,
read.as_mut_ptr(),
read.len() as u16,
self.get_timeout_tick(),
)
};
self.get_error_code(result)
}
fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
let result = unsafe {
csdk::HAL_I2C_Master_Transmit(
&mut self.handle,
(address as u16) << 1,
write.as_ptr() as *mut u8,
write.len() as u16,
self.get_timeout_tick(),
)
};
self.get_error_code(result)
}
fn blocking_write_read(
&mut self,
address: u8,
write: &[u8],
read: &mut [u8],
) -> Result<(), Error> {
self.blocking_read(address, read)?;
self.blocking_write(address, write)?;
Ok(())
}
fn blocking_transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Error> {
for op in operations {
match op {
embedded_hal_1::i2c::Operation::Read(read) => self.blocking_read(address, read)?,
embedded_hal_1::i2c::Operation::Write(write) => self.blocking_write(address, write)?,
}
}
Ok(())
}
}
#[derive(Copy, Clone)]
struct Timeout {
#[cfg(feature = "time")]
deadline: Instant,
}
#[allow(dead_code)]
impl Timeout {
#[inline]
fn check(self) -> Result<(), Error> {
#[cfg(feature = "time")]
if Instant::now() > self.deadline {
return Err(Error::Timeout);
}
Ok(())
}
#[inline]
fn with<R>(
self,
fut: impl Future<Output = Result<R, Error>>,
) -> impl Future<Output = Result<R, Error>> {
#[cfg(feature = "time")]
{
use futures_util::FutureExt;
embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| {
match r {
embassy_futures::select::Either::First(_) => Err(Error::Timeout),
embassy_futures::select::Either::Second(r) => r,
}
})
}
#[cfg(not(feature = "time"))]
fut
}
}
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Nack => embedded_hal_1::i2c::ErrorKind::NoAcknowledge(
embedded_hal_1::i2c::NoAcknowledgeSource::Unknown,
),
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
#[cfg(feature = "peri-dma")]
Self::Dma => embedded_hal_1::i2c::ErrorKind::Other,
#[cfg(feature = "peri-dma")]
Self::DmaParam => embedded_hal_1::i2c::ErrorKind::Other,
Self::Size => embedded_hal_1::i2c::ErrorKind::Other,
Self::Csdk => embedded_hal_1::i2c::ErrorKind::Other,
Self::Other => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<M: Mode> embedded_hal_1::i2c::ErrorType for I2c<M> {
type Error = Error;
}
impl<M: Mode> embedded_hal_1::i2c::I2c for I2c<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.blocking_transaction(address, operations)
}
}