mc_sst25/
device.rs

1//! # Non-Blocking & blocking SPI protocol abstraction
2//!
3//! ## Setup
4//!
5//! Creating a [device](Flash) instance requires the following peripherals:
6//! * An SPI bus implementing [embedded-hal SpiDevice trait](embedded_hal::spi::SpiDevice)
7//! * Three GPIO pins connected to EN, WP and HOLD of the flash chip implementing [embedded-hal OutputPin](embedded_hal::digital::OutputPin)
8//!
9//! The device can be communicated with either in blocking or non-blocking mode:
10//! * In the case of blocking mode, the library waits internally until the respective operation is completely finished.
11//! * In the case of non-blocking mode, it is up to the caller to check the busy flag of the status register. (s. [Reading status register](#reading-status))
12//!
13//! ````
14//!# use mc_sst25::device::{Flash, Memory};
15//!# use mc_sst25::example::{MockBus, MockPin};
16//!#
17//!# let bus = MockBus::default();
18//!# let pin_hold = MockPin::default();
19//!# let pin_wp = MockPin::default();
20//!#
21//! let mut device = Flash::new(bus, pin_wp, pin_hold);
22//!
23//! // Blocking mode (default)
24//! device.set_blocking();
25//!
26//! // Non-blocking
27//! device.set_non_blocking();
28//! ````
29//!
30//! ## Reading status
31//!
32//! The device contains eight status bits, which are mapped to [Status] struct.
33//!
34//! ````
35//!# use mc_sst25::device::{Flash, Memory};
36//!# use mc_sst25::example::{MockBus, MockPin};
37//!#
38//!# let bus = MockBus::default();
39//!# let pin_hold = MockPin::default();
40//!# let pin_wp = MockPin::default();
41//!#
42//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
43//!#
44//! let status = device.read_status().unwrap();
45//!
46//! assert!(!status.busy);
47//! assert!(!status.block0_protected);
48//! assert!(!status.write_enabled);
49//! ````
50//!
51//! ## Writing status
52//!
53//! The following status flags are used for (write) protecting memory segments.
54//! On device power-up all memory blocks are protected.
55//!
56//! ````
57//!# use mc_sst25::device::{Flash, Memory, Status};
58//!# use mc_sst25::example::{MockBus, MockPin};
59//!#
60//!# let bus = MockBus::default();
61//!# let pin_hold = MockPin::default();
62//!# let pin_wp = MockPin::default();
63//!#
64//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
65//!#
66//! let mut  status = Status::default();
67//! status.block0_protected = false;
68//! status.block1_protected = false;
69//! status.block2_protected = true;
70//! status.block3_protected = true;
71//! status.bits_read_only = false;
72//!
73//! device.write_status(status).unwrap();
74//! ````
75//!
76//! ## Writing single bytes
77//!
78//! The following method is used for writing single bytes.
79//!
80//! *Note: Memory region needs to be unprotected (s. [Reading status](#reading-status)), otherwise
81//! write operation is ignored by device*
82//! ````
83//!# use mc_sst25::device::{Flash, Memory, Status};
84//!# use mc_sst25::example::{MockBus, MockPin};
85//!#
86//!# let bus = MockBus::default();
87//!# let pin_hold = MockPin::default();
88//!# let pin_wp = MockPin::default();
89//!#
90//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
91//!#
92//! // Writing byte 0x64 to address 0xc8
93//! device.byte_program(0xc8, 0x64).unwrap();
94//! ````
95//!
96//! ## Writing larger data
97//!
98//! Auto-address-increment method is used for writing larger amount of data. The given buffer needs to
99//! contain an even amount of data. (e.g 2, 4, 6, 8, ... bytes).
100//!
101//! *Note: Memory region needs to be unprotected (s. [Reading status](#reading-status)), otherwise
102//! write operation is ignored by device*
103//! ````
104//!# use mc_sst25::device::{Flash, Memory, Status};
105//!# use mc_sst25::example::{MockBus, MockPin};
106//!#
107//!# let bus = MockBus::default();
108//!# let pin_hold = MockPin::default();
109//!# let pin_wp = MockPin::default();
110//!#
111//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
112//!#
113//! // Writing four bytes to address 0x5, so the following data is written:
114//! // Address 0x5 contains byte 0x1
115//! // Address 0x6 contains byte 0x2
116//! // ...
117//! device.aai_program(0x5, &[0x1, 0x2, 0x3, 0x4]).unwrap();
118//! ````
119//!
120//! ## Sector erase
121//!
122//! The chip supports erasing single sectors. One sector has the size of 4 KByte.
123//!
124//! *Note: All memory blocks needs to be unprotected (s. [Reading status](#reading-status)), otherwise
125//! erase operation is ignored by device*
126//! ````
127//!# use mc_sst25::device::{Flash, Memory, Status};
128//!# use mc_sst25::example::{MockBus, MockPin};
129//!#
130//!# let bus = MockBus::default();
131//!# let pin_hold = MockPin::default();
132//!# let pin_wp = MockPin::default();
133//!#
134//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
135//!#
136//! // Erases the 32nd sector
137//! device.erase_sector(0x8000).unwrap();
138//! ````
139//!
140//! ## Full chip erase
141//!
142//! The chip supports erasing the entire memory.
143//!
144//! *Note: All memory blocks needs to be unprotected (s. [Reading status](#reading-status)), otherwise
145//! erase operation is ignored by device*
146//! ````
147//!# use mc_sst25::device::{Flash, Memory, Status};
148//!# use mc_sst25::example::{MockBus, MockPin};
149//!#
150//!# let bus = MockBus::default();
151//!# let pin_hold = MockPin::default();
152//!# let pin_wp = MockPin::default();
153//!#
154//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
155//!#
156//! device.erase_full().unwrap();
157//! ````
158//!
159//! ## Reading memory
160//!
161//! Reading an arbitrary amount of data starting at the given address. The data amount is determined
162//! by the generic const L.
163//!
164//! *Note: If the maximum address is within range, the chip wraps automatically and continuous
165//! at the first address.*
166//!
167//! ````
168//!# use mc_sst25::device::{Flash, Status, Memory};
169//!# use mc_sst25::example::{MockBus, MockPin};
170//!#
171//!# let bus = MockBus::default();
172//!# let pin_hold = MockPin::default();
173//!# let pin_wp = MockPin::default();
174//!#
175//!# let mut device = Flash::new(bus, pin_wp, pin_hold);
176//!#
177//! // Reading four bytes starting at address 0x0
178//! let data = device.read::<4>(0x0).unwrap();
179//! assert_eq!([0xa, 0xb, 0xc, 0xd], data);
180//! ````
181use core::fmt::{Debug, Formatter};
182use embedded_hal::digital::OutputPin;
183use embedded_hal::spi::{Operation, SpiDevice};
184
185/// General flash memory interface
186pub trait Memory {
187    type Error: Debug;
188
189    /// Switches to blocking mode
190    fn set_blocking(&mut self);
191
192    /// Switches to non-blocking mode
193    fn set_non_blocking(&mut self);
194
195    /// Reads and returns the status registers
196    fn read_status(&mut self) -> Result<Status, Self::Error>;
197
198    /// Enables write operations
199    fn write_enable(&mut self) -> Result<(), Self::Error>;
200
201    /// Enables write operations
202    fn write_disable(&mut self) -> Result<(), Self::Error>;
203
204    /// Writes the given status to status registers
205    fn write_status(&mut self, status: Status) -> Result<(), Self::Error>;
206
207    /// The Sector-Erase instruction clears all bits in the selected 4 KByte sector to FFH.
208    /// A Sector-Erase instruction applied to a protected memory area will be ignored
209    fn erase_sector(&mut self, address: u32) -> Result<(), Self::Error>;
210
211    /// Erases the full chip.
212    fn erase_full(&mut self) -> Result<(), Self::Error>;
213
214    /// Programs/Writes the given byte at the given address.
215    fn byte_program(&mut self, address: u32, data: u8) -> Result<(), Self::Error>;
216
217    /// Auto address increment (AAI) programming for writing larger amount of data
218    fn aai_program(&mut self, address: u32, buffer: &[u8]) -> Result<(), Self::Error>;
219
220    /// Reads data with length L starting at the given address
221    fn read<const L: usize>(&mut self, address: u32) -> Result<[u8; L], Self::Error>;
222}
223
224/// SS25* flash memory chip
225pub struct Flash<B: SpiDevice<u8>, P: OutputPin> {
226    /// SPI bus
227    bus: B,
228
229    /// GPIO WP pin
230    pin_write_protection: P,
231
232    /// GPIO Hold pin
233    pin_hold: P,
234
235    /// Is the device configured?
236    configured: bool,
237
238    /// True if blocks on longer lasting operations
239    blocking: bool,
240}
241
242/// Error when communicating with the device
243#[derive(PartialEq, Eq)]
244pub enum CommandError<B: SpiDevice<u8>, P: OutputPin> {
245    /// SPI transfer error
246    TransferError(B::Error),
247
248    /// Error while setting GPIO state of HOLD pin
249    HoldPinError(P::Error),
250
251    /// Error while setting GPIO state of WP pin
252    WriteProtectionPinError(P::Error),
253
254    /// Chip is still busy executing another operation
255    Busy,
256
257    /// The given memory address is out of range
258    InvalidAddress,
259
260    /// The given buffer size is too small for the called operation
261    BufferTooSmall,
262
263    /// The called operation requires an even buffer size
264    BufferUneven,
265}
266
267const CMD_AAI_PROGRAM: u8 = 0b1010_1101;
268
269impl<B: SpiDevice<u8>, P: OutputPin> Memory for Flash<B, P>
270where
271    P::Error: Debug,
272{
273    type Error = CommandError<B, P>;
274
275    /// Switches to blocking mode
276    fn set_blocking(&mut self) {
277        self.blocking = true;
278    }
279
280    /// Switches to non-blocking mode
281    fn set_non_blocking(&mut self) {
282        self.blocking = false;
283    }
284
285    /// Reads and returns the status registers
286    fn read_status(&mut self) -> Result<Status, CommandError<B, P>> {
287        self.configure()?;
288        let mut buffer = [0x0];
289        self.bus
290            .transaction(&mut [Operation::Write(&[0b0000_0101]), Operation::Read(&mut buffer)])
291            .map_err(CommandError::TransferError)?;
292
293        Ok(Status::from_register(buffer[0]))
294    }
295
296    /// Enables write operations
297    fn write_enable(&mut self) -> Result<(), CommandError<B, P>> {
298        self.write(&mut [0b0000_0110])?;
299        Ok(())
300    }
301
302    /// Enables write operations
303    fn write_disable(&mut self) -> Result<(), CommandError<B, P>> {
304        self.write(&mut [0b0000_0100])?;
305        Ok(())
306    }
307
308    /// Writes the given status to status registers
309    fn write_status(&mut self, status: Status) -> Result<(), CommandError<B, P>> {
310        self.write_enable()?;
311
312        self.bus.write(&[0x0]).map_err(CommandError::TransferError)?;
313        self.write(&mut [0b0000_0001, status.to_registers()])?;
314
315        Ok(())
316    }
317
318    /// The Sector-Erase instruction clears all bits in the selected 4 KByte sector to FFH.
319    /// A Sector-Erase instruction applied to a protected memory area will be ignored
320    fn erase_sector(&mut self, address: u32) -> Result<(), Self::Error> {
321        self.assert_valid_address(address)?;
322
323        self.write_enable()?;
324        self.assert_not_busy()?;
325
326        let mut frame = [0b0010_0000, 0x0, 0x0, 0x0];
327        self.address_command(address, &mut frame);
328        self.write(&mut frame)?;
329
330        self.wait(false)
331    }
332
333    /// Erases the full chip.
334    /// Waits until operation is completed in blocking mode, otherwise returns when command is sent
335    fn erase_full(&mut self) -> Result<(), CommandError<B, P>> {
336        self.write_enable()?;
337        self.assert_not_busy()?;
338
339        self.write(&mut [0b0110_0000])?;
340        self.wait(false)
341    }
342
343    /// Programs/Writes the given byte at the given address. Disables internal write protection.
344    /// Waits until operation is completed in blocking mode, otherwise returns when command is sent
345    fn byte_program(&mut self, address: u32, data: u8) -> Result<(), CommandError<B, P>> {
346        self.assert_valid_address(address)?;
347
348        self.write_enable()?;
349        self.assert_not_busy()?;
350
351        let mut frame = [0b0000_0010, 0x0, 0x0, 0x0, data];
352        self.address_command(address, &mut frame);
353
354        self.write(&mut frame)?;
355        self.wait(false)
356    }
357
358    /// Auto address increment (AAI) programming for writing larger amount of data
359    /// Buffer needs to contain at least two bytes and an even data amount
360    fn aai_program(&mut self, address: u32, buffer: &[u8]) -> Result<(), CommandError<B, P>> {
361        self.assert_valid_address(address)?;
362
363        if buffer.len() < 2 {
364            return Err(CommandError::BufferTooSmall);
365        }
366
367        if buffer.len() & 1 == 1 {
368            return Err(CommandError::BufferUneven);
369        }
370
371        self.write_enable()?;
372        self.assert_not_busy()?;
373
374        let mut frame = [CMD_AAI_PROGRAM, 0x0, 0x0, 0x0, buffer[0], buffer[1]];
375        self.address_command(address, &mut frame);
376        self.write(&mut frame)?;
377        self.wait(true)?;
378
379        for chunk in buffer[2..].chunks(2) {
380            self.write(&mut [CMD_AAI_PROGRAM, chunk[0], chunk[1]])?;
381            self.wait(true)?;
382        }
383
384        self.write_disable()
385    }
386
387    /// Reads data with length L starting at the given address
388    fn read<const L: usize>(&mut self, address: u32) -> Result<[u8; L], CommandError<B, P>> {
389        self.assert_valid_address(address)?;
390        self.configure()?;
391
392        let mut frame = [0b0000_0011, 0x0, 0x0, 0x0];
393        self.address_command(address, &mut frame);
394
395        let mut buffer = [0x0; L];
396        self.bus
397            .transaction(&mut [Operation::Write(&frame), Operation::Read(&mut buffer)])
398            .map_err(CommandError::TransferError)?;
399
400        Ok(buffer)
401    }
402}
403
404impl<B: SpiDevice<u8>, P: OutputPin> Flash<B, P>
405where
406    P::Error: Debug,
407{
408    pub fn new(bus: B, pin_write_protection: P, pin_hold: P) -> Self {
409        Self {
410            bus,
411            pin_write_protection,
412            pin_hold,
413            configured: false,
414            blocking: true,
415        }
416    }
417
418    /// Writes the given data
419    fn write<'a>(&'a mut self, data: &'a mut [u8]) -> Result<(), CommandError<B, P>> {
420        self.configure()?;
421        self.bus.write(data).map_err(CommandError::TransferError)
422    }
423
424    /// Adds the given memory address to the command frame
425    fn address_command(&mut self, address: u32, frame: &mut [u8]) {
426        frame[1] = (address >> 16) as u8;
427        frame[2] = (address >> 8) as u8;
428        frame[3] = address as u8;
429    }
430
431    /// Returns an error in case device is busy
432    fn assert_not_busy(&mut self) -> Result<(), CommandError<B, P>> {
433        if self.read_status()?.busy {
434            return Err(CommandError::Busy);
435        }
436
437        Ok(())
438    }
439
440    /// Returns an error if the given address is out of range
441    fn assert_valid_address(&self, address: u32) -> Result<(), CommandError<B, P>> {
442        if address > 16777216 {
443            return Err(CommandError::InvalidAddress);
444        }
445
446        Ok(())
447    }
448
449    /// Blocks until device is not busy anymore
450    fn wait(&mut self, force: bool) -> Result<(), CommandError<B, P>> {
451        while (self.blocking || force) && self.read_status()?.busy {}
452        Ok(())
453    }
454
455    /// Sets the base GPIO states once
456    fn configure(&mut self) -> Result<(), CommandError<B, P>> {
457        if self.configured {
458            return Ok(());
459        }
460
461        self.pin_hold.set_high().map_err(CommandError::HoldPinError)?;
462        self.pin_write_protection
463            .set_low()
464            .map_err(CommandError::WriteProtectionPinError)?;
465        self.configured = true;
466
467        Ok(())
468    }
469}
470
471/// Mapped status register
472#[derive(Clone, Debug, Default, PartialEq)]
473pub struct Status {
474    /// True if internal write operation is in progress
475    pub busy: bool,
476
477    /// True if device memory write is enabled
478    pub write_enabled: bool,
479
480    /// True if first block is write-protected
481    pub block0_protected: bool,
482
483    /// True if second block is write-protected
484    pub block1_protected: bool,
485
486    /// True if third block is write-protected
487    pub block2_protected: bool,
488
489    /// True if fourth block is write-protected
490    pub block3_protected: bool,
491
492    /// True => AAI programming mode,
493    /// False => Byte-Program mode
494    pub aai_programming_mode: bool,
495
496    /// True if  BP3, BP2, BP1, BP0 are read-only
497    pub bits_read_only: bool,
498}
499
500impl Status {
501    /// Maps status bits to object
502    pub(crate) fn from_register(data: u8) -> Self {
503        Self {
504            busy: data & (1 << 0) != 0,
505            write_enabled: data & (1 << 1) != 0,
506            block0_protected: data & (1 << 2) != 0,
507            block1_protected: data & (1 << 3) != 0,
508            block2_protected: data & (1 << 4) != 0,
509            block3_protected: data & (1 << 5) != 0,
510            aai_programming_mode: data & (1 << 6) != 0,
511            bits_read_only: data & (1 << 7) != 0,
512        }
513    }
514
515    /// Converts the status to register byte. Only writable bits are used
516    pub(crate) fn to_registers(&self) -> u8 {
517        let mut result = 0x0;
518
519        if self.block0_protected {
520            result |= 0x1 << 2;
521        }
522
523        if self.block1_protected {
524            result |= 0x1 << 3;
525        }
526
527        if self.block2_protected {
528            result |= 0x1 << 4;
529        }
530
531        if self.block3_protected {
532            result |= 0x1 << 5;
533        }
534
535        if self.bits_read_only {
536            result |= 0x1 << 7;
537        }
538
539        result
540    }
541}
542
543impl<B: SpiDevice<u8>, P: OutputPin> Debug for CommandError<B, P>
544where
545    P::Error: Debug,
546{
547    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
548        match self {
549            CommandError::TransferError(error) => write!(f, "StatusWriteError{error:?}"),
550            CommandError::HoldPinError(error) => write!(f, "StatusWriteError{error:?}"),
551            CommandError::WriteProtectionPinError(error) => write!(f, "WriteProtectionPinError{error:?}"),
552            CommandError::Busy => f.write_str("Busy"),
553            CommandError::InvalidAddress => f.write_str("InvalidAddress"),
554            CommandError::BufferTooSmall => f.write_str("BufferTooSmall"),
555            CommandError::BufferUneven => f.write_str("BufferUneven"),
556        }
557    }
558}