Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement flash read/erase/program #347

Merged
merged 2 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
STM32F417, STM32F423, STM32F427, STM32F429, STM32F437, STM32F439, STM32F469,
and STM32F479 [#262]
- Added `gpio::gpiox::Pxi::downgrade2` method [#272]
- Added flash driver

[#231]: https://github.com/stm32-rs/stm32f4xx-hal/pull/231
[#262]: https://github.com/stm32-rs/stm32f4xx-hal/pull/262
Expand Down
166 changes: 166 additions & 0 deletions src/flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use crate::signature::FlashSize;
use crate::stm32::FLASH;
use core::{ptr, slice};

/// Flash erase/program error
#[derive(Debug, Clone, Copy)]
pub enum Error {
ProgrammingSequence,
ProgrammingParallelism,
ProgrammingAlignment,
WriteProtection,
Operation,
}

impl Error {
fn read(flash: &FLASH) -> Option<Self> {
let sr = flash.sr.read();
if sr.pgserr().bit() {
Some(Error::ProgrammingSequence)
} else if sr.pgperr().bit() {
Some(Error::ProgrammingParallelism)
} else if sr.pgaerr().bit() {
Some(Error::ProgrammingAlignment)
} else if sr.wrperr().bit() {
Some(Error::WriteProtection)
} else if sr.operr().bit() {
Some(Error::Operation)
} else {
None
}
}
}

/// Flash methods implemented for `stm32::FLASH`
#[allow(clippy::len_without_is_empty)]
pub trait FlashExt {
/// Memory-mapped address
fn address(&self) -> usize;
/// Size in bytes
fn len(&self) -> usize;
/// Returns a read-only view of flash memory
fn read(&self) -> &[u8] {
let ptr = self.address() as *const _;
unsafe { slice::from_raw_parts(ptr, self.len()) }
}
/// Unlock flash for erasing/programming until this method's
/// result is dropped
fn unlocked(&mut self) -> UnlockedFlash;
}

impl FlashExt for FLASH {
fn address(&self) -> usize {
0x0800_0000
}

fn len(&self) -> usize {
FlashSize::get().bytes()
}

fn unlocked(&mut self) -> UnlockedFlash {
unlock(self);
UnlockedFlash { flash: self }
}
}

const PSIZE_X8: u8 = 0b00;

/// Result of `FlashExt::unlocked()`
pub struct UnlockedFlash<'a> {
flash: &'a mut FLASH,
}

/// Automatically lock flash erase/program when leaving scope
impl Drop for UnlockedFlash<'_> {
fn drop(&mut self) {
lock(&self.flash);
}
}

impl UnlockedFlash<'_> {
/// Erase a flash sector
///
/// Refer to the reference manual to see which sector corresponds
/// to which memory address.
pub fn erase(&mut self, sector: u8) -> Result<(), Error> {
let snb = if sector < 12 { sector } else { sector + 4 };

#[rustfmt::skip]
self.flash.cr.modify(|_, w| unsafe {
w
// start
.strt().set_bit()
.psize().bits(PSIZE_X8)
// sector number
.snb().bits(snb)
// sectore erase
.ser().set_bit()
// no programming
.pg().clear_bit()
});
self.wait_ready();
self.ok()
}

/// Program bytes with offset into flash memory,
/// aligned to 128-bit rows
pub fn program<'a, I>(&mut self, mut offset: usize, mut bytes: I) -> Result<(), Error>
where
I: Iterator<Item = &'a u8>,
{
let ptr = self.flash.address() as *mut u8;
let mut bytes_written = 1;
while bytes_written > 0 {
bytes_written = 0;
let amount = 16 - (offset % 16);

#[rustfmt::skip]
self.flash.cr.modify(|_, w| unsafe {
w
.psize().bits(PSIZE_X8)
// no sector erase
.ser().clear_bit()
// programming
.pg().set_bit()
});
for _ in 0..amount {
match bytes.next() {
Some(byte) => {
unsafe {
ptr::write_volatile(ptr.add(offset), *byte);
}
offset += 1;
bytes_written += 1;
}
None => break,
}
}
self.wait_ready();
self.ok()?;
}
self.flash.cr.modify(|_, w| w.pg().clear_bit());

Ok(())
}

fn ok(&self) -> Result<(), Error> {
Error::read(&self.flash).map(Err).unwrap_or(Ok(()))
}

fn wait_ready(&self) {
while self.flash.sr.read().bsy().bit() {}
}
}

const UNLOCK_KEY1: u32 = 0x45670123;
const UNLOCK_KEY2: u32 = 0xCDEF89AB;

fn unlock(flash: &FLASH) {
flash.keyr.write(|w| unsafe { w.key().bits(UNLOCK_KEY1) });
flash.keyr.write(|w| unsafe { w.key().bits(UNLOCK_KEY2) });
assert!(!flash.cr.read().lock().bit())
}

fn lock(flash: &FLASH) {
flash.cr.modify(|_, w| w.lock().set_bit());
}
6 changes: 3 additions & 3 deletions src/gpio/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,17 +603,17 @@ impl<MODE, const P: char, const N: u8> Pin<MODE, P, N> {
fn mode<M: PinMode>(&mut self) {
let offset = 2 * N;
unsafe {
&(*Gpio::<P>::ptr()).pupdr.modify(|r, w| {
(*Gpio::<P>::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::PUPDR) << offset))
});

if let Some(otyper) = M::OTYPER {
&(*Gpio::<P>::ptr())
(*Gpio::<P>::ptr())
.otyper
.modify(|r, w| w.bits(r.bits() & !(0b1 << N) | (u32::from(otyper) << N)));
}

&(*Gpio::<P>::ptr()).moder.modify(|r, w| {
(*Gpio::<P>::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::MODER) << offset))
});
}
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ compile_error!(
stm32f479"
);

#[cfg(feature = "device-selected")]
pub use embedded_hal as hal;

#[cfg(feature = "device-selected")]
pub use nb;
#[cfg(feature = "device-selected")]
pub use nb::block;

#[cfg(feature = "stm32f401")]
Expand Down Expand Up @@ -141,6 +144,8 @@ pub use pac as stm32;
pub mod dma;
#[cfg(feature = "device-selected")]
pub mod dwt;
#[cfg(feature = "device-selected")]
pub mod flash;
#[cfg(all(
feature = "device-selected",
feature = "fsmc_lcd",
Expand Down Expand Up @@ -176,7 +181,9 @@ pub mod timer;
#[cfg(feature = "device-selected")]
pub mod watchdog;

#[cfg(feature = "device-selected")]
mod sealed {
pub trait Sealed {}
}
#[cfg(feature = "device-selected")]
pub(crate) use sealed::Sealed;