diff options
| author | Ulf Lilleengen <[email protected]> | 2025-05-23 14:15:15 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-05-23 14:15:15 +0200 |
| commit | 94f9b2707486ca3eade5bf4b237edf3d6aa90f35 (patch) | |
| tree | ed91d32d5c462bc919957bc6ea3eccccd9c0d260 | |
| parent | f7405493c184ce453ac3f7ba97f7f2689f978194 (diff) | |
| parent | c88bc972316634da586589adcefa49bb02a2cf0f (diff) | |
Merge pull request #4228 from okhsunrog/adding_eeprom
Adding EEPROM support to embassy-stm32
| -rw-r--r-- | embassy-stm32/build.rs | 42 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/eeprom.rs | 236 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/l.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/mod.rs | 10 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/eeprom.rs | 32 | ||||
| -rw-r--r-- | examples/stm32l1/src/bin/eeprom.rs | 32 | ||||
| -rw-r--r-- | tests/stm32/Cargo.toml | 10 | ||||
| -rw-r--r-- | tests/stm32/src/bin/eeprom.rs | 30 |
8 files changed, 390 insertions, 4 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b00b6a7ac..bb5ef53d7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1923,6 +1923,48 @@ fn main() { | |||
| 1923 | )); | 1923 | )); |
| 1924 | 1924 | ||
| 1925 | // ======== | 1925 | // ======== |
| 1926 | // Generate EEPROM constants | ||
| 1927 | |||
| 1928 | cfgs.declare("eeprom"); | ||
| 1929 | |||
| 1930 | let eeprom_memory_regions: Vec<&MemoryRegion> = | ||
| 1931 | memory.iter().filter(|x| x.kind == MemoryRegionKind::Eeprom).collect(); | ||
| 1932 | |||
| 1933 | if !eeprom_memory_regions.is_empty() { | ||
| 1934 | cfgs.enable("eeprom"); | ||
| 1935 | |||
| 1936 | let mut sorted_eeprom_regions = eeprom_memory_regions.clone(); | ||
| 1937 | sorted_eeprom_regions.sort_by_key(|r| r.address); | ||
| 1938 | |||
| 1939 | let first_eeprom_address = sorted_eeprom_regions[0].address; | ||
| 1940 | let mut total_eeprom_size = 0; | ||
| 1941 | let mut current_expected_address = first_eeprom_address; | ||
| 1942 | |||
| 1943 | for region in sorted_eeprom_regions.iter() { | ||
| 1944 | if region.address != current_expected_address { | ||
| 1945 | // For STM32L0 and STM32L1, EEPROM regions (if multiple) are expected to be contiguous. | ||
| 1946 | // If they are not, this indicates an issue with the chip metadata or an unsupported configuration. | ||
| 1947 | panic!( | ||
| 1948 | "EEPROM regions for chip {} are not contiguous, which is unexpected for L0/L1 series. \ | ||
| 1949 | First region: '{}' at {:#X}. Found next non-contiguous region: '{}' at {:#X}. \ | ||
| 1950 | Please verify chip metadata. Embassy currently assumes contiguous EEPROM for these series.", | ||
| 1951 | chip_name, sorted_eeprom_regions[0].name, first_eeprom_address, region.name, region.address | ||
| 1952 | ); | ||
| 1953 | } | ||
| 1954 | total_eeprom_size += region.size; | ||
| 1955 | current_expected_address += region.size; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | let eeprom_base_usize = first_eeprom_address as usize; | ||
| 1959 | let total_eeprom_size_usize = total_eeprom_size as usize; | ||
| 1960 | |||
| 1961 | g.extend(quote! { | ||
| 1962 | pub const EEPROM_BASE: usize = #eeprom_base_usize; | ||
| 1963 | pub const EEPROM_SIZE: usize = #total_eeprom_size_usize; | ||
| 1964 | }); | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | // ======== | ||
| 1926 | // Generate macro-tables | 1968 | // Generate macro-tables |
| 1927 | 1969 | ||
| 1928 | for irq in METADATA.interrupts { | 1970 | for irq in METADATA.interrupts { |
diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs new file mode 100644 index 000000000..cc3529eb9 --- /dev/null +++ b/embassy-stm32/src/flash/eeprom.rs | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | use embassy_hal_internal::drop::OnDrop; | ||
| 2 | |||
| 3 | use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 4 | |||
| 5 | #[cfg(eeprom)] | ||
| 6 | impl<'d> Flash<'d, Blocking> { | ||
| 7 | // --- Internal helpers --- | ||
| 8 | |||
| 9 | /// Checks if the given offset and size are within the EEPROM bounds. | ||
| 10 | fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> { | ||
| 11 | if offset | ||
| 12 | .checked_add(size) | ||
| 13 | .filter(|&end| end <= EEPROM_SIZE as u32) | ||
| 14 | .is_some() | ||
| 15 | { | ||
| 16 | Ok(()) | ||
| 17 | } else { | ||
| 18 | Err(Error::Size) | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | // --- Unlocked (unsafe, internal) functions --- | ||
| 23 | |||
| 24 | /// Writes a slice of bytes to EEPROM at the given offset without locking. | ||
| 25 | /// | ||
| 26 | /// # Safety | ||
| 27 | /// Caller must ensure EEPROM is unlocked and offset is valid. | ||
| 28 | unsafe fn eeprom_write_u8_slice_unlocked(&self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 29 | for (i, &byte) in data.iter().enumerate() { | ||
| 30 | let addr = EEPROM_BASE as u32 + offset + i as u32; | ||
| 31 | core::ptr::write_volatile(addr as *mut u8, byte); | ||
| 32 | family::wait_ready_blocking()?; | ||
| 33 | family::clear_all_err(); | ||
| 34 | } | ||
| 35 | Ok(()) | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Writes a slice of u16 values to EEPROM at the given offset without locking. | ||
| 39 | /// | ||
| 40 | /// # Safety | ||
| 41 | /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. | ||
| 42 | unsafe fn eeprom_write_u16_slice_unlocked(&self, offset: u32, data: &[u16]) -> Result<(), Error> { | ||
| 43 | for (i, &value) in data.iter().enumerate() { | ||
| 44 | let addr = EEPROM_BASE as u32 + offset + i as u32 * 2; | ||
| 45 | core::ptr::write_volatile(addr as *mut u16, value); | ||
| 46 | family::wait_ready_blocking()?; | ||
| 47 | family::clear_all_err(); | ||
| 48 | } | ||
| 49 | Ok(()) | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Writes a slice of u32 values to EEPROM at the given offset without locking. | ||
| 53 | /// | ||
| 54 | /// # Safety | ||
| 55 | /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. | ||
| 56 | unsafe fn eeprom_write_u32_slice_unlocked(&self, offset: u32, data: &[u32]) -> Result<(), Error> { | ||
| 57 | for (i, &value) in data.iter().enumerate() { | ||
| 58 | let addr = EEPROM_BASE as u32 + offset + i as u32 * 4; | ||
| 59 | core::ptr::write_volatile(addr as *mut u32, value); | ||
| 60 | family::wait_ready_blocking()?; | ||
| 61 | family::clear_all_err(); | ||
| 62 | } | ||
| 63 | Ok(()) | ||
| 64 | } | ||
| 65 | |||
| 66 | // --- Public, safe API --- | ||
| 67 | |||
| 68 | /// Writes a single byte to EEPROM at the given offset. | ||
| 69 | pub fn eeprom_write_u8(&mut self, offset: u32, value: u8) -> Result<(), Error> { | ||
| 70 | self.check_eeprom_offset(offset, 1)?; | ||
| 71 | unsafe { | ||
| 72 | family::unlock(); | ||
| 73 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 74 | self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 75 | } | ||
| 76 | Ok(()) | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Writes a single 16-bit value to EEPROM at the given offset. | ||
| 80 | /// | ||
| 81 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 82 | pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> { | ||
| 83 | if offset % 2 != 0 { | ||
| 84 | return Err(Error::Unaligned); | ||
| 85 | } | ||
| 86 | self.check_eeprom_offset(offset, 2)?; | ||
| 87 | unsafe { | ||
| 88 | family::unlock(); | ||
| 89 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 90 | self.eeprom_write_u16_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 91 | } | ||
| 92 | Ok(()) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Writes a single 32-bit value to EEPROM at the given offset. | ||
| 96 | /// | ||
| 97 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 98 | pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> { | ||
| 99 | if offset % 4 != 0 { | ||
| 100 | return Err(Error::Unaligned); | ||
| 101 | } | ||
| 102 | self.check_eeprom_offset(offset, 4)?; | ||
| 103 | unsafe { | ||
| 104 | family::unlock(); | ||
| 105 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 106 | self.eeprom_write_u32_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 107 | } | ||
| 108 | Ok(()) | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Writes a slice of bytes to EEPROM at the given offset. | ||
| 112 | pub fn eeprom_write_u8_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 113 | self.check_eeprom_offset(offset, data.len() as u32)?; | ||
| 114 | unsafe { | ||
| 115 | family::unlock(); | ||
| 116 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 117 | self.eeprom_write_u8_slice_unlocked(offset, data)?; | ||
| 118 | } | ||
| 119 | Ok(()) | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Writes a slice of 16-bit values to EEPROM at the given offset. | ||
| 123 | /// | ||
| 124 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 125 | pub fn eeprom_write_u16_slice(&mut self, offset: u32, data: &[u16]) -> Result<(), Error> { | ||
| 126 | if offset % 2 != 0 { | ||
| 127 | return Err(Error::Unaligned); | ||
| 128 | } | ||
| 129 | self.check_eeprom_offset(offset, data.len() as u32 * 2)?; | ||
| 130 | unsafe { | ||
| 131 | family::unlock(); | ||
| 132 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 133 | self.eeprom_write_u16_slice_unlocked(offset, data)?; | ||
| 134 | } | ||
| 135 | Ok(()) | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Writes a slice of 32-bit values to EEPROM at the given offset. | ||
| 139 | /// | ||
| 140 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 141 | pub fn eeprom_write_u32_slice(&mut self, offset: u32, data: &[u32]) -> Result<(), Error> { | ||
| 142 | if offset % 4 != 0 { | ||
| 143 | return Err(Error::Unaligned); | ||
| 144 | } | ||
| 145 | self.check_eeprom_offset(offset, data.len() as u32 * 4)?; | ||
| 146 | unsafe { | ||
| 147 | family::unlock(); | ||
| 148 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 149 | self.eeprom_write_u32_slice_unlocked(offset, data)?; | ||
| 150 | } | ||
| 151 | Ok(()) | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Writes a byte slice to EEPROM at the given offset, handling alignment. | ||
| 155 | /// | ||
| 156 | /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. | ||
| 157 | pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 158 | self.check_eeprom_offset(offset, data.len() as u32)?; | ||
| 159 | let start = offset; | ||
| 160 | let misalign = (start % 4) as usize; | ||
| 161 | let prefix_len = if misalign == 0 { | ||
| 162 | 0 | ||
| 163 | } else { | ||
| 164 | (4 - misalign).min(data.len()) | ||
| 165 | }; | ||
| 166 | let (prefix, rest) = data.split_at(prefix_len); | ||
| 167 | let aligned_len = (rest.len() / 4) * 4; | ||
| 168 | let (bytes_for_u32_write, suffix) = rest.split_at(aligned_len); | ||
| 169 | |||
| 170 | unsafe { | ||
| 171 | family::unlock(); | ||
| 172 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 173 | |||
| 174 | if !prefix.is_empty() { | ||
| 175 | self.eeprom_write_u8_slice_unlocked(start, prefix)?; | ||
| 176 | } | ||
| 177 | if !bytes_for_u32_write.is_empty() { | ||
| 178 | let aligned_eeprom_offset = start + prefix_len as u32; | ||
| 179 | let base_eeprom_addr = EEPROM_BASE as u32 + aligned_eeprom_offset; | ||
| 180 | for (i, chunk) in bytes_for_u32_write.chunks_exact(4).enumerate() { | ||
| 181 | // Safely read a u32 from a potentially unaligned pointer into the chunk. | ||
| 182 | let value = (chunk.as_ptr() as *const u32).read_unaligned(); | ||
| 183 | let current_eeprom_addr = base_eeprom_addr + (i * 4) as u32; | ||
| 184 | core::ptr::write_volatile(current_eeprom_addr as *mut u32, value); | ||
| 185 | family::wait_ready_blocking()?; | ||
| 186 | family::clear_all_err(); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | if !suffix.is_empty() { | ||
| 190 | let suffix_offset = start + (prefix_len + aligned_len) as u32; | ||
| 191 | self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Reads a single byte from EEPROM at the given offset. | ||
| 198 | pub fn eeprom_read_u8(&self, offset: u32) -> Result<u8, Error> { | ||
| 199 | self.check_eeprom_offset(offset, 1)?; | ||
| 200 | let addr = EEPROM_BASE as u32 + offset; | ||
| 201 | Ok(unsafe { core::ptr::read_volatile(addr as *const u8) }) | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Reads a single 16-bit value from EEPROM at the given offset. | ||
| 205 | /// | ||
| 206 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 207 | pub fn eeprom_read_u16(&self, offset: u32) -> Result<u16, Error> { | ||
| 208 | if offset % 2 != 0 { | ||
| 209 | return Err(Error::Unaligned); | ||
| 210 | } | ||
| 211 | self.check_eeprom_offset(offset, 2)?; | ||
| 212 | let addr = EEPROM_BASE as u32 + offset; | ||
| 213 | Ok(unsafe { core::ptr::read_volatile(addr as *const u16) }) | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Reads a single 32-bit value from EEPROM at the given offset. | ||
| 217 | /// | ||
| 218 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 219 | pub fn eeprom_read_u32(&self, offset: u32) -> Result<u32, Error> { | ||
| 220 | if offset % 4 != 0 { | ||
| 221 | return Err(Error::Unaligned); | ||
| 222 | } | ||
| 223 | self.check_eeprom_offset(offset, 4)?; | ||
| 224 | let addr = EEPROM_BASE as u32 + offset; | ||
| 225 | Ok(unsafe { core::ptr::read_volatile(addr as *const u32) }) | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Reads a slice of bytes from EEPROM at the given offset into the provided buffer. | ||
| 229 | pub fn eeprom_read_slice(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { | ||
| 230 | self.check_eeprom_offset(offset, buf.len() as u32)?; | ||
| 231 | let addr = EEPROM_BASE as u32 + offset; | ||
| 232 | let src = unsafe { core::slice::from_raw_parts(addr as *const u8, buf.len()) }; | ||
| 233 | buf.copy_from_slice(src); | ||
| 234 | Ok(()) | ||
| 235 | } | ||
| 236 | } | ||
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() { | |||
| 162 | pac::FLASH.nssr().modify(|_| {}); | 162 | pac::FLASH.nssr().modify(|_| {}); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | unsafe fn wait_ready_blocking() -> Result<(), Error> { | 165 | pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { |
| 166 | loop { | 166 | loop { |
| 167 | #[cfg(not(flash_l5))] | 167 | #[cfg(not(flash_l5))] |
| 168 | { | 168 | { |
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index adc45db9c..a3f9e00f7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -5,13 +5,20 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | |||
| 5 | mod asynch; | 5 | mod asynch; |
| 6 | #[cfg(flash)] | 6 | #[cfg(flash)] |
| 7 | mod common; | 7 | mod common; |
| 8 | #[cfg(eeprom)] | ||
| 9 | mod eeprom; | ||
| 8 | 10 | ||
| 9 | #[cfg(flash_f4)] | 11 | #[cfg(flash_f4)] |
| 10 | pub use asynch::InterruptHandler; | 12 | pub use asynch::InterruptHandler; |
| 11 | #[cfg(flash)] | 13 | #[cfg(flash)] |
| 12 | pub use common::*; | 14 | pub use common::*; |
| 15 | #[cfg(eeprom)] | ||
| 16 | #[allow(unused_imports)] | ||
| 17 | pub use eeprom::*; | ||
| 13 | 18 | ||
| 14 | pub use crate::_generated::flash_regions::*; | 19 | pub use crate::_generated::flash_regions::*; |
| 20 | #[cfg(eeprom)] | ||
| 21 | pub use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; | ||
| 15 | pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; | 22 | pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; |
| 16 | 23 | ||
| 17 | /// Get all flash regions. | 24 | /// Get all flash regions. |
| @@ -83,7 +90,8 @@ pub enum FlashBank { | |||
| 83 | /// OTP region, | 90 | /// OTP region, |
| 84 | Otp, | 91 | Otp, |
| 85 | } | 92 | } |
| 86 | 93 | #[cfg(all(eeprom, not(any(flash_l0, flash_l1))))] | |
| 94 | compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is an unsupported configuration."); | ||
| 87 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] | 95 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] |
| 88 | #[cfg_attr(flash_f0, path = "f0.rs")] | 96 | #[cfg_attr(flash_f0, path = "f0.rs")] |
| 89 | #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] | 97 | #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = embassy_stm32::init(Default::default()); | ||
| 12 | |||
| 13 | info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); | ||
| 14 | |||
| 15 | const ADDR: u32 = 0x0; | ||
| 16 | |||
| 17 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 18 | |||
| 19 | info!("Reading..."); | ||
| 20 | let mut buf = [0u8; 8]; | ||
| 21 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 22 | info!("Read: {=[u8]:x}", buf); | ||
| 23 | |||
| 24 | info!("Writing..."); | ||
| 25 | unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | ||
| 26 | |||
| 27 | info!("Reading..."); | ||
| 28 | let mut buf = [0u8; 8]; | ||
| 29 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 30 | info!("Read: {=[u8]:x}", buf); | ||
| 31 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 32 | } | ||
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = embassy_stm32::init(Default::default()); | ||
| 12 | |||
| 13 | info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); | ||
| 14 | |||
| 15 | const ADDR: u32 = 0x0; | ||
| 16 | |||
| 17 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 18 | |||
| 19 | info!("Reading..."); | ||
| 20 | let mut buf = [0u8; 8]; | ||
| 21 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 22 | info!("Read: {=[u8]:x}", buf); | ||
| 23 | |||
| 24 | info!("Writing..."); | ||
| 25 | unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | ||
| 26 | |||
| 27 | info!("Reading..."); | ||
| 28 | let mut buf = [0u8; 8]; | ||
| 29 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 30 | info!("Read: {=[u8]:x}", buf); | ||
| 31 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 32 | } | ||
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index e78520e40..8d10f6593 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -19,8 +19,8 @@ stm32h563zi = ["embassy-stm32/stm32h563zi", "spi-v345", "chrono", "eth", "rng", | |||
| 19 | stm32h753zi = ["embassy-stm32/stm32h753zi", "spi-v345", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] | 19 | stm32h753zi = ["embassy-stm32/stm32h753zi", "spi-v345", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] |
| 20 | stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "spi-v345", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] | 20 | stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "spi-v345", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] |
| 21 | stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "spi-v345", "not-gpdma", "rng", "fdcan"] | 21 | stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "spi-v345", "not-gpdma", "rng", "fdcan"] |
| 22 | stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] | 22 | stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng", "eeprom"] |
| 23 | stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma"] | 23 | stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma", "eeprom"] |
| 24 | stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] | 24 | stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] |
| 25 | stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] | 25 | stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] |
| 26 | stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-bank"] | 26 | stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-bank"] |
| @@ -55,6 +55,7 @@ ucpd = [] | |||
| 55 | cordic = ["dep:num-traits"] | 55 | cordic = ["dep:num-traits"] |
| 56 | dual-bank = ["embassy-stm32/dual-bank"] | 56 | dual-bank = ["embassy-stm32/dual-bank"] |
| 57 | single-bank = ["embassy-stm32/single-bank"] | 57 | single-bank = ["embassy-stm32/single-bank"] |
| 58 | eeprom = [] | ||
| 58 | 59 | ||
| 59 | cm0 = ["portable-atomic/unsafe-assume-single-core"] | 60 | cm0 = ["portable-atomic/unsafe-assume-single-core"] |
| 60 | 61 | ||
| @@ -120,6 +121,11 @@ path = "src/bin/dac_l1.rs" | |||
| 120 | required-features = [ "stm32l152re",] | 121 | required-features = [ "stm32l152re",] |
| 121 | 122 | ||
| 122 | [[bin]] | 123 | [[bin]] |
| 124 | name = "eeprom" | ||
| 125 | path = "src/bin/eeprom.rs" | ||
| 126 | required-features = [ "eeprom",] | ||
| 127 | |||
| 128 | [[bin]] | ||
| 123 | name = "eth" | 129 | name = "eth" |
| 124 | path = "src/bin/eth.rs" | 130 | path = "src/bin/eth.rs" |
| 125 | required-features = [ "eth",] | 131 | required-features = [ "eth",] |
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | // required-features: eeprom | ||
| 5 | |||
| 6 | #[path = "../common.rs"] | ||
| 7 | mod common; | ||
| 8 | |||
| 9 | use common::*; | ||
| 10 | use defmt::assert_eq; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_stm32::flash::Flash; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | // Initialize the board and obtain a Peripherals instance | ||
| 18 | let p: embassy_stm32::Peripherals = init(); | ||
| 19 | |||
| 20 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 21 | const ADDR: u32 = 0x0; | ||
| 22 | |||
| 23 | unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | ||
| 24 | let mut buf = [0u8; 8]; | ||
| 25 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 26 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 27 | |||
| 28 | info!("Test OK"); | ||
| 29 | cortex_m::asm::bkpt(); | ||
| 30 | } | ||
