Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support I2C transfers of more than 255 bytes #154

Merged
merged 5 commits into from
Oct 20, 2020
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 195 additions & 105 deletions src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ use cast::u8;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// Bus error
Bus,
/// Arbitration loss
Arbitration,
/// Bus error
Bus,
/// Bus busy
Busy,
/// Not Acknowledge received
Nack,
// Overrun, // slave mode only
// Pec, // SMBUS mode only
// Timeout, // SMBUS mode only
Expand Down Expand Up @@ -57,15 +61,21 @@ macro_rules! busy_wait {
($i2c:expr, $flag:ident, $variant:ident) => {
loop {
let isr = $i2c.isr.read();
let icr = &$i2c.icr;

if isr.$flag().$variant() {
break;
if isr.arlo().is_lost() {
icr.write(|w| w.arlocf().clear());
return Err(Error::Arbitration);
} else if isr.berr().is_error() {
icr.write(|w| w.berrcf().clear());
return Err(Error::Bus);
} else if isr.arlo().is_lost() {
return Err(Error::Arbitration);
} else {
// try again
} else if isr.nackf().is_nack() {
while $i2c.isr.read().stopf().is_no_stop() {}
icr.write(|w| w.nackcf().clear());
icr.write(|w| w.stopcf().clear());
return Err(Error::Nack);
} else if isr.$flag().$variant() {
break;
}
}
};
Expand Down Expand Up @@ -181,34 +191,52 @@ macro_rules! hal {
impl<PINS> Read for I2c<$I2CX, PINS> {
type Error = Error;
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
// TODO support transfers of more than 255 bytes
assert!(buffer.len() < 256 && buffer.len() > 0);

// TODO do we have to explicitly wait here if the bus is busy (e.g. another
// master is communicating)?

// START and prepare to receive `bytes`
self.i2c.cr2.write(|w| {
w.sadd()
.bits(u16::from(addr << 1))
.rd_wrn()
.read()
.nbytes()
.bits(buffer.len() as u8)
.start()
.start()
.autoend()
.automatic()
});
assert!(buffer.len() > 0);

for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne, is_not_empty);
// Detect Bus busy
if self.i2c.isr.read().busy().is_busy() {
return Err(Error::Busy);
}

*byte = self.i2c.rxdr.read().rxdata().bits();
let end = buffer.len() / 0xFF;

// Process 255 bytes at a time
for (i, buffer) in buffer.chunks_mut(0xFF).enumerate() {
// Prepare to receive `bytes`
self.i2c.cr2.modify(|_, w| {
if i == 0 {
w
.add10().bit7()
.sadd().bits((addr << 1) as u16)
.rd_wrn().read()
.start().start();
}
w.nbytes().bits(buffer.len() as u8);
if i != end {
w.reload().not_completed()
} else {
w.reload().completed().autoend().automatic()
}
});

for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne, is_not_empty);

*byte = self.i2c.rxdr.read().rxdata().bits();
}

if i != end {
// Wait until the last transmission is finished
busy_wait!(self.i2c, tcr, is_complete);
}
}

// automatic STOP
// Wait until the last transmission is finished
busy_wait!(self.i2c, stopf, is_stop);

self.i2c.icr.write(|w| w.stopcf().clear());

Ok(())
}
Expand All @@ -218,37 +246,67 @@ macro_rules! hal {
type Error = Error;

fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
// TODO support transfers of more than 255 bytes
assert!(bytes.len() < 256 && bytes.len() > 0);

// START and prepare to send `bytes`
self.i2c.cr2.modify(|_, w| {
w.sadd()
.bits(u16::from(addr << 1))
.rd_wrn()
.write()
.nbytes()
.bits(bytes.len() as u8)
.start()
.start()
.autoend()
.automatic()
});

for byte in bytes {
// Wait until we are allowed to send data (START has been ACKed or last byte
// when through)
busy_wait!(self.i2c, txis, is_empty);

// put byte on the wire
// NOTE(write): writes all non-reserved bits.
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
// Detect Bus busy
if self.i2c.isr.read().busy().is_busy() {
return Err(Error::Busy);
}

// Wait until the last transmission is finished ???
// busy_wait!(self.i2c, busy);
if bytes.len() == 0 {
// 0 byte write
self.i2c.cr2.modify(|_, w| {
w
.add10().bit7()
.sadd().bits((addr << 1) as u16)
.rd_wrn().write()
.nbytes().bits(0)
.reload().completed()
.autoend().automatic()
.start().start()
});
} else {
let end = bytes.len() / 0xFF;

// Process 255 bytes at a time
for (i, bytes) in bytes.chunks(0xFF).enumerate() {
// Prepare to send `bytes`
self.i2c.cr2.modify(|_, w| {
if i == 0 {
w
.add10().bit7()
.sadd().bits((addr << 1) as u16)
.rd_wrn().write()
.start().start();
}
w.nbytes().bits(bytes.len() as u8);
if i != end {
w.reload().not_completed()
} else {
w.reload().completed().autoend().automatic()
}
});

for byte in bytes {
// Wait until we are allowed to send data
// (START has been ACKed or last byte went through)
busy_wait!(self.i2c, txis, is_empty);

// Put byte on the wire
// NOTE(write): Writes all non-reserved bits.
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
}

if i != end {
// Wait until the last transmission is finished
busy_wait!(self.i2c, tcr, is_complete);
}
}
}

// automatic STOP
// Wait until the last transmission is finished
busy_wait!(self.i2c, stopf, is_stop);

self.i2c.icr.write(|w| w.stopcf().clear());

Ok(())
}
Expand All @@ -263,62 +321,94 @@ macro_rules! hal {
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Error> {
// TODO support transfers of more than 255 bytes
assert!(bytes.len() < 256 && bytes.len() > 0);
assert!(buffer.len() < 256 && buffer.len() > 0);

// TODO do we have to explicitly wait here if the bus is busy (e.g. another
// master is communicating)?

// START and prepare to send `bytes`
self.i2c.cr2.modify(|_, w| {
w.sadd()
.bits(u16::from(addr << 1))
.rd_wrn()
.write()
.nbytes()
.bits(bytes.len() as u8)
.start()
.start()
.autoend()
.software()
});
assert!(bytes.len() > 0 && buffer.len() > 0);

for byte in bytes {
// Wait until we are allowed to send data
// (START has been ACKed or last byte went through):
busy_wait!(self.i2c, txis, is_empty);
// Detect Bus busy
if self.i2c.isr.read().busy().is_busy() {
return Err(Error::Busy);
}

// put byte into TXDR
// NOTE(write): writes all non-reserved bits.
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
let end = buffer.len() / 0xFF;

// Process 255 bytes at a time
for (i, bytes) in bytes.chunks(0xFF).enumerate() {
// Prepare to send `bytes`
self.i2c.cr2.modify(|_, w| {
if i == 0 {
w
.add10().bit7()
.sadd().bits((addr << 1) as u16)
.rd_wrn().write()
.start().start();
}
w.nbytes().bits(bytes.len() as u8);
if i != end {
w.reload().not_completed()
} else {
w.reload().completed().autoend().software()
}
});

for byte in bytes {
// Wait until we are allowed to send data
// (START has been ACKed or last byte went through)
busy_wait!(self.i2c, txis, is_empty);

// Put byte on the wire
// NOTE(write): Writes all non-reserved bits.
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
}

if i != end {
// Wait until the last transmission is finished
busy_wait!(self.i2c, tcr, is_complete);
}
}

// Wait until the last byte transmission is finished:
// Wait until the last transmission is finished
busy_wait!(self.i2c, tc, is_complete);

// reSTART and prepare to receive bytes into `buffer`
self.i2c.cr2.modify(|_, w| {
w.sadd()
.bits(u16::from(addr << 1))
.rd_wrn()
.read()
.nbytes()
.bits(buffer.len() as u8)
.start()
.start()
.autoend()
.automatic()
});

for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne, is_not_empty);

*byte = self.i2c.rxdr.read().rxdata().bits();
// restart

let end = buffer.len() / 0xFF;

// Process 255 bytes at a time
for (i, buffer) in buffer.chunks_mut(0xFF).enumerate() {
// Prepare to receive `bytes`
self.i2c.cr2.modify(|_, w| {
if i == 0 {
w
.add10().bit7()
.sadd().bits((addr << 1) as u16)
.rd_wrn().read()
.start().start();
}
w.nbytes().bits(buffer.len() as u8);
if i != end {
w.reload().not_completed()
} else {
w.reload().completed().autoend().automatic()
}
});

for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne, is_not_empty);

*byte = self.i2c.rxdr.read().rxdata().bits();
}

if i != end {
// Wait until the last transmission is finished
busy_wait!(self.i2c, tcr, is_complete);
}
}

// automatic STOP
// Wait until the last transmission is finished
busy_wait!(self.i2c, stopf, is_stop);

self.i2c.icr.write(|w| w.stopcf().clear());

Ok(())
}
Expand Down