-
Notifications
You must be signed in to change notification settings - Fork 48
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
Flash changes #104
Flash changes #104
Changes from all commits
6f61dc1
045aa89
4ebb51d
7e3f7f6
7f71d41
922bf02
e3f7687
64b3123
afbed98
f57b4d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#![no_main] | ||
#![no_std] | ||
|
||
use cortex_m_rt::entry; | ||
use hal::flash::FlashExt; | ||
use hal::flash::FlashSize; | ||
use hal::stm32; | ||
use stm32g4xx_hal as hal; | ||
extern crate cortex_m_rt as rt; | ||
|
||
#[macro_use] | ||
mod utils; | ||
|
||
use utils::logger::println; | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
utils::logger::init(); | ||
|
||
let dp = stm32::Peripherals::take().expect("cannot take peripherals"); | ||
|
||
let mut flash = dp.FLASH.constrain(); | ||
let is_dual_bank = flash.is_dual_bank(); | ||
|
||
println!("Is dual bank: {}", is_dual_bank); | ||
|
||
let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K); | ||
|
||
let mut words = [0; 1]; | ||
flash_writer.read_exact(0x0000_0000, &mut words[..]); | ||
println!("Bank 1 - first 4 bytes: {:#X}", words[0]); | ||
|
||
if is_dual_bank { | ||
let address = 0x0004_0000; | ||
flash_writer.read_exact(address, &mut words[..]); | ||
println!("Bank 2 - First 4 bytes before write: {:#X}", words[0]); | ||
|
||
flash_writer.page_erase(address).unwrap(); | ||
flash_writer.read_exact(address, &mut words[..]); | ||
println!("Bank 2 - First 4 bytes after erase: {:#X}", words[0]); | ||
|
||
let new_value = words[0].wrapping_add(2); | ||
println!("Bank 2 - Writing: {:#X} to first 4 bytes", new_value); | ||
flash_writer | ||
.write(address, &new_value.to_ne_bytes(), true) | ||
.unwrap(); | ||
flash_writer.read_exact(address, &mut words[..]); | ||
println!("Bank 2 - First 4 bytes after write: {:#X}", words[0]); | ||
} | ||
|
||
println!("Done!"); | ||
|
||
loop { | ||
cortex_m::asm::nop() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
|
||
mod utils; | ||
|
||
#[rtic::app(device = stm32g4xx_hal::stm32g4::stm32g474, peripherals = true)] | ||
#[rtic::app(device = stm32g4xx_hal::stm32, peripherals = true)] | ||
mod app { | ||
use crate::utils::logger; | ||
use stm32g4xx_hal::flash::{FlashExt, FlashSize, FlashWriter, Parts}; | ||
|
@@ -25,18 +25,6 @@ mod app { | |
#[local] | ||
struct Local {} | ||
|
||
fn compare_arrays(a: &[u8], b: &[u8]) -> bool { | ||
if a.len() != b.len() { | ||
return false; | ||
} | ||
for i in 0..a.len() { | ||
if a[i] != b[i] { | ||
return false; | ||
} | ||
} | ||
true | ||
} | ||
|
||
#[init] | ||
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { | ||
crate::utils::logger::init(); | ||
|
@@ -45,258 +33,74 @@ mod app { | |
let cp = cx.core; | ||
|
||
let rcc = dp.RCC.constrain(); | ||
let mut pll_config = stm32g4xx_hal::rcc::PllConfig::default(); | ||
|
||
// Sysclock is based on PLL_R | ||
pll_config.mux = stm32g4xx_hal::rcc::PLLSrc::HSI; // 16MHz | ||
pll_config.n = stm32g4xx_hal::rcc::PllNMul::MUL_32; | ||
pll_config.m = stm32g4xx_hal::rcc::PllMDiv::DIV_2; // f(vco) = 16MHz*32/2 = 256MHz | ||
pll_config.r = Some(stm32g4xx_hal::rcc::PllRDiv::DIV_2); // f(sysclock) = 256MHz/2 = 128MHz | ||
|
||
// Note to future self: The AHB clock runs the timers, among other things. | ||
// Please refer to the Clock Tree manual to determine if it is worth | ||
// changing to a lower speed for battery life savings. | ||
let mut clock_config = stm32g4xx_hal::rcc::Config::default() | ||
.pll_cfg(pll_config) | ||
.clock_src(stm32g4xx_hal::rcc::SysClockSrc::PLL); | ||
|
||
// After clock configuration, the following should be true: | ||
// Sysclock is 128MHz | ||
// AHB clock is 128MHz | ||
// APB1 clock is 128MHz | ||
// APB2 clock is 128MHz | ||
// The ADC will ultimately be put into synchronous mode and will derive | ||
// its clock from the AHB bus clock, with a prescalar of 2 or 4. | ||
|
||
let pwr = dp.PWR.constrain().freeze(); | ||
let mut rcc = rcc.freeze(clock_config, pwr); | ||
|
||
unsafe { | ||
let mut flash = &(*stm32g4xx_hal::stm32::FLASH::ptr()); | ||
flash.acr.modify(|_, w| { | ||
w.latency().bits(0b1000) // 8 wait states | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even with the custom clock setup above, this should not really be needed, right? This should be done automatically here when setting the clock settings. However by my math, the auto-thing would set it to 4 wait states for a f_sys of 128MHz. Does it for some reason need to be 8, or is that incorrect? |
||
let mut rcc = rcc.freeze(stm32g4xx_hal::rcc::Config::hsi(), pwr); | ||
let mut delay = cp.SYST.delay(&rcc.clocks); | ||
|
||
// *** FLASH Memory *** | ||
let one_byte = [0x12 as u8]; | ||
let two_bytes = [0xAB, 0xCD as u8]; | ||
let three_bytes = [0x12, 0x34, 0x56 as u8]; | ||
let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA as u8]; | ||
let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 as u8]; | ||
let one_byte = [0x12]; | ||
let two_bytes = [0xAB, 0xCD]; | ||
let three_bytes = [0x12, 0x34, 0x56]; | ||
let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA]; | ||
let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; | ||
let sixteen_bytes = [ | ||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, | ||
0xDE, 0xF0 as u8, | ||
0xDE, 0xF0, | ||
]; | ||
let mut flash = dp.FLASH.constrain(); | ||
let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K); | ||
|
||
const FLASH_SPACING: u32 = 16; // Separate flash writes by 16 bytes | ||
const FLASH_EXAMPLE_START_ADDRESS: u32 = 0x1FC00; | ||
|
||
let address = |i| FLASH_EXAMPLE_START_ADDRESS + i as u32 * FLASH_SPACING; | ||
|
||
logger::info!( | ||
"Erasing 128 bytes at address {}", | ||
"Erasing 128 bytes at address {:#X}", | ||
FLASH_EXAMPLE_START_ADDRESS | ||
); | ||
flash_writer | ||
.erase(FLASH_EXAMPLE_START_ADDRESS, 128) | ||
.unwrap(); // Erase entire page | ||
|
||
for i in 0..6 { | ||
match i { | ||
0 => { | ||
// This test should fail, as the data needs to be divisible by 8 and force padding is false | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&one_byte, | ||
false, | ||
); | ||
assert!(result.is_err()); | ||
assert_eq!( | ||
result.err().unwrap(), | ||
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 | ||
); | ||
|
||
// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&one_byte, | ||
true, | ||
); | ||
assert!(result.is_ok()); | ||
logger::info!( | ||
"Wrote 1 byte to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
1 => { | ||
// This test should fail, as the data needs to be divisible by 8 and force padding is false | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&two_bytes, | ||
false, | ||
); | ||
assert!(result.is_err()); | ||
assert_eq!( | ||
result.err().unwrap(), | ||
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 | ||
); | ||
|
||
// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&two_bytes, | ||
true, | ||
); | ||
assert!(result.is_ok()); | ||
logger::info!( | ||
"Wrote 2 bytes to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
2 => { | ||
// This test should fail, as the data needs to be divisible by 8 and force padding is false | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&three_bytes, | ||
false, | ||
); | ||
assert!(result.is_err()); | ||
assert_eq!( | ||
result.err().unwrap(), | ||
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 | ||
); | ||
let datasets = [ | ||
&one_byte[..], | ||
&two_bytes[..], | ||
&three_bytes[..], | ||
&four_bytes[..], | ||
&eight_bytes[..], | ||
&sixteen_bytes[..], | ||
]; | ||
|
||
// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&three_bytes, | ||
true, | ||
); | ||
assert!(result.is_ok()); | ||
logger::info!( | ||
"Wrote 3 bytes to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
3 => { | ||
// This test should fail, as the data needs to be divisible by 8 and force padding is false | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&four_bytes, | ||
false, | ||
); | ||
assert!(result.is_err()); | ||
assert_eq!( | ||
result.err().unwrap(), | ||
stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 | ||
); | ||
for (i, data) in datasets.into_iter().enumerate() { | ||
let address = address(i); | ||
let mut do_write = |force_padding| flash_writer.write(address, &data, force_padding); | ||
|
||
// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF | ||
let result = flash_writer.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&four_bytes, | ||
true, | ||
); | ||
assert!(result.is_ok()); | ||
logger::info!( | ||
"Wrote 4 bytes to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
4 => { | ||
flash_writer | ||
.write( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
&eight_bytes, | ||
false, | ||
) | ||
.unwrap(); | ||
logger::info!( | ||
"Wrote 8 bytes to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
5 => { | ||
flash_writer | ||
.write(FLASH_EXAMPLE_START_ADDRESS + i * 16, &sixteen_bytes, false) | ||
.unwrap(); | ||
logger::info!( | ||
"Wrote 16 bytes to address {}", | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING | ||
); | ||
} | ||
_ => (), | ||
if data.len() % 8 != 0 { | ||
// This test should fail, as the data needs to be divisible by 8 and force padding is false | ||
assert_eq!( | ||
do_write(false), | ||
Err(stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8) | ||
); | ||
} | ||
} | ||
|
||
// This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF | ||
do_write(true).unwrap(); | ||
logger::info!("Wrote {} byte(s) to address {:#X}", data.len(), address); | ||
} | ||
logger::info!("Validating data written data by performing read and compare"); | ||
|
||
for i in 0..6 { | ||
match i { | ||
0 => { | ||
let bytes = flash_writer | ||
.read(FLASH_EXAMPLE_START_ADDRESS as u32, one_byte.len()) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &one_byte)); | ||
logger::info!("Validated 1 byte data"); | ||
} | ||
1 => { | ||
let bytes = flash_writer | ||
.read( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
two_bytes.len(), | ||
) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &two_bytes)); | ||
logger::info!("Validated 2 byte data"); | ||
} | ||
2 => { | ||
let bytes = flash_writer | ||
.read( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
three_bytes.len(), | ||
) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &three_bytes)); | ||
logger::info!("Validated 3 byte data"); | ||
} | ||
3 => { | ||
let bytes = flash_writer | ||
.read( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
four_bytes.len(), | ||
) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &four_bytes)); | ||
logger::info!("Validated 4 byte data"); | ||
} | ||
4 => { | ||
let bytes = flash_writer | ||
.read( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
eight_bytes.len(), | ||
) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &eight_bytes)); | ||
logger::info!("Validated 8 byte data"); | ||
} | ||
5 => { | ||
let bytes = flash_writer | ||
.read( | ||
FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, | ||
sixteen_bytes.len(), | ||
) | ||
.unwrap(); | ||
assert!(compare_arrays(&bytes, &sixteen_bytes)); | ||
logger::info!("Validated 5 byte data"); | ||
} | ||
_ => (), | ||
} | ||
for (i, data) in datasets.into_iter().enumerate() { | ||
logger::info!("Stuff: {}", i); | ||
let mut bytes = [0; 16]; | ||
flash_writer.read_exact(address(i), &mut bytes[..data.len()]); | ||
|
||
assert_eq!(&bytes[..data.len()], *data); | ||
logger::info!("Validated {} byte data", data.len()); | ||
} | ||
|
||
logger::info!( | ||
"Finished flash example at address {}", | ||
"Finished flash example at address {:#X}", | ||
FLASH_EXAMPLE_START_ADDRESS | ||
); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I am wrong but the clock setup is not really needed for this example, right? Thought I'd remove all but the essentials to give more focus on the flash related things