Skip to content

Commit a980f01

Browse files
rfuestmvertescher
authored andcommitted
Add support for flash programming
1 parent 07365ca commit a980f01

File tree

3 files changed

+336
-0
lines changed

3 files changed

+336
-0
lines changed

examples/flash.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Erases a flash sector and programs data.
2+
3+
#![deny(unsafe_code)]
4+
#![deny(warnings)]
5+
#![no_main]
6+
#![no_std]
7+
8+
extern crate panic_semihosting;
9+
10+
use cortex_m_rt::entry;
11+
use cortex_m_semihosting::hprintln;
12+
13+
use stm32f7xx_hal::{device, flash::Flash};
14+
15+
const DATA: &[u8] = &[0, 1, 2, 3, 4];
16+
17+
#[entry]
18+
fn main() -> ! {
19+
let p = device::Peripherals::take().unwrap();
20+
21+
let mut flash = Flash::new(p.FLASH);
22+
23+
// The flash needs to be unlocked before any erase or program operations.
24+
flash.unlock();
25+
26+
// Erase flash sector 3, which is located at address 0x0800C000
27+
flash.blocking_erase_sector(3).unwrap();
28+
29+
// Program the DATA slice into the flash memory starting at offset 0xC00 from the
30+
// beginning of the flash memory.
31+
flash.blocking_program(0xC000, &DATA).unwrap();
32+
33+
// Lock the flash memory to prevent any accidental modification of the flash content.
34+
flash.lock();
35+
36+
// Create a slice that can be used to read the written data.
37+
#[allow(unsafe_code)]
38+
let flash_data = unsafe { core::slice::from_raw_parts(0x0800C000 as *const u8, DATA.len()) };
39+
40+
// Compare the written data with the expected value.
41+
if flash_data == DATA {
42+
hprintln!("Flash programming successful").unwrap();
43+
}
44+
45+
loop {}
46+
}

