use crate::hal;
use crate::hal::gpio::Speed;
use crate::hal::prelude::*;
use crate::hal::xspi::{Config, Qspi, QspiMode, QspiWord};
use crate::pins::FlashPins;
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;
const MAX_ADDRESS: u32 = 0x7FFFFF;
pub struct Flash {
driver: Qspi<hal::pac::QUADSPI>,
}
impl Flash {
pub fn new(
clocks: &hal::rcc::CoreClocks,
qspi_device: hal::device::QUADSPI,
qspi_peripheral: hal::rcc::rec::Qspi,
pins: FlashPins,
) -> Self {
let mut cs = pins.CS.into_alternate::<10>();
let mut sck = pins.SCK.into_alternate::<9>();
let mut io0 = pins.IO0.into_alternate::<10>();
let mut io1 = pins.IO1.into_alternate::<10>();
let mut io2 = pins.IO2.into_alternate::<9>();
let mut io3 = pins.IO3.into_alternate::<9>();
cs.set_speed(Speed::VeryHigh);
sck.set_speed(Speed::VeryHigh);
io0.set_speed(Speed::VeryHigh);
io1.set_speed(Speed::VeryHigh);
io2.set_speed(Speed::VeryHigh);
io3.set_speed(Speed::VeryHigh);
let qspi = qspi_device.bank1(
(sck, io0, io1, io2, io3),
Config::new(133.MHz()).mode(QspiMode::OneBit),
clocks,
qspi_peripheral,
);
let mut flash = Self { driver: qspi };
flash.enable_qpi_mode();
flash.reset_status_register();
flash.reset_read_register();
flash
}
pub fn read(&mut self, address: u32, buffer: &mut [u8]) {
assert!(address <= MAX_ADDRESS);
for (i, chunk) in buffer.chunks_mut(32).enumerate() {
self.driver
.read_extended(
QspiWord::U8(FAST_READ_QUAD_IO_CMD),
QspiWord::U24(address + i as u32 * 32),
QspiWord::U8(0x00),
8,
chunk,
)
.unwrap();
}
}
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;
for (i, chunk) in data[start_cursor..start_cursor + size]
.chunks(32)
.enumerate()
{
self.enable_write();
self.driver
.write_extended(
QspiWord::U8(WRITE_CMD),
QspiWord::U24(address + i as u32 * 32),
QspiWord::None,
chunk,
)
.unwrap();
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();
self.driver
.write_extended(
QspiWord::U8(SECTOR_ERASE_CMD),
QspiWord::U24(address),
QspiWord::None,
&[],
)
.unwrap();
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 reset_status_register(&mut self) {
self.enable_write();
self.driver
.write_extended(
QspiWord::U8(WRITE_STATUS_REGISTRY_CMD),
QspiWord::U8(0b0000_0010),
QspiWord::None,
&[],
)
.unwrap();
self.wait_for_write();
}
fn reset_read_register(&mut self) {
self.enable_write();
self.driver
.write_extended(
QspiWord::U8(SET_READ_PARAMETERS_CMD),
QspiWord::U8(0b1111_1000),
QspiWord::None,
&[],
)
.unwrap();
self.wait_for_write();
}
fn enable_qpi_mode(&mut self) {
self.enable_write();
self.driver
.write_extended(
QspiWord::U8(ENTER_QPI_MODE_CMD),
QspiWord::None,
QspiWord::None,
&[],
)
.unwrap();
self.driver.configure_mode(QspiMode::FourBit).unwrap();
self.wait_for_write();
}
fn enable_write(&mut self) {
self.driver
.write_extended(
QspiWord::U8(WRITE_ENABLE_CMD),
QspiWord::None,
QspiWord::None,
&[],
)
.unwrap();
}
fn wait_for_write(&mut self) {
loop {
let mut status: [u8; 1] = [0xFF; 1];
self.driver
.read_extended(
QspiWord::U8(READ_STATUS_REGISTRY_CMD),
QspiWord::None,
QspiWord::None,
0,
&mut status,
)
.unwrap();
if status[0] & 0x01 == 0 {
break;
}
}
}
}