Skip to content
This repository has been archived by the owner on Oct 18, 2022. It is now read-only.

Microchip 25LCxxx support #25

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
Cargo.lock
.idea/
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ categories = ["embedded"]
readme = "README.md"
license = "0BSD"

[features]
# Microchip 25LCxxx series chip extras
25lc = []
default = []

# cargo-release configuration
[package.metadata.release]
tag-message = "{{version}}"
Expand Down
63 changes: 55 additions & 8 deletions src/series25.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Driver for 25-series SPI Flash and EEPROM chips.

use crate::{utils::HexSlice, BlockDevice, Error, Read};
#[cfg(not(feature = "25lc"))]
use crate::utils::HexSlice;
use crate::{BlockDevice, Error, Read};
use bitflags::bitflags;
use core::convert::TryInto;
#[cfg(not(feature = "25lc"))]
use core::fmt;
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::digital::v2::OutputPin;

/// 3-Byte JEDEC manufacturer and device identification.
#[cfg(not(feature = "25lc"))]
pub struct Identification {
/// Data collected
/// - First byte is the manufacturer's ID code from eg JEDEC Publication No. 106AJ
Expand All @@ -18,6 +21,7 @@ pub struct Identification {
continuations: u8,
}

#[cfg(not(feature = "25lc"))]
impl Identification {
/// Build an Identification from JEDEC ID bytes.
pub fn from_jedec_id(buf: &[u8]) -> Identification {
Expand Down Expand Up @@ -60,6 +64,7 @@ impl Identification {
}
}

#[cfg(not(feature = "25lc"))]
impl fmt::Debug for Identification {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Identification")
Expand All @@ -75,6 +80,7 @@ enum Opcode {
/// Read the 8-bit manufacturer and device IDs.
ReadMfDId = 0x90,
/// Read 16-bit manufacturer ID and 8-bit device ID.
#[cfg(not(feature = "25lc"))]
ReadJedecId = 0x9F,
/// Set the write enable latch.
WriteEnable = 0x06,
Expand All @@ -86,9 +92,14 @@ enum Opcode {
WriteStatus = 0x01,
Read = 0x03,
PageProg = 0x02, // directly writes to EEPROMs too
#[cfg(not(feature = "25lc"))]
SectorErase = 0x20,
#[cfg(feature = "25lc")]
SectorErase = 0x42,
BlockErase = 0xD8,
ChipErase = 0xC7,
#[cfg(feature = "25lc")]
DeepSleep = 0xBF,
}

bitflags! {
Expand Down Expand Up @@ -116,6 +127,7 @@ bitflags! {
pub struct Flash<SPI: Transfer<u8>, CS: OutputPin> {
spi: SPI,
cs: CS,
page_size: usize
}

impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
Expand All @@ -128,7 +140,20 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
/// * **`cs`**: The **C**hip-**S**elect Pin connected to the `\CS`/`\CE` pin
/// of the flash chip. Will be driven low when accessing the device.
pub fn init(spi: SPI, cs: CS) -> Result<Self, Error<SPI, CS>> {
let mut this = Self { spi, cs };
Flash::init_with_page_size(spi, cs, 256)
}

/// Creates a new 25-series flash driver.
///
/// # Parameters
///
/// * **`spi`**: An SPI master. Must be configured to operate in the correct
/// mode for the device.
/// * **`cs`**: The **C**hip-**S**elect Pin connected to the `\CS`/`\CE` pin
/// of the flash chip. Will be driven low when accessing the device.
/// * **`page_size`**: Page size of the chip
pub fn init_with_page_size(spi: SPI, cs: CS, page_size: usize) -> Result<Self, Error<SPI, CS>> {
let mut this = Self { spi, cs, page_size };
let status = this.read_status()?;
info!("Flash::init: status = {:?}", status);

Expand All @@ -137,7 +162,6 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
if !(status & (Status::BUSY | Status::WEL)).is_empty() {
return Err(Error::UnexpectedStatus);
}

Ok(this)
}

Expand All @@ -151,6 +175,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
}

/// Reads the JEDEC manufacturer/device identification.
#[cfg(not(feature = "25lc"))]
pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
// Optimistically read 12 bytes, even though some identifiers will be shorter
let mut buf: [u8; 12] = [0; 12];
Expand All @@ -171,15 +196,36 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {

fn write_enable(&mut self) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [Opcode::WriteEnable as u8];
self.command(&mut cmd_buf)?;
Ok(())
self.command(&mut cmd_buf)
}

fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
// TODO: Consider changing this to a delay based pattern
while self.read_status()?.contains(Status::BUSY) {}
Ok(())
}

pub fn set_write_protection(&mut self, protection: u8) -> Result<(), Error<SPI, CS>> {
let protection = protection & 0b0111;
let status = self.read_status()?;
let status = (status.bits & 0b1_1100) | protection;
let mut buf = [Opcode::WriteStatus as u8, status];
self.command(&mut buf)
}

#[cfg(feature = "25lc")]
pub fn deep_sleep(&mut self) -> Result<(), Error<SPI, CS>> {
let mut cmd_buf = [Opcode::DeepSleep as u8];
self.command(&mut cmd_buf)
}

#[cfg(feature = "25lc")]
pub fn wake_up_and_get_manufacturer_id(&mut self) -> Result<u8, Error<SPI, CS>> {
// <Instruction byte><Dummy address 3 bytes><Manufacturer ID byte>
let mut cmd_buf = [Opcode::ReadDeviceId as u8, 0, 0, 0, 0];
self.command(&mut cmd_buf)?;
Ok(cmd_buf[3])
}
}

impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
Expand Down Expand Up @@ -220,7 +266,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u32, SPI, CS> for Flash<SPI,
for c in 0..amount {
self.write_enable()?;

let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
let current_addr: u32 = (addr as usize + c * self.page_size).try_into().unwrap();
let mut cmd_buf = [
Opcode::SectorErase as u8,
(current_addr >> 16) as u8,
Expand All @@ -238,7 +284,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u32, SPI, CS> for Flash<SPI,
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 current_addr: u32 = (addr as usize + c * self.page_size).try_into().unwrap();
let mut cmd_buf = [
Opcode::PageProg as u8,
(current_addr >> 16) as u8,
Expand Down Expand Up @@ -272,6 +318,7 @@ mod tests {
use super::*;

#[test]
#[cfg(not(feature = "25lc"))]
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);
Expand Down