Skip to content

Commit

Permalink
Merge pull request #154 from Piroro-hs/i2c
Browse files Browse the repository at this point in the history
Support I2C transfers of more than 255 bytes
  • Loading branch information
Sh3Rm4n committed Oct 20, 2020
2 parents c44988a + fe25fbb commit 92d9357
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 105 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#99](https://github.com/stm32-rs/stm32f3xx-hal/pull/99))
- SPI4 peripheral for supported
devices. ([#99](https://github.com/stm32-rs/stm32f3xx-hal/pull/99))
- Support for I2C transfer of more than 255 bytes, and 0 byte write ([#154](https://github.com/stm32-rs/stm32f3xx-hal/pull/154))

### Changed

Expand Down
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

0 comments on commit 92d9357

Please sign in to comment.