#![no_std]
#![doc = include_str!("../README.md")]
#![deny(unsafe_code)]
#![warn(missing_docs)]
use core::{fmt::Debug, marker::PhantomData};
use derive_more::TryFrom;
use embedded_hal::digital::{OutputPin, PinState};
use embedded_hal_async::spi::SpiDevice;
use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
mod commands_impl;
pub struct Q;
pub struct X;
pub trait NorSeries {
const PAGE_SIZE: u32;
const SECTOR_SIZE: u32;
}
impl NorSeries for Q {
const PAGE_SIZE: u32 = 256;
const SECTOR_SIZE: u32 = Self::PAGE_SIZE * 16;
}
impl NorSeries for X {
const PAGE_SIZE: u32 = 256;
const SECTOR_SIZE: u32 = Self::PAGE_SIZE * 16;
}
pub trait Reset {}
impl Reset for Q {}
#[repr(u8)]
enum Command {
PageProgram = 0x02,
ReadData = 0x03,
ReadStatusRegister1 = 0x05,
WriteEnable = 0x06,
SectorErase = 0x20,
JedecId = 0x9F,
UniqueId = 0x4B,
Block32Erase = 0x52,
Block64Erase = 0xD8,
ChipErase = 0xC7,
EnableReset = 0x66,
PowerDown = 0xB9,
ReleasePowerDown = 0xAB,
Reset = 0x99,
}
pub struct W25<Series, SPI, HOLD, WP> {
spi: SPI,
hold: HOLD,
wp: WP,
capacity: u32,
_pantom: PhantomData<Series>,
}
impl<Series: NorSeries, SPI, HOLD, WP> W25<Series, SPI, HOLD, WP> {
pub fn capacity(&self) -> u32 {
self.capacity
}
fn n_sectors(&self) -> u32 {
self.capacity / Series::SECTOR_SIZE
}
fn n_blocks_32k(&self) -> u32 {
self.capacity / 32768
}
fn n_blocks_64k(&self) -> u32 {
self.capacity / 65536
}
}
impl<Series: NorSeries, SPI, S: Debug, P: Debug, HOLD, WP> W25<Series, SPI, HOLD, WP>
where
SPI: embedded_hal::spi::ErrorType<Error = S>,
HOLD: OutputPin<Error = P>,
WP: OutputPin<Error = P>,
{
pub fn new(spi: SPI, hold: HOLD, wp: WP, capacity: u32) -> Result<Self, P> {
let mut flash = W25 {
spi,
hold,
wp,
capacity,
_pantom: PhantomData,
};
flash.hold.set_high()?;
flash.wp.set_high()?;
Ok(flash)
}
pub fn set_hold(&mut self, value: PinState) -> Result<(), P> {
self.hold.set_state(value)
}
pub fn set_wp(&mut self, value: PinState) -> Result<(), P> {
self.wp.set_state(value)
}
}
impl<Series: NorSeries, SPI, S: Debug> W25<Series, SPI, (), ()>
where
SPI: embedded_hal::spi::ErrorType<Error = S>,
{
pub fn new_no_pins(spi: SPI, capacity: u32) -> Self {
Self {
spi,
hold: (),
wp: (),
capacity,
_pantom: PhantomData,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum AutodetectError<S: Debug, P: Debug> {
DeviceError(Error<S>),
PinError(P),
ManufacturerNotRecognized(u8),
DeviceNotRecognized(u8),
}
impl<S: Debug, P: Debug> From<Error<S>> for AutodetectError<S, P> {
fn from(value: Error<S>) -> Self {
Self::DeviceError(value)
}
}
impl<Series: NorSeries, SPI, S: Debug, P: Debug, HOLD, WP> W25<Series, SPI, HOLD, WP>
where
SPI: SpiDevice<Error = S>,
HOLD: OutputPin<Error = P>,
WP: OutputPin<Error = P>,
{
pub async fn new_autodetect(
spi: SPI,
hold: HOLD,
wp: WP,
) -> Result<Self, AutodetectError<S, P>> {
let mut flash = W25 {
spi,
hold,
wp,
capacity: 0, _pantom: PhantomData,
};
flash.hold.set_high().map_err(AutodetectError::PinError)?;
flash.wp.set_high().map_err(AutodetectError::PinError)?;
let jedec_id = flash.jedec_id().await?;
let manufacturer = jedec_id.manufacturer();
if jedec_id.manufacturer() != JedecId::MANUFACTURER {
return Err(AutodetectError::ManufacturerNotRecognized(manufacturer));
}
let major_device_id = jedec_id
.major_device_id()
.map_err(AutodetectError::DeviceNotRecognized)?;
flash.capacity = major_device_id.capacity();
Ok(flash)
}
}
impl<Series: NorSeries, SPI, S: Debug, HOLD, WP> ErrorType for W25<Series, SPI, HOLD, WP>
where
SPI: embedded_hal::spi::ErrorType<Error = S>,
{
type Error = Error<S>;
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error<S: Debug> {
SpiError(S),
NotAligned,
OutOfBounds,
WriteEnableFail,
}
impl<S: Debug> NorFlashError for Error<S> {
fn kind(&self) -> NorFlashErrorKind {
match self {
Error::NotAligned => NorFlashErrorKind::NotAligned,
Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,
_ => NorFlashErrorKind::Other,
}
}
}
#[allow(clippy::identity_op)]
fn command_and_address(command: u8, address: u32) -> [u8; 4] {
[
command,
((address & 0xFF0000) >> 16) as u8,
((address & 0x00FF00) >> 8) as u8,
((address & 0x0000FF) >> 0) as u8,
]
}
#[derive(Debug, Clone, Copy, TryFrom)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
#[try_from(repr)]
pub enum MajorDeviceId {
W25_05 = 0x05,
W25_10 = 0x10,
W25_20 = 0x11,
W25_40 = 0x12,
W25_80 = 0x13,
W25_16 = 0x14,
W25_32 = 0x15,
W25_64 = 0x16,
W25_128 = 0x17,
W25_256 = 0x18,
W25_512 = 0x19,
W25_01 = 0x20,
W25_02 = 0x21,
}
impl MajorDeviceId {
pub const fn capacity(&self) -> u32 {
let capacity_kilobits = match *self {
MajorDeviceId::W25_05 => 512,
MajorDeviceId::W25_10 => 1024,
MajorDeviceId::W25_20 => 2 * 1024,
MajorDeviceId::W25_40 => 4 * 1024,
MajorDeviceId::W25_80 => 8 * 1024,
MajorDeviceId::W25_16 => 16 * 1024,
MajorDeviceId::W25_32 => 32 * 1024,
MajorDeviceId::W25_64 => 64 * 1024,
MajorDeviceId::W25_128 => 128 * 1024,
MajorDeviceId::W25_256 => 256 * 1024,
MajorDeviceId::W25_512 => 512 * 1024,
MajorDeviceId::W25_01 => 1024 * 1024,
MajorDeviceId::W25_02 => 2 * 1024 * 1024,
};
const FACTOR_KILOBITS_BYTES: u32 = 1024 / 8;
capacity_kilobits * FACTOR_KILOBITS_BYTES
}
}
pub struct JedecId([u8; 3]);
impl JedecId {
pub const MANUFACTURER: u8 = 0xEF;
pub const fn manufacturer(&self) -> u8 {
self.0[0]
}
pub fn major_device_id(&self) -> Result<MajorDeviceId, u8> {
let b = self.0[2];
MajorDeviceId::try_from(b.checked_sub(1).ok_or(b)?).map_err(|_e| b)
}
pub const fn minor_device_id(&self) -> u8 {
self.0[1]
}
}