use embedded_hal::{delay::DelayNs, digital::OutputPin, i2c::{NoAcknowledgeSource, SevenBitAddress, TenBitAddress}};
use crate::{gpio::{BidirectionPin, BidirectionPinMode}, tick::Delay};
pub const FREQ_I2C_SCL_100K: u32 = 100_000;
pub const FREQ_I2C_SCL_400K: u32 = 400_000;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SoftI2CError {
Address,
WriteData,
}
#[derive(Debug, Clone, Copy)]
pub enum SoftI2CAddr {
SevenBitAddress(u8),
TenBitAddress(u16)
}
pub struct SoftI2C<C, D, const CLK_HZ: u32> {
scl: C,
sda: D,
delay: Delay,
}
impl<C, D, const CLK_HZ: u32> SoftI2C<C, D, CLK_HZ>
where
C: OutputPin,
D: BidirectionPin,
{
pub fn new(mut scl: C, sda: D) -> Self {
let delay = Delay::new();
scl.set_high().ok();
sda.mode_ctrl(BidirectionPinMode::OutOD);
sda.set(true);
SoftI2C { scl, sda, delay }
}
#[inline]
fn i2c_delay(&mut self) {
self.delay.delay_ns(500_000_000/CLK_HZ);
}
fn scl_hi(&mut self) {
self.scl.set_high().ok();
self.i2c_delay();
}
fn scl_lo(&mut self) {
self.scl.set_low().ok();
self.i2c_delay();
}
fn sda_hi(&mut self) {
self.sda.set(true);
self.i2c_delay();
}
fn sda_lo(&mut self) {
self.sda.set(false);
self.i2c_delay();
}
#[inline]
fn sda_input(&mut self) {
self.sda.mode_ctrl(BidirectionPinMode::InFloating);
}
#[inline]
fn sda_output(&mut self) {
self.sda.mode_ctrl(BidirectionPinMode::OutOD);
}
fn start(&mut self) {
self.sda_output();
self.sda.set(true);
self.scl_hi();
self.sda_lo();
self.scl_lo();
}
fn stop(&mut self) {
self.sda_output();
self.sda_lo();
self.scl_hi();
self.sda.set(true);
}
fn clock_pulse(&mut self) {
self.scl_hi();
self.scl_lo();
}
fn write_byte(&mut self, mut data_byte: u8) {
self.sda_output();
for _ in 0..8 {
self.sda.set(data_byte & 0x80 != 0);
data_byte <<= 1;
self.clock_pulse();
}
}
fn read_bit(&mut self) -> bool {
self.scl_hi();
let sda_bit = self.sda.get();
self.scl_lo();
sda_bit
}
fn read_byte(&mut self) -> u8 {
let mut out_byte = 0;
self.sda_input();
for _ in 0..8 {
out_byte <<= 1;
out_byte |= (self.read_bit() as u8) & 1;
}
out_byte
}
fn read_ack(&mut self, err: SoftI2CError) -> Result<(), SoftI2CError> {
self.sda_input();
if self.read_bit() {
self.stop();
Err(err)
} else {
Ok(())
}
}
fn mak(&mut self) {
self.sda_output();
self.sda_lo();
self.clock_pulse();
}
fn nmak(&mut self) {
self.sda_output();
self.sda_hi();
self.clock_pulse();
}
fn address(&mut self, address: SoftI2CAddr, is_read: bool) -> Result<(), SoftI2CError> {
let r_w_bit = if is_read { 1 } else { 0 };
match address {
SoftI2CAddr::SevenBitAddress(addr) => {
self.write_byte((addr << 1) | r_w_bit);
self.read_ack(SoftI2CError::Address)?
},
SoftI2CAddr::TenBitAddress(addr) => {
let h_addr = ((((addr >> 7) as u8 & 0xFE) | r_w_bit) & 0x07) | 0xF0;
let l_addr = (addr & 0xFF) as u8;
for byte in [h_addr, l_addr] {
self.write_byte(byte);
self.read_ack(SoftI2CError::Address)?
}
},
}
Ok(())
}
pub fn soft_i2c_write_read(&mut self, address: SoftI2CAddr, write: &[u8], read: &mut [u8]) -> Result<(), SoftI2CError> {
self.start();
self.address(address, false)?;
for i in 0..write.len() {
self.write_byte(write[i]);
self.read_ack(SoftI2CError::WriteData)?;
}
self.soft_i2c_read(address, read)
}
pub fn soft_i2c_write(&mut self, address: SoftI2CAddr, write: &[u8]) -> Result<(), SoftI2CError> {
self.start();
self.address(address, false)?;
for i in 0..write.len() {
self.write_byte(write[i]);
self.read_ack(SoftI2CError::WriteData)?;
}
self.stop();
Ok(())
}
pub fn soft_i2c_read(&mut self, address: SoftI2CAddr, read: &mut [u8]) -> Result<(), SoftI2CError> {
self.start();
self.address(address, true)?;
for i in 0..read.len() {
read[i] = self.read_byte();
if i < read.len() - 1 {
self.mak();
} else {
self.nmak();
}
}
self.stop();
Ok(())
}
}
impl embedded_hal::i2c::Error for SoftI2CError {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
match *self {
SoftI2CError::Address => embedded_hal::i2c::ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address),
SoftI2CError::WriteData => embedded_hal::i2c::ErrorKind::NoAcknowledge(NoAcknowledgeSource::Data),
}
}
}
impl<C: OutputPin, D: BidirectionPin, const CLK_HZ: u32> embedded_hal::i2c::ErrorType for SoftI2C<C, D, CLK_HZ> {
type Error = SoftI2CError;
}
impl<C: OutputPin, D: BidirectionPin, const CLK_HZ: u32> embedded_hal::i2c::I2c<SevenBitAddress> for SoftI2C<C, D, CLK_HZ> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.soft_i2c_read(SoftI2CAddr::SevenBitAddress(address), read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.soft_i2c_write(SoftI2CAddr::SevenBitAddress(address), write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.soft_i2c_write_read(SoftI2CAddr::SevenBitAddress(address), write, read)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}
impl<C: OutputPin, D: BidirectionPin, const CLK_HZ: u32> embedded_hal::i2c::I2c<TenBitAddress> for SoftI2C<C, D, CLK_HZ> {
fn read(&mut self, address: u16, read: &mut [u8]) -> Result<(), Self::Error> {
self.soft_i2c_read(SoftI2CAddr::TenBitAddress(address), read)
}
fn write(&mut self, address: u16, write: &[u8]) -> Result<(), Self::Error> {
self.soft_i2c_write(SoftI2CAddr::TenBitAddress(address), write)
}
fn write_read(&mut self, address: u16, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.soft_i2c_write_read(SoftI2CAddr::TenBitAddress(address), write, read)
}
fn transaction(
&mut self,
_address: u16,
_operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
}
}