#![no_std]
use core::fmt::Debug;
use embassy_time::{Duration, Timer};
use embedded_hal::digital::OutputPin;
use embedded_hal_async::i2c::I2c;
use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
use embedded_storage_async::nor_flash::{
MultiwriteNorFlash as AsyncMultiwriteNorFlash, NorFlash as AsyncNorFlash,
ReadNorFlash as AsyncReadNorFlash,
};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum M24512Error<E> {
I2c(E),
InvalidAddress,
PageBoundaryExceeded,
InvalidLength,
WriteProtected,
Timeout,
}
impl<E: Debug> NorFlashError for M24512Error<E> {
fn kind(&self) -> NorFlashErrorKind {
match self {
M24512Error::I2c(_) => NorFlashErrorKind::Other,
M24512Error::InvalidAddress => NorFlashErrorKind::OutOfBounds,
M24512Error::PageBoundaryExceeded => NorFlashErrorKind::Other,
M24512Error::InvalidLength => NorFlashErrorKind::Other,
M24512Error::WriteProtected => NorFlashErrorKind::Other,
M24512Error::Timeout => NorFlashErrorKind::Other,
}
}
}
pub struct M24512<I2C, WC> {
device_address: u8,
i2c: I2C,
write_control_pin: WC,
write_enabled: bool,
}
impl<I2C, WC, E> M24512<I2C, WC>
where
I2C: I2c<Error = E>,
WC: OutputPin,
E: Debug,
{
pub const MEMORY_SIZE: usize = 65536;
pub const PAGE_SIZE: usize = 128;
pub const WRITE_CYCLE_TIME_MS: u64 = 5;
pub const DEFAULT_ADDRESS: u8 = 0x50;
pub fn new(i2c: I2C, mut write_control_pin: WC, device_address: u8) -> Self {
let _ = write_control_pin.set_high();
Self {
device_address,
i2c,
write_control_pin,
write_enabled: false,
}
}
pub fn new_default(i2c: I2C, write_control_pin: WC) -> Self {
Self::new(i2c, write_control_pin, Self::DEFAULT_ADDRESS)
}
pub fn enable_write(&mut self) -> Result<(), WC::Error> {
self.write_enabled = true;
self.write_control_pin.set_low()
}
pub fn disable_write(&mut self) -> Result<(), WC::Error> {
self.write_enabled = false;
self.write_control_pin.set_high()
}
pub fn is_write_enabled(&self) -> bool {
self.write_enabled
}
pub async fn read_byte(&mut self, address: u16) -> Result<u8, M24512Error<E>> {
let mut buffer = [0u8; 1];
self.read_bytes(address, &mut buffer).await?;
Ok(buffer[0])
}
pub async fn read_bytes(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), M24512Error<E>> {
if address as usize + buffer.len() > Self::MEMORY_SIZE {
return Err(M24512Error::InvalidAddress);
}
let addr_bytes = address.to_be_bytes();
self.i2c
.write_read(self.device_address, &addr_bytes, buffer)
.await
.map_err(M24512Error::I2c)
}
pub async fn write_bytes(&mut self, address: u16, data: &[u8]) -> Result<(), M24512Error<E>> {
if address as usize + data.len() > Self::MEMORY_SIZE {
return Err(M24512Error::InvalidAddress);
}
if data.is_empty() {
return Ok(());
}
let mut current_address = address;
let mut bytes_written = 0;
while bytes_written < data.len() {
let page_offset = (current_address as usize) % Self::PAGE_SIZE;
let bytes_remaining_in_page = Self::PAGE_SIZE - page_offset;
let bytes_to_write = core::cmp::min(
bytes_remaining_in_page,
data.len() - bytes_written,
);
self.write_page(
current_address,
&data[bytes_written..bytes_written + bytes_to_write],
)
.await?;
bytes_written += bytes_to_write;
current_address += bytes_to_write as u16;
}
Ok(())
}
async fn write_page(&mut self, address: u16, data: &[u8]) -> Result<(), M24512Error<E>> {
if data.is_empty() || data.len() > Self::PAGE_SIZE {
return Err(M24512Error::InvalidLength);
}
let addr_bytes = address.to_be_bytes();
let mut write_buf = heapless::Vec::<u8, 130>::new();
write_buf.extend_from_slice(&addr_bytes).ok();
write_buf.extend_from_slice(data).ok();
self.i2c
.write(self.device_address, &write_buf)
.await
.map_err(M24512Error::I2c)?;
Timer::after(Duration::from_millis(Self::WRITE_CYCLE_TIME_MS)).await;
Ok(())
}
pub async fn is_ready(&mut self) -> Result<bool, M24512Error<E>> {
match self.i2c.write(self.device_address, &[]).await {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
pub async fn wait_ready(&mut self, timeout_ms: u64) -> Result<(), M24512Error<E>> {
let start = embassy_time::Instant::now();
let timeout = Duration::from_millis(timeout_ms);
while embassy_time::Instant::now() - start < timeout {
if self.is_ready().await? {
return Ok(());
}
Timer::after(Duration::from_millis(1)).await;
}
Err(M24512Error::Timeout)
}
pub const fn capacity(&self) -> usize {
Self::MEMORY_SIZE
}
pub fn device_address(&self) -> u8 {
self.device_address
}
pub fn destroy(self) -> (I2C, WC) {
(self.i2c, self.write_control_pin)
}
}
impl<I2C, WC, E> ErrorType for M24512<I2C, WC>
where
I2C: I2c<Error = E>,
E: Debug,
{
type Error = M24512Error<E>;
}
impl<I2C, WC, E> AsyncReadNorFlash for M24512<I2C, WC>
where
I2C: I2c<Error = E>,
WC: OutputPin,
E: Debug,
{
const READ_SIZE: usize = 1;
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read_bytes(offset as u16, bytes).await
}
fn capacity(&self) -> usize {
Self::MEMORY_SIZE
}
}
impl<I2C, WC, E> AsyncNorFlash for M24512<I2C, WC>
where
I2C: I2c<Error = E>,
WC: OutputPin,
E: Debug,
{
const WRITE_SIZE: usize = 1;
const ERASE_SIZE: usize = Self::PAGE_SIZE;
async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
let _ = self.enable_write();
let result = self.write_bytes(offset as u16, bytes).await;
let _ = self.disable_write();
result
}
async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
let _ = self.enable_write();
let mut offset = from;
while offset < to {
let chunk_size = core::cmp::min(128, (to - offset) as usize);
let erase_data = [0xFFu8; 128];
self.write_bytes(offset as u16, &erase_data[..chunk_size]).await?;
offset += chunk_size as u32;
}
let _ = self.disable_write();
Ok(())
}
}
impl<I2C, WC, E> AsyncMultiwriteNorFlash for M24512<I2C, WC>
where
I2C: I2c<Error = E>,
WC: OutputPin,
E: Debug,
{}