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}