Skip to content

Commit 09cc9c6

Browse files
CNLHCDirbaio
authored andcommitted
feat: mmap mode for qspi and example
1 parent 33d6f65 commit 09cc9c6

File tree

7 files changed

+373
-0
lines changed

7 files changed

+373
-0
lines changed

ci.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ cargo batch \
231231
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
232232
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
233233
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
234+
--- build --release --manifest-path examples/stm32l432/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l432 \
234235
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
235236
--- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32u0 \
236237
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \

embassy-stm32/src/qspi/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,26 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
201201
T::REGS.fcr().modify(|v| v.set_ctcf(true));
202202
}
203203

204+
/// Enable memory map mode
205+
pub fn enable_memory_map(&mut self, transaction: &TransferConfig) {
206+
T::REGS.fcr().modify(|v| {
207+
v.set_csmf(true);
208+
v.set_ctcf(true);
209+
v.set_ctef(true);
210+
v.set_ctof(true);
211+
});
212+
T::REGS.ccr().write(|v| {
213+
v.set_fmode(QspiMode::MemoryMapped.into());
214+
v.set_imode(transaction.iwidth.into());
215+
v.set_instruction(transaction.instruction);
216+
v.set_admode(transaction.awidth.into());
217+
v.set_adsize(self.config.address_size.into());
218+
v.set_dmode(transaction.dwidth.into());
219+
v.set_abmode(QspiWidth::NONE.into());
220+
v.set_dcyc(transaction.dummy.into());
221+
});
222+
}
223+
204224
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {
205225
match (transaction.address, transaction.awidth) {
206226
(Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"),

examples/stm32l432/.cargo/config.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2+
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3+
#runner = "probe-rs run --chip STM32L475VGT6"
4+
#runner = "probe-rs run --chip STM32L475VG"
5+
#runner = "probe-rs run --chip STM32L4S5QI"
6+
runner = "probe-rs run --chip STM32L432KCUx --connect-under-reset --speed 3300"
7+
8+
9+
[build]
10+
target = "thumbv7em-none-eabi"
11+
12+
[env]
13+
DEFMT_LOG = "trace"

examples/stm32l432/Cargo.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
edition = "2021"
3+
name = "embassy-stm32l4-examples"
4+
version = "0.1.1"
5+
license = "MIT OR Apache-2.0"
6+
7+
[dependencies]
8+
# Change stm32l4s5vi to your chip name, if necessary.
9+
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l432kc", "memory-x", "time-driver-any", "exti", "chrono"] }
10+
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = [ "defmt" ] }
11+
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt" ] }
12+
embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "tick-hz-32_768" ] }
13+
defmt = "0.3"
14+
defmt-rtt = "0.4"
15+
16+
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
17+
cortex-m-rt = "0.7.0"
18+
embedded-hal = "0.2.6"
19+
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
20+
embedded-hal-async = { version = "1.0" }
21+
embedded-hal-bus = { version = "0.1", features = ["async"] }
22+
panic-probe = { version = "0.3", features = ["print-defmt"] }
23+
24+
[profile.release]
25+
debug = 2
26+
27+
[[bin]]
28+
name = "qspi_mmap"
29+
path = "src/bin/qspi_mmap.rs"
30+
test = false

examples/stm32l432/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
# Examples for STM32L432 family
3+
4+
Examples in this repo should work with [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) board.
5+
6+
Run individual examples with
7+
```
8+
cargo run --bin <module-name>
9+
```
10+
for example
11+
```
12+
cargo run --bin blinky
13+
```
14+
15+
16+
17+
## Checklist before running examples
18+
You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
19+
20+
* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L432KCU6 it should be `probe-rs run --chip STM32L432KCUx`. (use `probe-rs chip list` to find your chip)
21+
* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L432KCU6 it should be `stm32l432kc`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
22+
* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
23+
* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
24+
25+
If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
26+
27+
* Which example you are trying to run
28+
* Which chip and board you are using
29+
30+
Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org

