use crate::{addr_size, page_size, private, unique_serial, Eeprom24x, Error, SlaveAddr};
use core::marker::PhantomData;
use embedded_hal::i2c::I2c;
pub trait MultiSizeAddr: private::Sealed {
const ADDRESS_BYTES: usize;
fn fill_address(address: u32, payload: &mut [u8]);
}
impl MultiSizeAddr for addr_size::OneByte {
const ADDRESS_BYTES: usize = 1;
fn fill_address(address: u32, payload: &mut [u8]) {
payload[0] = address as u8;
}
}
impl MultiSizeAddr for addr_size::TwoBytes {
const ADDRESS_BYTES: usize = 2;
fn fill_address(address: u32, payload: &mut [u8]) {
payload[0] = (address >> 8) as u8;
payload[1] = address as u8;
}
}
impl<I2C, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN> {
pub fn destroy(self) -> I2C {
self.i2c
}
}
impl<I2C, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
where
AS: MultiSizeAddr,
{
fn get_device_address<E>(&self, memory_address: u32) -> Result<u8, Error<E>> {
if memory_address >= (1 << self.address_bits) {
return Err(Error::InvalidAddr);
}
let addr = self.address.devaddr(
memory_address,
self.address_bits,
AS::ADDRESS_BYTES as u8 * 8,
);
Ok(addr)
}
}
impl<I2C, E, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
where
I2C: I2c<Error = E>,
AS: MultiSizeAddr,
{
pub fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error<E>> {
let devaddr = self.get_device_address(address)?;
let mut payload = [0; 3];
AS::fill_address(address, &mut payload);
payload[AS::ADDRESS_BYTES] = data;
self.i2c
.write(devaddr, &payload[..=AS::ADDRESS_BYTES])
.map_err(Error::I2C)
}
pub fn read_byte(&mut self, address: u32) -> Result<u8, Error<E>> {
let devaddr = self.get_device_address(address)?;
let mut memaddr = [0; 2];
AS::fill_address(address, &mut memaddr);
let mut data = [0; 1];
self.i2c
.write_read(devaddr, &memaddr[..AS::ADDRESS_BYTES], &mut data)
.map_err(Error::I2C)
.and(Ok(data[0]))
}
pub fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error<E>> {
let devaddr = self.get_device_address(address)?;
let mut memaddr = [0; 2];
AS::fill_address(address, &mut memaddr);
self.i2c
.write_read(devaddr, &memaddr[..AS::ADDRESS_BYTES], data)
.map_err(Error::I2C)
}
}
impl<I2C, E, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
where
I2C: I2c<Error = E>,
{
pub fn read_current_address(&mut self) -> Result<u8, Error<E>> {
let mut data = [0];
self.i2c
.read(self.address.addr(), &mut data)
.map_err(Error::I2C)
.and(Ok(data[0]))
}
}
impl<I2C, E> Eeprom24x<I2C, page_size::No, addr_size::OneByte, unique_serial::No>
where
I2C: I2c<Error = E>,
{
pub fn new_24x00(i2c: I2C, address: SlaveAddr) -> Self {
Eeprom24x {
i2c,
address,
address_bits: 4,
_ps: PhantomData,
_as: PhantomData,
_sn: PhantomData,
}
}
}
macro_rules! impl_create {
( $dev:expr, $part:expr, $address_bits:expr, $create:ident ) => {
impl_create! {
@gen [$create, $address_bits,
concat!("Create a new instance of a ", $dev, " device (e.g. ", $part, ")")]
}
};
(@gen [$create:ident, $address_bits:expr, $doc:expr] ) => {
#[doc = $doc]
pub fn $create(i2c: I2C, address: SlaveAddr) -> Self {
Self::new(i2c, address, $address_bits)
}
};
}
macro_rules! impl_for_page_size {
( $AS:ident, $addr_bytes:expr, $PS:ident, $page_size:expr,
$( [ $dev:expr, $part:expr, $address_bits:expr, $SN:ident, $create:ident ] ),* ) => {
impl_for_page_size!{
@gen [$AS, $addr_bytes, $PS, $page_size,
concat!("Specialization for devices with a page size of ", stringify!($page_size), " bytes."),
concat!("Create generic instance for devices with a page size of ", stringify!($page_size), " bytes."),
$( [ $dev, $part, $address_bits, $SN, $create ] ),* ]
}
};
(@gen [$AS:ident, $addr_bytes:expr, $PS:ident, $page_size:expr, $doc_impl:expr, $doc_new:expr,
$( [ $dev:expr, $part:expr, $address_bits:expr, $SN:ident, $create:ident ] ),* ] ) => {
$(
impl<I2C, E> Eeprom24x<I2C, page_size::$PS, addr_size::$AS, unique_serial::$SN>
where
I2C: I2c<Error = E>
{
impl_create!($dev, $part, $address_bits, $create);
}
)*
#[doc = $doc_impl]
impl<I2C, E, SN> Eeprom24x<I2C, page_size::$PS, addr_size::$AS, SN>
where
I2C: I2c<Error = E>
{
#[doc = $doc_new]
fn new(i2c: I2C, address: SlaveAddr, address_bits: u8) -> Self {
Eeprom24x {
i2c,
address,
address_bits,
_ps: PhantomData,
_as: PhantomData,
_sn: PhantomData,
}
}
}
impl<I2C, E, AS, SN> Eeprom24x<I2C, page_size::$PS, AS, SN>
where
I2C: I2c<Error = E>,
AS: MultiSizeAddr,
{
pub fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>> {
if data.len() == 0 {
return Ok(());
}
if data.len() > $page_size {
return Err(Error::TooMuchData);
}
let page_boundary = address | ($page_size as u32 - 1);
if address + data.len() as u32 > page_boundary + 1 {
return Err(Error::TooMuchData);
}
let devaddr = self.get_device_address(address)?;
let mut payload: [u8; $addr_bytes + $page_size] = [0; $addr_bytes + $page_size];
AS::fill_address(address, &mut payload);
payload[$addr_bytes..$addr_bytes + data.len()].copy_from_slice(&data);
self.i2c
.write(devaddr, &payload[..$addr_bytes + data.len()])
.map_err(Error::I2C)
}
}
impl<I2C, E, AS, SN> PageWrite<E> for Eeprom24x<I2C, page_size::$PS, AS, SN>
where
I2C: I2c<Error = E>,
AS: MultiSizeAddr,
{
fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>> {
self.write_page(address, data)
}
fn page_size(&self) -> usize {
$page_size
}
}
impl<I2C, E, AS, SN> crate::Eeprom24xTrait for Eeprom24x<I2C, page_size::$PS, AS, SN>
where
I2C: I2c<Error = E>,
AS: MultiSizeAddr
{
type Error = E;
fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error<Self::Error>>
{
self.write_byte(address, data)
}
fn read_byte(&mut self, address: u32) -> Result<u8, Error<Self::Error>>
{
self.read_byte(address)
}
fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error<Self::Error>>
{
self.read_data(address, data)
}
fn read_current_address(&mut self) -> Result<u8, Error<Self::Error>>
{
self.read_current_address()
}
fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error<Self::Error>>
{
self.write_page(address, &data)
}
fn page_size(&self) -> usize
{
$page_size
}
}
};
}
pub trait PageWrite<E> {
fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>>;
fn page_size(&self) -> usize;
}
impl_for_page_size!(
OneByte,
1,
B8,
8,
["24x01", "AT24C01", 7, No, new_24x01],
["24x02", "AT24C02", 8, No, new_24x02],
["24CSx01", "24CS01", 7, Yes, new_24csx01],
["24CSx02", "24CS02", 8, Yes, new_24csx02],
["24x02E48", "24AA02E48", 8, No, new_24x02e48],
["24x02E64", "24AA02E64", 8, No, new_24x02e64]
);
impl_for_page_size!(
OneByte,
1,
B16,
16,
["24x04", "AT24C04", 9, No, new_24x04],
["24x08", "AT24C08", 10, No, new_24x08],
["24x16", "AT24C16", 11, No, new_24x16],
["24CSx04", "AT24CS04", 9, Yes, new_24csx04],
["24CSx08", "AT24CS08", 10, Yes, new_24csx08],
["24CSx16", "AT24CS16", 11, Yes, new_24csx16],
["24x025E48", "24AA025E48", 8, No, new_24x025e48],
["24x025E64", "24AA025E64", 8, No, new_24x025e64],
["M24C01", "M24C01", 7, No, new_m24x01],
["M24C02", "M24C02", 8, No, new_m24x02]
);
impl_for_page_size!(
TwoBytes,
2,
B32,
32,
["24x32", "AT24C32", 12, No, new_24x32],
["24x64", "AT24C64", 13, No, new_24x64],
["24CSx32", "AT24CS32", 12, Yes, new_24csx32],
["24CSx64", "AT24CS64", 13, Yes, new_24csx64]
);
impl_for_page_size!(
TwoBytes,
2,
B64,
64,
["24x128", "AT24C128", 14, No, new_24x128],
["24x256", "AT24C256", 15, No, new_24x256]
);
impl_for_page_size!(
TwoBytes,
2,
B128,
128,
["24x512", "AT24C512", 16, No, new_24x512]
);
impl_for_page_size!(
TwoBytes,
2,
B256,
256,
["24xM01", "AT24CM01", 17, No, new_24xm01],
["24xM02", "AT24CM02", 18, No, new_24xm02]
);