From ab5e0150d46b8b97d5ffe430b3e7a1caaae0438c Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Tue, 20 May 2025 18:48:44 +0300 Subject: [PATCH 01/14] update stm32-metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 413c92cce..2ab4f9960 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } vcell = "0.1.3" nb = "1.0.0" @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } [features] default = ["rt"] From e93ae32546b754ee9b54405aca81d936087ea8c7 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Tue, 20 May 2025 21:31:32 +0300 Subject: [PATCH 02/14] adding eeprom constants to _generated.rs --- embassy-stm32/build.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b00b6a7ac..b91934af3 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1922,6 +1922,47 @@ fn main() { pub const WRITE_SIZE: usize = #write_size; )); + // ======== + // Generate EEPROM constants + + let eeprom_memory_regions: Vec<&MemoryRegion> = + memory.iter().filter(|x| x.kind == MemoryRegionKind::Eeprom).collect(); + + if !eeprom_memory_regions.is_empty() { + cfgs.enable("eeprom"); + cfgs.declare("eeprom"); + + let mut sorted_eeprom_regions = eeprom_memory_regions.clone(); + sorted_eeprom_regions.sort_by_key(|r| r.address); + + let first_eeprom_address = sorted_eeprom_regions[0].address; + let mut total_eeprom_size = 0; + let mut current_expected_address = first_eeprom_address; + + for region in sorted_eeprom_regions.iter() { + if region.address != current_expected_address { + // For STM32L0 and STM32L1, EEPROM regions (if multiple) are expected to be contiguous. + // If they are not, this indicates an issue with the chip metadata or an unsupported configuration. + panic!( + "EEPROM regions for chip {} are not contiguous, which is unexpected for L0/L1 series. \ + First region: '{}' at {:#X}. Found next non-contiguous region: '{}' at {:#X}. \ + Please verify chip metadata. Embassy currently assumes contiguous EEPROM for these series.", + chip_name, sorted_eeprom_regions[0].name, first_eeprom_address, region.name, region.address + ); + } + total_eeprom_size += region.size; + current_expected_address += region.size; + } + + let eeprom_base_usize = first_eeprom_address as usize; + let total_eeprom_size_usize = total_eeprom_size as usize; + + g.extend(quote! { + pub const EEPROM_BASE: usize = #eeprom_base_usize; + pub const EEPROM_SIZE: usize = #total_eeprom_size_usize; + }); + } + // ======== // Generate macro-tables From c9f0afa494636533c838473bcb5d27ff8f011fb7 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 00:37:46 +0300 Subject: [PATCH 03/14] import and re-export EEPROM_BASE and EEPROM_SIZE in flash module --- embassy-stm32/src/flash/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index adc45db9c..ab956664b 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -13,6 +13,8 @@ pub use common::*; pub use crate::_generated::flash_regions::*; pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; +#[cfg(eeprom)] +pub use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; /// Get all flash regions. pub fn get_flash_regions() -> &'static [&'static FlashRegion] { From d335e309012020b5eab6f0d04ee63053365140ea Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 00:44:24 +0300 Subject: [PATCH 04/14] only support eeprom for l0 and l1 --- embassy-stm32/src/flash/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index ab956664b..26f357370 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -12,9 +12,9 @@ pub use asynch::InterruptHandler; pub use common::*; pub use crate::_generated::flash_regions::*; -pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; #[cfg(eeprom)] pub use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; +pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; /// Get all flash regions. pub fn get_flash_regions() -> &'static [&'static FlashRegion] { @@ -85,7 +85,8 @@ pub enum FlashBank { /// OTP region, Otp, } - +#[cfg(all(eeprom, not(any(flash_l0, flash_l1))))] +compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is an unsupported configuration."); #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f0, path = "f0.rs")] #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] From 7be84f137c4f56842b6ebd6de3538b0081d42a09 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 01:21:43 +0300 Subject: [PATCH 05/14] eeprom support --- embassy-stm32/src/flash/common.rs | 97 +++++++++++++++++++++++++++++++ embassy-stm32/src/flash/l.rs | 2 +- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 10023e637..81d8966e9 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -11,6 +11,9 @@ use crate::Peri; use crate::_generated::FLASH_BASE; use crate::peripherals::FLASH; +#[cfg(eeprom)] +use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; + /// Internal flash memory driver. pub struct Flash<'d, MODE = Async> { pub(crate) inner: Peri<'d, FLASH>, @@ -72,6 +75,100 @@ 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/l.rs b/embassy-stm32/src/flash/l.rs index 65cea005c..1b82704ec 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -162,7 +162,7 @@ pub(crate) unsafe fn clear_all_err() { pac::FLASH.nssr().modify(|_| {}); } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { #[cfg(not(flash_l5))] { From 6d19f2fd4cd073359f543776475c6ef6c5fddd00 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 02:43:41 +0300 Subject: [PATCH 06/14] nightly fmt --- embassy-stm32/src/flash/common.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 81d8966e9..fb899b813 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -9,10 +9,9 @@ use super::{ }; use crate::Peri; use crate::_generated::FLASH_BASE; -use crate::peripherals::FLASH; - #[cfg(eeprom)] use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; +use crate::peripherals::FLASH; /// Internal flash memory driver. pub struct Flash<'d, MODE = Async> { From 21004fce0dae25a5f27b627861fad8912e7256f5 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 02:50:36 +0300 Subject: [PATCH 07/14] always declare eeprom --- embassy-stm32/build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b91934af3..bb5ef53d7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1925,12 +1925,13 @@ fn main() { // ======== // Generate EEPROM constants + cfgs.declare("eeprom"); + let eeprom_memory_regions: Vec<&MemoryRegion> = memory.iter().filter(|x| x.kind == MemoryRegionKind::Eeprom).collect(); if !eeprom_memory_regions.is_empty() { cfgs.enable("eeprom"); - cfgs.declare("eeprom"); let mut sorted_eeprom_regions = eeprom_memory_regions.clone(); sorted_eeprom_regions.sort_by_key(|r| r.address); From 1accd560054f4203e193c6e77baca1c448fbf1c9 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 08:30:38 +0300 Subject: [PATCH 08/14] new API --- embassy-stm32/src/flash/common.rs | 96 ------------- embassy-stm32/src/flash/eeprom.rs | 224 ++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 5 + 3 files changed, 229 insertions(+), 96 deletions(-) create mode 100644 embassy-stm32/src/flash/eeprom.rs 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)] From bc265b98b7597ffb90fe4e951beee5d65c6c481f Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 08:41:04 +0300 Subject: [PATCH 09/14] adding docs --- embassy-stm32/src/flash/eeprom.rs | 88 ++++++++++++++++--------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index 74aedb7c0..99c6b9c10 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs @@ -4,6 +4,7 @@ use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; impl<'d> Flash<'d, Blocking> { // --- Internal helpers --- + /// Checks if the given offset and size are within the EEPROM bounds. fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> { if offset .checked_add(size) @@ -18,6 +19,10 @@ impl<'d> Flash<'d, Blocking> { // --- Unlocked (unsafe, internal) functions --- + /// Writes a slice of bytes to EEPROM at the given offset without locking. + /// + /// # Safety + /// Caller must ensure EEPROM is unlocked and offset is valid. 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; @@ -28,6 +33,10 @@ impl<'d> Flash<'d, Blocking> { Ok(()) } + /// Writes a slice of u16 values to EEPROM at the given offset without locking. + /// + /// # Safety + /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. 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; @@ -38,6 +47,10 @@ impl<'d> Flash<'d, Blocking> { Ok(()) } + /// Writes a slice of u32 values to EEPROM at the given offset without locking. + /// + /// # Safety + /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. 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; @@ -50,20 +63,20 @@ impl<'d> Flash<'d, Blocking> { // --- Public, safe API --- + /// Writes a single byte 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)?; unsafe { family::unlock(); - } - unsafe { self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?; - } - unsafe { family::lock(); } Ok(()) } + /// Writes a single 16-bit value to EEPROM at the given offset. + /// + /// Returns an error if the offset is not 2-byte aligned. pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> { if offset % 2 != 0 { return Err(Error::Unaligned); @@ -71,16 +84,15 @@ impl<'d> Flash<'d, Blocking> { 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(()) } + /// Writes a single 32-bit value to EEPROM at the given offset. + /// + /// Returns an error if the offset is not 4-byte aligned. pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> { if offset % 4 != 0 { return Err(Error::Unaligned); @@ -88,30 +100,26 @@ impl<'d> Flash<'d, Blocking> { 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(()) } + /// Writes a slice of bytes to EEPROM at the given offset. 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(()) } + /// Writes a slice of 16-bit values to EEPROM at the given offset. + /// + /// Returns an error if the offset is not 2-byte aligned. pub fn eeprom_write_u16_slice(&mut self, offset: u32, data: &[u16]) -> Result<(), Error> { if offset % 2 != 0 { return Err(Error::Unaligned); @@ -119,16 +127,15 @@ impl<'d> Flash<'d, Blocking> { 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(()) } + /// Writes a slice of 32-bit values to EEPROM at the given offset. + /// + /// Returns an error if the offset is not 4-byte aligned. pub fn eeprom_write_u32_slice(&mut self, offset: u32, data: &[u32]) -> Result<(), Error> { if offset % 4 != 0 { return Err(Error::Unaligned); @@ -136,23 +143,18 @@ impl<'d> Flash<'d, Blocking> { 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(()) } + /// Writes a byte slice to EEPROM at the given offset, handling alignment. + /// + /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. pub fn eeprom_write(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { + self.check_eeprom_offset(offset, data.len() as u32)?; 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 @@ -165,37 +167,33 @@ impl<'d> Flash<'d, Blocking> { unsafe { family::unlock(); - } - if !prefix.is_empty() { - unsafe { + if !prefix.is_empty() { 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 { + if !aligned.is_empty() { + let aligned_offset = start + prefix_len as u32; + let u32_data = core::slice::from_raw_parts(aligned.as_ptr() as *const u32, aligned.len() / 4); 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 { + if !suffix.is_empty() { + let suffix_offset = start + (prefix_len + aligned_len) as u32; self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?; } - } - unsafe { family::lock(); } Ok(()) } + /// Reads a single byte from EEPROM at the given offset. 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) }) } + /// Reads a single 16-bit value from EEPROM at the given offset. + /// + /// Returns an error if the offset is not 2-byte aligned. pub fn eeprom_read_u16(&self, offset: u32) -> Result { if offset % 2 != 0 { return Err(Error::Unaligned); @@ -205,6 +203,9 @@ impl<'d> Flash<'d, Blocking> { Ok(unsafe { core::ptr::read_volatile(addr as *const u16) }) } + /// Reads a single 32-bit value from EEPROM at the given offset. + /// + /// Returns an error if the offset is not 4-byte aligned. pub fn eeprom_read_u32(&self, offset: u32) -> Result { if offset % 4 != 0 { return Err(Error::Unaligned); @@ -214,6 +215,7 @@ impl<'d> Flash<'d, Blocking> { Ok(unsafe { core::ptr::read_volatile(addr as *const u32) }) } + /// Reads a slice of bytes from EEPROM at the given offset into the provided buffer. 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; From 437e45df2b468caf13475ffaf3d55cc4f01e2d6b Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 09:44:28 +0300 Subject: [PATCH 10/14] make API more consistent --- embassy-stm32/src/flash/eeprom.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index 99c6b9c10..5bccb2ecc 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs @@ -152,7 +152,7 @@ impl<'d> Flash<'d, Blocking> { /// Writes a byte slice to EEPROM at the given offset, handling alignment. /// /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. - pub fn eeprom_write(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { + pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { self.check_eeprom_offset(offset, data.len() as u32)?; let start = offset; let misalign = (start % 4) as usize; From 27ca627fc83974d926630b4a1bfc9783c3c86bb9 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 09:54:19 +0300 Subject: [PATCH 11/14] added examples --- examples/stm32l0/src/bin/eeprom.rs | 32 ++++++++++++++++++++++++++++++ examples/stm32l1/src/bin/eeprom.rs | 32 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 examples/stm32l0/src/bin/eeprom.rs create mode 100644 examples/stm32l1/src/bin/eeprom.rs diff --git a/examples/stm32l0/src/bin/eeprom.rs b/examples/stm32l0/src/bin/eeprom.rs new file mode 100644 index 000000000..370246644 --- /dev/null +++ b/examples/stm32l0/src/bin/eeprom.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); + + const ADDR: u32 = 0x0; + + let mut f = Flash::new_blocking(p.FLASH); + + info!("Reading..."); + let mut buf = [0u8; 8]; + unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + + info!("Reading..."); + let mut buf = [0u8; 8]; + unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); +} diff --git a/examples/stm32l1/src/bin/eeprom.rs b/examples/stm32l1/src/bin/eeprom.rs new file mode 100644 index 000000000..370246644 --- /dev/null +++ b/examples/stm32l1/src/bin/eeprom.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); + + const ADDR: u32 = 0x0; + + let mut f = Flash::new_blocking(p.FLASH); + + info!("Reading..."); + let mut buf = [0u8; 8]; + unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + + info!("Reading..."); + let mut buf = [0u8; 8]; + unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); +} From 966186064ebf36634e9930dd4b2d83ed675f6eb0 Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 20:59:49 +0300 Subject: [PATCH 12/14] fix UB --- embassy-stm32/src/flash/eeprom.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index 5bccb2ecc..68bcc6c15 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs @@ -154,6 +154,7 @@ impl<'d> Flash<'d, Blocking> { /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { self.check_eeprom_offset(offset, data.len() as u32)?; + let start = offset; let misalign = (start % 4) as usize; let prefix_len = if misalign == 0 { @@ -163,17 +164,24 @@ impl<'d> Flash<'d, Blocking> { }; let (prefix, rest) = data.split_at(prefix_len); let aligned_len = (rest.len() / 4) * 4; - let (aligned, suffix) = rest.split_at(aligned_len); + let (bytes_for_u32_write, suffix) = rest.split_at(aligned_len); unsafe { family::unlock(); if !prefix.is_empty() { self.eeprom_write_u8_slice_unlocked(start, prefix)?; } - if !aligned.is_empty() { - let aligned_offset = start + prefix_len as u32; - let u32_data = core::slice::from_raw_parts(aligned.as_ptr() as *const u32, aligned.len() / 4); - self.eeprom_write_u32_slice_unlocked(aligned_offset, u32_data)?; + if !bytes_for_u32_write.is_empty() { + let aligned_eeprom_offset = start + prefix_len as u32; + let base_eeprom_addr = EEPROM_BASE as u32 + aligned_eeprom_offset; + for (i, chunk) in bytes_for_u32_write.chunks_exact(4).enumerate() { + // Safely read a u32 from a potentially unaligned pointer into the chunk. + let value = (chunk.as_ptr() as *const u32).read_unaligned(); + let current_eeprom_addr = base_eeprom_addr + (i * 4) as u32; + core::ptr::write_volatile(current_eeprom_addr as *mut u32, value); + family::wait_ready_blocking()?; + family::clear_all_err(); + } } if !suffix.is_empty() { let suffix_offset = start + (prefix_len + aligned_len) as u32; From e4a6d7aeddd686e47e1a26cd129e7bb646dcb0be Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 21:30:23 +0300 Subject: [PATCH 13/14] fix: lock on early return --- embassy-stm32/src/flash/eeprom.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index 68bcc6c15..cc3529eb9 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs @@ -1,3 +1,5 @@ +use embassy_hal_internal::drop::OnDrop; + use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; #[cfg(eeprom)] @@ -68,8 +70,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, 1)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?; - family::lock(); } Ok(()) } @@ -84,8 +86,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, 2)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u16_slice_unlocked(offset, core::slice::from_ref(&value))?; - family::lock(); } Ok(()) } @@ -100,8 +102,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, 4)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u32_slice_unlocked(offset, core::slice::from_ref(&value))?; - family::lock(); } Ok(()) } @@ -111,8 +113,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, data.len() as u32)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u8_slice_unlocked(offset, data)?; - family::lock(); } Ok(()) } @@ -127,8 +129,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, data.len() as u32 * 2)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u16_slice_unlocked(offset, data)?; - family::lock(); } Ok(()) } @@ -143,8 +145,8 @@ impl<'d> Flash<'d, Blocking> { self.check_eeprom_offset(offset, data.len() as u32 * 4)?; unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); self.eeprom_write_u32_slice_unlocked(offset, data)?; - family::lock(); } Ok(()) } @@ -154,7 +156,6 @@ impl<'d> Flash<'d, Blocking> { /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { self.check_eeprom_offset(offset, data.len() as u32)?; - let start = offset; let misalign = (start % 4) as usize; let prefix_len = if misalign == 0 { @@ -168,6 +169,8 @@ impl<'d> Flash<'d, Blocking> { unsafe { family::unlock(); + let _on_drop = OnDrop::new(|| family::lock()); + if !prefix.is_empty() { self.eeprom_write_u8_slice_unlocked(start, prefix)?; } @@ -187,7 +190,6 @@ impl<'d> Flash<'d, Blocking> { let suffix_offset = start + (prefix_len + aligned_len) as u32; self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?; } - family::lock(); } Ok(()) } From c88bc972316634da586589adcefa49bb02a2cf0f Mon Sep 17 00:00:00 2001 From: okhsunrog Date: Wed, 21 May 2025 22:33:44 +0300 Subject: [PATCH 14/14] added eeprom to tests --- tests/stm32/Cargo.toml | 10 ++++++++-- tests/stm32/src/bin/eeprom.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/stm32/src/bin/eeprom.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 6036a5a97..5a483849c 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -19,8 +19,8 @@ stm32h563zi = ["embassy-stm32/stm32h563zi", "spi-v345", "chrono", "eth", "rng", stm32h753zi = ["embassy-stm32/stm32h753zi", "spi-v345", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "spi-v345", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "spi-v345", "not-gpdma", "rng", "fdcan"] -stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] -stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma"] +stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng", "eeprom"] +stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma", "eeprom"] stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-bank"] @@ -55,6 +55,7 @@ ucpd = [] cordic = ["dep:num-traits"] dual-bank = ["embassy-stm32/dual-bank"] single-bank = ["embassy-stm32/single-bank"] +eeprom = [] cm0 = ["portable-atomic/unsafe-assume-single-core"] @@ -119,6 +120,11 @@ name = "dac_l1" path = "src/bin/dac_l1.rs" required-features = [ "stm32l152re",] +[[bin]] +name = "eeprom" +path = "src/bin/eeprom.rs" +required-features = [ "eeprom",] + [[bin]] name = "eth" path = "src/bin/eth.rs" diff --git a/tests/stm32/src/bin/eeprom.rs b/tests/stm32/src/bin/eeprom.rs new file mode 100644 index 000000000..61d249fd7 --- /dev/null +++ b/tests/stm32/src/bin/eeprom.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +// required-features: eeprom + +#[path = "../common.rs"] +mod common; + +use common::*; +use defmt::assert_eq; +use embassy_executor::Spawner; +use embassy_stm32::flash::Flash; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = init(); + + let mut f = Flash::new_blocking(p.FLASH); + const ADDR: u32 = 0x0; + + unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + let mut buf = [0u8; 8]; + unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); + assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); + + info!("Test OK"); + cortex_m::asm::bkpt(); +}