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

Adding support for the SPI stack chips #10

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
06d9b55
adding the FlashWrite, EepromWrite and Read traits
hargoniX Aug 18, 2019
08197ad
reworking the new traits
hargoniX Aug 19, 2019
cbd5159
adding erase_all and write_bytes implementations
hargoniX Aug 20, 2019
233effe
finalizing the trait impl on series25 chips, adding a prelude file wh…
hargoniX Aug 21, 2019
e28690d
finalizing the trait impl on series25 chips, adding a prelude file wh…
hargoniX Aug 21, 2019
e07ac1d
Merge branch 'master' of github.com:hargoniX/spi-memory
hargoniX Aug 21, 2019
ceda1d6
addressing review points
hargoniX Aug 21, 2019
4bfdf97
Update comment on erase times
hargoniX Aug 21, 2019
a3422c2
Update src/lib.rs
hargoniX Aug 21, 2019
7a4755a
more review points addressed
hargoniX Aug 22, 2019
7e41934
Merge branch 'master' of github.com:hargoniX/spi-memory
hargoniX Aug 22, 2019
8a518a0
commented the wrong addr
hargoniX Aug 22, 2019
e82af20
Nit 1
hargoniX Aug 22, 2019
a2e816e
Nit 2
hargoniX Aug 22, 2019
fd9b3bc
adding an (undocumented) implementation of the w25m concept
hargoniX Aug 25, 2019
fa2488a
merged conflicts
hargoniX Aug 25, 2019
a6d2db2
adding BlockDevice and Read impls for w25m
hargoniX Aug 25, 2019
816d9f1
read, write and reset working roughly for w25n, more testing to be d…
hargoniX Aug 31, 2019
8127c02
working API for the w25n series, everything tested
hargoniX Sep 29, 2019
33834ca
adding support for spi stack chips
hargoniX Oct 3, 2019
4e3b729
Resolve merge conflicts
hargoniX Oct 3, 2019
e72f625
Update src/lib.rs
hargoniX Oct 6, 2019
eebfde2
increasing code reusage by only having one central spi_command function
hargoniX Oct 6, 2019
a446e71
formatting and a bit of docs
hargoniX Oct 6, 2019
1474850
Merge branch 'master' of github.com:hargoniX/spi-memory
hargoniX Oct 6, 2019
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ bitflags = "1.0.4"
[dev-dependencies]
cortex-m = "0.6.0"
cortex-m-rt = "0.6.8"
cortex-m-semihosting = "0.3.3"
stm32f4xx-hal = { version = "0.5.0", features = ["stm32f401"] }
panic-semihosting = "0.5.2"
cortex-m-semihosting = "0.3.4"

[profile.dev]
opt-level = "z"
Expand Down
24 changes: 14 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
mod log;
pub mod prelude;
pub mod series25;
pub mod w25m;
pub mod w25n;
mod utils;

use core::fmt::{self, Debug};
Expand Down Expand Up @@ -62,34 +64,36 @@ where
}

