spi_flash is a no_std compatible SPI flash driver designed primarily for
Winbond W25Q series and similar NOR flash memory devices. It provides a clean, safe, and efficient API
for interacting with SPI flash memory in embedded Rust applications.
no_std support out of the boxstd feature)The library is organized into several modules to separate concerns and maintain clean architecture.
| Module | Description |
|---|---|
device |
Device-specific logic (manufacturer detection, status registers, etc.) |
error |
Error types for the library |
interface |
Trait definitions for SPI interface, CS pin, and delay |
misc |
Miscellaneous utility functions (address conversions, etc.) |
mock |
Mock implementations for testing (only available when "std" feature is enabled) |
types |
Type definitions for commands, status enums, and sizes |
This section provides a detailed, man-page style documentation for the core SpiFlash driver.
All operations are safe and block until completion (or until an error is encountered).
| Signature | pub fn new(spi: SPI, cs: CS, timer: Timer) -> Result<Self, SpiFlashError>
|
| Description | Constructs a new SpiFlash instance and initializes communication with the
device. This function automatically issues a JEDEC ID command to detect the flash
manufacturer, memory type, size, and computes the internal page, sector, and block
counts. |
| Arguments |
|
These methods return information about the detected device properties. They do not initiate SPI transactions and are virtually instantaneous.
| Method | Signature | Description |
|---|---|---|
| manufacturer | fn manufacturer(&self) -> Manufacturer |
Returns the detected flash manufacturer (e.g., Winbond, Macronix). |
| size | fn size(&self) -> Size |
Returns the capacity size of the device (e.g., Mbit1, Mbit256). |
| memory_type | fn memory_type(&self) -> u8 |
Returns the specific memory type identifier returned by the JEDEC ID query. |
| page_count | fn page_count(&self) -> u32 |
Total number of 256-byte pages in the device. |
| sector_count | fn sector_count(&self) -> u32 |
Total number of 4KB sectors in the device. |
| block_count | fn block_count(&self) -> u32 |
Total number of 64KB blocks in the device. |
Read functions are bounded by the internal flash structures. Attempting to read beyond the boundary of
the specified unit (page/sector/block) using an offset will return a
SpiFlashError::Protocol.
| Signature | pub fn read_page(&mut self, page_number: u32, data: &mut [u8], size: u32, offset: u32) -> Result<(), SpiFlashError>pub fn read_sector(&mut self, sector_number: u32, data: &mut [u8], size: u32, offset: u32) -> Result<(), SpiFlashError>pub fn read_block(&mut self, block_number: u32, data: &mut [u8], size: u32, offset: u32) -> Result<(), SpiFlashError>
|
| Description | Reads a sequence of bytes from a specific page, sector, or block, bounded by the unit's size limit. |
| Arguments |
|
Before writing, ensure that the target region has been erased. Flash memory can only be written from 1 to 0; a 0 cannot be written back to a 1 without an erase cycle. The driver handles cross-page boundary logic internally for higher-level writes.
| Address Write | pub fn write_address(&mut self, address: u32, data: &[u8], size: u32) -> Result<(), SpiFlashError>Writes data sequentially starting from an absolute byte address. Automatically fragments the operation across page boundaries as required by the flash controller. |
| Unit Writes | pub fn write_page(&mut self, page_number: u32, data: &[u8], size: u32, offset: u32) -> Result<(), SpiFlashError>pub fn write_sector(&mut self, sector_number: u32, data: &[u8], size: u32, offset: u32) -> Result<(), SpiFlashError>pub fn write_block(&mut self, block_number: u32, data: &[u8], size: u32, offset: u32) -> Result<(), SpiFlashError>Writes data into a specific unit, bounded by the maximum capacity of that unit minus the starting offset. |
Erasing resets all bits in the targeted region to 1 (0xFF). Erase operations are typically much slower than read or write operations. The driver will actively block until the internal erase cycle is fully completed by polling the status register.
| erase_chip | pub fn erase_chip(&mut self) -> Result<(), SpiFlashError>Erases the entire flash device. Can take several seconds on large chips. |
| erase_sector | pub fn erase_sector(&mut self, sector_number: u32) -> Result<(), SpiFlashError>Erases a specific 4KB sector. |
| erase_block | pub fn erase_block(&mut self, block_number: u32) -> Result<(), SpiFlashError>Erases a specific 64KB block. |
Add this as a dependency in your Cargo.toml:
[dependencies]
spi_flash = { path = "path/to/spi_flash" }
Example of initializing and reading a page:
// Initialize your SPI bus, Chip Select pin, and Delay provider
// let spi = ...;
// let cs = ...;
// let delay = ...;
let mut flash = SpiFlash::new(spi, cs, delay).unwrap();
// Read device info
let mfg = flash.manufacturer();
let size = flash.size();
// Read a page from address 0x000000
let mut buffer = [0u8; 256];
flash.read_page(0x000000, &mut buffer).unwrap();
The standard flow for interacting with the SPI flash memory involves asserting the Chip Select (CS) pin, sending a command byte, sending any required address bytes or dummy bytes, transferring data, and finally deasserting CS.