Skip to content

Commit

Permalink
i2c: add timeouts (hardcoded for now)
Browse files Browse the repository at this point in the history
Inspired by f1 hal
  • Loading branch information
valpackett committed Feb 23, 2022
1 parent 723a425 commit 017ad89
Showing 1 changed file with 117 additions and 56 deletions.
173 changes: 117 additions & 56 deletions src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ use crate::prelude::*;
use crate::rcc::Rcc;
use crate::stm32::{I2C1, I2C2};
use crate::time::Hertz;
use cortex_m::peripheral::DWT;

/// I2C abstraction
pub struct I2c<I2C, PINS> {
i2c: I2C,
pins: PINS,
speed: Hertz,
clock: u32,
}

pub trait Pins<I2c> {
Expand Down Expand Up @@ -39,10 +42,56 @@ impl Pins<I2C2> for (PB10<Output<OpenDrain>>, PB11<Output<OpenDrain>>) {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Error {
OVERRUN,
NACK,
Bus,
Arbitration,
Acknowledge,
Overrun,
Timeout,
}

macro_rules! wait_for_flag {
($i2c:expr, $flag:ident) => {{
let sr1 = $i2c.sr1.read();

if sr1.berr().bit_is_set() {
$i2c.sr1.write(|w| w.berr().clear_bit());
Err(Error::Bus)
} else if sr1.arlo().bit_is_set() {
$i2c.sr1.write(|w| w.arlo().clear_bit());
Err(Error::Arbitration)
} else if sr1.af().bit_is_set() {
$i2c.sr1.write(|w| w.af().clear_bit());
Err(Error::Acknowledge)
} else if sr1.ovr().bit_is_set() {
$i2c.sr1.write(|w| w.ovr().clear_bit());
Err(Error::Overrun)
} else {
Ok(sr1.$flag().bit_is_set())
}
}};
}

macro_rules! busy_wait {
($nb_expr:expr, $exit_cond:expr) => {{
loop {
match $nb_expr {
Err(e) => break Err(e),
Ok(true) => break Ok(()),
Ok(false) if $exit_cond => break Err(Error::Timeout),
Ok(false) => {}
}
}
}};
}

macro_rules! busy_wait_cycles {
($nb_expr:expr, $cycles:expr) => {{
let started = DWT::cycle_count();
let cycles = $cycles;
busy_wait!($nb_expr, DWT::cycle_count().wrapping_sub(started) >= cycles)
}};
}

macro_rules! i2c {
Expand All @@ -62,14 +111,30 @@ macro_rules! i2c {
rcc.rb.apb1rstr.modify(|_, w| w.$i2crst().set_bit());
rcc.rb.apb1rstr.modify(|_, w| w.$i2crst().clear_bit());

// Make sure the I2C unit is disabled so we can configure it
i2c.cr1.modify(|_, w| w.pe().clear_bit());

// Calculate settings for I2C speed modes
let clock = rcc.clocks.apb1_clk().0;
let freq = clock / 1_000_000;
assert!(freq >= 2 && freq <= 50);

let mut i2c = I2c {
i2c,
pins,
speed,
clock,
};
i2c.init();
i2c
}

fn init(&mut self) {
let i2c = &mut self.i2c;
let speed = self.speed;
let clock = self.clock;
let freq = clock / 1_000_000;

// Make sure the I2C unit is disabled so we can configure it
i2c.cr1.modify(|_, w| w.pe().clear_bit());

// Configure bus frequency into I2C peripheral
i2c.cr2.write(|w| unsafe { w.freq().bits(freq as u8) });

Expand Down Expand Up @@ -125,40 +190,54 @@ macro_rules! i2c {

// Enable the I2C processing
i2c.cr1.modify(|_, w| w.pe().set_bit());

I2c { i2c, pins }
}

pub fn release(self) -> ($I2CX, PINS) {
(self.i2c, self.pins)
}

fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
// Send a START condition
self.i2c.cr1.modify(|_, w| w.start().set_bit());
fn send_start(&mut self) -> Result<(), Error> {
let mut retries_left = 10;
let mut last_err = Err(Error::Timeout);
while retries_left > 0 {
// Send a START condition
self.i2c.cr1.modify(|_, w| w.start().set_bit());

// Wait until START condition was generated
last_err = busy_wait_cycles!(wait_for_flag!(self.i2c, sb), 24_0000);

if last_err.is_err() {
self.i2c.cr1.write(|w| w.pe().set_bit().swrst().set_bit());
self.i2c.cr1.reset();
self.init();
} else {
break;
}

retries_left -= 1;
}
last_err
}

// Wait until START condition was generated
while {
let sr1 = self.i2c.sr1.read();
sr1.sb().bit_is_clear()
} {}
fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
self.send_start()?;

// Also wait until signalled we're master and everything is waiting for us
while {
let sr2 = self.i2c.sr2.read();
sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear()
} {}
busy_wait_cycles!(
{
let sr2 = self.i2c.sr2.read();
Ok(sr2.msl().bit_is_set() && sr2.busy().bit_is_set())
},
24_0000
)?;

// Set up current address, we're trying to talk to
self.i2c
.dr
.write(|w| unsafe { w.bits(u32::from(addr) << 1) });

// Wait until address was sent
while {
let sr1 = self.i2c.sr1.read();
sr1.addr().bit_is_clear()
} {}
busy_wait_cycles!(wait_for_flag!(self.i2c, addr), 24_0000)?;

// Clear condition by reading SR2
self.i2c.sr2.read();
Expand All @@ -174,28 +253,19 @@ macro_rules! i2c {

fn send_byte(&self, byte: u8) -> Result<(), Error> {
// Wait until we're ready for sending
while self.i2c.sr1.read().tx_e().bit_is_clear() {}
busy_wait_cycles!(wait_for_flag!(self.i2c, tx_e), 24_0000)?;

// Push out a byte of data
self.i2c.dr.write(|w| unsafe { w.bits(u32::from(byte)) });

// While until byte is transferred
while {
let sr1 = self.i2c.sr1.read();

// If we received a NACK, then this is an error
if sr1.af().bit_is_set() {
return Err(Error::NACK);
}

sr1.btf().bit_is_clear()
} {}
// Wait until byte is transferred
busy_wait_cycles!(wait_for_flag!(self.i2c, btf), 24_0000)?;

Ok(())
}

fn recv_byte(&self) -> Result<u8, Error> {
while self.i2c.sr1.read().rx_ne().bit_is_clear() {}
busy_wait_cycles!(wait_for_flag!(self.i2c, rx_ne), 24_0000)?;
let value = self.i2c.dr.read().bits() as u8;
Ok(value)
}
Expand Down Expand Up @@ -243,33 +313,24 @@ macro_rules! i2c {

fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
if let Some((last, buffer)) = buffer.split_last_mut() {
// Send a START condition and set ACK bit
self.i2c
.cr1
.modify(|_, w| w.start().set_bit().ack().set_bit());

// Wait until START condition was generated
while {
let sr1 = self.i2c.sr1.read();
sr1.sb().bit_is_clear()
} {}
self.send_start()?;

// Also wait until signalled we're master and everything is waiting for us
while {
let sr2 = self.i2c.sr2.read();
sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear()
} {}
busy_wait_cycles!(
{
let sr2 = self.i2c.sr2.read();
Ok(sr2.msl().bit_is_set() && sr2.busy().bit_is_set())
},
24_0000
)?;

// Set up current address, we're trying to talk to
self.i2c
.dr
.write(|w| unsafe { w.bits((u32::from(addr) << 1) + 1) });

// Wait until address was sent
while {
let sr1 = self.i2c.sr1.read();
sr1.addr().bit_is_clear()
} {}
busy_wait_cycles!(wait_for_flag!(self.i2c, addr), 24_0000)?;

// Clear condition by reading SR2
self.i2c.sr2.read();
Expand All @@ -290,7 +351,7 @@ macro_rules! i2c {
// Fallthrough is success
Ok(())
} else {
Err(Error::OVERRUN)
Err(Error::Overrun)
}
}
}
Expand Down

0 comments on commit 017ad89

Please sign in to comment.