diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..0e40fe8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6649a8c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..985436a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/spi-memory.iml b/.idea/spi-memory.iml
new file mode 100644
index 0000000..29e2276
--- /dev/null
+++ b/.idea/spi-memory.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index f8941de..19d99c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,7 @@ search = "https://docs.rs/spi-memory/[a-z0-9\\.-]+"
replace = "https://docs.rs/spi-memory/{{version}}"
[dependencies]
-embedded-hal = "0.2.3"
+embedded-hal = "0.2.5"
log = { version = "0.4.6", optional = true }
bitflags = "1.0.4"
diff --git a/src/lib.rs b/src/lib.rs
index 1444ffe..0a252f3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -31,7 +31,7 @@ pub trait Read, CS: OutputPin> {
/// # Parameters
/// * `addr`: The address to start reading at.
/// * `buf`: The buffer to read `buf.len()` bytes into.
- fn read(&mut self, addr: Addr, buf: &mut [u8]) -> Result<(), Error>;
+ fn read(&mut self, spi: &mut SPI, addr: Addr, buf: &mut [u8]) -> Result<(), Error>;
}
/// A trait for writing and erasing operations on a memory chip.
@@ -41,13 +41,18 @@ pub trait BlockDevice, CS: OutputPin> {
/// # Parameters
/// * `addr`: The address to start erasing at. If the address is not on a sector boundary,
/// the lower bits can be ignored in order to make it fit.
- fn erase_sectors(&mut self, addr: Addr, amount: usize) -> Result<(), Error>;
+ fn erase_sectors(
+ &mut self,
+ spi: &mut SPI,
+ addr: Addr,
+ amount: usize,
+ ) -> Result<(), Error>;
/// Erases the memory chip fully.
///
/// Warning: Full erase operations can take a significant amount of time.
/// Check your device's datasheet for precise numbers.
- fn erase_all(&mut self) -> Result<(), Error>;
+ fn erase_all(&mut self, spi: &mut SPI) -> Result<(), Error>;
/// Writes bytes onto the memory chip. This method is supposed to assume that the sectors
/// it is writing to have already been erased and should not do any erasing themselves.
@@ -55,5 +60,10 @@ pub trait BlockDevice, CS: OutputPin> {
/// # Parameters
/// * `addr`: The address to write to.
/// * `data`: The bytes to write to `addr`.
- fn write_bytes(&mut self, addr: Addr, data: &mut [u8]) -> Result<(), Error>;
+ fn write_bytes(
+ &mut self,
+ spi: &mut SPI,
+ addr: Addr,
+ data: &mut [u8],
+ ) -> Result<(), Error>;
}
diff --git a/src/series25.rs b/src/series25.rs
index 9c361c4..25b4079 100644
--- a/src/series25.rs
+++ b/src/series25.rs
@@ -4,7 +4,7 @@ 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::blocking::{delay::DelayUs, spi::Transfer};
use embedded_hal::digital::v2::OutputPin;
/// 3-Byte JEDEC manufacturer and device identification.
@@ -68,6 +68,7 @@ impl fmt::Debug for Identification {
}
}
+#[repr(u8)]
#[allow(unused)] // TODO support more features
enum Opcode {
/// Read the 8-bit legacy device ID.
@@ -89,6 +90,7 @@ enum Opcode {
SectorErase = 0x20,
BlockErase = 0xD8,
ChipErase = 0xC7,
+ PowerDown = 0xB9,
}
bitflags! {
@@ -113,12 +115,13 @@ bitflags! {
/// * **`CS`**: The **C**hip-**S**elect line attached to the `\CS`/`\CE` pin of
/// the flash chip.
#[derive(Debug)]
-pub struct Flash, CS: OutputPin> {
- spi: SPI,
+//pub struct Flash, CS: OutputPin> {
+pub struct Flash {
+ // spi: &mut SPI,
cs: CS,
}
-impl, CS: OutputPin> Flash {
+impl Flash {
/// Creates a new 25-series flash driver.
///
/// # Parameters
@@ -127,9 +130,9 @@ impl, CS: OutputPin> Flash {
/// 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.
- pub fn init(spi: SPI, cs: CS) -> Result> {
- let mut this = Self { spi, cs };
- let status = this.read_status()?;
+ pub fn init>(spi: &mut SPI, cs: CS) -> Result> {
+ let mut this = Self { cs };
+ let status = this.read_status(spi)?;
info!("Flash::init: status = {:?}", status);
// Here we don't expect any writes to be in progress, and the latch must
@@ -141,48 +144,106 @@ impl, CS: OutputPin> Flash {
Ok(this)
}
- fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
+ fn command>(
+ &mut self,
+ spi: &mut SPI,
+ bytes: &mut [u8],
+ ) -> Result<(), Error> {
// If the SPI transfer fails, make sure to disable CS anyways
self.cs.set_low().map_err(Error::Gpio)?;
- let spi_result = self.spi.transfer(bytes).map_err(Error::Spi);
+ let spi_result = spi.transfer(bytes).map_err(Error::Spi);
self.cs.set_high().map_err(Error::Gpio)?;
spi_result?;
Ok(())
}
/// Reads the JEDEC manufacturer/device identification.
- pub fn read_jedec_id(&mut self) -> Result> {
+ pub fn read_jedec_id>(
+ &mut self,
+ spi: &mut SPI,
+ ) -> Result> {
// Optimistically read 12 bytes, even though some identifiers will be shorter
let mut buf: [u8; 12] = [0; 12];
buf[0] = Opcode::ReadJedecId as u8;
- self.command(&mut buf)?;
+ self.command(spi, &mut buf)?;
// Skip buf[0] (SPI read response byte)
Ok(Identification::from_jedec_id(&buf[1..]))
}
/// Reads the status register.
- pub fn read_status(&mut self) -> Result> {
+ pub fn read_status>(
+ &mut self,
+ spi: &mut SPI,
+ ) -> Result> {
let mut buf = [Opcode::ReadStatus as u8, 0];
- self.command(&mut buf)?;
+ self.command(spi, &mut buf)?;
Ok(Status::from_bits_truncate(buf[1]))
}
- fn write_enable(&mut self) -> Result<(), Error> {
+ fn write_enable>(&mut self, spi: &mut SPI) -> Result<(), Error> {
let mut cmd_buf = [Opcode::WriteEnable as u8];
- self.command(&mut cmd_buf)?;
+ self.command(spi, &mut cmd_buf)?;
Ok(())
}
- fn wait_done(&mut self) -> Result<(), Error> {
+ fn wait_done>(&mut self, spi: &mut SPI) -> Result<(), Error> {
// TODO: Consider changing this to a delay based pattern
- while self.read_status()?.contains(Status::BUSY) {}
+ while self.read_status(spi)?.contains(Status::BUSY) {}
+ Ok(())
+ }
+
+ /// Enters power down mode.
+ /// Datasheet, 8.2.35: Power-down:
+ /// Although the standby current during normal operation is relatively low, standby current can be further
+ /// reduced with the Power-down instruction. The lower power consumption makes the Power-down
+ /// instruction especially useful for battery powered applications (See ICC1 and ICC2 in AC Characteristics).
+ /// The instruction is initiated by driving the /CS pin low and shifting the instruction code “B9h” as shown in
+ /// Figure 44.
+ ///
+ /// The /CS pin must be driven high after the eighth bit has been latched. If this is not done the Power-down
+ /// instruction will not be executed. After /CS is driven high, the power-down state will entered within the time
+ /// duration of tDP (See AC Characteristics). While in the power-down state only the Release Power-down /
+ /// Device ID (ABh) instruction, which restores the device to normal operation, will be recognized. All other
+ /// instructions are ignored. This includes the Read Status Register instruction, which is always available
+ /// during normal operation. Ignoring all but one instruction makes the Power Down state a useful condition
+ /// for securing maximum write protection. The device always powers-up in the normal operation with the
+ /// standby current of ICC1.
+ pub fn power_down>(&mut self, spi: &mut SPI) -> Result<(), Error> {
+ let mut buf = [Opcode::PowerDown as u8];
+ self.command(spi, &mut buf)?;
+
+ Ok(())
+ }
+
+ /// Exits Power Down Mode
+ /// Datasheet, 8.2.36: Release Power-down:
+ /// The Release from Power-down / Device ID instruction is a multi-purpose instruction. It can be used to
+ /// release the device from the power-down state, or obtain the devices electronic identification (ID) number.
+ /// To release the device from the power-down state, the instruction is issued by driving the /CS pin low,
+ /// shifting the instruction code “ABh” and driving /CS high as shown in Figure 45. Release from power-down
+ /// will take the time duration of tRES1 (See AC Characteristics) before the device will resume normal
+ /// operation and other instructions are accepted. The /CS pin must remain high during the tRES1 time
+ /// duration.
+ ///
+ /// Note: must manually delay after running this, IOC
+ pub fn release_power_down, D: DelayUs>(
+ &mut self,
+ spi: &mut SPI,
+ delay: &mut D,
+ ) -> Result<(), Error> {
+ // Same command as reading ID.. Wakes instead of reading ID if not followed by 3 dummy bytes.
+ let mut buf = [Opcode::ReadDeviceId as u8];
+ self.command(spi, &mut buf)?;
+
+ delay.delay_us(6); // Table 9.7: AC Electrical Characteristics: tRES1 = max 3us.
+
Ok(())
}
}
-impl, CS: OutputPin> Read for Flash {
+impl, CS: OutputPin> Read for Flash {
/// Reads flash contents into `buf`, starting at `addr`.
///
/// Note that `addr` is not fully decoded: Flash chips will typically only
@@ -195,7 +256,7 @@ impl, CS: OutputPin> Read for Flash {
///
/// * `addr`: 24-bit address to start reading at.
/// * `buf`: Destination buffer to fill.
- fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error> {
+ fn read(&mut self, spi: &mut SPI, addr: u32, buf: &mut [u8]) -> Result<(), Error> {
// TODO what happens if `buf` is empty?
let mut cmd_buf = [
@@ -206,19 +267,24 @@ impl, CS: OutputPin> Read for Flash {
];
self.cs.set_low().map_err(Error::Gpio)?;
- let mut spi_result = self.spi.transfer(&mut cmd_buf);
+ let mut spi_result = spi.transfer(&mut cmd_buf);
if spi_result.is_ok() {
- spi_result = self.spi.transfer(buf);
+ spi_result = spi.transfer(buf);
}
self.cs.set_high().map_err(Error::Gpio)?;
spi_result.map(|_| ()).map_err(Error::Spi)
}
}
-impl, CS: OutputPin> BlockDevice for Flash {
- fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error> {
+impl, CS: OutputPin> BlockDevice for Flash {
+ fn erase_sectors(
+ &mut self,
+ spi: &mut SPI,
+ addr: u32,
+ amount: usize,
+ ) -> Result<(), Error> {
for c in 0..amount {
- self.write_enable()?;
+ self.write_enable(spi)?;
let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
let mut cmd_buf = [
@@ -227,16 +293,21 @@ impl, CS: OutputPin> BlockDevice for Flash> 8) as u8,
current_addr as u8,
];
- self.command(&mut cmd_buf)?;
- self.wait_done()?;
+ self.command(spi, &mut cmd_buf)?;
+ self.wait_done(spi)?;
}
Ok(())
}
- fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error> {
+ fn write_bytes(
+ &mut self,
+ spi: &mut SPI,
+ addr: u32,
+ data: &mut [u8],
+ ) -> Result<(), Error> {
for (c, chunk) in data.chunks_mut(256).enumerate() {
- self.write_enable()?;
+ self.write_enable(spi)?;
let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
let mut cmd_buf = [
@@ -247,22 +318,22 @@ impl, CS: OutputPin> BlockDevice for Flash Result<(), Error> {
- self.write_enable()?;
+ fn erase_all(&mut self, spi: &mut SPI) -> Result<(), Error> {
+ self.write_enable(spi)?;
let mut cmd_buf = [Opcode::ChipErase as u8];
- self.command(&mut cmd_buf)?;
- self.wait_done()?;
+ self.command(spi, &mut cmd_buf)?;
+ self.wait_done(spi)?;
Ok(())
}
}