use crate::{utils::HexSlice, BlockDevice, Error, Read};
use bitflags::bitflags;
use core::convert::TryInto;
use core::fmt;
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::digital::v2::OutputPin;
pub struct Identification {
bytes: [u8; 3],
continuations: u8,
}
impl Identification {
pub fn from_jedec_id(buf: &[u8]) -> Identification {
let mut start_idx = 0;
for i in 0..(buf.len() - 2) {
if buf[i] != 0x7F {
start_idx = i;
break;
}
}
Self {
bytes: [buf[start_idx], buf[start_idx + 1], buf[start_idx + 2]],
continuations: start_idx as u8,
}
}
pub fn mfr_code(&self) -> u8 {
self.bytes[0]
}
pub fn device_id(&self) -> &[u8] {
self.bytes[1..].as_ref()
}
pub fn continuation_count(&self) -> u8 {
self.continuations
}
}
impl fmt::Debug for Identification {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Identification")
.field(&HexSlice(self.bytes))
.finish()
}
}
#[allow(unused)] enum Opcode {
ReadDeviceId = 0xAB,
ReadMfDId = 0x90,
ReadJedecId = 0x9F,
WriteEnable = 0x06,
WriteDisable = 0x04,
ReadStatus = 0x05,
WriteStatus = 0x01,
Read = 0x03,
PageProg = 0x02, SectorErase = 0x20,
BlockErase = 0xD8,
ChipErase = 0xC7,
}
bitflags! {
pub struct Status: u8 {
const BUSY = 1 << 0;
const WEL = 1 << 1;
const PROT = 0b00011100;
const SRWD = 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 };
let status = this.read_status()?;
info!("Flash::init: status = {:?}", status);
if !(status & (Status::BUSY | Status::WEL)).is_empty() {
return Err(Error::UnexpectedStatus);
}
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_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
let mut buf: [u8; 12] = [0; 12];
buf[0] = Opcode::ReadJedecId as u8;
self.command(&mut buf)?;
Ok(Identification::from_jedec_id(&buf[1..]))
}
pub fn read_status(&mut self) -> Result<Status, Error<SPI, CS>> {
let mut buf = [Opcode::ReadStatus as u8, 0];
self.command(&mut buf)?;
Ok(Status::from_bits_truncate(buf[1]))
}
fn write_enable(&mut self) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [Opcode::WriteEnable as u8];
self.command(&mut cmd_buf)?;
Ok(())
}
fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
while self.read_status()?.contains(Status::BUSY) {}
Ok(())
}
}
impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [
Opcode::Read as u8,
(addr >> 16) 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<u32, SPI, CS> for Flash<SPI, CS> {
fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, CS>> {
for c in 0..amount {
self.write_enable()?;
let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
let mut cmd_buf = [
Opcode::SectorErase as u8,
(current_addr >> 16) as u8,
(current_addr >> 8) as u8,
current_addr as u8,
];
self.command(&mut cmd_buf)?;
self.wait_done()?;
}
Ok(())
}
fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
for (c, chunk) in data.chunks_mut(256).enumerate() {
self.write_enable()?;
let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
let mut cmd_buf = [
Opcode::PageProg as u8,
(current_addr >> 16) as u8,
(current_addr >> 8) as u8,
current_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(chunk);
}
self.cs.set_high().map_err(Error::Gpio)?;
spi_result.map(|_| ()).map_err(Error::Spi)?;
self.wait_done()?;
}
Ok(())
}
fn erase_all(&mut self) -> Result<(), Error<SPI, CS>> {
self.write_enable()?;
let mut cmd_buf = [Opcode::ChipErase as u8];
self.command(&mut cmd_buf)?;
self.wait_done()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decode_jedec_id() {
let cypress_id_bytes = [0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xC2, 0x22, 0x08];
let ident = Identification::from_jedec_id(&cypress_id_bytes);
assert_eq!(0xC2, ident.mfr_code());
assert_eq!(6, ident.continuation_count());
let device_id = ident.device_id();
assert_eq!(device_id[0], 0x22);
assert_eq!(device_id[1], 0x08);
}
}