examples/stm32l432/build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
println!("cargo:rustc-link-arg-bins=--nmagic");
3+
println!("cargo:rustc-link-arg-bins=-Tlink.x");
4+
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
5+
}
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#![no_std]
2+
#![no_main]
3+
#![allow(dead_code)]
4+
/// This example demonstrates how to use the QSPI peripheral in both indirect-mode and memory-mapped mode.
5+
/// If you want to test this example, please pay attention to flash pins and check flash device datasheet
6+
/// to make sure operations in this example are compatible with your device, especially registers I/O operations.
7+
use defmt::info;
8+
use embassy_stm32::mode;
9+
use embassy_stm32::qspi::enums::{
10+
AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth,
11+
};
12+
use embassy_stm32::qspi::{self, Instance, TransferConfig};
13+
pub struct FlashMemory<I: Instance> {
14+
qspi: qspi::Qspi<'static, I, mode::Async>,
15+
}
16+
use embassy_executor::Spawner;
17+
use embassy_time::Timer;
18+
use {defmt_rtt as _, panic_probe as _};
19+
20+
const MEMORY_PAGE_SIZE: usize = 256;
21+
const CMD_READ_SR: u8 = 0x05;
22+
const CMD_READ_CR: u8 = 0x35;
23+
const CMD_QUAD_READ: u8 = 0x6B;
24+
const CMD_QUAD_WRITE_PG: u8 = 0x32;
25+
const CMD_READ_ID: u8 = 0x9F;
26+
const CMD_READ_MID: u8 = 0x90;
27+
const CMD_READ_UUID: u8 = 0x4B;
28+
const CMD_ENABLE_RESET: u8 = 0x66;
29+
const CMD_RESET: u8 = 0x99;
30+
const CMD_WRITE_ENABLE: u8 = 0x06;
31+
const CMD_SECTOR_ERASE: u8 = 0x20;
32+
33+
const CMD_WRITE_SR: u8 = 0x01;
34+
35+
impl<I: Instance> FlashMemory<I> {
36+
pub fn new(qspi: qspi::Qspi<'static, I, mode::Async>) -> Self {
37+
let mut memory = Self { qspi };
38+
39+
memory.reset_memory();
40+
memory.enable_quad();
41+
42+
memory
43+
}
44+
fn enable_quad(&mut self) {
45+
let sr = self.read_sr_lsb();
46+
let cr = self.read_sr_msb();
47+
48+
self.write_sr(sr, cr | 0x02);
49+
}
50+
fn read_register(&mut self, cmd: u8) -> u8 {
51+
let mut buffer = [0; 1];
52+
let transaction = TransferConfig {
53+
iwidth: QspiWidth::SING,
54+
awidth: QspiWidth::NONE,
55+
dwidth: QspiWidth::SING,
56+
instruction: cmd,
57+
address: None,
58+
dummy: DummyCycles::_0,
59+
};
60+
self.qspi.blocking_read(&mut buffer, transaction);
61+
buffer[0]
62+
}
63+
64+
fn write_register(&mut self, cmd: u8, value: u8) {
65+
let buffer = [value; 1];
66+
let transaction: TransferConfig = TransferConfig {
67+
iwidth: QspiWidth::SING,
68+
awidth: QspiWidth::NONE,
69+
dwidth: QspiWidth::SING,
70+
instruction: cmd,
71+
address: None,
72+
dummy: DummyCycles::_0,
73+
};
74+
self.qspi.blocking_write(&buffer, transaction);
75+
}
76+
pub fn write_sr(&mut self, lsb: u8, msb: u8) {
77+
let buffer = [lsb, msb];
78+
let transaction: TransferConfig = TransferConfig {
79+
iwidth: QspiWidth::SING,
80+
awidth: QspiWidth::NONE,
81+
dwidth: QspiWidth::SING,
82+
instruction: CMD_WRITE_SR,
83+
address: None,
84+
dummy: DummyCycles::_0,
85+
};
86+
self.qspi.blocking_write(&buffer, transaction);
87+
}
88+
89+
pub fn read_sr_lsb(&mut self) -> u8 {
90+
self.read_register(CMD_READ_SR)
91+
}
92+
pub fn read_sr_msb(&mut self) -> u8 {
93+
self.read_register(CMD_READ_CR)
94+
}
95+
96+
pub fn reset_memory(&mut self) {
97+
self.exec_command(CMD_ENABLE_RESET);
98+
self.exec_command(CMD_RESET);
99+
self.wait_write_finish();
100+
}
101+
fn exec_command(&mut self, cmd: u8) {
102+
let transaction = TransferConfig {
103+
iwidth: QspiWidth::SING,
104+
awidth: QspiWidth::NONE,
105+
dwidth: QspiWidth::NONE,
106+
instruction: cmd,
107+
address: None,
108+
dummy: DummyCycles::_0,
109+
};
110+
self.qspi.blocking_command(transaction);
111+
}
112+
fn wait_write_finish(&mut self) {
113+
while (self.read_sr_lsb() & 0x01) != 0 {}
114+
}
115+
116+
pub fn read_mid(&mut self) -> [u8; 2] {
117+
let mut buffer = [0; 2];
118+
let transaction: TransferConfig = TransferConfig {
119+
iwidth: QspiWidth::SING,
120+
awidth: QspiWidth::SING,
121+
dwidth: QspiWidth::SING,
122+
instruction: CMD_READ_MID,
123+
address: Some(0),
124+
dummy: DummyCycles::_0,
125+
};
126+
self.qspi.blocking_read(&mut buffer, transaction);
127+
buffer
128+
}
129+
pub fn read_uuid(&mut self) -> [u8; 16] {
130+
let mut buffer = [0; 16];
131+
let transaction: TransferConfig = TransferConfig {
132+
iwidth: QspiWidth::SING,
133+
awidth: QspiWidth::SING,
134+
dwidth: QspiWidth::SING,
135+
instruction: CMD_READ_UUID,
136+
address: Some(0),
137+
dummy: DummyCycles::_8,
138+
};
139+
self.qspi.blocking_read(&mut buffer, transaction);
140+
buffer
141+
}
142+
pub fn read_id(&mut self) -> [u8; 3] {
143+
let mut buffer = [0; 3];
144+
let transaction: TransferConfig = TransferConfig {
145+
iwidth: QspiWidth::SING,
146+
awidth: QspiWidth::NONE,
147+
dwidth: QspiWidth::SING,
148+
instruction: CMD_READ_ID,
149+
address: None,
150+
dummy: DummyCycles::_0,
151+
};
152+
self.qspi.blocking_read(&mut buffer, transaction);
153+
buffer
154+
}
155+
156+
pub fn enable_mmap(&mut self) {
157+
let transaction: TransferConfig = TransferConfig {
158+
iwidth: QspiWidth::SING,
159+
awidth: QspiWidth::SING,
160+
dwidth: QspiWidth::QUAD,
161+
instruction: CMD_QUAD_READ,
162+
address: Some(0),
163+
dummy: DummyCycles::_8,
164+
};
165+
self.qspi.enable_memory_map(&transaction);
166+
}
167+
fn perform_erase(&mut self, addr: u32, cmd: u8) {
168+
let transaction = TransferConfig {
169+
iwidth: QspiWidth::SING,
170+
awidth: QspiWidth::SING,
171+
dwidth: QspiWidth::NONE,
172+
instruction: cmd,
173+
address: Some(addr),
174+
dummy: DummyCycles::_0,
175+
};
176+
self.enable_write();
177+
self.qspi.blocking_command(transaction);
178+
self.wait_write_finish();
179+
}
180+
pub fn enable_write(&mut self) {
181+
self.exec_command(CMD_WRITE_ENABLE);
182+
}
183+
pub fn erase_sector(&mut self, addr: u32) {
184+
self.perform_erase(addr, CMD_SECTOR_ERASE);
185+
}
186+
fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {
187+
assert!(
188+
(len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,
189+
"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}",
190+
len,
191+
addr
192+
);
193+
194+
let transaction = TransferConfig {
195+
iwidth: QspiWidth::SING,
196+
awidth: QspiWidth::SING,
197+
dwidth: QspiWidth::QUAD,
198+
instruction: CMD_QUAD_WRITE_PG,
199+
address: Some(addr),
200+
dummy: DummyCycles::_0,
201+
};
202+
self.enable_write();
203+
if use_dma {
204+
self.qspi.blocking_write_dma(buffer, transaction);
205+
} else {
206+
self.qspi.blocking_write(buffer, transaction);
207+
}
208+
self.wait_write_finish();
209+
}
210+
pub fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {
211+
let mut left = buffer.len();
212+
let mut place = addr;
213+
let mut chunk_start = 0;
214+
215+
while left > 0 {
216+
let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
217+
let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };
218+
let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
219+
self.write_page(place, chunk, chunk_size, use_dma);
220+
place += chunk_size as u32;
221+
left -= chunk_size;
222+
chunk_start += chunk_size;
223+
}
224+
}
225+
226+
pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {
227+
let transaction = TransferConfig {
228+
iwidth: QspiWidth::SING,
229+
awidth: QspiWidth::SING,
230+
dwidth: QspiWidth::QUAD,
231+
instruction: CMD_QUAD_READ,
232+
address: Some(addr),
233+
dummy: DummyCycles::_8,
234+
};
235+
if use_dma {
236+
self.qspi.blocking_read_dma(buffer, transaction);
237+
} else {
238+
self.qspi.blocking_read(buffer, transaction);
239+
}
240+
}
241+
}
242+
243+
const MEMORY_ADDR: u32 = 0x00000000 as u32;
244+
245+
#[embassy_executor::main]
246+
async fn main(_spawner: Spawner) {
247+
let p = embassy_stm32::init(Default::default());
248+
249+
let config = qspi::Config {
250+
memory_size: MemorySize::_16MiB,
251+
address_size: AddressSize::_24bit,
252+
prescaler: 200,
253+
cs_high_time: ChipSelectHighTime::_1Cycle,
254+
fifo_threshold: FIFOThresholdLevel::_16Bytes,
255+
};
256+
let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config);
257+
let mut flash = FlashMemory::new(driver);
258+
let mut wr_buf = [0u8; 256];
259+
for i in 0..32 {
260+
wr_buf[i] = i as u8;
261+
}
262+
let mut rd_buf = [0u8; 32];
263+
flash.erase_sector(MEMORY_ADDR);
264+
flash.write_memory(MEMORY_ADDR, &wr_buf, false);
265+
flash.read_memory(MEMORY_ADDR, &mut rd_buf, false);
266+
267+
info!("data read from indirect mode: {}", rd_buf);
268+
flash.enable_mmap();
269+
let qspi_base = unsafe { core::slice::from_raw_parts(0x9000_0000 as *const u8, 32) };
270+
info!("data read from mmap: {}", qspi_base);
271+
loop {
272+
Timer::after_millis(1000).await;
273+
}
274+
}

0 commit comments

Comments
 (0)