diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index fb899b813..10023e637 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -9,8 +9,6 @@ use super::{ }; use crate::Peri; use crate::_generated::FLASH_BASE; -#[cfg(eeprom)] -use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; use crate::peripherals::FLASH; /// Internal flash memory driver. @@ -74,100 +72,6 @@ impl<'d, MODE> Flash<'d, MODE> { } } -#[cfg(eeprom)] -impl<'d> Flash<'d, Blocking> { - fn eeprom_base(&self) -> u32 { - EEPROM_BASE as u32 - } - - fn eeprom_size(&self) -> u32 { - EEPROM_SIZE as u32 - } - - fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> { - if offset + size > self.eeprom_size() { - Err(Error::Size) - } else { - Ok(()) - } - } - - /// Read a byte (u8) from EEPROM at the given offset. - pub fn eeprom_read_u8(&self, offset: u32) -> Result { - self.check_eeprom_offset(offset, 1)?; - let addr = self.eeprom_base() + offset; - Ok(unsafe { core::ptr::read_volatile(addr as *const u8) }) - } - - /// Read a half-word (u16) from EEPROM at the given offset. - pub fn eeprom_read_u16(&self, offset: u32) -> Result { - if offset % 2 != 0 { - return Err(Error::Unaligned); - } - self.check_eeprom_offset(offset, 2)?; - let addr = self.eeprom_base() + offset; - Ok(unsafe { core::ptr::read_volatile(addr as *const u16) }) - } - - /// Read a word (u32) from EEPROM at the given offset. - pub fn eeprom_read_u32(&self, offset: u32) -> Result { - if offset % 4 != 0 { - return Err(Error::Unaligned); - } - self.check_eeprom_offset(offset, 4)?; - let addr = self.eeprom_base() + offset; - Ok(unsafe { core::ptr::read_volatile(addr as *const u32) }) - } - - /// Write a byte (u8) to EEPROM at the given offset. - pub fn eeprom_write_u8(&mut self, offset: u32, value: u8) -> Result<(), Error> { - self.check_eeprom_offset(offset, 1)?; - let addr = self.eeprom_base() + offset; - unsafe { - family::unlock(); - core::ptr::write_volatile(addr as *mut u8, value); - family::wait_ready_blocking()?; - family::clear_all_err(); - family::lock(); - } - Ok(()) - } - - /// Write a half-word (u16) to EEPROM at the given offset. - pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> { - if offset % 2 != 0 { - return Err(Error::Unaligned); - } - self.check_eeprom_offset(offset, 2)?; - let addr = self.eeprom_base() + offset; - unsafe { - family::unlock(); - core::ptr::write_volatile(addr as *mut u16, value); - family::wait_ready_blocking()?; - family::clear_all_err(); - family::lock(); - } - Ok(()) - } - - /// Write a word (u32) to EEPROM at the given offset. - pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> { - if offset % 4 != 0 { - return Err(Error::Unaligned); - } - self.check_eeprom_offset(offset, 4)?; - let addr = self.eeprom_base() + offset; - unsafe { - family::unlock(); - core::ptr::write_volatile(addr as *mut u32, value); - family::wait_ready_blocking()?; - family::clear_all_err(); - family::lock(); - } - Ok(()) - } -} - pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs new file mode 100644 index 000000000..74aedb7c0 --- /dev/null +++ b/embassy-stm32/src/flash/eeprom.rs @@ -0,0 +1,224 @@ +use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; + +#[cfg(eeprom)] +impl<'d> Flash<'d, Blocking> { + // --- Internal helpers --- + + fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> { + if offset + .checked_add(size) + .filter(|&end| end <= EEPROM_SIZE as u32) + .is_some() + { + Ok(()) + } else { + Err(Error::Size) + } + } + + // --- Unlocked (unsafe, internal) functions --- + + unsafe fn eeprom_write_u8_slice_unlocked(&self, offset: u32, data: &[u8]) -> Result<(), Error> { + for (i, &byte) in data.iter().enumerate() { + let addr = EEPROM_BASE as u32 + offset + i as u32; + core::ptr::write_volatile(addr as *mut u8, byte); + family::wait_ready_blocking()?; + family::clear_all_err(); + } + Ok(()) + } + + unsafe fn eeprom_write_u16_slice_unlocked(&self, offset: u32, data: &[u16]) -> Result<(), Error> { + for (i, &value) in data.iter().enumerate() { + let addr = EEPROM_BASE as u32 + offset + i as u32 * 2; + core::ptr::write_volatile(addr as *mut u16, value); + family::wait_ready_blocking()?; + family::clear_all_err(); + } + Ok(()) + } + + unsafe fn eeprom_write_u32_slice_unlocked(&self, offset: u32, data: &[u32]) -> Result<(), Error> { + for (i, &value) in data.iter().enumerate() { + let addr = EEPROM_BASE as u32 + offset + i as u32 * 4; + core::ptr::write_volatile(addr as *mut u32, value); + family::wait_ready_blocking()?; + family::clear_all_err(); + } + Ok(()) + } + + // --- Public, safe API --- + + pub fn eeprom_write_u8(&mut self, offset: u32, value: u8) -> Result<(), Error> { + self.check_eeprom_offset(offset, 1)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> { + if offset % 2 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, 2)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u16_slice_unlocked(offset, core::slice::from_ref(&value))?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> { + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, 4)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u32_slice_unlocked(offset, core::slice::from_ref(&value))?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write_u8_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { + self.check_eeprom_offset(offset, data.len() as u32)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u8_slice_unlocked(offset, data)?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write_u16_slice(&mut self, offset: u32, data: &[u16]) -> Result<(), Error> { + if offset % 2 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, data.len() as u32 * 2)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u16_slice_unlocked(offset, data)?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write_u32_slice(&mut self, offset: u32, data: &[u32]) -> Result<(), Error> { + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, data.len() as u32 * 4)?; + unsafe { + family::unlock(); + } + unsafe { + self.eeprom_write_u32_slice_unlocked(offset, data)?; + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_write(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { + let start = offset; + let end = offset.checked_add(data.len() as u32).ok_or(Error::Size)?; + if end > EEPROM_SIZE as u32 { + return Err(Error::Size); + } + + let misalign = (start % 4) as usize; + let prefix_len = if misalign == 0 { + 0 + } else { + (4 - misalign).min(data.len()) + }; + let (prefix, rest) = data.split_at(prefix_len); + let aligned_len = (rest.len() / 4) * 4; + let (aligned, suffix) = rest.split_at(aligned_len); + + unsafe { + family::unlock(); + } + if !prefix.is_empty() { + unsafe { + self.eeprom_write_u8_slice_unlocked(start, prefix)?; + } + } + if !aligned.is_empty() { + let aligned_offset = start + prefix_len as u32; + let u32_data = unsafe { core::slice::from_raw_parts(aligned.as_ptr() as *const u32, aligned.len() / 4) }; + unsafe { + self.eeprom_write_u32_slice_unlocked(aligned_offset, u32_data)?; + } + } + if !suffix.is_empty() { + let suffix_offset = start + (prefix_len + aligned_len) as u32; + unsafe { + self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?; + } + } + unsafe { + family::lock(); + } + Ok(()) + } + + pub fn eeprom_read_u8(&self, offset: u32) -> Result { + self.check_eeprom_offset(offset, 1)?; + let addr = EEPROM_BASE as u32 + offset; + Ok(unsafe { core::ptr::read_volatile(addr as *const u8) }) + } + + pub fn eeprom_read_u16(&self, offset: u32) -> Result { + if offset % 2 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, 2)?; + let addr = EEPROM_BASE as u32 + offset; + Ok(unsafe { core::ptr::read_volatile(addr as *const u16) }) + } + + pub fn eeprom_read_u32(&self, offset: u32) -> Result { + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + self.check_eeprom_offset(offset, 4)?; + let addr = EEPROM_BASE as u32 + offset; + Ok(unsafe { core::ptr::read_volatile(addr as *const u32) }) + } + + pub fn eeprom_read_slice(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { + self.check_eeprom_offset(offset, buf.len() as u32)?; + let addr = EEPROM_BASE as u32 + offset; + let src = unsafe { core::slice::from_raw_parts(addr as *const u8, buf.len()) }; + buf.copy_from_slice(src); + Ok(()) + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 26f357370..a3f9e00f7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -5,11 +5,16 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; mod asynch; #[cfg(flash)] mod common; +#[cfg(eeprom)] +mod eeprom; #[cfg(flash_f4)] pub use asynch::InterruptHandler; #[cfg(flash)] pub use common::*; +#[cfg(eeprom)] +#[allow(unused_imports)] +pub use eeprom::*; pub use crate::_generated::flash_regions::*; #[cfg(eeprom)]