Skip to main content

w25/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![deny(unsafe_code)]
4#![warn(missing_docs)]
5
6use core::{fmt::Debug, marker::PhantomData};
7use derive_more::TryFrom;
8use embassy_futures::yield_now;
9use embedded_hal::digital::{OutputPin, PinState};
10use embedded_hal_async::spi::SpiDevice;
11use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
12
13mod commands_impl;
14
15/// The Q series
16pub struct Q;
17/// The X series
18pub struct X;
19
20/// Any series that is a NOR flash implements this trait
21pub trait NorSeries {
22    /// The size of a page in bytes
23    const PAGE_SIZE: u32;
24    /// The size of a sector in bytes
25    const SECTOR_SIZE: u32;
26}
27
28impl NorSeries for Q {
29    const PAGE_SIZE: u32 = 256;
30    const SECTOR_SIZE: u32 = Self::PAGE_SIZE * 16;
31}
32
33impl NorSeries for X {
34    const PAGE_SIZE: u32 = 256;
35    const SECTOR_SIZE: u32 = Self::PAGE_SIZE * 16;
36}
37
38/// This trait is implemented when the flash supports the reset commands
39pub trait Reset {}
40
41impl Reset for Q {}
42
43/// Easily readable representation of the command bytes used by the flash chip.
44#[repr(u8)]
45enum Command {
46    PageProgram = 0x02,
47    ReadData = 0x03,
48    ReadStatusRegister1 = 0x05,
49    WriteEnable = 0x06,
50    SectorErase = 0x20,
51    JedecId = 0x9F,
52    UniqueId = 0x4B,
53    Block32Erase = 0x52,
54    Block64Erase = 0xD8,
55    ChipErase = 0xC7,
56    EnableReset = 0x66,
57    PowerDown = 0xB9,
58    ReleasePowerDown = 0xAB,
59    Reset = 0x99,
60}
61
62/// Low level driver for the w25 flash memory chip.
63pub struct W25<Series, SPI, HOLD, WP> {
64    spi: SPI,
65    hold: HOLD,
66    wp: WP,
67    capacity: u32,
68    _pantom: PhantomData<Series>,
69}
70
71impl<Series: NorSeries, SPI, HOLD, WP> W25<Series, SPI, HOLD, WP> {
72    /// Get the total capacity of the flash in bytes
73    pub fn capacity(&self) -> u32 {
74        self.capacity
75    }
76
77    fn n_sectors(&self) -> u32 {
78        self.capacity / Series::SECTOR_SIZE
79    }
80
81    fn n_blocks_32k(&self) -> u32 {
82        self.capacity / 32768
83    }
84
85    fn n_blocks_64k(&self) -> u32 {
86        self.capacity / 65536
87    }
88}
89
90impl<Series: NorSeries, SPI, S: Debug, P: Debug, HOLD, WP> W25<Series, SPI, HOLD, WP>
91where
92    SPI: embedded_hal::spi::ErrorType<Error = S> + embedded_hal_async::spi::SpiDevice,
93    HOLD: OutputPin<Error = P>,
94    WP: OutputPin<Error = P>,
95{
96    /// Create a new instance of the flash.
97    ///
98    /// The capacity must be the total chip capacity in bytes.
99    /// Weird things can happen if you provide the wrong value.
100    /// No checks are done, you're believed at your word.
101    pub async fn new(spi: SPI, hold: HOLD, wp: WP, capacity: u32) -> Result<Self, InitError<S, P>> {
102        let mut flash = W25 {
103            spi,
104            hold,
105            wp,
106            capacity,
107            _pantom: PhantomData,
108        };
109
110        flash.hold.set_high().map_err(InitError::PinError)?;
111        flash.wp.set_high().map_err(InitError::PinError)?;
112
113        // Ensure the device is not busy from before a MCU restart
114        while flash.busy().await? {
115            // Avoid starving the executor when the SPI is
116            // fast enough for the busy check to not yield
117            yield_now().await;
118        }
119
120        Ok(flash)
121    }
122
123    /// Set the hold pin state.
124    ///
125    /// The driver doesn't do anything with this pin. When using the chip, make sure the hold pin is not asserted.
126    /// By default this means the pin needs to be high (true).
127    ///
128    /// This function sets the pin directly and can cause the chip to not work.
129    pub fn set_hold(&mut self, value: PinState) -> Result<(), P> {
130        self.hold.set_state(value)
131    }
132
133    /// Set the write protect pin state.
134    ///
135    /// The driver doesn't do anything with this pin. When using the chip, make sure the hold pin is not asserted.
136    /// By default this means the pin needs to be high (true).
137    ///
138    /// This function sets the pin directly and can cause the chip to not work.
139    pub fn set_wp(&mut self, value: PinState) -> Result<(), P> {
140        self.wp.set_state(value)
141    }
142}
143
144impl<Series: NorSeries, SPI, S: Debug> W25<Series, SPI, (), ()>
145where
146    SPI: embedded_hal::spi::ErrorType<Error = S> + embedded_hal_async::spi::SpiDevice,
147{
148    /// Create a new instance of the flash, but without the nHold and nWP pins.
149    ///
150    /// The capacity must be the total chip capacity.
151    /// Weird things can happen if you provide the wrong value.
152    /// No checks are done, you're believed at your word.
153    pub async fn new_no_pins(spi: SPI, capacity: u32) -> Result<Self, Error<S>> {
154        let mut flash = Self {
155            spi,
156            hold: (),
157            wp: (),
158            capacity,
159            _pantom: PhantomData,
160        };
161
162        // Ensure the device is not busy from before a MCU restart
163        while flash.busy().await? {
164            // Avoid starving the executor when the SPI is
165            // fast enough for the busy check to not yield
166            yield_now().await;
167        }
168
169        Ok(flash)
170    }
171}
172
173/// Errors that can occur during autodetect initialization.
174#[derive(Debug)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176#[non_exhaustive]
177pub enum InitError<S: Debug, P: Debug> {
178    /// Something went wrong with the flash device
179    DeviceError(Error<S>),
180    /// Something went wrong with a pin
181    PinError(P),
182    /// Device reported that it was not manufactured by Winbond
183    ManufacturerNotRecognized(u8),
184    /// The device ID did not match any known devices
185    DeviceNotRecognized(u8),
186}
187
188impl<S: Debug, P: Debug> From<Error<S>> for InitError<S, P> {
189    fn from(value: Error<S>) -> Self {
190        Self::DeviceError(value)
191    }
192}
193
194impl<Series: NorSeries, SPI, S: Debug, P: Debug, HOLD, WP> W25<Series, SPI, HOLD, WP>
195where
196    SPI: SpiDevice<Error = S>,
197    HOLD: OutputPin<Error = P>,
198    WP: OutputPin<Error = P>,
199{
200    /// Create a new instance of the flash, autodetecting the chip variant and capacity.
201    pub async fn new_autodetect(spi: SPI, hold: HOLD, wp: WP) -> Result<Self, InitError<S, P>> {
202        let mut flash = W25 {
203            spi,
204            hold,
205            wp,
206            capacity: 0, // For now do not set the capacity of the device, as we do not know yet.
207            _pantom: PhantomData,
208        };
209
210        flash.hold.set_high().map_err(InitError::PinError)?;
211        flash.wp.set_high().map_err(InitError::PinError)?;
212
213        let jedec_id = flash.jedec_id().await?;
214        let manufacturer = jedec_id.manufacturer();
215        if jedec_id.manufacturer() != JedecId::MANUFACTURER {
216            return Err(InitError::ManufacturerNotRecognized(manufacturer));
217        }
218
219        let major_device_id = jedec_id
220            .major_device_id()
221            .map_err(InitError::DeviceNotRecognized)?;
222
223        // Update the capacity.
224        flash.capacity = major_device_id.capacity();
225
226        // Ensure the device is not busy from before a MCU restart
227        while flash.busy().await? {
228            // Avoid starving the executor when the SPI is
229            // fast enough for the busy check to not yield
230            yield_now().await;
231        }
232
233        Ok(flash)
234    }
235}
236
237impl<Series: NorSeries, SPI, S: Debug, HOLD, WP> ErrorType for W25<Series, SPI, HOLD, WP>
238where
239    SPI: embedded_hal::spi::ErrorType<Error = S>,
240{
241    type Error = Error<S>;
242}
243
244/// Custom error type for the various errors that can be thrown by driver.
245/// Can be converted into a NorFlashError.
246#[derive(Debug)]
247#[cfg_attr(feature = "defmt", derive(defmt::Format))]
248#[non_exhaustive]
249pub enum Error<S: Debug> {
250    /// Something went wrong with the SPI
251    SpiError(S),
252    /// An operation was not aligned
253    NotAligned,
254    /// An operation was out of bounds
255    OutOfBounds,
256    /// Setting the write enable bit failed for some reason
257    WriteEnableFail,
258}
259
260impl<S: Debug> NorFlashError for Error<S> {
261    fn kind(&self) -> NorFlashErrorKind {
262        match self {
263            Error::NotAligned => NorFlashErrorKind::NotAligned,
264            Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,
265            _ => NorFlashErrorKind::Other,
266        }
267    }
268}
269
270#[allow(clippy::identity_op)]
271fn command_and_address(command: u8, address: u32) -> [u8; 4] {
272    [
273        command,
274        // MSB, BE
275        ((address & 0xFF0000) >> 16) as u8,
276        ((address & 0x00FF00) >> 8) as u8,
277        ((address & 0x0000FF) >> 0) as u8,
278    ]
279}
280
281/// Major byte of the device identification (ID7-ID0) denoting chip capacity.
282///
283/// Note that the repr value corresponds to the Manufacturer/DeviceID command (0x90) and not the JEDEC ID (0x9F).
284/// The latter is the same value, but incremented by one (with exception of W25_512, which is incremented by seven).
285#[derive(Debug, Clone, Copy, TryFrom)]
286#[cfg_attr(feature = "defmt", derive(defmt::Format))]
287#[repr(u8)]
288#[try_from(repr)]
289pub enum MajorDeviceId {
290    /// W25X10 512kb device
291    W25_05 = 0x05,
292    /// W25[QX]10 1Mb device
293    W25_10 = 0x10,
294    /// W25[QX]20 2Mb device
295    W25_20 = 0x11,
296    /// W25[QX]40 4Mb device
297    W25_40 = 0x12,
298    /// W25[QX]80 8Mb device
299    W25_80 = 0x13,
300    /// W25[QX]16 16Mb device
301    W25_16 = 0x14,
302    /// W25[QX]32 32Mb device
303    W25_32 = 0x15,
304    /// W25[QX]64 64Mb device
305    W25_64 = 0x16,
306    /// W25Q128 128Mb device
307    W25_128 = 0x17,
308    /// W25Q256 256Mb device
309    W25_256 = 0x18,
310    /// W25Q512 512Mb device
311    W25_512 = 0x19,
312    /// W25Q01 1Gb device
313    W25_01 = 0x20,
314    /// W25Q02 2Gb device
315    W25_02 = 0x21,
316}
317
318impl MajorDeviceId {
319    /// Capacity of a device that has the identifier assigned, in bytes.
320    pub const fn capacity(&self) -> u32 {
321        let capacity_kilobits = match *self {
322            MajorDeviceId::W25_05 => 512,
323            MajorDeviceId::W25_10 => 1024,
324            MajorDeviceId::W25_20 => 2 * 1024,
325            MajorDeviceId::W25_40 => 4 * 1024,
326            MajorDeviceId::W25_80 => 8 * 1024,
327            MajorDeviceId::W25_16 => 16 * 1024,
328            MajorDeviceId::W25_32 => 32 * 1024,
329            MajorDeviceId::W25_64 => 64 * 1024,
330            MajorDeviceId::W25_128 => 128 * 1024,
331            MajorDeviceId::W25_256 => 256 * 1024,
332            MajorDeviceId::W25_512 => 512 * 1024,
333            MajorDeviceId::W25_01 => 1024 * 1024,
334            MajorDeviceId::W25_02 => 2 * 1024 * 1024,
335        };
336
337        const FACTOR_KILOBITS_BYTES: u32 = 1024 / 8;
338        capacity_kilobits * FACTOR_KILOBITS_BYTES
339    }
340}
341
342/// Result from the
343pub struct JedecId([u8; 3]);
344
345impl JedecId {
346    /// ID assigned to Winbond.
347    pub const MANUFACTURER: u8 = 0xEF;
348
349    /// Manufacturer byte of the [JedecId].
350    ///
351    /// Should always be 0xEF for Winbond.
352    pub const fn manufacturer(&self) -> u8 {
353        self.0[0]
354    }
355
356    /// Try to get the major device identifier ID7-0, denoting the chip capacity.
357    ///
358    /// If the ID does not match any known device, returns the **original** JEDEC Major Device ID,
359    /// which is incremented by one compared to the value returned by the Manufacturer/DeviceID command (0x90).
360    pub fn major_device_id(&self) -> Result<MajorDeviceId, u8> {
361        let b_jedec = self.0[2];
362        let b_device_id = match b_jedec {
363            0x20 => 0x19, // Note W25_512 has JEDEC ID 0x20 and not 0x1F.
364            _ => b_jedec.checked_sub(1).ok_or(b_jedec)?,
365        };
366        MajorDeviceId::try_from(b_device_id).map_err(|_e| b_jedec)
367    }
368
369    /// Return the minor device identifier ID8-15, denoting the package and variant of the chip.
370    pub const fn minor_device_id(&self) -> u8 {
371        self.0[1]
372    }
373}