use crate::{ BlockDevice, Error, Read };
use bitflags::bitflags;
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::digital::v2::OutputPin;
const PAGE_SIZE: u16 = 32;
enum Opcode {
WriteEnable = 0x06,
WriteDisable = 0x04,
ReadStatusRegister = 0x05,
#[allow(dead_code)]
WriteStatusRegister = 0x01,
Read = 0x03,
Write = 0x02,
}
bitflags! {
pub struct Status: u8 {
const WRITE_IN_PROGRESS = 1 << 0;
const WRITE_ENABLE_LATCH = 1 << 1;
const BLOCK_PROTECT = 0b00001100;
const STATUS_REGISTER_WRITE_DISABLE = 1 << 7;
}
}
#[derive(Debug)]
pub struct Flash<SPI: Transfer<u8>, CS: OutputPin> {
spi: SPI,
cs: CS,
}
impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
pub fn init(spi: SPI, cs: CS) -> Result<Self, Error<SPI, CS>> {
let mut this = Self { spi, cs };
this.cs.set_high().map_err(Error::Gpio)?;
let status = this.read_status()?;
info!("Flash::init: status = {:?}", status);
if !(status & (Status::WRITE_IN_PROGRESS)).is_empty() {
return Err(Error::UnexpectedStatus);
}
if !(status & (Status::WRITE_ENABLE_LATCH)).is_empty() {
warn!("Write Enable Latch was set on init! Going to assume we're okay and disable it");
let result = this._write_disable();
match result {
Err(_) => return Err(Error::UnexpectedStatus),
Ok(_) => (),
};
}
Ok(this)
}
fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI, CS>> {
self.cs.set_low().map_err(Error::Gpio)?;
let spi_result = self.spi.transfer(bytes).map_err(Error::Spi);
self.cs.set_high().map_err(Error::Gpio)?;
spi_result?;
Ok(())
}
pub fn read_status(&mut self) -> Result<Status, Error<SPI, CS>> {
let mut buf = [Opcode::ReadStatusRegister as u8, 0];
self.command(&mut buf)?;
Ok(Status::from_bits_truncate(buf[1]))
}
pub fn _write_enable(&mut self) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [Opcode::WriteEnable as u8];
self.command(&mut cmd_buf)?;
Ok(())
}
pub fn _write_disable(&mut self) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [Opcode::WriteDisable as u8];
self.command(&mut cmd_buf)?;
Ok(())
}
fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
while self.read_status()?.contains(Status::WRITE_IN_PROGRESS) {}
Ok(())
}
fn write_bytes_to_page(&mut self, addr: u16, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
if addr > 2u16.pow(12)-1 {
return Err(Error::AddressOutOfBounds(addr.into()))
}
self._write_enable()?;
let mut cmd_buf = [
Opcode::Write as u8,
(addr >> 8) as u8,
addr as u8,
];
self.cs.set_low().map_err(Error::Gpio)?;
let mut spi_result = self.spi.transfer(&mut cmd_buf);
if spi_result.is_ok() {
spi_result = self.spi.transfer(data);
}
self.cs.set_high().map_err(Error::Gpio)?;
spi_result.map(|_| ()).map_err(Error::Spi)?;
self.wait_done()?;
Ok(())
}
}
impl<SPI: Transfer<u8>, CS: OutputPin> Read<u16, SPI, CS> for Flash<SPI, CS> {
fn read(&mut self, addr: u16, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [
Opcode::Read as u8,
(addr >> 8) as u8,
addr as u8,
];
self.cs.set_low().map_err(Error::Gpio)?;
let mut spi_result = self.spi.transfer(&mut cmd_buf);
if spi_result.is_ok() {
spi_result = self.spi.transfer(buf);
}
self.cs.set_high().map_err(Error::Gpio)?;
spi_result.map(|_| ()).map_err(Error::Spi)
}
}
impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u16, SPI, CS> for Flash<SPI, CS> {
fn erase_sectors(&mut self, addr: u16, amount: usize) -> Result<(), Error<SPI, CS>> {
let first_chunk_length = PAGE_SIZE - (addr % PAGE_SIZE);
let mut buf = [0; 32];
self.write_bytes(addr, &mut buf[..first_chunk_length.into()])?;
let mut current_addr = addr + first_chunk_length;
for _ in 1..amount {
let mut buf = [0; 32];
self.write_bytes(current_addr, &mut buf)?;
current_addr += PAGE_SIZE;
}
Ok(())
}
fn write_bytes(&mut self, addr: u16, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
let mut current_addr: u16 = addr;
let first_chunk: &mut [u8];
let mut rest_of_data: &mut [u8] = &mut [];
if (data.len() + addr as usize) < PAGE_SIZE.into() {
first_chunk = data;
} else {
let first_chunk_length = PAGE_SIZE - (current_addr % PAGE_SIZE);
(first_chunk, rest_of_data) = data.split_at_mut(first_chunk_length.into());
}
self.write_bytes_to_page(addr, first_chunk)?;
if rest_of_data.len() > 0 {
let remainder = current_addr % PAGE_SIZE;
current_addr = current_addr - remainder + PAGE_SIZE;
for chunk_data in rest_of_data.rchunks_mut(PAGE_SIZE.into()).rev() {
self.write_bytes_to_page(current_addr, chunk_data)?;
current_addr += PAGE_SIZE;
}
}
Ok(())
}
fn erase_all(&mut self) -> Result<(), Error<SPI, CS>> {
self.erase_sectors(0, (PAGE_SIZE/125).into())?;
Ok(())
}
}