Merge branch 'embassy-rs:main' into dfu-verify

This commit is contained in:
Gerhard de Clercq 2025-05-26 16:51:10 +02:00 committed by GitHub
commit 305d1f2ed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 404 additions and 13 deletions

5
ci.sh
View File

@ -363,6 +363,11 @@ rm out/tests/pimoroni-pico-plus-2/flash
rm out/tests/pimoroni-pico-plus-2/i2c
# The pico2 plus doesn't have the adcs hooked up like the picoW does.
rm out/tests/pimoroni-pico-plus-2/adc
# temporarily disabled
rm out/tests/pimoroni-pico-plus-2/pwm
# temporarily disabled, bad hardware connection.
rm -f out/tests/rpi-pico/*
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
echo No teleprobe token found, skipping running HIL tests

View File

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-stm32"
version = "0.2.0"
version = "0.3.0"
description = "Bootloader lib for STM32 chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"

View File

@ -1922,6 +1922,48 @@ fn main() {
pub const WRITE_SIZE: usize = #write_size;
));
// ========
// 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");
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

View File

@ -0,0 +1,236 @@
use embassy_hal_internal::drop::OnDrop;
use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE};
#[cfg(eeprom)]
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)
.filter(|&end| end <= EEPROM_SIZE as u32)
.is_some()
{
Ok(())
} else {
Err(Error::Size)
}
}
// --- 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;
core::ptr::write_volatile(addr as *mut u8, byte);
family::wait_ready_blocking()?;
family::clear_all_err();
}
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;
core::ptr::write_volatile(addr as *mut u16, value);
family::wait_ready_blocking()?;
family::clear_all_err();
}
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;
core::ptr::write_volatile(addr as *mut u32, value);
family::wait_ready_blocking()?;
family::clear_all_err();
}
Ok(())
}
// --- 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();
let _on_drop = OnDrop::new(|| family::lock());
self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?;
}
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);
}
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))?;
}
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);
}
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))?;
}
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();
let _on_drop = OnDrop::new(|| family::lock());
self.eeprom_write_u8_slice_unlocked(offset, data)?;
}
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);
}
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)?;
}
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);
}
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)?;
}
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_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 {
0
} else {
(4 - misalign).min(data.len())
};
let (prefix, rest) = data.split_at(prefix_len);
let aligned_len = (rest.len() / 4) * 4;
let (bytes_for_u32_write, suffix) = rest.split_at(aligned_len);
unsafe {
family::unlock();
let _on_drop = OnDrop::new(|| family::lock());
if !prefix.is_empty() {
self.eeprom_write_u8_slice_unlocked(start, prefix)?;
}
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;
self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?;
}
}
Ok(())
}
/// Reads a single byte from EEPROM at the given offset.
pub fn eeprom_read_u8(&self, offset: u32) -> Result<u8, Error> {
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<u16, Error> {
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) })
}
/// 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<u32, Error> {
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) })
}
/// 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;
let src = unsafe { core::slice::from_raw_parts(addr as *const u8, buf.len()) };
buf.copy_from_slice(src);
Ok(())
}
}

View File

@ -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))]
{

View File

@ -5,13 +5,20 @@ 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)]
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.
@ -83,7 +90,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")]

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32" }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" }
embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] }

View File

@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
defmt = { version = "1.0.1", optional = true }

View File

@ -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]);
}

View File

@ -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]);
}

View File

@ -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"

View File

@ -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();
}