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}