Skip to content

Commit 0654d98

Browse files
bors[bot]X-yl
andauthored
Merge #645
645: Add a basic blocking QSPI interface r=burrbull a=X-yl This module implements the QuadSPI interface which allows high speed communication with external flash memory. Limitations: - Interrupts are not supported. - Status polling mode is not supported. Co-authored-by: x-yl <[email protected]>
2 parents dcb7f06 + 0ecd154 commit 0654d98

File tree

7 files changed

+1101
-5
lines changed

7 files changed

+1101
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717
### Added
1818

1919
- Extended 64-bit monotonic timer [#640]
20+
- Basic blocking QSPI interface [#645]
2021

2122
### Fixed
2223

@@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2627
[#635]: https://github.com/stm32-rs/stm32f4xx-hal/pull/635
2728
[#636]: https://github.com/stm32-rs/stm32f4xx-hal/pull/636
2829
[#640]: https://github.com/stm32-rs/stm32f4xx-hal/pull/640
30+
[#645]: https://github.com/stm32-rs/stm32f4xx-hal/pull/645
2931

3032
## [v0.16.0] - 2023-05-07
3133

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ required-features = ["stm32f411"]
394394
name = "qei"
395395
required-features = ["tim2"] # stm32f411
396396

397+
[[example]]
398+
name = "qspi-w25q"
399+
required-features = ["stm32f412"]
400+
397401
[[example]]
398402
name = "rng-display"
399403
required-features = ["rng"] # stm32f407

examples/qspi-w25q.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//! Example of using the QSPI peripheral with a W25Q flash chip (W25Q128JV).
2+
//! Pins configured for QSPI Bank1 of STM32F412 board. Adjust as needed.
3+
4+
#![no_std]
5+
#![no_main]
6+
7+
use cortex_m_rt::entry;
8+
use cortex_m_semihosting::hprintln;
9+
use panic_semihosting as _;
10+
use stm32f4xx_hal as hal;
11+
use stm32f4xx_hal::gpio::GpioExt;
12+
use stm32f4xx_hal::qspi::{
13+
FlashSize, MemoryMapped, Qspi, QspiConfig, QspiError, QspiMemoryMappedConfig, QspiMode,
14+
QspiPins, QspiReadCommand, QspiWriteCommand,
15+
};
16+
17+
pub struct W25Q<PINS: QspiPins> {
18+
qspi: Qspi<PINS>,
19+
}
20+
21+
pub struct DeviceId(u8);
22+
23+
impl<PINS> W25Q<PINS>
24+
where
25+
PINS: QspiPins,
26+
{
27+
pub fn new(qspi: Qspi<PINS>) -> Result<Self, QspiError> {
28+
let mut chip = Self { qspi };
29+
chip.release_from_power_down()?;
30+
chip.quad_enable()?;
31+
Ok(chip)
32+
}
33+
34+
pub fn release_from_power_down(&mut self) -> Result<DeviceId, QspiError> {
35+
let mut buf = [0u8; 1];
36+
37+
self.qspi.indirect_read(
38+
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
39+
.instruction(0xAB, QspiMode::SingleChannel)
40+
.address(0x0, QspiMode::SingleChannel),
41+
)?;
42+
43+
Ok(DeviceId(buf[0]))
44+
}
45+
46+
pub fn wait_on_busy(&mut self) -> Result<(), QspiError> {
47+
let mut buf = [0u8; 1];
48+
49+
loop {
50+
self.qspi.indirect_read(
51+
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
52+
.instruction(0x05, QspiMode::SingleChannel),
53+
)?;
54+
55+
if buf[0] & 0x01 == 0 {
56+
return Ok(());
57+
}
58+
}
59+
}
60+
61+
pub fn erase_sector(&mut self, address: u32) -> Result<(), QspiError> {
62+
self.write_enable()?;
63+
self.qspi.indirect_write(
64+
QspiWriteCommand::default()
65+
.instruction(0x20, QspiMode::SingleChannel)
66+
.address(address, QspiMode::SingleChannel),
67+
)?;
68+
69+
self.wait_on_busy()?;
70+
Ok(())
71+
}
72+
73+
pub fn write_enable(&mut self) -> Result<(), QspiError> {
74+
self.qspi.indirect_write(
75+
QspiWriteCommand::default().instruction(0x06, QspiMode::SingleChannel),
76+
)?;
77+
self.wait_on_busy()?;
78+
79+
Ok(())
80+
}
81+
82+
pub fn quad_enable(&mut self) -> Result<(), QspiError> {
83+
// First check if quad is already enabled
84+
let mut buf = [0u8; 1];
85+
self.qspi.indirect_read(
86+
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
87+
.instruction(0x35, QspiMode::SingleChannel),
88+
)?;
89+
90+
if buf[0] & 0x02 == 0x02 {
91+
return Ok(());
92+
}
93+
94+
// If not, first we need to make the register writable
95+
self.write_enable()?;
96+
97+
// Then we can set the quad enable bit
98+
self.qspi.indirect_write(
99+
QspiWriteCommand::default()
100+
.instruction(0x31, QspiMode::SingleChannel)
101+
.address(0x0, QspiMode::SingleChannel)
102+
.data(&[buf[0] | 0x2], QspiMode::SingleChannel),
103+
)?;
104+
Ok(())
105+
}
106+
107+
pub fn program_page(&mut self, address: u32, data: &[u8]) -> Result<(), QspiError> {
108+
self.write_enable()?;
109+
110+
self.qspi.indirect_write(
111+
QspiWriteCommand::default()
112+
.instruction(0x32, QspiMode::SingleChannel)
113+
.address(address, QspiMode::SingleChannel)
114+
.data(data, QspiMode::QuadChannel),
115+
)?;
116+
117+
self.wait_on_busy()?;
118+
Ok(())
119+
}
120+
121+
pub fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), QspiError> {
122+
self.qspi.indirect_read(
123+
QspiReadCommand::new(data, QspiMode::QuadChannel)
124+
.instruction(0xEB, QspiMode::SingleChannel)
125+
.address(address, QspiMode::QuadChannel)
126+
.dummy_cycles(6),
127+
)?;
128+
129+
Ok(())
130+
}
131+
132+
pub fn memory_mapped<'a>(&'a mut self) -> Result<MemoryMapped<'a, PINS>, QspiError> {
133+
self.qspi.memory_mapped(
134+
QspiMemoryMappedConfig::default()
135+
.instruction(0xEB, QspiMode::SingleChannel)
136+
.address_mode(QspiMode::QuadChannel)
137+
.data_mode(QspiMode::QuadChannel)
138+
.dummy_cycles(6),
139+
)
140+
}
141+
}
142+
143+
#[entry]
144+
fn main() -> ! {
145+
if let Some(dp) = stm32f4xx_hal::pac::Peripherals::take() {
146+
let gpioa = dp.GPIOA.split();
147+
let gpiob = dp.GPIOB.split();
148+
let gpiod = dp.GPIOD.split();
149+
let gpioe = dp.GPIOE.split();
150+
151+
let qspi = Qspi::bank1(
152+
dp.QUADSPI,
153+
(
154+
gpiob.pb6, gpiod.pd11, gpiod.pd12, gpioe.pe2, gpioa.pa1, gpiob.pb1,
155+
),
156+
QspiConfig::default()
157+
.address_size(hal::qspi::AddressSize::Addr24Bit)
158+
.flash_size(FlashSize::from_megabytes(16))
159+
.clock_prescaler(0)
160+
.sample_shift(hal::qspi::SampleShift::HalfACycle),
161+
);
162+
163+
let mut flash = W25Q::new(qspi).unwrap();
164+
flash.erase_sector(0).unwrap();
165+
flash.program_page(0, "Hello, world!".as_bytes()).unwrap();
166+
167+
let mut buf = [0u8; 13];
168+
flash.read(0, &mut buf).unwrap();
169+
170+
hprintln!("Read: {:?}", core::str::from_utf8(&buf));
171+
172+
let mem_mapped = flash.memory_mapped().unwrap();
173+
hprintln!(
174+
"Mapped: {:?}",
175+
core::str::from_utf8(&mem_mapped.buffer()[0..13])
176+
);
177+
}
178+
179+
loop {}
180+
}

src/gpio/alt.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,11 +368,11 @@ pub trait QuadSpiBanks {
368368
}
369369
#[cfg(feature = "quadspi")]
370370
pub trait QuadSpiBank {
371-
type Io0;
372-
type Io1;
373-
type Io2;
374-
type Io3;
375-
type Ncs;
371+
type Io0: crate::gpio::PinSpeed;
372+
type Io1: crate::gpio::PinSpeed;
373+
type Io2: crate::gpio::PinSpeed;
374+
type Io3: crate::gpio::PinSpeed;
375+
type Ncs: crate::gpio::PinSpeed;
376376
}
377377

378378
// SAI pins

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ pub mod fsmc_lcd;
153153
pub mod prelude;
154154
#[cfg(feature = "device-selected")]
155155
pub mod qei;
156+
#[cfg(feature = "quadspi")]
157+
pub mod qspi;
156158
#[cfg(feature = "device-selected")]
157159
pub mod rcc;
158160
#[cfg(feature = "device-selected")]

0 commit comments

Comments
 (0)