use core::ops::Deref;
use crate::pac::{bsc0, BSC0, BSC1, GPIO};
use crate::timer::Timer;
#[cfg(feature = "async")]
mod asynch;
#[cfg(feature = "async")]
pub use asynch::on_irq;
const TIMEOUT_BASE_US: u64 = 5_000;
const TIMEOUT_PER_BYTE_US: u64 = 1_000;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
NoAcknowledge,
ZeroLengthUnsupported,
Timeout,
Incomplete {
received: usize,
requested: usize,
},
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
match self {
Self::NoAcknowledge => embedded_hal::i2c::ErrorKind::NoAcknowledge(
embedded_hal::i2c::NoAcknowledgeSource::Unknown,
),
Self::ZeroLengthUnsupported | Self::Timeout | Self::Incomplete { .. } => {
embedded_hal::i2c::ErrorKind::Other
}
}
}
}
pub fn divider_for(core_hz: u32, target_hz: u32) -> u16 {
let exact = core_hz.div_ceil(target_hz.max(1));
exact.saturating_add(exact % 2).clamp(2, 65534) as u16
}
pub struct I2c<'a, I = BSC1> {
bsc: I,
timer: &'a Timer,
}
impl<'a> I2c<'a, BSC1> {
pub fn init(gpio: &GPIO, bsc1: BSC1, clock_divider: u16, timer: &'a Timer) -> Self {
gpio.gpfsel0()
.modify(|_, w| w.fsel2().sda1().fsel3().scl1());
Self::configure(bsc1, clock_divider, timer)
}
}
impl<'a> I2c<'a, BSC0> {
pub fn init(gpio: &GPIO, bsc0: BSC0, clock_divider: u16, timer: &'a Timer) -> Self {
gpio.gpfsel4()
.modify(|_, w| w.fsel44().sda0().fsel45().scl0());
Self::configure(bsc0, clock_divider, timer)
}
pub fn init_id(gpio: &GPIO, bsc0: BSC0, clock_divider: u16, timer: &'a Timer) -> Self {
gpio.gpfsel0()
.modify(|_, w| w.fsel0().sda0().fsel1().scl0());
Self::configure(bsc0, clock_divider, timer)
}
}
impl<'a, I: Deref<Target = bsc0::RegisterBlock>> I2c<'a, I> {
fn configure(bsc: I, clock_divider: u16, timer: &'a Timer) -> Self {
unsafe {
bsc.div().write(|w| w.cdiv().bits(clock_divider));
}
bsc.c().write(|w| w.i2cen().bit(true));
Self { bsc, timer }
}
fn timeout_us(len: usize) -> u64 {
TIMEOUT_BASE_US + TIMEOUT_PER_BYTE_US * len as u64
}
fn abandon(&mut self) {
self.bsc.c().write(|w| {
unsafe { w.clear().bits(0b11) };
w.i2cen().bit(true)
});
self.clear_status();
}
fn clear_status(&self) {
self.bsc
.s()
.write(|w| w.done().bit(true).err().bit(true).clkt().bit(true));
}
fn one_shot(&mut self, address: u8, is_read: bool, len: usize) {
self.bsc.a().write(|w| unsafe { w.addr().bits(address) });
self.clear_status();
unsafe {
self.bsc.dlen().write(|w| w.dlen().bits(len as u16));
}
self.bsc.c().write(|w| {
unsafe { w.clear().bits(0b11) };
w.i2cen().bit(true).read().bit(is_read).st().set_bit()
});
}
fn write_one(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
if bytes.is_empty() {
return Err(Error::ZeroLengthUnsupported);
}
self.one_shot(address, false, bytes.len());
let deadline = self.timer.now_micros() + Self::timeout_us(bytes.len());
let mut sent = 0;
loop {
let status = self.bsc.s().read();
if status.err().bit_is_set() {
self.clear_status();
return Err(Error::NoAcknowledge);
}
if status.clkt().bit_is_set() {
self.abandon();
return Err(Error::Timeout);
}
if sent < bytes.len() && status.txd().bit_is_set() {
unsafe {
self.bsc.fifo().write(|w| w.data().bits(bytes[sent]));
}
sent += 1;
}
if status.done().bit_is_set() {
break;
}
if self.timer.now_micros() > deadline {
self.abandon();
return Err(Error::Timeout);
}
}
self.clear_status();
Ok(())
}
fn read_one(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::ZeroLengthUnsupported);
}
self.one_shot(address, true, buffer.len());
let deadline = self.timer.now_micros() + Self::timeout_us(buffer.len());
let mut received = 0;
loop {
let status = self.bsc.s().read();
if status.err().bit_is_set() {
self.clear_status();
return Err(Error::NoAcknowledge);
}
if status.clkt().bit_is_set() {
self.abandon();
return Err(Error::Timeout);
}
if received < buffer.len() && status.rxd().bit_is_set() {
buffer[received] = self.bsc.fifo().read().data().bits();
received += 1;
}
if status.done().bit_is_set() && received >= buffer.len() {
break;
}
if self.timer.now_micros() > deadline {
let complete = self.bsc.s().read().done().bit_is_set();
self.abandon();
return Err(if complete {
Error::Incomplete {
received,
requested: buffer.len(),
}
} else {
Error::Timeout
});
}
}
self.clear_status();
Ok(())
}
}
impl<I: Deref<Target = bsc0::RegisterBlock>> embedded_hal::i2c::ErrorType for I2c<'_, I> {
type Error = Error;
}
impl<I: Deref<Target = bsc0::RegisterBlock>> embedded_hal::i2c::I2c for I2c<'_, I> {
fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
embedded_hal::i2c::Operation::Read(buffer) => self.read_one(address, buffer)?,
embedded_hal::i2c::Operation::Write(bytes) => self.write_one(address, bytes)?,
}
}
Ok(())
}
}