Skip to content

Commit 9cf2d52

Browse files
astroburrbull
authored andcommitted
Implement flash read/erase/program
1 parent 1be2fdc commit 9cf2d52

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
131131
STM32F417, STM32F423, STM32F427, STM32F429, STM32F437, STM32F439, STM32F469,
132132
and STM32F479 [#262]
133133
- Added `gpio::gpiox::Pxi::downgrade2` method [#272]
134+
- Added flash driver
134135

135136
[#231]: https://github.com/stm32-rs/stm32f4xx-hal/pull/231
136137
[#262]: https://github.com/stm32-rs/stm32f4xx-hal/pull/262

src/flash.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
use crate::signature::FlashSize;
2+
use crate::stm32::FLASH;
3+
use core::{ptr, slice};
4+
5+
/// Flash erase/program error
6+
#[derive(Debug, Clone, Copy)]
7+
pub enum Error {
8+
ProgrammingSequence,
9+
ProgrammingParallelism,
10+
ProgrammingAlignment,
11+
WriteProtection,
12+
Operation,
13+
}
14+
15+
impl Error {
16+
fn read(flash: &FLASH) -> Option<Self> {
17+
let sr = flash.sr.read();
18+
if sr.pgserr().bit() {
19+
Some(Error::ProgrammingSequence)
20+
} else if sr.pgperr().bit() {
21+
Some(Error::ProgrammingParallelism)
22+
} else if sr.pgaerr().bit() {
23+
Some(Error::ProgrammingAlignment)
24+
} else if sr.wrperr().bit() {
25+
Some(Error::WriteProtection)
26+
} else if sr.operr().bit() {
27+
Some(Error::Operation)
28+
} else {
29+
None
30+
}
31+
}
32+
}
33+
34+
/// Flash methods implemented for `stm32::FLASH`
35+
pub trait FlashExt {
36+
/// Memory-mapped address
37+
fn address(&self) -> usize;
38+
/// Size in bytes
39+
fn len(&self) -> usize;
40+
/// Returns a read-only view of flash memory
41+
fn read(&self) -> &[u8] {
42+
let ptr = self.address() as *const _;
43+
unsafe { slice::from_raw_parts(ptr, self.len()) }
44+
}
45+
/// Unlock flash for erasing/programming until this method's
46+
/// result is dropped
47+
fn unlocked(&mut self) -> UnlockedFlash;
48+
}
49+
50+
impl FlashExt for FLASH {
51+
fn address(&self) -> usize {
52+
0x0800_0000
53+
}
54+
55+
fn len(&self) -> usize {
56+
FlashSize::get().bytes()
57+
}
58+
59+
fn unlocked(&mut self) -> UnlockedFlash {
60+
unlock(self);
61+
UnlockedFlash { flash: self }
62+
}
63+
}
64+
65+
const PSIZE_X8: u8 = 0b00;
66+
67+
/// Result of `FlashExt::unlocked()`
68+
pub struct UnlockedFlash<'a> {
69+
flash: &'a mut FLASH,
70+
}
71+
72+
/// Automatically lock flash erase/program when leaving scope
73+
impl Drop for UnlockedFlash<'_> {
74+
fn drop(&mut self) {
75+
lock(&self.flash);
76+
}
77+
}
78+
79+
impl UnlockedFlash<'_> {
80+
/// Erase a flash sector
81+
///
82+
/// Refer to the reference manual to see which sector corresponds
83+
/// to which memory address.
84+
pub fn erase(&mut self, sector: u8) -> Result<(), Error> {
85+
let snb = if sector < 12 { sector } else { sector + 4 };
86+
87+
#[rustfmt::skip]
88+
self.flash.cr.modify(|_, w| unsafe {
89+
w
90+
// start
91+
.strt().set_bit()
92+
.psize().bits(PSIZE_X8)
93+
// sector number
94+
.snb().bits(snb)
95+
// sectore erase
96+
.ser().set_bit()
97+
// no programming
98+
.pg().clear_bit()
99+
});
100+
self.wait_ready();
101+
self.ok()
102+
}
103+
104+
/// Program bytes with offset into flash memory,
105+
/// aligned to 128-bit rows
106+
pub fn program<'a, I>(&mut self, mut offset: usize, mut bytes: I) -> Result<(), Error>
107+
where
108+
I: Iterator<Item = &'a u8>,
109+
{
110+
let ptr = self.flash.address() as *mut u8;
111+
let mut bytes_written = 1;
112+
while bytes_written > 0 {
113+
bytes_written = 0;
114+
let amount = 16 - (offset % 16);
115+
116+
#[rustfmt::skip]
117+
self.flash.cr.modify(|_, w| unsafe {
118+
w
119+
.psize().bits(PSIZE_X8)
120+
// no sector erase
121+
.ser().clear_bit()
122+
// programming
123+
.pg().set_bit()
124+
});
125+
for _ in 0..amount {
126+
match bytes.next() {
127+
Some(byte) => {
128+
unsafe {
129+
ptr::write_volatile(ptr.add(offset), *byte);
130+
}
131+
offset += 1;
132+
bytes_written += 1;
133+
}
134+
None => break,
135+
}
136+
}
137+
self.wait_ready();
138+
self.ok()?;
139+
}
140+
self.flash.cr.modify(|_, w| w.pg().clear_bit());
141+
142+
Ok(())
143+
}
144+
145+
fn ok(&self) -> Result<(), Error> {
146+
Error::read(&self.flash).map(Err).unwrap_or(Ok(()))
147+
}
148+
149+
fn wait_ready(&self) {
150+
while self.flash.sr.read().bsy().bit() {}
151+
}
152+
}
153+
154+
const UNLOCK_KEY1: u32 = 0x45670123;
155+
const UNLOCK_KEY2: u32 = 0xCDEF89AB;
156+
157+
fn unlock(flash: &FLASH) {
158+
flash.keyr.write(|w| unsafe { w.key().bits(UNLOCK_KEY1) });
159+
flash.keyr.write(|w| unsafe { w.key().bits(UNLOCK_KEY2) });
160+
assert!(!flash.cr.read().lock().bit())
161+
}
162+
163+
fn lock(flash: &FLASH) {
164+
flash.cr.modify(|_, w| w.lock().set_bit());
165+
}

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ compile_error!(
2323
stm32f479"
2424
);
2525

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

29+
#[cfg(feature = "device-selected")]
2830
pub use nb;
31+
#[cfg(feature = "device-selected")]
2932
pub use nb::block;
3033

3134
#[cfg(feature = "stm32f401")]
@@ -141,6 +144,8 @@ pub use pac as stm32;
141144
pub mod dma;
142145
#[cfg(feature = "device-selected")]
143146
pub mod dwt;
147+
#[cfg(feature = "device-selected")]
148+
pub mod flash;
144149
#[cfg(all(
145150
feature = "device-selected",
146151
feature = "fsmc_lcd",
@@ -176,7 +181,9 @@ pub mod timer;
176181
#[cfg(feature = "device-selected")]
177182
pub mod watchdog;
178183

184+
#[cfg(feature = "device-selected")]
179185
mod sealed {
180186
pub trait Sealed {}
181187
}
188+
#[cfg(feature = "device-selected")]
182189
pub(crate) use sealed::Sealed;

0 commit comments

Comments
 (0)