use crate::time::Hertz;
use crate::traits::wg::blocking::i2c::{Read, Write, WriteRead};
use crate::typestates::pin::{
flexcomm::{
I2c,
I2cPins,
},
PinId,
};
use core::cmp::min;
use core::marker::PhantomData;
pub mod prelude {
pub use super::Error as I2cError;
pub use super::I2cMaster;
pub use super::Result as I2cResult;
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Bus,
ArbitrationLoss,
NackAddress,
NackData,
StartStop,
}
pub type Result<T> = core::result::Result<T, Error>;
pub struct I2cMaster<PIO1, PIO2, I2C, PINS>
where
PIO1: PinId,
PIO2: PinId,
I2C: I2c,
PINS: I2cPins<PIO1, PIO2, I2C>,
{
i2c: I2C,
pins: PINS,
_pin1: PhantomData<PIO1>,
_pin2: PhantomData<PIO2>,
}
impl<PIO1, PIO2, I2C, PINS> I2cMaster<PIO1, PIO2, I2C, PINS>
where
PIO1: PinId,
PIO2: PinId,
I2C: I2c,
PINS: I2cPins<PIO1, PIO2, I2C>,
{
pub fn new<Speed: Into<Hertz>>(i2c: I2C, pins: PINS, speed: Speed) -> Self {
let speed: Hertz = speed.into();
let speed: u32 = speed.0;
assert!(speed <= 1_000_000);
i2c.cfg.modify(
|_, w| w.msten().enabled(), );
let mut best_div: u16 = 0;
let mut best_scl: u8 = 0;
let mut best_err: u32 = 0;
for scl in (2..=9).rev() {
let denominator = 2 * scl * speed;
let div = min(10_000, 12_000_000 / denominator);
let err = 12_000_000 - div * denominator;
if err < best_err || best_err == 0 {
best_div = div as u16; best_scl = scl as u8; best_err = err;
}
if err == 0 || div >= 10_000 {
break;
}
}
i2c.clkdiv
.modify(|_, w| unsafe { w.divval().bits(best_div - 1) });
i2c.msttime.modify(|_, w| {
w.mstsclhigh()
.bits(best_scl - 2)
.mstscllow()
.bits(best_scl - 2)
});
Self {
i2c,
pins,
_pin1: PhantomData,
_pin2: PhantomData,
}
}
pub fn release(self) -> (I2C, PINS) {
(self.i2c, self.pins)
}
#[inline(always)]
fn return_on_error(&self) -> Result<()> {
if self.i2c.stat.read().mststate().is_nack_data() {
return Err(Error::NackData);
}
if self.i2c.stat.read().mststate().is_nack_address() {
return Err(Error::NackAddress);
}
if self.i2c.stat.read().mstarbloss().is_arbitration_loss() {
return Err(Error::ArbitrationLoss);
}
if self.i2c.stat.read().mstststperr().is_error() {
return Err(Error::StartStop);
}
Ok(())
}
fn write_without_stop(&mut self, addr: u8, bytes: &[u8]) -> Result<()> {
self.return_on_error()?;
self.i2c
.mstdat
.modify(|_, w| unsafe { w.data().bits(addr << 1) });
self.i2c.mstctl.write(|w| w.mststart().start());
while self.i2c.stat.read().mstpending().is_in_progress() {
continue;
}
self.return_on_error()?;
if !self.i2c.stat.read().mststate().is_transmit_ready() {
return Err(Error::Bus);
}
for byte in bytes {
self.i2c
.mstdat
.modify(|_, w| unsafe { w.data().bits(*byte) });
self.i2c.mstctl.write(|w| w.mstcontinue().continue_());
while self.i2c.stat.read().mstpending().is_in_progress() {
continue;
}
self.return_on_error()?;
if !self.i2c.stat.read().mststate().is_transmit_ready() {
return Err(Error::Bus);
}
}
Ok(())
}
fn stop(&mut self) -> Result<()> {
self.i2c.mstctl.write(|w| w.mststop().stop());
while self.i2c.stat.read().mstpending().is_in_progress() {}
self.return_on_error()?;
if !self.i2c.stat.read().mststate().is_idle() {
return Err(Error::Bus);
}
Ok(())
}
}
impl<PIO1, PIO2, I2C, PINS> Write for I2cMaster<PIO1, PIO2, I2C, PINS>
where
PIO1: PinId,
PIO2: PinId,
I2C: I2c,
PINS: I2cPins<PIO1, PIO2, I2C>,
{
type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<()> {
self.write_without_stop(addr, bytes)?;
self.stop()
}
}
impl<PIO1, PIO2, I2C, PINS> Read for I2cMaster<PIO1, PIO2, I2C, PINS>
where
PIO1: PinId,
PIO2: PinId,
I2C: I2c,
PINS: I2cPins<PIO1, PIO2, I2C>,
{
type Error = Error;
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<()> {
if let Some((last, buffer)) = buffer.split_last_mut() {
self.i2c
.mstdat
.modify(|_, w| unsafe { w.data().bits((addr << 1) | 1) });
self.i2c.mstctl.write(|w| w.mststart().start());
while self.i2c.stat.read().mstpending().is_in_progress() {}
self.return_on_error()?;
if !self.i2c.stat.read().mststate().is_receive_ready() {
return Err(Error::Bus);
}
for byte in buffer {
*byte = self.i2c.mstdat.read().data().bits();
self.i2c.mstctl.write(|w| w.mstcontinue().continue_());
while self.i2c.stat.read().mstpending().is_in_progress() {}
self.return_on_error()?;
if !self.i2c.stat.read().mststate().is_receive_ready() {
return Err(Error::Bus);
}
}
*last = self.i2c.mstdat.read().data().bits();
self.stop()?;
}
Ok(())
}
}
impl<PIO1, PIO2, I2C, PINS> WriteRead for I2cMaster<PIO1, PIO2, I2C, PINS>
where
PIO1: PinId,
PIO2: PinId,
I2C: I2c,
PINS: I2cPins<PIO1, PIO2, I2C>,
{
type Error = Error;
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
self.write_without_stop(addr, bytes)?;
self.read(addr, buffer)?;
Ok(())
}
}