stm32_rust_template/bsp/
spi_flash.rs

1//! SPI Flash HAL Driver
2
3use crate::driver::spi::{Result, Spi};
4use core::marker::PhantomData;
5
6pub const SPIF_PAGE_SIZE: usize = 0x100;
7pub const SPIF_SECTOR_SIZE: usize = 0x1000;
8pub const SPIF_BLOCK_SIZE: usize = 0x10000;
9
10/// Manufacturer and size codes, matching C header.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Manufacturer {
13    Error = 0,
14    Winbond = 0xEF,
15    ISSI = 0x9D,
16    Micron = 0x20,
17    GigaDevice = 0xC8,
18    Macronix = 0xC2,
19    Spansion = 0x01,
20    Amic = 0x37,
21    Sst = 0xBF,
22    Hyundai = 0xAD,
23    Atmel = 0x1F,
24    Fudan = 0xA1,
25    Esmt = 0x8C,
26    Intel = 0x89,
27    Sanyo = 0x62,
28    Fujitsu = 0x04,
29    Eon = 0x1C,
30    Puya = 0x85,
31    Unknown = 0xFF,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum FlashSize {
36    Error = 0,
37    M1bit = 0x11,
38    M2bit = 0x12,
39    M4bit = 0x13,
40    M8bit = 0x14,
41    M16bit = 0x15,
42    M32bit = 0x16,
43    M64bit = 0x17,
44    M128bit = 0x18,
45    M256bit = 0x19,
46    M512bit = 0x20,
47    Unknown = 0xFF,
48}
49
50/// Flash device configuration/handle.
51pub struct SpiFlash<'a, SPI: Spi<'a>> {
52    spi: SPI,
53    cs: fn(active: bool), // software control of CS
54    pub manufacturer: Manufacturer,
55    pub size: FlashSize,
56    pub page_count: u32,
57    pub sector_count: u32,
58    pub block_count: u32,
59    four_byte_addr: bool,
60    busy: bool,
61    _phantom: PhantomData<&'a ()>,
62}
63
64impl<'a, SPI: Spi<'a>> SpiFlash<'a, SPI> {
65    pub fn new(spi: SPI, cs: fn(active: bool)) -> Self {
66        Self {
67            spi,
68            cs,
69            manufacturer: Manufacturer::Unknown,
70            size: FlashSize::Unknown,
71            page_count: 0,
72            sector_count: 0,
73            block_count: 0,
74            four_byte_addr: false,
75            busy: false,
76            _phantom: PhantomData,
77        }
78    }
79
80    /// Pull CS low
81    fn select(&self) {
82        (self.cs)(false);
83    }
84    /// Pull CS high
85    fn deselect(&self) {
86        (self.cs)(true);
87    }
88
89    /// Send command and optional address/data; optionally read.
90    fn cmd(
91        &mut self,
92        cmd: u8,
93        addr: Option<u32>,
94        tx_data: Option<&[u8]>,
95        rx_data: Option<&mut [u8]>,
96    ) -> Result<()> {
97        let mut buf = [0u8; 5];
98        let mut n = 1;
99        buf[0] = cmd;
100        if let Some(a) = addr {
101            if self.four_byte_addr {
102                buf[1] = ((a >> 24) & 0xFF) as u8;
103                buf[2] = ((a >> 16) & 0xFF) as u8;
104                buf[3] = ((a >> 8) & 0xFF) as u8;
105                buf[4] = (a & 0xFF) as u8;
106                n += 4;
107            } else {
108                buf[1] = ((a >> 16) & 0xFF) as u8;
109                buf[2] = ((a >> 8) & 0xFF) as u8;
110                buf[3] = (a & 0xFF) as u8;
111                n += 3;
112            }
113        }
114        self.select();
115        self.spi.send(&buf[0..n])?;
116        if let Some(tx) = tx_data {
117            self.spi.send(tx)?;
118        }
119        if let Some(rx) = rx_data {
120            self.spi.receive(rx)?;
121        }
122        self.deselect();
123        Ok(())
124    }
125
126    pub fn find_chip(&mut self) -> Result<()> {
127        let mut rx = [0xFFu8; 4];
128        self.select();
129        self.spi.send(&[0x9F])?;
130        self.spi.receive(&mut rx[1..4])?;
131        self.deselect();
132
133        self.manufacturer = match rx[1] {
134            0xEF => Manufacturer::Winbond,
135            0x9D => Manufacturer::ISSI,
136            0x20 => Manufacturer::Micron,
137            0xC8 => Manufacturer::GigaDevice,
138            0xC2 => Manufacturer::Macronix,
139            0x01 => Manufacturer::Spansion,
140            0x37 => Manufacturer::Amic,
141            0xBF => Manufacturer::Sst,
142            0xAD => Manufacturer::Hyundai,
143            0x1F => Manufacturer::Atmel,
144            0xA1 => Manufacturer::Fudan,
145            0x8C => Manufacturer::Esmt,
146            0x89 => Manufacturer::Intel,
147            0x62 => Manufacturer::Sanyo,
148            0x04 => Manufacturer::Fujitsu,
149            0x1C => Manufacturer::Eon,
150            0x85 => Manufacturer::Puya,
151            _ => Manufacturer::Error,
152        };
153        self.size = match rx[3] {
154            0x11 => FlashSize::M1bit,
155            0x12 => FlashSize::M2bit,
156            0x13 => FlashSize::M4bit,
157            0x14 => FlashSize::M8bit,
158            0x15 => FlashSize::M16bit,
159            0x16 => FlashSize::M32bit,
160            0x17 => FlashSize::M64bit,
161            0x18 => FlashSize::M128bit,
162            0x19 => FlashSize::M256bit,
163            0x20 => FlashSize::M512bit,
164            _ => FlashSize::Error,
165        };
166        self.block_count = match self.size {
167            FlashSize::M1bit => 2,
168            FlashSize::M2bit => 4,
169            FlashSize::M4bit => 8,
170            FlashSize::M8bit => 16,
171            FlashSize::M16bit => 32,
172            FlashSize::M32bit => 64,
173            FlashSize::M64bit => 128,
174            FlashSize::M128bit => 256,
175            FlashSize::M256bit => 512,
176            FlashSize::M512bit => 1024,
177            _ => 0,
178        };
179        self.sector_count = self.block_count * 16;
180        self.page_count = self.sector_count * (SPIF_SECTOR_SIZE as u32 / SPIF_PAGE_SIZE as u32);
181        self.four_byte_addr = self.block_count >= 512;
182        Ok(())
183    }
184
185    fn write_enable(&mut self) -> Result<()> {
186        self.cmd(0x06, None, None, None)
187    }
188
189    fn write_disable(&mut self) -> Result<()> {
190        self.cmd(0x04, None, None, None)
191    }
192
193    fn read_status(&mut self) -> Result<u8> {
194        let mut tx = [0x05, 0xA5];
195        let mut rx = [0u8; 2];
196        self.select();
197        self.spi.transfer(&tx, &mut rx)?;
198        self.deselect();
199        Ok(rx[1])
200    }
201
202    pub fn wait_ready(&mut self, timeout: u32, mut delay: impl FnMut(u32)) -> Result<()> {
203        let mut t = 0;
204        while t < timeout {
205            if self.read_status()? & 0x01 == 0 {
206                return Ok(());
207            }
208            delay(1);
209            t += 1;
210        }
211        Err(-1)
212    }
213
214    pub fn erase_chip(&mut self, mut delay: impl FnMut(u32)) -> Result<()> {
215        self.busy = true;
216        self.write_enable()?;
217        self.cmd(0x60, None, None, None)?; // erase command
218        self.wait_ready(self.block_count * 1000, &mut delay)?;
219        self.write_disable()?;
220        self.busy = false;
221        Ok(())
222    }
223
224    pub fn erase_sector(&mut self, sector: u32, mut delay: impl FnMut(u32)) -> Result<()> {
225        if sector >= self.sector_count {
226            return Err(-2);
227        }
228        self.busy = true;
229        let address = sector * SPIF_SECTOR_SIZE as u32;
230        self.write_enable()?;
231        self.cmd(
232            if self.four_byte_addr { 0x21 } else { 0x20 },
233            Some(address),
234            None,
235            None,
236        )?;
237        self.wait_ready(1000, &mut delay)?;
238        self.write_disable()?;
239        self.busy = false;
240        Ok(())
241    }
242
243    pub fn erase_block(&mut self, block: u32, mut delay: impl FnMut(u32)) -> Result<()> {
244        if block >= self.block_count {
245            return Err(-2);
246        }
247        self.busy = true;
248        let address = block * SPIF_BLOCK_SIZE as u32;
249        self.write_enable()?;
250        self.cmd(
251            if self.four_byte_addr { 0xDC } else { 0xD8 },
252            Some(address),
253            None,
254            None,
255        )?;
256        self.wait_ready(3000, &mut delay)?;
257        self.write_disable()?;
258        self.busy = false;
259        Ok(())
260    }
261
262    /// Write array to address, splits across pages as needed.
263    pub fn write_address(
264        &mut self,
265        mut address: u32,
266        mut data: &[u8],
267        mut delay: impl FnMut(u32),
268    ) -> Result<()> {
269        self.busy = true;
270        while !data.is_empty() {
271            let page = (address / SPIF_PAGE_SIZE as u32) as u32;
272            let offset = (address % SPIF_PAGE_SIZE as u32) as usize;
273            let max = SPIF_PAGE_SIZE - offset;
274            let to_write = core::cmp::min(data.len(), max);
275            self.write_page(page, &data[..to_write], offset, &mut delay)?;
276            address += to_write as u32;
277            data = &data[to_write..];
278        }
279        self.busy = false;
280        Ok(())
281    }
282
283    /// Write a partial page (up to 256-offset).
284    pub fn write_page(
285        &mut self,
286        page: u32,
287        data: &[u8],
288        offset: usize,
289        delay: &mut impl FnMut(u32),
290    ) -> Result<()> {
291        if offset >= SPIF_PAGE_SIZE {
292            return Err(-3);
293        }
294        let n = core::cmp::min(data.len(), SPIF_PAGE_SIZE - offset);
295        let address = page * SPIF_PAGE_SIZE as u32 + offset as u32;
296        self.write_enable()?;
297        self.cmd(
298            if self.four_byte_addr { 0x12 } else { 0x02 },
299            Some(address),
300            Some(&data[..n]),
301            None,
302        )?;
303        self.wait_ready(100, delay)?;
304        self.write_disable()?;
305        Ok(())
306    }
307
308    /// Read array from arbitrary address, splits if needed.
309    pub fn read_address(&mut self, mut address: u32, mut data: &mut [u8]) -> Result<()> {
310        self.busy = true;
311        let max_len = 256; // can tune based on stack
312        let mut off = 0;
313        while off < data.len() {
314            let sz = core::cmp::min(max_len, data.len() - off);
315            self.cmd(
316                if self.four_byte_addr { 0x13 } else { 0x03 },
317                Some(address + off as u32),
318                None,
319                Some(&mut data[off..off + sz]),
320            )?;
321            off += sz;
322        }
323        self.busy = false;
324        Ok(())
325    }
326
327    /// Read portion of a page
328    pub fn read_page(&mut self, page: u32, data: &mut [u8], offset: usize) -> Result<()> {
329        let max = SPIF_PAGE_SIZE - offset;
330        let sz = core::cmp::min(data.len(), max);
331        let address = page * SPIF_PAGE_SIZE as u32 + offset as u32;
332        self.read_address(address, &mut data[..sz])
333    }
334
335    pub fn write_sector(
336        &mut self,
337        sector: u32,
338        data: &[u8],
339        offset: usize,
340        mut delay: impl FnMut(u32),
341    ) -> Result<()> {
342        if offset >= SPIF_SECTOR_SIZE {
343            return Err(-3);
344        }
345        let mut written = 0;
346        let mut page = sector * (SPIF_SECTOR_SIZE as u32 / SPIF_PAGE_SIZE as u32);
347        page += (offset as u32) / SPIF_PAGE_SIZE as u32;
348        let mut rem = core::cmp::min(data.len(), SPIF_SECTOR_SIZE - offset);
349        let mut page_offset = offset % SPIF_PAGE_SIZE;
350        while rem > 0 && page < ((sector + 1) * (SPIF_SECTOR_SIZE as u32 / SPIF_PAGE_SIZE as u32)) {
351            let n = core::cmp::min(SPIF_PAGE_SIZE - page_offset, rem);
352            self.write_page(page, &data[written..written + n], page_offset, &mut delay)?;
353            written += n;
354            rem -= n;
355            page += 1;
356            page_offset = 0;
357        }
358        Ok(())
359    }
360
361    pub fn read_sector(&mut self, sector: u32, data: &mut [u8], offset: usize) -> Result<()> {
362        if offset >= SPIF_SECTOR_SIZE {
363            return Err(-3);
364        }
365        let n = core::cmp::min(data.len(), SPIF_SECTOR_SIZE - offset);
366        let address = sector * SPIF_SECTOR_SIZE as u32 + offset as u32;
367        self.read_address(address, &mut data[..n])
368    }
369
370    pub fn write_block(
371        &mut self,
372        block: u32,
373        data: &[u8],
374        offset: usize,
375        mut delay: impl FnMut(u32),
376    ) -> Result<()> {
377        if offset >= SPIF_BLOCK_SIZE {
378            return Err(-3);
379        }
380        let mut written = 0;
381        let mut page = block * (SPIF_BLOCK_SIZE as u32 / SPIF_PAGE_SIZE as u32);
382        page += (offset as u32) / SPIF_PAGE_SIZE as u32;
383        let mut rem = core::cmp::min(data.len(), SPIF_BLOCK_SIZE - offset);
384        let mut page_offset = offset % SPIF_PAGE_SIZE;
385        while rem > 0 && page < ((block + 1) * (SPIF_BLOCK_SIZE as u32 / SPIF_PAGE_SIZE as u32)) {
386            let n = core::cmp::min(SPIF_PAGE_SIZE - page_offset, rem);
387            self.write_page(page, &data[written..written + n], page_offset, &mut delay)?;
388            written += n;
389            rem -= n;
390            page += 1;
391            page_offset = 0;
392        }
393        Ok(())
394    }
395
396    pub fn read_block(&mut self, block: u32, data: &mut [u8], offset: usize) -> Result<()> {
397        if offset >= SPIF_BLOCK_SIZE {
398            return Err(-3);
399        }
400        let n = core::cmp::min(data.len(), SPIF_BLOCK_SIZE - offset);
401        let address = block * SPIF_BLOCK_SIZE as u32 + offset as u32;
402        self.read_address(address, &mut data[..n])
403    }
404
405    pub fn is_busy(&self) -> bool {
406        self.busy
407    }
408}