#![cfg_attr(not(test), no_std)]
use core::fmt::Debug;
use embedded_hal_async::i2c::{Error as I2cError, I2c};
#[derive(Debug, Clone, Copy)]
pub struct Address(pub u8, pub u8);
const DEVICE_TYPE_CODE: u8 = 0b1010;
impl From<Address> for u8 {
fn from(a: Address) -> Self {
(DEVICE_TYPE_CODE << 3) | (a.1 << 2) | (a.0 << 1)
}
}
const MEMORY_ADDRESS_BYTES: usize = 2;
const CAPACITY_BYTES: usize = 128 * 1024;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error<E: Debug + I2cError> {
I2c(E),
OutOfBounds,
BufferTooSmall,
}
pub struct Fm24v10<'buf, I2C> {
i2c: I2C,
base_address: u8,
write_buffer: &'buf mut [u8],
}
impl<'buf, I2C, E> Fm24v10<'buf, I2C>
where
I2C: I2c<Error = E>,
E: Debug + I2cError,
{
pub fn new(i2c: I2C, address_pins: Address, write_buffer: &'buf mut [u8]) -> Self {
Self {
i2c,
base_address: address_pins.into(),
write_buffer,
}
}
fn get_address_for_offset(&self, memory_offset: u32) -> Result<u8, Error<E>> {
if memory_offset >= CAPACITY_BYTES as u32 {
return Err(Error::OutOfBounds);
}
let page_select_bit = (memory_offset >> 16) & 0x01;
let final_address = self.base_address | (page_select_bit as u8);
Ok(final_address)
}
pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error<E>> {
if bytes.is_empty() {
return Ok(());
}
if offset >= CAPACITY_BYTES as u32 || bytes.len() > (CAPACITY_BYTES - offset as usize) {
return Err(Error::OutOfBounds);
}
let address = self.get_address_for_offset(offset)?;
let mem_addr_payload: [u8; MEMORY_ADDRESS_BYTES] =
[((offset >> 8) & 0xFF) as u8, (offset & 0xFF) as u8];
self.i2c
.write_read(address, &mem_addr_payload, bytes)
.await
.map_err(Error::I2c)?;
Ok(())
}
pub async fn capacity(&self) -> Result<usize, Error<E>> {
Ok(CAPACITY_BYTES)
}
pub async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Error<E>> {
if data.is_empty() {
return Ok(());
}
if offset >= CAPACITY_BYTES as u32 || data.len() > (CAPACITY_BYTES - offset as usize) {
return Err(Error::OutOfBounds);
}
let required_buffer_len = MEMORY_ADDRESS_BYTES + data.len();
if self.write_buffer.len() < required_buffer_len {
return Err(Error::BufferTooSmall);
}
let i2c_7bit_address = self.get_address_for_offset(offset)?;
self.write_buffer[0] = ((offset >> 8) & 0xFF) as u8;
self.write_buffer[1] = (offset & 0xFF) as u8;
self.write_buffer[MEMORY_ADDRESS_BYTES..required_buffer_len].copy_from_slice(data);
self.i2c
.write(i2c_7bit_address, &self.write_buffer[..required_buffer_len])
.await
.map_err(Error::I2c)?;
Ok(())
}
}