use crate::pac::FLASH;
use nb::block;
const FLASH_BASE: *mut u8 = 0x800_0000 as *mut u8;
const MAX_FLASH_ADDRESS: *mut u8 = 0x81F_FFFF as *mut u8;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Busy,
Locked,
EraseSequence,
ProgrammingParallelism,
ProgrammingAlignment,
WriteProtection,
}
pub struct Flash {
registers: FLASH,
}
impl Flash {
pub fn new(flash: FLASH) -> Self {
Self { registers: flash }
}
pub fn unlock(&mut self) {
if !self.is_locked() {
return;
}
self.registers.keyr.write(|w| w.key().bits(0x45670123));
self.registers.keyr.write(|w| w.key().bits(0xCDEF89AB));
}
pub fn lock(&mut self) {
self.registers.cr.modify(|_, w| w.lock().set_bit());
}
fn is_locked(&self) -> bool {
self.registers.cr.read().lock().is_locked()
}
fn is_busy(&self) -> bool {
self.registers.sr.read().bsy().bit_is_set()
}
pub fn erase_sector(&mut self, sector_number: u8) -> Result<EraseSequence<'_>, Error> {
EraseSequence::new_erase_sector(self, sector_number)
}
pub fn blocking_erase_sector(&mut self, sector_number: u8) -> Result<(), Error> {
let mut sequence = self.erase_sector(sector_number)?;
block!(sequence.wait())
}
pub fn mass_erase(&mut self) -> Result<EraseSequence<'_>, Error> {
EraseSequence::new_mass_erase(self)
}
pub fn blocking_mass_erase(&mut self) -> Result<(), Error> {
let mut sequence = self.mass_erase()?;
block!(sequence.wait())
}
pub fn program<'a, 'b>(
&'a mut self,
start_offset: usize,
data: &'b [u8],
) -> Result<ProgrammingSequence<'a, 'b>, Error> {
ProgrammingSequence::new(self, start_offset, data)
}
pub fn blocking_program(&mut self, start_offset: usize, data: &[u8]) -> Result<(), Error> {
let mut sequence = self.program(start_offset, data)?;
block!(sequence.wait())
}
pub fn free(self) -> FLASH {
self.registers
}
fn check_locked_or_busy(&self) -> Result<(), Error> {
if self.is_locked() {
Err(Error::Locked)
} else if self.is_busy() {
Err(Error::Busy)
} else {
Ok(())
}
}
fn check_errors(&self) -> Result<(), Error> {
let sr = self.registers.sr.read();
if sr.erserr().bit_is_set() {
Err(Error::EraseSequence)
} else if sr.pgperr().bit_is_set() {
Err(Error::ProgrammingParallelism)
} else if sr.pgaerr().bit_is_set() {
Err(Error::ProgrammingAlignment)
} else if sr.wrperr().bit_is_set() {
Err(Error::WriteProtection)
} else {
Ok(())
}
}
fn clear_errors(&mut self) {
self.registers.sr.write(|w| {
w.erserr()
.set_bit()
.pgperr()
.set_bit()
.pgaerr()
.set_bit()
.wrperr()
.set_bit()
});
}
}
pub struct EraseSequence<'a> {
flash: &'a mut Flash,
}
impl<'a> EraseSequence<'a> {
fn new_erase_sector(flash: &'a mut Flash, sector_number: u8) -> Result<Self, Error> {
flash.check_locked_or_busy()?;
flash.clear_errors();
flash.registers.cr.modify(|_, w| unsafe {
#[cfg(any(
feature = "stm32f765",
feature = "stm32f767",
feature = "stm32f769",
feature = "stm32f777",
feature = "stm32f778",
feature = "stm32f779",
))]
w.mer1().clear_bit().mer2().clear_bit();
#[cfg(not(any(
feature = "stm32f765",
feature = "stm32f767",
feature = "stm32f769",
feature = "stm32f777",
feature = "stm32f778",
feature = "stm32f779",
)))]
w.mer().clear_bit();
w.ser().set_bit().snb().bits(sector_number)
});
flash.registers.cr.modify(|_, w| w.strt().start());
Ok(Self { flash })
}
fn new_mass_erase(flash: &'a mut Flash) -> Result<Self, Error> {
flash.check_locked_or_busy()?;
flash.clear_errors();
flash.registers.cr.modify(|_, w| unsafe {
#[cfg(any(
feature = "stm32f765",
feature = "stm32f767",
feature = "stm32f769",
feature = "stm32f777",
feature = "stm32f778",
feature = "stm32f779",
))]
w.mer1().set_bit().mer2().set_bit();
#[cfg(not(any(
feature = "stm32f765",
feature = "stm32f767",
feature = "stm32f769",
feature = "stm32f777",
feature = "stm32f778",
feature = "stm32f779",
)))]
w.mer().clear_bit();
w.ser().clear_bit()
});
flash.registers.cr.modify(|_, w| w.strt().start());
Ok(Self { flash })
}
pub fn wait(&mut self) -> nb::Result<(), Error> {
self.flash.check_errors().map_err(nb::Error::from)?;
if self.flash.is_busy() {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
}
}
pub struct ProgrammingSequence<'a, 'b> {
flash: &'a mut Flash,
data: &'b [u8],
address: *mut u8,
}
impl<'a, 'b> ProgrammingSequence<'a, 'b> {
fn new(flash: &'a mut Flash, start_offset: usize, data: &'b [u8]) -> Result<Self, Error> {
flash.check_locked_or_busy()?;
flash.clear_errors();
flash
.registers
.cr
.modify(|_, w| w.psize().psize8().pg().set_bit());
let address = unsafe { FLASH_BASE.add(start_offset) };
Ok(Self {
flash,
data,
address,
})
}
pub fn wait(&mut self) -> nb::Result<(), Error> {
if self.flash.is_busy() {
return Err(nb::Error::WouldBlock);
}
if let Err(error) = self.flash.check_errors() {
self.flash.registers.cr.modify(|_, w| w.pg().clear_bit());
return Err(error.into());
}
if let Some((first, rest)) = self.data.split_first() {
if self.address >= FLASH_BASE && self.address <= MAX_FLASH_ADDRESS {
unsafe {
core::ptr::write_volatile(self.address, *first);
}
}
cortex_m::asm::dmb();
self.address = unsafe { self.address.add(1) };
self.data = rest;
Err(nb::Error::WouldBlock)
} else {
self.flash.registers.cr.modify(|_, w| w.pg().clear_bit());
Ok(())
}
}
}