#![cfg_attr(not(test), no_std)]
use embedded_hal::blocking::i2c::{Write, WriteRead};
pub const ADDRESS_LAST: usize = 256 * 32;
pub struct IdentificationPage;
pub struct NoIdentificationPage;
#[derive(Debug)]
pub enum Error<I> {
I2C(I),
Conn,
Address,
Port,
}
#[repr(u8)]
enum Dest {
Memory = 0x50,
Identification = 0x58,
}
#[derive(Default)]
pub struct Config {
pub address: u8,
}
pub struct M24C64<I2C, F> {
_device_family: F,
config: Config,
i2c: I2C,
cmd_buf: [u8; 34],
}
impl<I2C, S, F> M24C64<I2C, F>
where
I2C: Write<u8, Error = S> + WriteRead<u8, Error = S>,
{
pub fn new(i2c: I2C, config: Config) -> M24C64<I2C, NoIdentificationPage> {
M24C64 {
_device_family: NoIdentificationPage,
config,
i2c,
cmd_buf: [0; 34],
}
}
pub fn with_id_page(self) -> M24C64<I2C, IdentificationPage> {
M24C64 {
_device_family: IdentificationPage,
config: self.config,
i2c: self.i2c,
cmd_buf: self.cmd_buf,
}
}
fn write_raw(&mut self, dest: Dest, address: usize, bytes: &[u8]) -> Result<(), Error<S>> {
self.cmd_buf[0] = (address >> 8) as u8;
self.cmd_buf[1] = (address & 0xff) as u8;
self.cmd_buf[2..bytes.len() + 2].copy_from_slice(bytes);
self.i2c
.write(
self.config.address | dest as u8,
&self.cmd_buf[0..bytes.len() + 2],
)
.map_err(Error::I2C)
}
fn read_raw(&mut self, dest: Dest, address: usize, bytes: &mut [u8]) -> Result<(), Error<S>> {
self.cmd_buf[0] = (address >> 8) as u8;
self.cmd_buf[1] = (address & 0xff) as u8;
self.i2c
.write_read(self.config.address | dest as u8, &self.cmd_buf[0..2], bytes)
.map_err(Error::I2C)
}
pub fn write_page(&mut self, page: usize, bytes: &[u8; 32]) -> Result<(), Error<S>> {
self.write_raw(Dest::Memory, page * 32, bytes)
}
pub fn write(&mut self, address: usize, bytes: &[u8]) -> Result<(), Error<S>> {
let start_idx = address % 32;
if start_idx + bytes.len() > 32 {
return Err(Error::Address);
}
self.write_raw(Dest::Memory, address, bytes)
}
pub fn read_page(&mut self, page: usize, bytes: &mut [u8; 32]) -> Result<(), Error<S>> {
self.read_raw(Dest::Memory, page * 32, bytes)
}
pub fn read(&mut self, address: usize, bytes: &mut [u8]) -> Result<(), Error<S>> {
if address + bytes.len() > ADDRESS_LAST {
return Err(Error::Address);
}
self.read_raw(Dest::Memory, address, bytes)
}
}
impl<I2C, S> M24C64<I2C, IdentificationPage>
where
I2C: Write<u8, Error = S> + WriteRead<u8, Error = S>,
{
pub fn write_id(&mut self, mut address: usize, bytes: &[u8]) -> Result<(), Error<S>> {
if address + bytes.len() > 32 {
return Err(Error::Address);
}
address &= !(1 << 10);
self.write_raw(Dest::Identification, address, bytes)
}
pub fn write_id_page(&mut self, bytes: &[u8; 32]) -> Result<(), Error<S>> {
self.write_raw(Dest::Identification, 0, bytes)
}
pub fn lock_id_page(&mut self) -> Result<(), Error<S>> {
let address = 0x400;
let data_byte = 0x2;
self.write_raw(Dest::Identification, address, &[data_byte])
}
pub fn read_id(&mut self, address: usize, bytes: &mut [u8]) -> Result<(), Error<S>> {
if address + bytes.len() > 32 {
return Err(Error::Address);
}
self.read_raw(Dest::Identification, address, bytes)
}
pub fn read_id_page(&mut self, bytes: &mut [u8; 32]) -> Result<(), Error<S>> {
self.read_raw(Dest::Identification, 0, bytes)
}
}