diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f15ce4..a5cbc08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Implement `Display` for `Error` +* Make BlockDevice depend on embedded_hal's Write and make write_bytes buffer reference non-mutable ## 0.2.0 - 2020-03-25 diff --git a/src/error.rs b/src/error.rs index ff23805..5566c96 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,4 @@ use core::fmt::{self, Debug, Display}; -use embedded_hal::blocking::spi::Transfer; use embedded_hal::digital::v2::OutputPin; mod private { @@ -11,9 +10,9 @@ mod private { /// /// This can encapsulate an SPI or GPIO error, and adds its own protocol errors /// on top of that. -pub enum Error, GPIO: OutputPin> { +pub enum Error { /// An SPI transfer failed. - Spi(SPI::Error), + Spi(E), /// A GPIO could not be set. Gpio(GPIO::Error), @@ -29,9 +28,9 @@ pub enum Error, GPIO: OutputPin> { __NonExhaustive(private::Private), } -impl, GPIO: OutputPin> Debug for Error +impl Debug for Error where - SPI::Error: Debug, + E: Debug, GPIO::Error: Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -44,9 +43,9 @@ where } } -impl, GPIO: OutputPin> Display for Error +impl Display for Error where - SPI::Error: Display, + E: Display, GPIO::Error: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/lib.rs b/src/lib.rs index 1444ffe..3877749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ mod utils; pub use crate::error::Error; -use embedded_hal::blocking::spi::Transfer; +use embedded_hal::blocking::spi::{Transfer, Write}; use embedded_hal::digital::v2::OutputPin; /// A trait for reading operations from a memory chip. @@ -31,23 +31,31 @@ 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, addr: Addr, buf: &mut [u8]) -> Result<(), Error>; } /// A trait for writing and erasing operations on a memory chip. -pub trait BlockDevice, CS: OutputPin> { +pub trait BlockDevice< + Addr, + SPI: Transfer + Write, + CS: OutputPin, +> +{ + type SpiError; + /// Erases sectors from the memory chip. /// /// # 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, 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) -> 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 +63,5 @@ 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, addr: Addr, data: &[u8]) -> Result<(), Error>; } diff --git a/src/series25.rs b/src/series25.rs index 9c361c4..fb0f975 100644 --- a/src/series25.rs +++ b/src/series25.rs @@ -4,7 +4,9 @@ use crate::{utils::HexSlice, BlockDevice, Error, Read}; use bitflags::bitflags; use core::convert::TryInto; use core::fmt; +use embedded_hal::blocking::delay::DelayUs; use embedded_hal::blocking::spi::Transfer; +use embedded_hal::blocking::spi::Write; use embedded_hal::digital::v2::OutputPin; /// 3-Byte JEDEC manufacturer and device identification. @@ -88,6 +90,7 @@ enum Opcode { PageProg = 0x02, // directly writes to EEPROMs too SectorErase = 0x20, BlockErase = 0xD8, + DeepPowerDown = 0xB9, ChipErase = 0xC7, } @@ -113,7 +116,7 @@ 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> { +pub struct Flash { spi: SPI, cs: CS, } @@ -127,7 +130,7 @@ 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> { + pub fn init(spi: SPI, cs: CS) -> Result> { let mut this = Self { spi, cs }; let status = this.read_status()?; info!("Flash::init: status = {:?}", status); @@ -141,7 +144,7 @@ impl, CS: OutputPin> Flash { Ok(this) } - fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + fn command(&mut self, 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); @@ -151,7 +154,7 @@ impl, CS: OutputPin> Flash { } /// Reads the JEDEC manufacturer/device identification. - pub fn read_jedec_id(&mut self) -> Result> { + pub fn read_jedec_id(&mut self) -> 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; @@ -162,20 +165,48 @@ impl, CS: OutputPin> Flash { } /// Reads the status register. - pub fn read_status(&mut self) -> Result> { + pub fn read_status(&mut self) -> Result> { 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> { + pub fn deep_power_down>( + &mut self, + timer: Option<&mut D>, + ) -> Result<(), Error> { + let mut buf = [Opcode::DeepPowerDown as u8]; + self.command(&mut buf)?; + + if let Some(timer) = timer { + // Wait for low power mode to be entered + timer.delay_us(10); + } + + Ok(()) + } + + pub fn wake_up>(&mut self, timer: &mut D) -> Result<(), Error> { + // Wait minimum time required to be in low power mode before waking up again (t_DPDD) + timer.delay_us(30); + // Pull CS down for at least t_CRCP (20ns) + self.cs.set_low().map_err(Error::Gpio)?; + timer.delay_us(1); + self.cs.set_high().map_err(Error::Gpio)?; + // Wait for the recovery time + timer.delay_us(35); + + Ok(()) + } + + fn write_enable(&mut self) -> Result<(), Error> { let mut cmd_buf = [Opcode::WriteEnable as u8]; self.command(&mut cmd_buf)?; Ok(()) } - fn wait_done(&mut self) -> Result<(), Error> { + fn wait_done(&mut self) -> Result<(), Error> { // TODO: Consider changing this to a delay based pattern while self.read_status()?.contains(Status::BUSY) {} Ok(()) @@ -195,7 +226,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, addr: u32, buf: &mut [u8]) -> Result<(), Error> { // TODO what happens if `buf` is empty? let mut cmd_buf = [ @@ -215,8 +246,11 @@ impl, CS: OutputPin> Read for Flash { } } -impl, CS: OutputPin> BlockDevice for Flash { - fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error> { +impl + Write, CS: OutputPin> + BlockDevice for Flash +{ + type SpiError = E; + fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error> { for c in 0..amount { self.write_enable()?; @@ -234,8 +268,8 @@ impl, CS: OutputPin> BlockDevice for Flash Result<(), Error> { - for (c, chunk) in data.chunks_mut(256).enumerate() { + fn write_bytes(&mut self, addr: u32, data: &[u8]) -> Result<(), Error> { + for (c, chunk) in data.chunks(256).enumerate() { self.write_enable()?; let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap(); @@ -247,10 +281,11 @@ impl, CS: OutputPin> BlockDevice for Flash, CS: OutputPin> BlockDevice for Flash Result<(), Error> { + fn erase_all(&mut self) -> Result<(), Error> { self.write_enable()?; let mut cmd_buf = [Opcode::ChipErase as u8]; self.command(&mut cmd_buf)?;