/// A trait for reading operations from a memory chip.
pub trait Read<Addr, SPI: Transfer<u8>, CS: OutputPin> {
pub trait Read<SPI: Transfer<u8>, CS: OutputPin> {
/// Reads bytes from a memory chip.
///
/// # 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: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>>;
}

/// A trait for writing and erasing operations on a memory chip.
pub trait BlockDevice<Addr, SPI: Transfer<u8>, CS: OutputPin> {
/// Erases sectors from the memory chip.
pub trait BlockDevice<SPI: Transfer<u8>, CS: OutputPin> {
/// Erases the smallest erasable unit from the memory chip. For Flash this should be
/// blocks or sectors, for EEPROM single bytes
///
/// # 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>>;
/// * `addr`: The address to start erasing at. If the address is not on a block boundary,
/// the lower bits can be ignored in order to make it fit
fn erase(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, 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>>;
/// 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.
/// Writes bytes onto the smallest writable unit of 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: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>>;
}
6 changes: 3 additions & 3 deletions src/series25.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
}
}

impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
impl<SPI: Transfer<u8>, CS: OutputPin> Read<SPI, CS> for Flash<SPI, CS> {
/// Reads flash contents into `buf`, starting at `addr`.
///
/// Note that `addr` is not fully decoded: Flash chips will typically only
Expand Down Expand Up @@ -171,8 +171,8 @@ 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<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<SPI, CS> for Flash<SPI, CS> {
fn erase(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, CS>> {
for c in 0..amount {
self.write_enable()?;

Expand Down
154 changes: 154 additions & 0 deletions src/w25m.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//! Provides an implementation for switching between the two dies stacked upon each other inside the W25M series
use crate::{BlockDevice, Error, Read};
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::digital::v2::OutputPin;
use core::marker::PhantomData;
use core::mem;

#[allow(missing_debug_implementations)]
pub struct Die0;
#[allow(missing_debug_implementations)]
pub struct Die1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can just #[derive(Debug)], there shouldn't be any harm in that

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also make them enums instead of structs since they're just used as type markers


/// All dies which are supposed to be supported by the W25M struct have to implement this trait
pub trait Stackable<SPI: Transfer<u8>, CS: OutputPin>: BlockDevice<SPI, CS> + Read<SPI, CS> + Sized {
fn new(spi: SPI, cs: CS) -> Result<Self, Error<SPI, CS>>;
/// Returns the SPI and chip select objects so they can be used elsewhere
fn free(self) -> (SPI, CS);
}

#[derive(Debug)]
pub struct Flash<DIE0, DIE1, DIE>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation to this type that explains what the type parameters are for

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DIE could also be bounded by an ActiveDie trait, which would be implemented by Die0 and Die1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, DIE is actually supposed to be a type state, it doesn`t have to fulfill any trait bounds it is just there so rust knows which switch_die implementation it is supposed to use.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. If you bound it by a trait that is only implemented by the 2 types that are supposed to be used though, users wouldn't be able to name invalid Flash types like Flash<Bla, Bla, ()>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean technically the user is just supposed to _ the type state as the constructor only produces Die0 types, so if a user starts to mess around with the state parameter he is most likely doing something wrong isn't he?

{
inner: Inner<DIE0, DIE1>,
_die: PhantomData<DIE>,
}

#[derive(Debug)]
enum Inner<DIE0, DIE1> {
Die0(DIE0),
Die1(DIE1),
Dummy,
}

impl<DIE0, DIE1> Flash<DIE0, DIE1, Die0>
{
/// Creates a new W25M device
///
/// At
/// the moment the only way to call this function is sadly
/// ```
/// let mut flash: Flash<W25N<_, _>, W25N<_, _>, _> = Flash::init(spi, cs).unwrap();
/// ```
/// TODO: Improve this API, its not very convenient
pub fn init<SPI, CS>(spi: SPI, cs: CS) -> Result<Flash<DIE0, DIE1, Die0>, Error<SPI, CS>>
where
SPI: Transfer<u8>,
CS: OutputPin,
DIE0: Stackable<SPI, CS>,
DIE1: Stackable<SPI, CS>,
{
Ok(Flash{
inner: Inner::Die0(DIE0::new(spi, cs)?),
_die: PhantomData
})
}
}

impl<DIE0, DIE1> Flash<DIE0, DIE1, Die0>

{
pub fn switch_die<SPI, CS>(mut self) -> Result<Flash<DIE0, DIE1, Die1>, Error<SPI, CS>>
where DIE0: Stackable<SPI, CS>,
DIE1: Stackable<SPI, CS>,
SPI: Transfer<u8>,
CS: OutputPin {
let (mut spi, mut cs) = match mem::replace(&mut self.inner, Inner::Dummy) {
Inner::Die0(die) => die.free(),
_ => unreachable!()
};
let mut command = [0xC2, 0x01];
cs.set_low().map_err(Error::Gpio)?;
let spi_result = spi.transfer(&mut command).map_err(Error::Spi);
cs.set_high().map_err(Error::Gpio)?;
spi_result?;

Ok(Flash{
inner: Inner::Die1(DIE1::new(spi, cs)?),
_die: PhantomData
})
}
}

impl<DIE0, DIE1> Flash<DIE0, DIE1, Die1>
{
pub fn switch_die<SPI, CS>(mut self) -> Result<Flash<DIE0, DIE1, Die0>, Error<SPI, CS>>
where DIE0: Stackable<SPI, CS>,
DIE1: Stackable<SPI, CS>,
SPI: Transfer<u8>,
CS: OutputPin
{
let (mut spi, mut cs) = match mem::replace(&mut self.inner, Inner::Dummy) {
Inner::Die1(die) => die.free(),
_ => unreachable!()
};

let mut command = [0xC2, 0x00];
cs.set_low().map_err(Error::Gpio)?;
let spi_result = spi.transfer(&mut command).map_err(Error::Spi);
cs.set_high().map_err(Error::Gpio)?;
spi_result?;


Ok(Flash{
inner: Inner::Die0(DIE0::new(spi, cs)?),
_die: PhantomData
})
}
}
hargoniX marked this conversation as resolved.
Show resolved Hide resolved

impl<DIE0, DIE1, SPI, CS, DIE> BlockDevice<SPI, CS> for Flash<DIE0, DIE1, DIE>
where DIE0: Stackable<SPI, CS>,
DIE1: Stackable<SPI, CS>,
SPI: Transfer<u8>,
CS: OutputPin
{
fn erase(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, CS>> {
match &mut self.inner {
Inner::Die0(die) => die.erase(addr, amount),
Inner::Die1(die) => die.erase(addr, amount),
_ => unreachable!()
}
}

fn erase_all(&mut self) -> Result<(), Error<SPI, CS>> {
match &mut self.inner {
Inner::Die0(die) => die.erase_all(),
Inner::Die1(die) => die.erase_all(),
_ => unreachable!()
}
}

fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
match &mut self.inner {
Inner::Die0(die) => die.write_bytes(addr, data),
Inner::Die1(die) => die.write_bytes(addr, data),
_ => unreachable!()
}
}
}

impl<DIE0, DIE1, SPI, CS, DIE> Read<SPI, CS> for Flash<DIE0, DIE1, DIE>
where DIE0: Stackable<SPI, CS>,
DIE1: Stackable<SPI, CS>,
SPI: Transfer<u8>,
CS: OutputPin
{
fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
match &mut self.inner {
Inner::Die0(die) => die.read(addr, buf),
Inner::Die1(die) => die.read(addr, buf),
_ => unreachable!()
}
}
}
Loading