src/flash.rs

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
//! Flash memory
2+
3+
use crate::device::FLASH;
4+
use nb::block;
5+
6+
/// Base address of flash memory on AXIM interface.
7+
const FLASH_BASE: *mut u8 = 0x800_0000 as *mut u8;
8+
9+
/// The last valid flash address in any STM32F7 device
10+
const MAX_FLASH_ADDRESS: *mut u8 = 0x81F_FFFF as *mut u8;
11+
12+
/// Flash programming error.
13+
#[derive(Debug, PartialEq, Eq)]
14+
pub enum Error {
15+
Busy,
16+
Locked,
17+
EraseSequence,
18+
ProgrammingParallelism,
19+
ProgrammingAlignment,
20+
WriteProtection,
21+
}
22+
23+
/// Embedded flash memory.
24+
pub struct Flash {
25+
registers: FLASH,
26+
}
27+
28+
impl Flash {
29+
/// Creates a new Flash instance.
30+
pub fn new(flash: FLASH) -> Self {
31+
Self { registers: flash }
32+
}
33+
34+
/// Unlocks the flash memory.
35+
pub fn unlock(&mut self) {
36+
if !self.is_locked() {
37+
// don't try to unlock the flash if it's already unlocked, because
38+
// trying to unlock the flash twice causes a HardFault
39+
return;
40+
}
41+
42+
self.registers.keyr.write(|w| w.key().bits(0x45670123));
43+
self.registers.keyr.write(|w| w.key().bits(0xCDEF89AB));
44+
}
45+
46+
/// Locks the flash memory.
47+
pub fn lock(&mut self) {
48+
self.registers.cr.modify(|_, w| w.lock().set_bit());
49+
}
50+
51+
/// Returns `true` if the flash memory is locked.
52+
fn is_locked(&self) -> bool {
53+
self.registers.cr.read().lock().is_locked()
54+
}
55+
56+
/// Returns `true` if a flash operation is in progress.
57+
fn is_busy(&self) -> bool {
58+
self.registers.sr.read().bsy().bit_is_set()
59+
}
60+
61+
/// Starts a sector erase sequence.
62+
///
63+
/// The returned `EraseSequence` object can be used to wait for the completion of the
64+
/// erase sequence by blocking on the `wait` method.
65+
pub fn erase_sector(&mut self, sector_number: u8) -> Result<EraseSequence<'_>, Error> {
66+
EraseSequence::new_erase_sector(self, sector_number)
67+
}
68+
69+
/// Erases a flash sector.
70+
///
71+
/// This method blocks until the sector is erased or an error occurred.
72+
pub fn blocking_erase_sector(&mut self, sector_number: u8) -> Result<(), Error> {
73+
let mut sequence = self.erase_sector(sector_number)?;
74+
block!(sequence.wait())
75+
}
76+
77+
/// Starts a mass erases of the flash memory.
78+
///
79+
/// The returned `EraseSequence` object can be used to wait for the completion of the
80+
/// erase sequence by blocking on the `wait` method.
81+
pub fn mass_erase(&mut self) -> Result<EraseSequence<'_>, Error> {
82+
EraseSequence::new_mass_erase(self)
83+
}
84+
85+
/// Mass erases the flash memory.
86+
///
87+
/// This method blocks until the flash is erased or an error occurred.
88+
pub fn blocking_mass_erase(&mut self) -> Result<(), Error> {
89+
let mut sequence = self.mass_erase()?;
90+
block!(sequence.wait())
91+
}
92+
93+
/// Starts a programming sequence.
94+
///
95+
/// Note that you must block on the `wait` method in the returned `ProgrammingSequence` object
96+
/// in order to program all bytes.
97+
pub fn program<'a, 'b>(
98+
&'a mut self,
99+
start_offset: usize,
100+
data: &'b [u8],
101+
) -> Result<ProgrammingSequence<'a, 'b>, Error> {
102+
ProgrammingSequence::new(self, start_offset, data)
103+
}
104+
105+
/// Programs a block of flash memory.
106+
///
107+
/// This method blocks until the block is programed or an error occurred.
108+
pub fn blocking_program<'a, 'b>(
109+
&'a mut self,
110+
start_offset: usize,
111+
data: &'b [u8],
112+
) -> Result<(), Error> {
113+
let mut sequence = self.program(start_offset, data)?;
114+
block!(sequence.wait())
115+
}
116+
117+
/// Releases the flash peripheral.
118+
pub fn free(self) -> FLASH {
119+
self.registers
120+
}
121+
122+
/// Returns an error if the flash is locked or busy.
123+
fn check_locked_or_busy(&self) -> Result<(), Error> {
124+
if self.is_locked() {
125+
return Err(Error::Locked);
126+
} else if self.is_busy() {
127+
return Err(Error::Busy);
128+
} else {
129+
Ok(())
130+
}
131+
}
132+
133+
/// Checks the error flags.
134+
fn check_errors(&self) -> Result<(), Error> {
135+
let sr = self.registers.sr.read();
136+
137+
if sr.erserr().bit_is_set() {
138+
Err(Error::EraseSequence)
139+
} else if sr.pgperr().bit_is_set() {
140+
Err(Error::ProgrammingParallelism)
141+
} else if sr.pgaerr().bit_is_set() {
142+
Err(Error::ProgrammingAlignment)
143+
} else if sr.wrperr().bit_is_set() {
144+
Err(Error::WriteProtection)
145+
} else {
146+
Ok(())
147+
}
148+
}
149+
150+
/// Clears all error flags.
151+
fn clear_errors(&mut self) {
152+
self.registers.sr.write(|w| {
153+
w.erserr()
154+
.set_bit()
155+
.pgperr()
156+
.set_bit()
157+
.pgaerr()
158+
.set_bit()
159+
.wrperr()
160+
.set_bit()
161+
});
162+
}
163+
}
164+
165+
/// Erase sequence.
166+
pub struct EraseSequence<'a> {
167+
flash: &'a mut Flash,
168+
}
169+
170+
impl<'a> EraseSequence<'a> {
171+
/// Creates a sector erase sequence.
172+
fn new_erase_sector(flash: &'a mut Flash, sector_number: u8) -> Result<Self, Error> {
173+
flash.check_locked_or_busy()?;
174+
flash.clear_errors();
175+
176+
//TODO: This should check if sector_number is valid for this device
177+
178+
flash.registers.cr.modify(|_, w| unsafe {
179+
w.mer()
180+
.clear_bit()
181+
.ser()
182+
.set_bit()
183+
.snb()
184+
.bits(sector_number)
185+
});
186+
flash.registers.cr.modify(|_, w| w.strt().start());
187+
188+
Ok(Self { flash })
189+
}
190+
191+
/// Creates a mass erase sequence.
192+
fn new_mass_erase(flash: &'a mut Flash) -> Result<Self, Error> {
193+
flash.check_locked_or_busy()?;
194+
flash.clear_errors();
195+
196+
flash
197+
.registers
198+
.cr
199+
.modify(|_, w| w.mer().set_bit().ser().clear_bit());
200+
flash.registers.cr.modify(|_, w| w.strt().start());
201+
202+
Ok(Self { flash })
203+
}
204+
205+
/// Waits until the erase sequence is finished.
206+
pub fn wait(&mut self) -> nb::Result<(), Error> {
207+
self.flash.check_errors().map_err(nb::Error::from)?;
208+
209+
if self.flash.is_busy() {
210+
Err(nb::Error::WouldBlock)
211+
} else {
212+
Ok(())
213+
}
214+
}
215+
}
216+
217+
/// Programming sequence.
218+
pub struct ProgrammingSequence<'a, 'b> {
219+
flash: &'a mut Flash,
220+
data: &'b [u8],
221+
address: *mut u8,
222+
}
223+
224+
impl<'a, 'b> ProgrammingSequence<'a, 'b> {
225+
/// Creates a programming sequence.
226+
fn new(flash: &'a mut Flash, start_offset: usize, data: &'b [u8]) -> Result<Self, Error> {
227+
flash.check_locked_or_busy()?;
228+
flash.clear_errors();
229+
230+
flash
231+
.registers
232+
.cr
233+
.modify(|_, w| w.psize().psize8().pg().set_bit());
234+
235+
let address = unsafe { FLASH_BASE.add(start_offset) };
236+
237+
Ok(Self {
238+
flash,
239+
data,
240+
address,
241+
})
242+
}
243+
244+
/// Waits until the programming sequence is finished.
245+
pub fn wait(&mut self) -> nb::Result<(), Error> {
246+
if self.flash.is_busy() {
247+
return Err(nb::Error::WouldBlock);
248+
}
249+
250+
if let Err(error) = self.flash.check_errors() {
251+
// make sure programing mode is disabled when an error occurred
252+
self.flash.registers.cr.modify(|_, w| w.pg().clear_bit());
253+
254+
return Err(error.into());
255+
}
256+
257+
if let Some((first, rest)) = self.data.split_first() {
258+
if self.address >= FLASH_BASE && self.address <= MAX_FLASH_ADDRESS {
259+
unsafe {
260+
core::ptr::write_volatile(self.address, *first);
261+
}
262+
}
263+
264+
// ensure data is written byte by byte to prevent programming parallelism errors
265+
cortex_m::asm::dmb();
266+
267+
self.address = unsafe { self.address.add(1) };
268+
self.data = rest;
269+
270+
Err(nb::Error::WouldBlock)
271+
} else {
272+
self.flash.registers.cr.modify(|_, w| w.pg().clear_bit());
273+
274+
Ok(())
275+
}
276+
}
277+
}

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ pub mod i2c;
107107
#[cfg(feature = "ltdc")]
108108
pub mod ltdc;
109109

110+
#[cfg(all(
111+
feature = "device-selected",
112+
not(any(
113+
feature = "stm32f765",
114+
feature = "stm32f767",
115+
feature = "stm32f769",
116+
feature = "stm32f777",
117+
feature = "stm32f778",
118+
feature = "stm32f779",
119+
))
120+
))]
121+
pub mod flash;
122+
110123
pub mod state {
111124
/// Indicates that a peripheral is enabled
112125
pub struct Enabled;

0 commit comments

Comments
 (0)