Skip to content

Commit

Permalink
Add example for using QSPI with a W25Q flash chip
Browse files Browse the repository at this point in the history
  • Loading branch information
X-yl committed May 28, 2023
1 parent efd5fed commit 0ecd154
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ required-features = ["stm32f411"]
name = "qei"
required-features = ["tim2"] # stm32f411

[[example]]
name = "qspi-w25q"
required-features = ["stm32f412"]

[[example]]
name = "rng-display"
required-features = ["rng"] # stm32f407
Expand Down
180 changes: 180 additions & 0 deletions examples/qspi-w25q.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
//! Example of using the QSPI peripheral with a W25Q flash chip (W25Q128JV).
//! Pins configured for QSPI Bank1 of STM32F412 board. Adjust as needed.

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
use panic_semihosting as _;
use stm32f4xx_hal as hal;
use stm32f4xx_hal::gpio::GpioExt;
use stm32f4xx_hal::qspi::{
FlashSize, MemoryMapped, Qspi, QspiConfig, QspiError, QspiMemoryMappedConfig, QspiMode,
QspiPins, QspiReadCommand, QspiWriteCommand,
};

pub struct W25Q<PINS: QspiPins> {
qspi: Qspi<PINS>,
}

pub struct DeviceId(u8);

impl<PINS> W25Q<PINS>
where
PINS: QspiPins,
{
pub fn new(qspi: Qspi<PINS>) -> Result<Self, QspiError> {
let mut chip = Self { qspi };
chip.release_from_power_down()?;
chip.quad_enable()?;
Ok(chip)
}

pub fn release_from_power_down(&mut self) -> Result<DeviceId, QspiError> {
let mut buf = [0u8; 1];

self.qspi.indirect_read(
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
.instruction(0xAB, QspiMode::SingleChannel)
.address(0x0, QspiMode::SingleChannel),
)?;

Ok(DeviceId(buf[0]))
}

pub fn wait_on_busy(&mut self) -> Result<(), QspiError> {
let mut buf = [0u8; 1];

loop {
self.qspi.indirect_read(
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
.instruction(0x05, QspiMode::SingleChannel),
)?;

if buf[0] & 0x01 == 0 {
return Ok(());
}
}
}

pub fn erase_sector(&mut self, address: u32) -> Result<(), QspiError> {
self.write_enable()?;
self.qspi.indirect_write(
QspiWriteCommand::default()
.instruction(0x20, QspiMode::SingleChannel)
.address(address, QspiMode::SingleChannel),
)?;

self.wait_on_busy()?;
Ok(())
}

pub fn write_enable(&mut self) -> Result<(), QspiError> {
self.qspi.indirect_write(
QspiWriteCommand::default().instruction(0x06, QspiMode::SingleChannel),
)?;
self.wait_on_busy()?;

Ok(())
}

pub fn quad_enable(&mut self) -> Result<(), QspiError> {
// First check if quad is already enabled
let mut buf = [0u8; 1];
self.qspi.indirect_read(
QspiReadCommand::new(&mut buf, QspiMode::SingleChannel)
.instruction(0x35, QspiMode::SingleChannel),
)?;

if buf[0] & 0x02 == 0x02 {
return Ok(());
}

// If not, first we need to make the register writable
self.write_enable()?;

// Then we can set the quad enable bit
self.qspi.indirect_write(
QspiWriteCommand::default()
.instruction(0x31, QspiMode::SingleChannel)
.address(0x0, QspiMode::SingleChannel)
.data(&[buf[0] | 0x2], QspiMode::SingleChannel),
)?;
Ok(())
}

pub fn program_page(&mut self, address: u32, data: &[u8]) -> Result<(), QspiError> {
self.write_enable()?;

self.qspi.indirect_write(
QspiWriteCommand::default()
.instruction(0x32, QspiMode::SingleChannel)
.address(address, QspiMode::SingleChannel)
.data(data, QspiMode::QuadChannel),
)?;

self.wait_on_busy()?;
Ok(())
}

pub fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), QspiError> {
self.qspi.indirect_read(
QspiReadCommand::new(data, QspiMode::QuadChannel)
.instruction(0xEB, QspiMode::SingleChannel)
.address(address, QspiMode::QuadChannel)
.dummy_cycles(6),
)?;

Ok(())
}

pub fn memory_mapped<'a>(&'a mut self) -> Result<MemoryMapped<'a, PINS>, QspiError> {
self.qspi.memory_mapped(
QspiMemoryMappedConfig::default()
.instruction(0xEB, QspiMode::SingleChannel)
.address_mode(QspiMode::QuadChannel)
.data_mode(QspiMode::QuadChannel)
.dummy_cycles(6),
)
}
}

#[entry]
fn main() -> ! {
if let Some(dp) = stm32f4xx_hal::pac::Peripherals::take() {
let gpioa = dp.GPIOA.split();
let gpiob = dp.GPIOB.split();
let gpiod = dp.GPIOD.split();
let gpioe = dp.GPIOE.split();

let qspi = Qspi::bank1(
dp.QUADSPI,
(
gpiob.pb6, gpiod.pd11, gpiod.pd12, gpioe.pe2, gpioa.pa1, gpiob.pb1,
),
QspiConfig::default()
.address_size(hal::qspi::AddressSize::Addr24Bit)
.flash_size(FlashSize::from_megabytes(16))
.clock_prescaler(0)
.sample_shift(hal::qspi::SampleShift::HalfACycle),
);

let mut flash = W25Q::new(qspi).unwrap();
flash.erase_sector(0).unwrap();
flash.program_page(0, "Hello, world!".as_bytes()).unwrap();

let mut buf = [0u8; 13];
flash.read(0, &mut buf).unwrap();

hprintln!("Read: {:?}", core::str::from_utf8(&buf));

let mem_mapped = flash.memory_mapped().unwrap();
hprintln!(
"Mapped: {:?}",
core::str::from_utf8(&mem_mapped.buffer()[0..13])
);
}

loop {}
}

0 comments on commit 0ecd154

Please sign in to comment.