Skip to main content

w25q_async/
lib.rs

1//! An [`embedded-hal-async`]-based W25Q SPI flash chip driver.
2//!
3//! Contributions are welcome!
4//!
5//! [`embedded-hal-async`]: https://docs.rs/embedded-hal-async/
6
7#![warn(missing_debug_implementations)]
8#![warn(missing_docs)]
9#![cfg_attr(not(test), no_std)]
10
11mod error;
12pub use crate::error::Error;
13
14use embedded_hal_async::delay::DelayNs;
15use embedded_hal_async::spi::SpiDevice;
16
17/// 3-Byte JEDEC manufacturer and device identification.
18#[derive(Copy, Clone)]
19pub struct JedecId {
20    /// The manufacturer ID.
21    pub manufacturer_id: u8,
22
23    /// The device ID.
24    pub device_id: u16,
25}
26
27impl JedecId {
28    /// Build an Identification from JEDEC ID bytes.
29    pub fn parse(buf: &[u8]) -> Option<Self> {
30        // Example response for Cypress part FM25V02A:
31        // 7F 7F 7F 7F 7F 7F C2 22 08  (9 bytes)
32        // 0x7F is a "continuation code", not part of the core manufacturer ID
33        // 0xC2 is the company identifier for Cypress (Ramtron)
34
35        let start = buf.iter().position(|&x| x != 0x7F)?;
36        let buf = &buf[start..];
37        if buf.len() < 3 {
38            return None;
39        }
40
41        let manufacturer_id = buf[0];
42        let device_id = u16::from_be_bytes([buf[1], buf[2]]);
43
44        Some(Self {
45            manufacturer_id,
46            device_id,
47        })
48    }
49}
50
51impl core::fmt::Debug for JedecId {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        let id = u32::from(self.manufacturer_id) << 16 | u32::from(self.device_id);
54        f.debug_tuple("Identification")
55            .field(&format_args!("0x{id:06X}"))
56            .finish()
57    }
58}
59
60#[repr(u8)]
61#[allow(unused)] // TODO support more features
62enum Opcode {
63    /// Read the 8-bit legacy device ID.
64    ReadDeviceId = 0xAB,
65    /// Read the 8-bit manufacturer and device IDs.
66    ReadMfDId = 0x90,
67    /// Read 16-bit manufacturer ID and 8-bit device ID.
68    ReadJedecId = 0x9F,
69    /// Set the write enable latch.
70    WriteEnable = 0x06,
71    /// Clear the write enable latch.
72    WriteDisable = 0x04,
73    /// Read the 8-bit status register.
74    ReadStatus = 0x05,
75    /// Write the 8-bit status register. Not all bits are writeable.
76    WriteStatus = 0x01,
77    Read = 0x03,
78    PageProg = 0x02, // directly writes to EEPROMs too
79    SectorErase = 0x20,
80    BlockErase = 0xD8,
81    ChipErase = 0xC7,
82    PowerDown = 0xB9,
83}
84
85/// Status of the flash memory.
86#[derive(Copy, Clone, Eq, PartialEq)]
87pub struct Status {
88    raw: u8,
89}
90
91impl Status {
92    /// Interpret a raw [`u8`] as a `Status`.
93    pub fn from_raw(raw: u8) -> Self {
94        Self { raw }
95    }
96
97    /// Get the raw [`u8`] value of the `Status`.
98    pub fn as_raw(self) -> u8 {
99        self.raw
100    }
101
102    /// Check if the flash memory is busy processing an erase or program command.
103    pub fn busy(&self) -> bool {
104        self.raw & 0x01 != 0
105    }
106
107    /// Check if the write enable latch is set.
108    ///
109    /// The write enable latch must be set in order to execute an erase or program command.
110    pub fn write_enable_latch(&self) -> bool {
111        self.raw & 0x02 != 0
112    }
113
114    /// Get the protection bits of the memory.
115    ///
116    /// Each bit correponds to a region of memory that can be protected.
117    /// See the datasheet of your flash memory for more details.
118    pub fn protection(&self) -> u8 {
119        (self.raw >> 2) & 0b111
120    }
121
122    /// TODO: what exactly is this?
123    pub fn status_register_write_disable(&self) -> u8 {
124        self.raw & 0x80
125    }
126}
127
128impl core::fmt::Debug for Status {
129    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130        f.debug_struct(core::any::type_name::<Self>())
131            .field("busy", &self.busy())
132            .field("write_enable_latch", &self.write_enable_latch())
133            .field("protection", &format_args!("0b{:03b}", self.protection()))
134            .field("status_register_write_disable", &self.status_register_write_disable())
135            .finish()
136    }
137}
138
139/// Information about the flash memory.
140#[derive(Debug)]
141pub struct FlashInfo {
142    /// The chip ID.
143    pub id: u32,
144
145    /// The page size (and alignment).
146    pub page_size: u16,
147
148    /// The sector size (and alignment).
149    pub sector_size: u32,
150
151    /// The number of pages in a sector.
152    pub page_count: u32,
153
154    /// The number of sectors in a block.
155    pub sector_count: u32,
156
157    /// The size of a block.
158    pub block_size: u32,
159
160    /// The number of blocks in the memory.
161    pub block_count: u32,
162
163    /// The total memory capacity in KiB.
164    pub capacity_kb: u32,
165}
166
167/// Driver for W25Q-series SPI Flash chips.
168#[derive(Debug)]
169pub struct Flash<SPI> {
170    spi: SPI,
171}
172
173// for multiple SPI use: https://crates.io/crates/shared-bus-rtic
174
175impl<SPI: SpiDevice> Flash<SPI> {
176    /// Creates a new W25Q-series flash driver.
177    pub async fn new(spi: SPI) -> Result<Self, Error<SPI::Error>> {
178        let mut this = Self { spi };
179
180        let status = this.read_status().await?;
181
182        // Here we don't expect any writes to be in progress, and the latch must also be deasserted.
183        if status.busy() || status.write_enable_latch() {
184            return Err(Error::UnexpectedStatus);
185        }
186
187        Ok(this)
188    }
189
190    /// Reads the JEDEC manufacturer/device identification.
191    pub async fn read_jedec_id(&mut self) -> Result<JedecId, Error<SPI::Error>> {
192        // Optimistically read 12 bytes, even though some identifiers will be shorter
193        let mut buf: [u8; 12] = [0; 12];
194        buf[0] = Opcode::ReadJedecId as u8;
195        self.spi_transfer_inplace(&mut buf).await?;
196
197        // Skip buf[0] (SPI read response byte)
198        JedecId::parse(&buf[1..])
199            .ok_or_else(|| Error::InvalidResponse)
200    }
201
202    /// Reads the status register.
203    pub async fn read_status(&mut self) -> Result<Status, Error<SPI::Error>> {
204        let mut buf = [Opcode::ReadStatus as u8, 0];
205        self.spi_transfer_inplace(&mut buf).await?;
206
207        Ok(Status { raw: buf[1] })
208    }
209
210    /// Get the device information.
211    pub async fn get_device_info(&mut self) -> Result<FlashInfo, Error<SPI::Error>> {
212        let id = self.read_jedec_id().await?;
213
214        // TODO: Why ignore manufacturer ID and the high byte of the device ID?
215        let block_count = match id.device_id & 0xFF {
216            0..=0x10 => 0, // TODO: Are these really unused?
217            0x11 => 2,    // W25Q10
218            0x12 => 4,    // W25Q20
219            0x13 => 8,    // W25Q40
220            0x14 => 16,   // W25Q80
221            0x15 => 32,   // W25Q16
222            0x16 => 64,   // W25Q32
223            0x17 => 128,  // W25Q64
224            0x18 => 256,  // W25Q128
225            0x19 => 512,  // W25Q256
226            0x1A..=0x1F => 0, // TODO: Are these really unused?
227            0x20 => 1024, // W25Q512
228            0x21.. => 0, // TODO: Are these really unused?
229        };
230
231        let sector_size = 0x1000;
232        let page_size = 256;
233        let block_size = sector_size * 16;
234        Ok(FlashInfo {
235            id: 0,
236            page_size,
237            sector_size,
238            sector_count: block_count * 16,
239            page_count: (block_count * block_size) / u32::from(page_size),
240            block_size,
241            block_count,
242            capacity_kb: (block_size * block_count) / 1024,
243        })
244    }
245
246    /// Set the write-enable bit on the flash device.
247    async fn write_enable(&mut self) -> Result<(), Error<SPI::Error>> {
248        self.spi_write(&[Opcode::WriteEnable as u8]).await
249    }
250
251    /// Wait for the BUSY status bit to clear.
252    async fn wait_done(&mut self) -> Result<(), Error<SPI::Error>> {
253        while self.read_status().await?.busy() {
254            // TODO: Consider sleeping here.
255        }
256        Ok(())
257    }
258
259    /// Enter power down mode.
260    ///
261    /// Datasheet, 8.2.35: Power-down:
262    /// Although  the  standby  current  during  normal  operation  is  relatively  low,  standby  current  can  be  further
263    /// reduced  with  the  Power-down  instruction.  The  lower  power  consumption  makes  the  Power-down
264    /// instruction especially useful for battery powered applications (See ICC1 and ICC2 in AC Characteristics).
265    /// The instruction is initiated by driving the /CS pin low and shifting the instruction code “B9h” as shown in
266    /// Figure 44.
267    ///
268    /// The /CS pin must be driven high after the eighth bit has been latched. If this is not done the Power-down
269    /// instruction will not be executed. After /CS is driven high, the power-down state will entered within the time
270    /// duration of tDP (See AC Characteristics). While in the power-down state only the Release Power-down /
271    /// Device ID (ABh) instruction, which restores the device to normal operation, will be recognized. All other
272    /// instructions  are  ignored.  This  includes  the  Read  Status  Register  instruction,  which  is  always  available
273    /// during normal operation. Ignoring all but one instruction makes the Power Down state a useful condition
274    /// for  securing maximum  write protection. The  device  always  powers-up  in the  normal  operation with  the
275    /// standby current of ICC1.
276    pub async fn power_down(&mut self) -> Result<(), Error<SPI::Error>> {
277        self.spi_write(&[Opcode::PowerDown as u8]).await
278    }
279
280    /// Exit power down mode.
281    ///
282    /// Datasheet, 8.2.36: Release Power-down:
283    /// The Release from Power-down /  Device ID instruction is  a multi-purpose instruction. It can be used to
284    /// release the device from the power-down state, or obtain the devices electronic identification (ID) number.
285    /// To  release the device  from  the  power-down state,  the instruction  is  issued by driving the  /CS  pin low,
286    /// shifting the instruction code “ABh” and driving /CS high as shown in Figure 45. Release from power-down
287    /// will  take  the  time  duration  of  tRES1  (See  AC  Characteristics)  before  the  device  will  resume  normal
288    /// operation  and  other  instructions  are  accepted.  The  /CS  pin  must  remain  high  during  the  tRES1  time
289    /// duration.
290    ///
291    /// Note: must manually delay after running this, IOC
292    pub async fn power_up<D: DelayNs>(
293        &mut self,
294        delay: &mut D,
295    ) -> Result<(), Error<SPI::Error>> {
296        // Same command as reading ID.. Wakes instead of reading ID if not followed by 3 dummy bytes.
297        self.spi_write(&[Opcode::ReadDeviceId as u8]).await?;
298
299        // Table 9.7: AC Electrical Characteristics: tRES1 = max 3us.
300        // TODO: Is this chip specific?
301        delay.delay_us(6).await;
302
303        Ok(())
304    }
305
306    /// Reads memory into `buf`, starting at `addr`.
307    ///
308    /// The read address must be aligned according to the requirements of the actual flash chip.
309    /// See the datasheet of your specific chip for details.
310    ///
311    /// Address is truncated to 24 bits before being transmitted.
312    /// See the datasheet of your flash chip to know what happens when the address exceeds the available memory.
313    pub async fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI::Error>> {
314        if buf.is_empty() {
315            return Ok(());
316        }
317
318        let addr = addr.to_le_bytes();
319        let cmd_buf = [
320            Opcode::Read as u8,
321            addr[2],
322            addr[1],
323            addr[0],
324        ];
325
326        let mut transaction = [
327            embedded_hal_async::spi::Operation::Write(&cmd_buf),
328            embedded_hal_async::spi::Operation::Read(buf),
329        ];
330        self.spi.transaction(&mut transaction)
331            .await
332            .map_err(Error::Spi)
333    }
334
335    /// Erase a sector of memory.
336    ///
337    /// The address must be sector aligned.
338    pub async fn erase_sector(&mut self, addr: u32) -> Result<(), Error<SPI::Error>> {
339        self.write_enable().await?;
340
341        let addr = addr.to_be_bytes();
342        let cmd_buf = [
343            Opcode::SectorErase as u8,
344            addr[2],
345            addr[1],
346            addr[0],
347        ];
348            self.spi_write(&cmd_buf).await?;
349            self.wait_done().await?;
350        Ok(())
351    }
352
353    /// Program (write) bytes to the memory.
354    ///
355    /// That start address must be page aligned,
356    /// and for most chips, the bytes being written to must have been erased
357    /// (see the datasheet of your specific chip for details).
358    pub async fn write_bytes(&mut self, addr: u32, data: &[u8]) -> Result<(), Error<SPI::Error>> {
359        let mut current_addr = addr;
360        for chunk in data.chunks(256) {
361            self.write_enable().await?;
362
363            let addr_bytes = current_addr.to_be_bytes();
364            current_addr += 256;
365            let cmd_buf = [
366                Opcode::PageProg as u8,
367                addr_bytes[2],
368                addr_bytes[1],
369                addr_bytes[0],
370            ];
371
372            let mut transaction = [
373                embedded_hal_async::spi::Operation::Write(&cmd_buf),
374                embedded_hal_async::spi::Operation::Write(chunk)
375            ];
376            self.spi.transaction(&mut transaction)
377                .await
378                .map_err(Error::Spi)?;
379            self.wait_done().await?;
380        }
381        Ok(())
382    }
383
384    /// Erase a single block.
385    pub async fn erase_block(&mut self, addr: u32) -> Result<(), Error<SPI::Error>> {
386        self.write_enable().await?;
387
388        let addr = addr.to_be_bytes();
389        let cmd_buf = [
390            Opcode::BlockErase as u8,
391            addr[2],
392            addr[1],
393            addr[0],
394        ];
395        self.spi_write(&cmd_buf).await?;
396        self.wait_done().await
397    }
398
399    /// Erase the entire memory.
400    pub async fn erase_all(&mut self) -> Result<(), Error<SPI::Error>> {
401        self.write_enable().await?;
402        self.spi_write(&[Opcode::ChipErase as u8]).await?;
403        self.wait_done().await?;
404        Ok(())
405    }
406
407    /// Perform an in-place SPI tranfer.
408    async fn spi_transfer_inplace(&mut self, buffer: &mut [u8]) -> Result<(), Error<SPI::Error>> {
409        self.spi.transfer_in_place(buffer).await.map_err(Error::Spi)
410    }
411
412    /// Perform an in-place SPI tranfer.
413    async fn spi_write(&mut self, buffer: &[u8]) -> Result<(), Error<SPI::Error>> {
414        self.spi.write(buffer).await.map_err(Error::Spi)
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use assert2::assert;
421    use super::*;
422
423    #[test]
424    fn test_decode_jedec_id() {
425        let cypress_id_bytes = [0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xC2, 0x22, 0x08];
426        assert!(let Some(ident) = JedecId::parse(&cypress_id_bytes));
427        assert!(ident.manufacturer_id == 0xC2);
428        assert!(ident.device_id == 0x2208);
429    }
430}