Skip to content
This repository was archived by the owner on Oct 18, 2022. It is now read-only.
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

* Implement `Display` for `Error`
* Make BlockDevice depend on embedded_hal's Write<u8> and make write_bytes buffer reference non-mutable

## 0.2.0 - 2020-03-25

Expand Down
13 changes: 6 additions & 7 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use core::fmt::{self, Debug, Display};
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::digital::v2::OutputPin;

mod private {
Expand All @@ -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<SPI: Transfer<u8>, GPIO: OutputPin> {
pub enum Error<E, GPIO: OutputPin> {
/// An SPI transfer failed.
Spi(SPI::Error),
Spi(E),

/// A GPIO could not be set.
Gpio(GPIO::Error),
Expand All @@ -29,9 +28,9 @@ pub enum Error<SPI: Transfer<u8>, GPIO: OutputPin> {
__NonExhaustive(private::Private),
}

impl<SPI: Transfer<u8>, GPIO: OutputPin> Debug for Error<SPI, GPIO>
impl<E, GPIO: OutputPin> Debug for Error<E, GPIO>
where
SPI::Error: Debug,
E: Debug,
GPIO::Error: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -44,9 +43,9 @@ where
}
}

impl<SPI: Transfer<u8>, GPIO: OutputPin> Display for Error<SPI, GPIO>
impl<E, GPIO: OutputPin> Display for Error<E, GPIO>
where
SPI::Error: Display,
E: Display,
GPIO::Error: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
20 changes: 14 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -31,29 +31,37 @@ pub trait Read<Addr, SPI: Transfer<u8>, 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<SPI, CS>>;
fn read(&mut self, addr: Addr, buf: &mut [u8]) -> Result<(), Error<SPI::Error, CS>>;
}

/// A trait for writing and erasing operations on a memory chip.
pub trait BlockDevice<Addr, SPI: Transfer<u8>, CS: OutputPin> {
pub trait BlockDevice<
Addr,
SPI: Transfer<u8, Error = Self::SpiError> + Write<u8, Error = Self::SpiError>,
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<SPI, CS>>;
fn erase_sectors(&mut self, addr: Addr, amount: usize)
-> Result<(), Error<Self::SpiError, CS>>;

/// 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<SPI, CS>>;
fn erase_all(&mut self) -> Result<(), Error<Self::SpiError, CS>>;

/// 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.
///
/// # 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<SPI, CS>>;
fn write_bytes(&mut self, addr: Addr, data: &[u8]) -> Result<(), Error<Self::SpiError, CS>>;
}
69 changes: 52 additions & 17 deletions src/series25.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -88,6 +90,7 @@ enum Opcode {
PageProg = 0x02, // directly writes to EEPROMs too
SectorErase = 0x20,
BlockErase = 0xD8,
DeepPowerDown = 0xB9,
ChipErase = 0xC7,
}

Expand All @@ -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<SPI: Transfer<u8>, CS: OutputPin> {
pub struct Flash<SPI, CS: OutputPin> {
spi: SPI,
cs: CS,
}
Expand All @@ -127,7 +130,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
/// 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<Self, Error<SPI, CS>> {
pub fn init(spi: SPI, cs: CS) -> Result<Self, Error<SPI::Error, CS>> {
let mut this = Self { spi, cs };
let status = this.read_status()?;
info!("Flash::init: status = {:?}", status);
Expand All @@ -141,7 +144,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
Ok(this)
}

fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI, CS>> {
fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI::Error, CS>> {
// 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);
Expand All @@ -151,7 +154,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
}

/// Reads the JEDEC manufacturer/device identification.
pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI::Error, CS>> {
// Optimistically read 12 bytes, even though some identifiers will be shorter
let mut buf: [u8; 12] = [0; 12];
buf[0] = Opcode::ReadJedecId as u8;
Expand All @@ -162,20 +165,48 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
}

/// Reads the status register.
pub fn read_status(&mut self) -> Result<Status, Error<SPI, CS>> {
pub fn read_status(&mut self) -> Result<Status, Error<SPI::Error, 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>> {
pub fn deep_power_down<D: DelayUs<u8>>(
&mut self,
timer: Option<&mut D>,
) -> Result<(), Error<SPI::Error, CS>> {
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<D: DelayUs<u8>>(&mut self, timer: &mut D) -> Result<(), Error<SPI::Error, CS>> {
// 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<SPI::Error, CS>> {
let mut cmd_buf = [Opcode::WriteEnable as u8];
self.command(&mut cmd_buf)?;
Ok(())
}

fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
fn wait_done(&mut self) -> Result<(), Error<SPI::Error, CS>> {
// TODO: Consider changing this to a delay based pattern
while self.read_status()?.contains(Status::BUSY) {}
Ok(())
Expand All @@ -195,7 +226,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
///
/// * `addr`: 24-bit address to start reading at.
/// * `buf`: Destination buffer to fill.
fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI::Error, CS>> {
// TODO what happens if `buf` is empty?

let mut cmd_buf = [
Expand All @@ -215,8 +246,11 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
}
}

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>> {
impl<E, SPI: Transfer<u8, Error = E> + Write<u8, Error = E>, CS: OutputPin>
BlockDevice<u32, SPI, CS> for Flash<SPI, CS>
{
type SpiError = E;
fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error<E, CS>> {
for c in 0..amount {
self.write_enable()?;

Expand All @@ -234,8 +268,8 @@ impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u32, SPI, CS> for Flash<SPI,
Ok(())
}

fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
for (c, chunk) in data.chunks_mut(256).enumerate() {
fn write_bytes(&mut self, addr: u32, data: &[u8]) -> Result<(), Error<E, CS>> {
for (c, chunk) in data.chunks(256).enumerate() {
self.write_enable()?;

let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
Expand All @@ -247,18 +281,19 @@ impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u32, SPI, CS> for Flash<SPI,
];

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);
}
let spi_result = self
.spi
.transfer(&mut cmd_buf)
.and_then(|_| self.spi.write(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>> {
fn erase_all(&mut self) -> Result<(), Error<E, CS>> {
self.write_enable()?;
let mut cmd_buf = [Opcode::ChipErase as u8];
self.command(&mut cmd_buf)?;
Expand Down