use crate::hal;
use crate::pins::FlashPins;
use embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize};
use hal::{
mode::Blocking,
peripherals::QUADSPI,
qspi::{
enums::{DummyCycles, QspiWidth},
Qspi, TransferConfig,
},
};
const WRITE_STATUS_REGISTRY_CMD: u8 = 0x01; const WRITE_CMD: u8 = 0x02; const READ_STATUS_REGISTRY_CMD: u8 = 0x05; const WRITE_ENABLE_CMD: u8 = 0x06; const ENTER_QPI_MODE_CMD: u8 = 0x35; const SET_READ_PARAMETERS_CMD: u8 = 0xC0; const SECTOR_ERASE_CMD: u8 = 0xD7; const FAST_READ_QUAD_IO_CMD: u8 = 0xEB;
const SECTOR_SIZE: u32 = 4096;
const PAGE_SIZE: u32 = 256;
pub struct FlashBuilder {
pub pins: FlashPins,
pub qspi: QUADSPI,
}
impl FlashBuilder {
pub fn build<'a>(self) -> Flash<'a> {
let config = hal::qspi::Config {
memory_size: MemorySize::_8MiB,
address_size: AddressSize::_24bit,
prescaler: 1,
cs_high_time: ChipSelectHighTime::_2Cycle,
fifo_threshold: FIFOThresholdLevel::_1Bytes,
};
let Self { pins, qspi } = self;
let qspi = Qspi::new_blocking_bank1(
qspi, pins.IO0, pins.IO1, pins.IO2, pins.IO3, pins.SCK, pins.CS, config,
);
let mut result = Flash { qspi };
result.enable_qpi_mode();
result.reset_status_register();
result.reset_read_register();
result
}
}
const MAX_ADDRESS: u32 = 0x7FFFFF;
pub struct Flash<'a> {
qspi: Qspi<'a, QUADSPI, Blocking>,
}
impl Flash<'_> {
pub fn read(&mut self, address: u32, buffer: &mut [u8]) {
assert!(address <= MAX_ADDRESS);
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::QUAD,
instruction: FAST_READ_QUAD_IO_CMD,
address: Some(address),
dummy: DummyCycles::_6,
};
self.qspi.blocking_read(buffer, transaction);
}
pub fn read_uuid(&mut self) -> [u8; 16] {
let mut buffer = [0; 16];
let transaction: TransferConfig = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::QUAD,
instruction: 0x4B,
address: Some(0x00),
dummy: DummyCycles::_6,
};
self.qspi.blocking_read(&mut buffer, transaction);
buffer
}
pub fn write(&mut self, mut address: u32, data: &[u8]) {
assert!(address <= MAX_ADDRESS);
assert!(!data.is_empty());
self.erase(address, data.len() as u32);
let mut length = data.len() as u32;
let mut start_cursor = 0;
loop {
let page_remainder = PAGE_SIZE - (address & (PAGE_SIZE - 1));
let size = page_remainder.min(length) as usize;
self.enable_write();
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::QUAD,
instruction: WRITE_CMD,
address: Some(address),
dummy: DummyCycles::_0,
};
self.qspi
.blocking_write(&data[start_cursor..start_cursor + size], transaction);
self.wait_for_write();
start_cursor += size;
if length <= page_remainder {
break;
}
length -= page_remainder;
address += page_remainder;
address %= MAX_ADDRESS;
}
}
pub fn erase(&mut self, mut address: u32, mut length: u32) {
assert!(address <= MAX_ADDRESS);
assert!(length > 0);
loop {
self.enable_write();
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::NONE,
instruction: SECTOR_ERASE_CMD,
address: Some(address),
dummy: DummyCycles::_0,
};
self.qspi.blocking_command(transaction);
self.wait_for_write();
let sector_remainder = SECTOR_SIZE - (address & (SECTOR_SIZE - 1));
if length <= sector_remainder {
break;
}
length -= sector_remainder;
address += sector_remainder;
address %= MAX_ADDRESS;
}
}
fn enable_write(&mut self) {
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::NONE,
instruction: WRITE_ENABLE_CMD,
address: None,
dummy: DummyCycles::_0,
};
self.qspi.blocking_command(transaction);
}
fn wait_for_write(&mut self) {
loop {
let mut status: [u8; 1] = [0xFF; 1];
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::QUAD,
instruction: READ_STATUS_REGISTRY_CMD,
address: None,
dummy: DummyCycles::_0,
};
self.qspi.blocking_read(&mut status, transaction);
if status[0] & 0x01 == 0 {
break;
}
}
}
fn reset_status_register(&mut self) {
self.enable_write();
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::NONE,
instruction: WRITE_STATUS_REGISTRY_CMD,
address: Some(0b0000_0010),
dummy: DummyCycles::_0,
};
self.qspi.blocking_command(transaction);
self.wait_for_write();
}
fn reset_read_register(&mut self) {
self.enable_write();
let transaction = TransferConfig {
iwidth: QspiWidth::QUAD,
awidth: QspiWidth::QUAD,
dwidth: QspiWidth::NONE,
instruction: SET_READ_PARAMETERS_CMD,
address: Some(0b1111_1000),
dummy: DummyCycles::_0,
};
self.qspi.blocking_command(transaction);
self.wait_for_write();
}
fn enable_qpi_mode(&mut self) {
self.enable_write();
let transaction = TransferConfig {
iwidth: QspiWidth::SING,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::NONE,
instruction: ENTER_QPI_MODE_CMD,
address: None,
dummy: DummyCycles::_0,
};
self.qspi.blocking_command(transaction);
self.wait_for_write();
}
}