1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*!
This is a platform agnostic Rust driver for the [23x series serial SRAM/NVSRAM SPI memory chips](https://www.microchip.com/en-us/products/memory/serial-sram-and-serial-nvsram),
based on the [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.

See the [Intro post](https://blog.a1w.ca/p/rust-embedded-driver-microchip-23x-sram).

This driver allows you to:

- Read a single byte from a memory address. See: `read_byte()`.
- Read a 32-byte page starting on a memory address. See: `read_page()`.
- Read an N-byte array starting on a memory address. See: `read_sequential()`.
- Write a single byte to a memory address. See: `write_byte()`.
- Write a 32-byte page starting on a memory address. See: `write_page()`.
- Write an N-byte array starting on a memory address. See: `write_sequential()`.
- Enable and disable transmission by managing the _HOLD_ pin.
- Get/Set the operating mode/status register.

Read the [API Documentation](https://docs.rs/sram23x) for more information.

# Supported devices

| Device | Memory bytes | Memory bits | HOLD pin | Datasheet |
|-------:|------------:|------------:|----------:|:-----------|
|   M23x640 |   8 KB |   64 Kbit | yes |    [23A640/23K640] |
|   M23x256  |  32 KB | 256 Kbit | yes |    [23A256/23K256] |
|   M23x512  |  64 KB | 512 Kbit | yes |   [23A512/23LC512] |
|  M23xv512  |  64 KB | 512 Kbit |  no |         [23LCV512] |
|  M23x1024  | 128 KB |   1 Mbit | yes | [23A1024/23LC1024] |
| M23xv1024  | 128 KB |   1 Mbit |  no |        [23LCV1024] |

[23A640/23K640]: http://ww1.microchip.com/downloads/en/DeviceDoc/22126E.pdf
[23A256/23K256]: http://ww1.microchip.com/downloads/en/DeviceDoc/22100F.pdf
[23A512/23LC512]: https://ww1.microchip.com/downloads/en/DeviceDoc/20005155B.pdf
[23LCV512]: https://ww1.microchip.com/downloads/en/DeviceDoc/25157A.pdf
[23A1024/23LC1024]: https://ww1.microchip.com/downloads/en/DeviceDoc/20005142C.pdf
[23LCV1024]: https://ww1.microchip.com/downloads/en/DeviceDoc/25156A.pdf

# Usage

Include [library](https://crates.io/crates/sram23x) as a dependency in your Cargo.toml

```toml
[dependencies]
sram23x = "0.3.1"
```

Some example usage:

```rust
extern crate sram23x;
use sram23x::*;

fn main() {
    // 1. Ensure spi, cs, and hold pins are defined. hold pin is required (any unused output pin will do)
    // (device specific)

    // 2. Instantiate memory device 23LCV1024
    let mut sram = Sram23x::new(spi, cs, hold, device_type::M23xv1024).unwrap();

    // 3. Check the operating mode register
    println!("Operating mode register: {:?}", sram.mode);

    // 4. Change the operating mode to sequential
    sram.set_mode(OperatingMode::Sequential as u8).unwrap();
    assert_eq!(sram.mode, 0b01);

    // 5. Write 4 bytes of data starting at address 0x00 from a buffer
    let mut data: [u8; 4] = ['t' as u8, 'e' as u8, 's' as u8, 't' as u8];
    sram.write_sequential(0x00_u32, &mut data).unwrap();

    // 6. Read 4 bytes of data starting at address 0x00 into a buffer
    sram.read_sequential(0x00_u32, &mut data).unwrap();
    println!("Read data: {:?}", data);
    assert_eq!(data[0], 't' as u8);
    assert_eq!(data[1], 'e' as u8);
    assert_eq!(data[2], 's' as u8);
    assert_eq!(data[3], 't' as u8);

    // 7. Write and read 1 byte to/from address 0x04
    sram.set_mode(OperatingMode::Byte as u8).unwrap();
    assert_eq!(sram.mode, 0b00);
    sram.write_byte(0x04_u32, 'a' as u8).unwrap();
    let byte = sram.read_byte(0x04_u32).unwrap();
    println!("Read 1 byte: {:?}", byte);
    assert_eq!(byte, 'a' as u8);

    // 8. Write and read a 32-byte page starting at address 0x00
    sram.set_mode(OperatingMode::Page as u8).unwrap();
    assert_eq!(sram.mode, 0b10);
    let mut data = "Microchip\n1Mbit serial\nsram test".as_bytes();
    sram.write_page(0x00_u32, data).unwrap();
    let page = sram.read_page(0x00_u32).unwrap();
    println!("Read a 32-byte page: {:?}", page);
    assert_eq!(page[0], 'M' as u8);
    assert_eq!(page[31], 't' as u8);
}
```

*/
#![deny(unsafe_code)]
#![no_std]

extern crate bit_field;
extern crate embedded_hal as hal;

mod sram23x;

/// Microchip SRAM 23x driver
#[derive(Debug, Default)]
pub struct Sram23x<SPI, CS, HOLD, DT> {
    /// The concrete SPI device implementation
    spi: SPI,
    /// The SPI chip select pin
    cs: CS,
    /// The SPI device hold pin
    hold: HOLD,
    /// The SRAM device type
    dt: DT,
    /// The operating mode of the device
    pub mode: u8,
}

/// All possible instructions
#[repr(u8)]
pub enum Instruction {
    /// Read data from memory
    Read = 0x03,
    /// Write data to memory
    Write = 0x02,
    /// Enter Dual I/O access
    EnterDualIo = 0x3B,
    /// Enter Quad I/O access
    EnterQuadIo = 0x38,
    /// Reset Dual/Quad I/O access
    ResetIo = 0xFF,
    /// Read the 8-bit mode/status register
    ReadMode = 0x05,
    /// Write the 8-bit mode/status register
    WriteMode = 0x01,
}

/// Modes of operation
#[repr(u8)]
pub enum OperatingMode {
    /// In this mode, the read/write operations are limited to only one byte
    Byte = 0b00_000000,
    /// In this mode, the read and write operations are limited to within the addressed page
    Page = 0b10_000000,
    /// In this mode, the entire array can be written to and read from
    Sequential = 0b01_000000,
    /// Reserved (do not use this mode)
    Reserved = 0b11_000000,
}

/// All possible errors in this crate
#[derive(Debug)]
#[repr(u8)]
pub enum Error<S, P> {
    /// SPI bus error
    SpiError(S),
    /// Pin error
    PinError(P),
    /// Too much data received/passed for a read or write
    TooMuchData,
    /// Memory address is out of range
    InvalidAddress,
    /// Address size is invalid
    InvalidAddressSize,
    /// Operating mode is invalid, use `set_mode()` to change it
    InvalidOperatingMode,
    /// Operating mode is unknown
    UnknownOperatingMode,
}

/// Types of devices supported by this crate
pub mod device_type {
    /// Microchip 23A640/23K640, 8KB (64Kbit) SRAM
    pub struct M23x640;
    /// Microchip 23A256/23K256, 32KB (256Kbit) SRAM
    pub struct M23x256;
    /// Microchip 23A512/23LC512, 64KB (512Kbit) SRAM
    pub struct M23x512;
    /// Microchip 23LCV512, 64KB (512Kbit) NVSRAM (VBat)
    pub struct M23xv512;
    /// Microchip 23A1024/23LC1024, 128KB (1Mbit) SRAM
    pub struct M23x1024;
    /// Microchip 23LCV1024, 128KB (1Mbit) NVSRAM (VBat)
    pub struct M23xv1024;
}

type SpiRes<S, P> = Result<(), Error<S, P>>;