2020-08-12 16:27:17 +08:00
|
|
|
use super::I2c;
|
2020-08-07 11:10:18 +08:00
|
|
|
use crate::time::Milliseconds;
|
2020-08-05 20:10:30 +08:00
|
|
|
use embedded_hal::timer::CountDown;
|
|
|
|
|
|
|
|
pub struct EEPROM<'a> {
|
2020-08-12 16:27:17 +08:00
|
|
|
i2c: &'a mut I2c,
|
2020-08-05 20:10:30 +08:00
|
|
|
port: u8,
|
|
|
|
address: u8,
|
2020-08-07 11:10:18 +08:00
|
|
|
page_size: u8,
|
|
|
|
count_down: crate::timer::global::CountDown<Milliseconds>
|
2020-08-05 20:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> EEPROM<'a> {
|
|
|
|
#[cfg(feature = "target_zc706")]
|
2020-08-12 16:27:17 +08:00
|
|
|
pub fn new(i2c: &'a mut I2c, page_size: u8) -> Self {
|
2020-08-05 20:10:30 +08:00
|
|
|
EEPROM {
|
|
|
|
i2c: i2c,
|
|
|
|
port: 2,
|
|
|
|
address: 0b1010100,
|
2020-08-07 11:10:18 +08:00
|
|
|
page_size: page_size,
|
|
|
|
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
|
2020-08-05 20:10:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "target_zc706")]
|
|
|
|
fn select(&mut self) -> Result<(), &'static str> {
|
|
|
|
let mask: u16 = 1 << self.port;
|
|
|
|
self.i2c.pca9548_select(0b1110100, mask as u8)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Random read
|
|
|
|
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
|
|
|
|
self.select()?;
|
|
|
|
|
|
|
|
self.i2c.start()?;
|
|
|
|
self.i2c.write(self.address << 1)?;
|
|
|
|
self.i2c.write(addr)?;
|
|
|
|
|
|
|
|
self.i2c.restart()?;
|
|
|
|
self.i2c.write((self.address << 1) | 1)?;
|
|
|
|
let buf_len = buf.len();
|
|
|
|
for (i, byte) in buf.iter_mut().enumerate() {
|
|
|
|
*byte = self.i2c.read(i < buf_len - 1)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.i2c.stop()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-08-07 11:10:18 +08:00
|
|
|
/// Smart multi-page writing
|
|
|
|
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
|
|
|
|
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
|
|
|
|
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
|
|
|
|
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
|
|
|
|
/// any roll-over when writing ambiguous memory regions.
|
2020-08-05 20:10:30 +08:00
|
|
|
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
|
|
|
|
self.select()?;
|
|
|
|
|
2020-08-07 11:10:18 +08:00
|
|
|
let buf_len = buf.len();
|
|
|
|
let mut pb: u8 = addr % self.page_size;
|
2020-08-05 20:10:30 +08:00
|
|
|
for (i, byte) in buf.iter().enumerate() {
|
2020-08-07 11:10:18 +08:00
|
|
|
if (i == 0) || (pb == 0) {
|
|
|
|
self.i2c.start()?;
|
|
|
|
self.i2c.write(self.address << 1)?;
|
|
|
|
self.i2c.write(addr + (i as u8))?;
|
|
|
|
}
|
2020-08-05 20:10:30 +08:00
|
|
|
self.i2c.write(*byte)?;
|
2020-08-07 11:10:18 +08:00
|
|
|
pb += 1;
|
2020-08-05 20:10:30 +08:00
|
|
|
|
2020-08-07 11:10:18 +08:00
|
|
|
if (i == buf_len-1) || (pb == self.page_size) {
|
|
|
|
self.i2c.stop()?;
|
|
|
|
self.poll(1_000)?;
|
|
|
|
pb = 0;
|
|
|
|
}
|
|
|
|
}
|
2020-08-05 20:10:30 +08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Poll
|
2020-08-07 11:10:18 +08:00
|
|
|
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
|
2020-08-05 20:10:30 +08:00
|
|
|
self.select()?;
|
|
|
|
|
2020-08-07 11:10:18 +08:00
|
|
|
self.count_down.start(Milliseconds(timeout_ms));
|
|
|
|
loop {
|
|
|
|
self.i2c.start()?;
|
|
|
|
let ack = self.i2c.write(self.address << 1)?;
|
|
|
|
self.i2c.stop()?;
|
|
|
|
if ack {
|
|
|
|
break
|
|
|
|
};
|
|
|
|
if !self.count_down.waiting() {
|
2020-08-05 20:10:30 +08:00
|
|
|
return Err("I2C polling timeout")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
|
|
|
|
let mut buffer = [0u8; 6];
|
|
|
|
self.read(0xFA, &mut buffer)?;
|
|
|
|
Ok(buffer)
|
|
|
|
}
|
|
|
|
}
|