add I2C and 24xx EEPROM driver
This commit is contained in:
parent
bc14409f44
commit
12b06a0fa9
0
Cargo.toml
Normal file → Executable file
0
Cargo.toml
Normal file → Executable file
23
src/eeprom.rs
Executable file
23
src/eeprom.rs
Executable file
@ -0,0 +1,23 @@
|
||||
use stm32h7::stm32h743 as pac;
|
||||
use super::i2c;
|
||||
|
||||
const I2C_ADDR: u8 = 0xa0;
|
||||
|
||||
pub fn read_eui48<'a>(i2c: &pac::I2C2) -> Result<[u8; 6], i2c::Error> {
|
||||
let mut buffer = [0u8; 6];
|
||||
i2c::write_read(i2c, I2C_ADDR, &[0xFAu8], &mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn read(i2c: &pac::I2C2, addr: u8, mut buffer: &mut [u8]) -> Result<(), i2c::Error> {
|
||||
i2c::write_read(i2c, I2C_ADDR, &[addr], &mut buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(i2c: &pac::I2C2, addr: u8, buffer: &[u8]) -> Result<(), i2c::Error> {
|
||||
for (i, &byte) in buffer.iter().enumerate() {
|
||||
i2c::write(i2c, I2C_ADDR, &[addr+i as u8, byte])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
182
src/i2c.rs
Executable file
182
src/i2c.rs
Executable file
@ -0,0 +1,182 @@
|
||||
use stm32h7::stm32h743 as pac;
|
||||
|
||||
// Adapted from stm32h7xx embedded-hal
|
||||
|
||||
// I2C error
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
// Bus error
|
||||
Bus,
|
||||
// Arbitration loss
|
||||
Arbitration,
|
||||
// Address not ACKd within a reasonable time (no device present?)
|
||||
Timeout,
|
||||
// Unexpected NACK during transfer
|
||||
NAck
|
||||
}
|
||||
|
||||
// Maximum number of times to retry NACKed address phase before timing out
|
||||
// Note that many devices indicate a busy condition by NACKing (e.g. 24xx
|
||||
// EEPROMs during write)
|
||||
const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock
|
||||
|
||||
|
||||
pub fn setup(rcc: &pac::RCC, i2c: &pac::I2C2) {
|
||||
rcc.apb1lenr.modify(|_,w|
|
||||
w.i2c2en().set_bit()
|
||||
);
|
||||
|
||||
// Disable the peripheral before setting timings
|
||||
i2c.cr1.modify(|_, w| w.pe().clear_bit());
|
||||
|
||||
// Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock
|
||||
i2c.timingr.modify( |_,w|
|
||||
w.presc().bits(1)
|
||||
.scldel().bits(0x12)
|
||||
.sdadel().bits(0)
|
||||
.sclh().bits(0xec)
|
||||
.scll().bits(0xff)
|
||||
);
|
||||
|
||||
// Enable the peripheral
|
||||
i2c.cr1.write(|w| w.pe().set_bit());
|
||||
}
|
||||
|
||||
|
||||
// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems
|
||||
macro_rules! busy_wait_errors {
|
||||
($i2c:expr, $flag:ident) => {
|
||||
loop {
|
||||
let isr = $i2c.isr.read();
|
||||
|
||||
if isr.berr().bit_is_set() {
|
||||
return Err(Error::Bus);
|
||||
} else if isr.arlo().bit_is_set() {
|
||||
return Err(Error::Arbitration);
|
||||
} else if isr.nackf().bit_is_set() {
|
||||
return Err(Error::NAck);
|
||||
} else if isr.$flag().bit_is_set() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn poll_for_start_ack(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
r_wn: bool,
|
||||
data_len: usize,
|
||||
autoend: bool,
|
||||
start: bool
|
||||
) -> Result<(), Error>
|
||||
{
|
||||
for _i in 0..N_RETRY {
|
||||
// START and prepare to send `data_len`
|
||||
i2c.cr2.write(|w| {
|
||||
w.start().bit(start)
|
||||
.sadd().bits(addr as u16)
|
||||
.add10().clear_bit()
|
||||
.rd_wrn().bit(r_wn)
|
||||
.nbytes().bits( data_len as u8 )
|
||||
.autoend().bit(autoend)
|
||||
});
|
||||
|
||||
loop {
|
||||
let isr = i2c.isr.read();
|
||||
|
||||
if isr.berr().bit_is_set() {
|
||||
return Err(Error::Bus);
|
||||
} else if isr.arlo().bit_is_set() {
|
||||
return Err(Error::Arbitration);
|
||||
} else if isr.nackf().bit_is_set() {
|
||||
i2c.icr.write(|w| { w.nackcf().set_bit() });
|
||||
// Wait to finish handling NACK-STOP
|
||||
loop {
|
||||
if i2c.isr.read().busy().bit_is_clear() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Error::Timeout);
|
||||
}
|
||||
|
||||
|
||||
pub fn write(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
bytes: &[u8]
|
||||
) -> Result<(), Error> {
|
||||
assert!(bytes.len() < 256 && bytes.len() > 0);
|
||||
|
||||
poll_for_start_ack(i2c, addr|0, false, bytes.len(), true, true)?;
|
||||
|
||||
for byte in bytes {
|
||||
busy_wait_errors!(i2c, txis);
|
||||
i2c.txdr.write(|w| w.txdata().bits(*byte));
|
||||
}
|
||||
// automatic STOP
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn write_read(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
assert!(bytes.len() < 256 && bytes.len() > 0);
|
||||
assert!(buffer.len() < 256 && buffer.len() > 0);
|
||||
|
||||
poll_for_start_ack(i2c, addr|0, false, bytes.len(), false, true)?;
|
||||
|
||||
for byte in bytes {
|
||||
// Wait until we are allowed to send data (START has been ACKed or last
|
||||
// byte when through)
|
||||
busy_wait_errors!(i2c, txis);
|
||||
i2c.txdr.write(|w| w.txdata().bits(*byte));
|
||||
}
|
||||
|
||||
// Wait until the last transmission is finished
|
||||
busy_wait_errors!(i2c, tc);
|
||||
|
||||
poll_for_start_ack(i2c, addr|1, true, buffer.len(), true, true)?;
|
||||
|
||||
for byte in buffer {
|
||||
// Wait until we have received something
|
||||
busy_wait_errors!(i2c, rxne);
|
||||
*byte = i2c.rxdr.read().rxdata().bits();
|
||||
}
|
||||
|
||||
// automatic STOP
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn read(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
assert!(buffer.len() < 256 && buffer.len() > 0);
|
||||
|
||||
poll_for_start_ack(i2c, addr|0, true, buffer.len(), true, true)?;
|
||||
|
||||
for byte in buffer {
|
||||
// Wait until we have received something
|
||||
busy_wait_errors!(i2c, rxne);
|
||||
*byte = i2c.rxdr.read().rxdata().bits();
|
||||
}
|
||||
|
||||
// automatic STOP
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user