Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of SerialFlash by
SerialFlash.cpp@1:5f1a6c63fa57, 2016-07-11 (annotated)
- Committer:
- lzbpli
- Date:
- Mon Jul 11 07:49:22 2016 +0000
- Revision:
- 1:5f1a6c63fa57
- Parent:
- 0:e5c9fd5789d7
053
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| manitou | 0:e5c9fd5789d7 | 1 | /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory |
| manitou | 0:e5c9fd5789d7 | 2 | * https://github.com/PaulStoffregen/SerialFlash |
| manitou | 0:e5c9fd5789d7 | 3 | * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com |
| manitou | 0:e5c9fd5789d7 | 4 | * |
| manitou | 0:e5c9fd5789d7 | 5 | * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. |
| manitou | 0:e5c9fd5789d7 | 6 | * Please support PJRC's efforts to develop open source software by purchasing |
| manitou | 0:e5c9fd5789d7 | 7 | * Teensy or other genuine PJRC products. |
| manitou | 0:e5c9fd5789d7 | 8 | * |
| manitou | 0:e5c9fd5789d7 | 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| manitou | 0:e5c9fd5789d7 | 10 | * of this software and associated documentation files (the "Software"), to deal |
| manitou | 0:e5c9fd5789d7 | 11 | * in the Software without restriction, including without limitation the rights |
| manitou | 0:e5c9fd5789d7 | 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| manitou | 0:e5c9fd5789d7 | 13 | * copies of the Software, and to permit persons to whom the Software is |
| manitou | 0:e5c9fd5789d7 | 14 | * furnished to do so, subject to the following conditions: |
| manitou | 0:e5c9fd5789d7 | 15 | * |
| manitou | 0:e5c9fd5789d7 | 16 | * The above copyright notice, development funding notice, and this permission |
| manitou | 0:e5c9fd5789d7 | 17 | * notice shall be included in all copies or substantial portions of the Software. |
| manitou | 0:e5c9fd5789d7 | 18 | * |
| manitou | 0:e5c9fd5789d7 | 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| manitou | 0:e5c9fd5789d7 | 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| manitou | 0:e5c9fd5789d7 | 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| manitou | 0:e5c9fd5789d7 | 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| manitou | 0:e5c9fd5789d7 | 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| manitou | 0:e5c9fd5789d7 | 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| manitou | 0:e5c9fd5789d7 | 25 | * THE SOFTWARE. |
| manitou | 0:e5c9fd5789d7 | 26 | */ |
| manitou | 0:e5c9fd5789d7 | 27 | #include "mbed.h" |
| manitou | 0:e5c9fd5789d7 | 28 | #include "SerialFlash.h" |
| manitou | 0:e5c9fd5789d7 | 29 | |
| lzbpli | 1:5f1a6c63fa57 | 30 | static SPI spi(PB_15,PB_14,PB_13); // mosi, miso, sclk |
| lzbpli | 1:5f1a6c63fa57 | 31 | DigitalOut cspin(PB_12); |
| manitou | 0:e5c9fd5789d7 | 32 | |
| manitou | 0:e5c9fd5789d7 | 33 | #define CSASSERT() cspin = 0 |
| manitou | 0:e5c9fd5789d7 | 34 | #define CSRELEASE() cspin = 1 |
| manitou | 0:e5c9fd5789d7 | 35 | |
| manitou | 0:e5c9fd5789d7 | 36 | uint16_t SerialFlashChip::dirindex = 0; |
| manitou | 0:e5c9fd5789d7 | 37 | uint8_t SerialFlashChip::flags = 0; |
| manitou | 0:e5c9fd5789d7 | 38 | uint8_t SerialFlashChip::busy = 0; |
| manitou | 0:e5c9fd5789d7 | 39 | |
| manitou | 0:e5c9fd5789d7 | 40 | |
| manitou | 0:e5c9fd5789d7 | 41 | #define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address |
| manitou | 0:e5c9fd5789d7 | 42 | #define FLAG_STATUS_CMD70 0x02 // requires special busy flag check |
| manitou | 0:e5c9fd5789d7 | 43 | #define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands |
| manitou | 0:e5c9fd5789d7 | 44 | #define FLAG_MULTI_DIE 0x08 // multiple die, don't read cross 32M barrier |
| manitou | 0:e5c9fd5789d7 | 45 | #define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks |
| manitou | 0:e5c9fd5789d7 | 46 | #define FLAG_DIE_MASK 0xC0 // top 2 bits count during multi-die erase |
| manitou | 0:e5c9fd5789d7 | 47 | |
| manitou | 0:e5c9fd5789d7 | 48 | void SerialFlashChip::wait(void) |
| manitou | 0:e5c9fd5789d7 | 49 | { |
| manitou | 0:e5c9fd5789d7 | 50 | uint32_t status; |
| manitou | 0:e5c9fd5789d7 | 51 | //Serial.print("wait-"); |
| manitou | 0:e5c9fd5789d7 | 52 | while (1) { |
| manitou | 0:e5c9fd5789d7 | 53 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 54 | if (flags & FLAG_STATUS_CMD70) { |
| manitou | 0:e5c9fd5789d7 | 55 | // some Micron chips require this different |
| manitou | 0:e5c9fd5789d7 | 56 | // command to detect program and erase completion |
| manitou | 0:e5c9fd5789d7 | 57 | spi.write(0x70); |
| manitou | 0:e5c9fd5789d7 | 58 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 59 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 60 | //Serial.printf("b=%02x.", status & 0xFF); |
| manitou | 0:e5c9fd5789d7 | 61 | if ((status & 0x80)) break; |
| manitou | 0:e5c9fd5789d7 | 62 | } else { |
| manitou | 0:e5c9fd5789d7 | 63 | // all others work by simply reading the status reg |
| manitou | 0:e5c9fd5789d7 | 64 | spi.write(0x05); |
| manitou | 0:e5c9fd5789d7 | 65 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 66 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 67 | //Serial.printf("b=%02x.", status & 0xFF); |
| manitou | 0:e5c9fd5789d7 | 68 | if (!(status & 1)) break; |
| manitou | 0:e5c9fd5789d7 | 69 | } |
| manitou | 0:e5c9fd5789d7 | 70 | } |
| manitou | 0:e5c9fd5789d7 | 71 | busy = 0; |
| manitou | 0:e5c9fd5789d7 | 72 | //Serial.println(); |
| manitou | 0:e5c9fd5789d7 | 73 | } |
| manitou | 0:e5c9fd5789d7 | 74 | |
| manitou | 0:e5c9fd5789d7 | 75 | void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) |
| manitou | 0:e5c9fd5789d7 | 76 | { |
| manitou | 0:e5c9fd5789d7 | 77 | uint8_t *p = (uint8_t *)buf; |
| manitou | 0:e5c9fd5789d7 | 78 | uint8_t b, f, status, cmd; |
| manitou | 0:e5c9fd5789d7 | 79 | |
| manitou | 0:e5c9fd5789d7 | 80 | memset(p, 0, len); |
| manitou | 0:e5c9fd5789d7 | 81 | f = flags; |
| manitou | 0:e5c9fd5789d7 | 82 | b = busy; |
| manitou | 0:e5c9fd5789d7 | 83 | if (b) { |
| manitou | 0:e5c9fd5789d7 | 84 | // read status register ... chip may no longer be busy |
| manitou | 0:e5c9fd5789d7 | 85 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 86 | if (flags & FLAG_STATUS_CMD70) { |
| manitou | 0:e5c9fd5789d7 | 87 | spi.write(0x70); |
| manitou | 0:e5c9fd5789d7 | 88 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 89 | if ((status & 0x80)) b = 0; |
| manitou | 0:e5c9fd5789d7 | 90 | } else { |
| manitou | 0:e5c9fd5789d7 | 91 | spi.write(0x05); |
| manitou | 0:e5c9fd5789d7 | 92 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 93 | if (!(status & 1)) b = 0; |
| manitou | 0:e5c9fd5789d7 | 94 | } |
| manitou | 0:e5c9fd5789d7 | 95 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 96 | if (b == 0) { |
| manitou | 0:e5c9fd5789d7 | 97 | // chip is no longer busy :-) |
| manitou | 0:e5c9fd5789d7 | 98 | busy = 0; |
| manitou | 0:e5c9fd5789d7 | 99 | } else if (b < 3) { |
| manitou | 0:e5c9fd5789d7 | 100 | // TODO: this may not work on Spansion chips |
| manitou | 0:e5c9fd5789d7 | 101 | // which apparently have 2 different suspend |
| manitou | 0:e5c9fd5789d7 | 102 | // commands, for program vs erase |
| manitou | 0:e5c9fd5789d7 | 103 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 104 | spi.write(0x06); // write enable (Micron req'd) |
| manitou | 0:e5c9fd5789d7 | 105 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 106 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 107 | cmd = 0x75; //Suspend program/erase for almost all chips |
| manitou | 0:e5c9fd5789d7 | 108 | // but Spansion just has to be different for program suspend! |
| manitou | 0:e5c9fd5789d7 | 109 | if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; |
| manitou | 0:e5c9fd5789d7 | 110 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 111 | spi.write(cmd); // Suspend command |
| manitou | 0:e5c9fd5789d7 | 112 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 113 | if (f & FLAG_STATUS_CMD70) { |
| manitou | 0:e5c9fd5789d7 | 114 | // Micron chips don't actually suspend until flags read |
| manitou | 0:e5c9fd5789d7 | 115 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 116 | spi.write(0x70); |
| manitou | 0:e5c9fd5789d7 | 117 | do { |
| manitou | 0:e5c9fd5789d7 | 118 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 119 | } while (!(status & 0x80)); |
| manitou | 0:e5c9fd5789d7 | 120 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 121 | } else { |
| manitou | 0:e5c9fd5789d7 | 122 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 123 | spi.write(0x05); |
| manitou | 0:e5c9fd5789d7 | 124 | do { |
| manitou | 0:e5c9fd5789d7 | 125 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 126 | } while ((status & 0x01)); |
| manitou | 0:e5c9fd5789d7 | 127 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 128 | } |
| manitou | 0:e5c9fd5789d7 | 129 | } else { |
| manitou | 0:e5c9fd5789d7 | 130 | // chip is busy with an operation that can not suspend |
| manitou | 0:e5c9fd5789d7 | 131 | wait(); // should we wait without ending |
| manitou | 0:e5c9fd5789d7 | 132 | b = 0; // the transaction?? |
| manitou | 0:e5c9fd5789d7 | 133 | } |
| manitou | 0:e5c9fd5789d7 | 134 | } |
| manitou | 0:e5c9fd5789d7 | 135 | do { |
| manitou | 0:e5c9fd5789d7 | 136 | uint32_t rdlen = len; |
| manitou | 0:e5c9fd5789d7 | 137 | if (f & FLAG_MULTI_DIE) { |
| manitou | 0:e5c9fd5789d7 | 138 | if ((addr & 0xFE000000) != ((addr + len - 1) & 0xFE000000)) { |
| manitou | 0:e5c9fd5789d7 | 139 | rdlen = 0x2000000 - (addr & 0x1FFFFFF); |
| manitou | 0:e5c9fd5789d7 | 140 | } |
| manitou | 0:e5c9fd5789d7 | 141 | } |
| manitou | 0:e5c9fd5789d7 | 142 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 143 | // TODO: FIFO optimize.... |
| manitou | 0:e5c9fd5789d7 | 144 | if (f & FLAG_32BIT_ADDR) { |
| manitou | 0:e5c9fd5789d7 | 145 | spi.write(0x03); |
| manitou | 0:e5c9fd5789d7 | 146 | spi.write(addr >> 24); |
| manitou | 0:e5c9fd5789d7 | 147 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 148 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 149 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 150 | } else { |
| manitou | 0:e5c9fd5789d7 | 151 | spi.write(3); |
| manitou | 0:e5c9fd5789d7 | 152 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 153 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 154 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 155 | } |
| manitou | 0:e5c9fd5789d7 | 156 | uint32_t i = rdlen; // need block transfer |
| manitou | 0:e5c9fd5789d7 | 157 | while(i>0){ |
| manitou | 0:e5c9fd5789d7 | 158 | *p++ = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 159 | i--; |
| manitou | 0:e5c9fd5789d7 | 160 | } |
| manitou | 0:e5c9fd5789d7 | 161 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 162 | addr += rdlen; |
| manitou | 0:e5c9fd5789d7 | 163 | len -= rdlen; |
| manitou | 0:e5c9fd5789d7 | 164 | } while (len > 0); |
| manitou | 0:e5c9fd5789d7 | 165 | if (b) { |
| manitou | 0:e5c9fd5789d7 | 166 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 167 | spi.write(0x06); // write enable (Micron req'd) |
| manitou | 0:e5c9fd5789d7 | 168 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 169 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 170 | cmd = 0x7A; |
| manitou | 0:e5c9fd5789d7 | 171 | if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; |
| manitou | 0:e5c9fd5789d7 | 172 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 173 | spi.write(cmd); // Resume program/erase |
| manitou | 0:e5c9fd5789d7 | 174 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 175 | } |
| manitou | 0:e5c9fd5789d7 | 176 | } |
| manitou | 0:e5c9fd5789d7 | 177 | |
| manitou | 0:e5c9fd5789d7 | 178 | void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) |
| manitou | 0:e5c9fd5789d7 | 179 | { |
| manitou | 0:e5c9fd5789d7 | 180 | const uint8_t *p = (const uint8_t *)buf; |
| manitou | 0:e5c9fd5789d7 | 181 | uint32_t max, pagelen; |
| manitou | 0:e5c9fd5789d7 | 182 | |
| manitou | 0:e5c9fd5789d7 | 183 | //Serial.printf("WR: addr %08X, len %d\n", addr, len); |
| manitou | 0:e5c9fd5789d7 | 184 | do { |
| manitou | 0:e5c9fd5789d7 | 185 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 186 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 187 | // write enable command |
| manitou | 0:e5c9fd5789d7 | 188 | spi.write(0x06); |
| manitou | 0:e5c9fd5789d7 | 189 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 190 | max = 256 - (addr & 0xFF); |
| manitou | 0:e5c9fd5789d7 | 191 | pagelen = (len <= max) ? len : max; |
| manitou | 0:e5c9fd5789d7 | 192 | //Serial.printf("WR: addr %08X, pagelen %d\n", addr, pagelen); |
| manitou | 0:e5c9fd5789d7 | 193 | wait_us(1); // TODO: reduce this, but prefer safety first |
| manitou | 0:e5c9fd5789d7 | 194 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 195 | if (flags & FLAG_32BIT_ADDR) { |
| manitou | 0:e5c9fd5789d7 | 196 | spi.write(0x02); // program page command |
| manitou | 0:e5c9fd5789d7 | 197 | spi.write(addr >> 24); |
| manitou | 0:e5c9fd5789d7 | 198 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 199 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 200 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 201 | } else { |
| manitou | 0:e5c9fd5789d7 | 202 | spi.write(2); |
| manitou | 0:e5c9fd5789d7 | 203 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 204 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 205 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 206 | } |
| manitou | 0:e5c9fd5789d7 | 207 | addr += pagelen; |
| manitou | 0:e5c9fd5789d7 | 208 | len -= pagelen; |
| manitou | 0:e5c9fd5789d7 | 209 | do { |
| manitou | 0:e5c9fd5789d7 | 210 | spi.write(*p++); |
| manitou | 0:e5c9fd5789d7 | 211 | } while (--pagelen > 0); |
| manitou | 0:e5c9fd5789d7 | 212 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 213 | busy = 4; |
| manitou | 0:e5c9fd5789d7 | 214 | } while (len > 0); |
| manitou | 0:e5c9fd5789d7 | 215 | } |
| manitou | 0:e5c9fd5789d7 | 216 | |
| manitou | 0:e5c9fd5789d7 | 217 | void SerialFlashChip::eraseAll() |
| manitou | 0:e5c9fd5789d7 | 218 | { |
| manitou | 0:e5c9fd5789d7 | 219 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 220 | uint8_t id[5]; |
| manitou | 0:e5c9fd5789d7 | 221 | readID(id); |
| manitou | 0:e5c9fd5789d7 | 222 | //Serial.printf("ID: %02X %02X %02X\n", id[0], id[1], id[2]); |
| manitou | 0:e5c9fd5789d7 | 223 | if (id[0] == 0x20 && id[2] >= 0x20 && id[2] <= 0x22) { |
| manitou | 0:e5c9fd5789d7 | 224 | // Micron's multi-die chips require special die erase commands |
| manitou | 0:e5c9fd5789d7 | 225 | // N25Q512A 20 BA 20 2 dies 32 Mbyte/die 65 nm transitors |
| manitou | 0:e5c9fd5789d7 | 226 | // N25Q00AA 20 BA 21 4 dies 32 Mbyte/die 65 nm transitors |
| manitou | 0:e5c9fd5789d7 | 227 | // MT25QL02GC 20 BA 22 2 dies 128 Mbyte/die 45 nm transitors |
| manitou | 0:e5c9fd5789d7 | 228 | uint8_t die_count = 2; |
| manitou | 0:e5c9fd5789d7 | 229 | if (id[2] == 0x21) die_count = 4; |
| manitou | 0:e5c9fd5789d7 | 230 | uint8_t die_index = flags >> 6; |
| manitou | 0:e5c9fd5789d7 | 231 | //Serial.printf("Micron die erase %d\n", die_index); |
| manitou | 0:e5c9fd5789d7 | 232 | flags &= 0x3F; |
| manitou | 0:e5c9fd5789d7 | 233 | if (die_index >= die_count) return; // all dies erased :-) |
| manitou | 0:e5c9fd5789d7 | 234 | uint8_t die_size = 2; // in 16 Mbyte units |
| manitou | 0:e5c9fd5789d7 | 235 | if (id[2] == 0x22) die_size = 8; |
| manitou | 0:e5c9fd5789d7 | 236 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 237 | spi.write(0x06); // write enable command |
| manitou | 0:e5c9fd5789d7 | 238 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 239 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 240 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 241 | // die erase command |
| manitou | 0:e5c9fd5789d7 | 242 | spi.write(0xC4); |
| manitou | 0:e5c9fd5789d7 | 243 | spi.write((die_index * die_size) ); |
| manitou | 0:e5c9fd5789d7 | 244 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 245 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 246 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 247 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 248 | //Serial.printf("Micron erase begin\n"); |
| manitou | 0:e5c9fd5789d7 | 249 | flags |= (die_index + 1) << 6; |
| manitou | 0:e5c9fd5789d7 | 250 | } else { |
| manitou | 0:e5c9fd5789d7 | 251 | // All other chips support the bulk erase command |
| manitou | 0:e5c9fd5789d7 | 252 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 253 | // write enable command |
| manitou | 0:e5c9fd5789d7 | 254 | spi.write(0x06); |
| manitou | 0:e5c9fd5789d7 | 255 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 256 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 257 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 258 | // bulk erase command |
| manitou | 0:e5c9fd5789d7 | 259 | spi.write(0xC7); |
| manitou | 0:e5c9fd5789d7 | 260 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 261 | } |
| manitou | 0:e5c9fd5789d7 | 262 | busy = 3; |
| manitou | 0:e5c9fd5789d7 | 263 | } |
| manitou | 0:e5c9fd5789d7 | 264 | |
| manitou | 0:e5c9fd5789d7 | 265 | void SerialFlashChip::eraseBlock(uint32_t addr) |
| manitou | 0:e5c9fd5789d7 | 266 | { |
| manitou | 0:e5c9fd5789d7 | 267 | uint8_t f = flags; |
| manitou | 0:e5c9fd5789d7 | 268 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 269 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 270 | spi.write(0x06); // write enable command |
| manitou | 0:e5c9fd5789d7 | 271 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 272 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 273 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 274 | if (f & FLAG_32BIT_ADDR) { |
| manitou | 0:e5c9fd5789d7 | 275 | spi.write(0xD8); |
| manitou | 0:e5c9fd5789d7 | 276 | spi.write(addr >> 24); |
| manitou | 0:e5c9fd5789d7 | 277 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 278 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 279 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 280 | } else { |
| manitou | 0:e5c9fd5789d7 | 281 | spi.write(0xD8); |
| manitou | 0:e5c9fd5789d7 | 282 | spi.write(addr >> 16); |
| manitou | 0:e5c9fd5789d7 | 283 | spi.write(addr >> 8); |
| manitou | 0:e5c9fd5789d7 | 284 | spi.write(addr); |
| manitou | 0:e5c9fd5789d7 | 285 | } |
| manitou | 0:e5c9fd5789d7 | 286 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 287 | busy = 2; |
| manitou | 0:e5c9fd5789d7 | 288 | } |
| manitou | 0:e5c9fd5789d7 | 289 | |
| manitou | 0:e5c9fd5789d7 | 290 | |
| manitou | 0:e5c9fd5789d7 | 291 | bool SerialFlashChip::ready() |
| manitou | 0:e5c9fd5789d7 | 292 | { |
| manitou | 0:e5c9fd5789d7 | 293 | uint32_t status; |
| manitou | 0:e5c9fd5789d7 | 294 | if (!busy) return true; |
| manitou | 0:e5c9fd5789d7 | 295 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 296 | if (flags & FLAG_STATUS_CMD70) { |
| manitou | 0:e5c9fd5789d7 | 297 | // some Micron chips require this different |
| manitou | 0:e5c9fd5789d7 | 298 | // command to detect program and erase completion |
| manitou | 0:e5c9fd5789d7 | 299 | spi.write(0x70); |
| manitou | 0:e5c9fd5789d7 | 300 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 301 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 302 | //Serial.printf("ready=%02x\n", status & 0xFF); |
| manitou | 0:e5c9fd5789d7 | 303 | if ((status & 0x80) == 0) return false; |
| manitou | 0:e5c9fd5789d7 | 304 | } else { |
| manitou | 0:e5c9fd5789d7 | 305 | // all others work by simply reading the status reg |
| manitou | 0:e5c9fd5789d7 | 306 | spi.write(0x05); |
| manitou | 0:e5c9fd5789d7 | 307 | status = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 308 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 309 | //Serial.printf("ready=%02x\n", status & 0xFF); |
| manitou | 0:e5c9fd5789d7 | 310 | if ((status & 1)) return false; |
| manitou | 0:e5c9fd5789d7 | 311 | } |
| manitou | 0:e5c9fd5789d7 | 312 | busy = 0; |
| manitou | 0:e5c9fd5789d7 | 313 | if (flags & 0xC0) { |
| manitou | 0:e5c9fd5789d7 | 314 | // continue a multi-die erase |
| manitou | 0:e5c9fd5789d7 | 315 | eraseAll(); |
| manitou | 0:e5c9fd5789d7 | 316 | return false; |
| manitou | 0:e5c9fd5789d7 | 317 | } |
| manitou | 0:e5c9fd5789d7 | 318 | return true; |
| manitou | 0:e5c9fd5789d7 | 319 | } |
| manitou | 0:e5c9fd5789d7 | 320 | |
| manitou | 0:e5c9fd5789d7 | 321 | |
| manitou | 0:e5c9fd5789d7 | 322 | #define ID0_WINBOND 0xEF |
| manitou | 0:e5c9fd5789d7 | 323 | #define ID0_SPANSION 0x01 |
| manitou | 0:e5c9fd5789d7 | 324 | #define ID0_MICRON 0x20 |
| manitou | 0:e5c9fd5789d7 | 325 | #define ID0_MACRONIX 0xC2 |
| manitou | 0:e5c9fd5789d7 | 326 | #define ID0_SST 0xBF |
| manitou | 0:e5c9fd5789d7 | 327 | |
| manitou | 0:e5c9fd5789d7 | 328 | //#define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address |
| manitou | 0:e5c9fd5789d7 | 329 | //#define FLAG_STATUS_CMD70 0x02 // requires special busy flag check |
| manitou | 0:e5c9fd5789d7 | 330 | //#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands |
| manitou | 0:e5c9fd5789d7 | 331 | //#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks |
| manitou | 0:e5c9fd5789d7 | 332 | |
| manitou | 0:e5c9fd5789d7 | 333 | bool SerialFlashChip::begin(uint8_t pin) |
| manitou | 0:e5c9fd5789d7 | 334 | { |
| manitou | 0:e5c9fd5789d7 | 335 | uint8_t id[5]; |
| manitou | 0:e5c9fd5789d7 | 336 | uint8_t f; |
| manitou | 0:e5c9fd5789d7 | 337 | uint32_t size; |
| manitou | 0:e5c9fd5789d7 | 338 | |
| manitou | 0:e5c9fd5789d7 | 339 | spi.frequency(30000000); // max |
| manitou | 0:e5c9fd5789d7 | 340 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 341 | readID(id); |
| manitou | 0:e5c9fd5789d7 | 342 | f = 0; |
| manitou | 0:e5c9fd5789d7 | 343 | size = capacity(id); |
| manitou | 0:e5c9fd5789d7 | 344 | if (size > 16777216) { |
| manitou | 0:e5c9fd5789d7 | 345 | // more than 16 Mbyte requires 32 bit addresses |
| manitou | 0:e5c9fd5789d7 | 346 | f |= FLAG_32BIT_ADDR; |
| manitou | 0:e5c9fd5789d7 | 347 | if (id[0] == ID0_SPANSION) { |
| manitou | 0:e5c9fd5789d7 | 348 | // spansion uses MSB of bank register |
| manitou | 0:e5c9fd5789d7 | 349 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 350 | spi.write(0x17); // bank register write |
| manitou | 0:e5c9fd5789d7 | 351 | spi.write(0x80); |
| manitou | 0:e5c9fd5789d7 | 352 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 353 | } else { |
| manitou | 0:e5c9fd5789d7 | 354 | // micron & winbond & macronix use command |
| manitou | 0:e5c9fd5789d7 | 355 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 356 | spi.write(0x06); // write enable |
| manitou | 0:e5c9fd5789d7 | 357 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 358 | wait_us(1); |
| manitou | 0:e5c9fd5789d7 | 359 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 360 | spi.write(0xB7); // enter 4 byte addr mode |
| manitou | 0:e5c9fd5789d7 | 361 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 362 | } |
| manitou | 0:e5c9fd5789d7 | 363 | if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; |
| manitou | 0:e5c9fd5789d7 | 364 | } |
| manitou | 0:e5c9fd5789d7 | 365 | if (id[0] == ID0_SPANSION) { |
| manitou | 0:e5c9fd5789d7 | 366 | // Spansion has separate suspend commands |
| manitou | 0:e5c9fd5789d7 | 367 | f |= FLAG_DIFF_SUSPEND; |
| manitou | 0:e5c9fd5789d7 | 368 | if (!id[4]) { |
| manitou | 0:e5c9fd5789d7 | 369 | // Spansion chips with id[4] == 0 use 256K sectors |
| manitou | 0:e5c9fd5789d7 | 370 | f |= FLAG_256K_BLOCKS; |
| manitou | 0:e5c9fd5789d7 | 371 | } |
| manitou | 0:e5c9fd5789d7 | 372 | } |
| manitou | 0:e5c9fd5789d7 | 373 | if (id[0] == ID0_MICRON) { |
| manitou | 0:e5c9fd5789d7 | 374 | // Micron requires busy checks with a different command |
| manitou | 0:e5c9fd5789d7 | 375 | f |= FLAG_STATUS_CMD70; // TODO: all or just multi-die chips? |
| manitou | 0:e5c9fd5789d7 | 376 | } |
| manitou | 0:e5c9fd5789d7 | 377 | flags = f; |
| manitou | 0:e5c9fd5789d7 | 378 | readID(id); |
| manitou | 0:e5c9fd5789d7 | 379 | return true; |
| manitou | 0:e5c9fd5789d7 | 380 | } |
| manitou | 0:e5c9fd5789d7 | 381 | |
| manitou | 0:e5c9fd5789d7 | 382 | // chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992 |
| manitou | 0:e5c9fd5789d7 | 383 | // |
| manitou | 0:e5c9fd5789d7 | 384 | void SerialFlashChip::sleep() |
| manitou | 0:e5c9fd5789d7 | 385 | { |
| manitou | 0:e5c9fd5789d7 | 386 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 387 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 388 | spi.write(0xB9); // Deep power down command |
| manitou | 0:e5c9fd5789d7 | 389 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 390 | } |
| manitou | 0:e5c9fd5789d7 | 391 | |
| manitou | 0:e5c9fd5789d7 | 392 | void SerialFlashChip::wakeup() |
| manitou | 0:e5c9fd5789d7 | 393 | { |
| manitou | 0:e5c9fd5789d7 | 394 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 395 | spi.write(0xAB); // Wake up from deep power down command |
| manitou | 0:e5c9fd5789d7 | 396 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 397 | } |
| manitou | 0:e5c9fd5789d7 | 398 | |
| manitou | 0:e5c9fd5789d7 | 399 | void SerialFlashChip::readID(uint8_t *buf) |
| manitou | 0:e5c9fd5789d7 | 400 | { |
| manitou | 0:e5c9fd5789d7 | 401 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 402 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 403 | spi.write(0x9F); |
| manitou | 0:e5c9fd5789d7 | 404 | buf[0] = spi.write(0); // manufacturer ID |
| manitou | 0:e5c9fd5789d7 | 405 | buf[1] = spi.write(0); // memory type |
| manitou | 0:e5c9fd5789d7 | 406 | buf[2] = spi.write(0); // capacity |
| manitou | 0:e5c9fd5789d7 | 407 | if (buf[0] == ID0_SPANSION) { |
| manitou | 0:e5c9fd5789d7 | 408 | buf[3] = spi.write(0); // ID-CFI |
| manitou | 0:e5c9fd5789d7 | 409 | buf[4] = spi.write(0); // sector size |
| manitou | 0:e5c9fd5789d7 | 410 | } |
| manitou | 0:e5c9fd5789d7 | 411 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 412 | //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); |
| manitou | 0:e5c9fd5789d7 | 413 | } |
| manitou | 0:e5c9fd5789d7 | 414 | |
| manitou | 0:e5c9fd5789d7 | 415 | void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes |
| manitou | 0:e5c9fd5789d7 | 416 | { |
| manitou | 0:e5c9fd5789d7 | 417 | if (busy) wait(); |
| manitou | 0:e5c9fd5789d7 | 418 | CSASSERT(); |
| manitou | 0:e5c9fd5789d7 | 419 | spi.write(0x4B); |
| manitou | 0:e5c9fd5789d7 | 420 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 421 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 422 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 423 | spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 424 | for (int i=0; i<8; i++) { |
| manitou | 0:e5c9fd5789d7 | 425 | buf[i] = spi.write(0); |
| manitou | 0:e5c9fd5789d7 | 426 | } |
| manitou | 0:e5c9fd5789d7 | 427 | CSRELEASE(); |
| manitou | 0:e5c9fd5789d7 | 428 | // Serial.printf("Serial Number: %02X %02X %02X %02X %02X %02X %02X %02X\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); |
| manitou | 0:e5c9fd5789d7 | 429 | } |
| manitou | 0:e5c9fd5789d7 | 430 | |
| manitou | 0:e5c9fd5789d7 | 431 | uint32_t SerialFlashChip::capacity(const uint8_t *id) |
| manitou | 0:e5c9fd5789d7 | 432 | { |
| manitou | 0:e5c9fd5789d7 | 433 | uint32_t n = 1048576; // unknown chips, default to 1 MByte |
| manitou | 0:e5c9fd5789d7 | 434 | |
| manitou | 0:e5c9fd5789d7 | 435 | if (id[2] >= 16 && id[2] <= 31) { |
| manitou | 0:e5c9fd5789d7 | 436 | n = 1ul << id[2]; |
| manitou | 0:e5c9fd5789d7 | 437 | } else |
| manitou | 0:e5c9fd5789d7 | 438 | if (id[2] >= 32 && id[2] <= 37) { |
| manitou | 0:e5c9fd5789d7 | 439 | n = 1ul << (id[2] - 6); |
| manitou | 0:e5c9fd5789d7 | 440 | } |
| manitou | 0:e5c9fd5789d7 | 441 | //Serial.printf("capacity %lu\n", n); |
| manitou | 0:e5c9fd5789d7 | 442 | return n; |
| manitou | 0:e5c9fd5789d7 | 443 | } |
| manitou | 0:e5c9fd5789d7 | 444 | |
| manitou | 0:e5c9fd5789d7 | 445 | uint32_t SerialFlashChip::blockSize() |
| manitou | 0:e5c9fd5789d7 | 446 | { |
| manitou | 0:e5c9fd5789d7 | 447 | // Spansion chips >= 512 mbit use 256K sectors |
| manitou | 0:e5c9fd5789d7 | 448 | if (flags & FLAG_256K_BLOCKS) return 262144; |
| manitou | 0:e5c9fd5789d7 | 449 | // everything else seems to have 64K sectors |
| manitou | 0:e5c9fd5789d7 | 450 | return 65536; |
| manitou | 0:e5c9fd5789d7 | 451 | } |
| manitou | 0:e5c9fd5789d7 | 452 | |
| manitou | 0:e5c9fd5789d7 | 453 | |
| manitou | 0:e5c9fd5789d7 | 454 | |
| manitou | 0:e5c9fd5789d7 | 455 | |
| manitou | 0:e5c9fd5789d7 | 456 | /* |
| manitou | 0:e5c9fd5789d7 | 457 | Chip Uniform Sector Erase |
| manitou | 0:e5c9fd5789d7 | 458 | 20/21 52 D8/DC |
| manitou | 0:e5c9fd5789d7 | 459 | ----- -- ----- |
| manitou | 0:e5c9fd5789d7 | 460 | W25Q64CV 4 32 64 |
| manitou | 0:e5c9fd5789d7 | 461 | W25Q128FV 4 32 64 |
| manitou | 0:e5c9fd5789d7 | 462 | S25FL127S 64 |
| manitou | 0:e5c9fd5789d7 | 463 | N25Q512A 4 64 |
| manitou | 0:e5c9fd5789d7 | 464 | N25Q00AA 4 64 |
| manitou | 0:e5c9fd5789d7 | 465 | S25FL512S 256 |
| manitou | 0:e5c9fd5789d7 | 466 | SST26VF032 4 |
| manitou | 0:e5c9fd5789d7 | 467 | */ |
| manitou | 0:e5c9fd5789d7 | 468 | |
| manitou | 0:e5c9fd5789d7 | 469 | |
| manitou | 0:e5c9fd5789d7 | 470 | |
| manitou | 0:e5c9fd5789d7 | 471 | // size sector busy pgm/erase chip |
| manitou | 0:e5c9fd5789d7 | 472 | // Part Mbyte kbyte ID bytes cmd suspend erase |
| manitou | 0:e5c9fd5789d7 | 473 | // ---- ---- ----- -------- --- ------- ----- |
| lzbpli | 1:5f1a6c63fa57 | 474 | // Winbond W25Q64CV 8 64 EF 40 17 |
| manitou | 0:e5c9fd5789d7 | 475 | // Winbond W25Q128FV 16 64 EF 40 18 05 single 60 & C7 |
| manitou | 0:e5c9fd5789d7 | 476 | // Winbond W25Q256FV 32 64 EF 40 19 |
| manitou | 0:e5c9fd5789d7 | 477 | // Spansion S25FL064A 8 ? 01 02 16 |
| manitou | 0:e5c9fd5789d7 | 478 | // Spansion S25FL127S 16 64 01 20 18 05 |
| manitou | 0:e5c9fd5789d7 | 479 | // Spansion S25FL128P 16 64 01 20 18 |
| manitou | 0:e5c9fd5789d7 | 480 | // Spansion S25FL256S 32 64 01 02 19 05 60 & C7 |
| manitou | 0:e5c9fd5789d7 | 481 | // Spansion S25FL512S 64 256 01 02 20 |
| manitou | 0:e5c9fd5789d7 | 482 | // Macronix MX25L12805D 16 ? C2 20 18 |
| manitou | 0:e5c9fd5789d7 | 483 | // Macronix MX66L51235F 64 C2 20 1A |
| manitou | 0:e5c9fd5789d7 | 484 | // Numonyx M25P128 16 ? 20 20 18 |
| manitou | 0:e5c9fd5789d7 | 485 | // Micron M25P80 1 ? 20 20 14 |
| manitou | 0:e5c9fd5789d7 | 486 | // Micron N25Q128A 16 64 20 BA 18 |
| manitou | 0:e5c9fd5789d7 | 487 | // Micron N25Q512A 64 ? 20 BA 20 70 single C4 x2 |
| manitou | 0:e5c9fd5789d7 | 488 | // Micron N25Q00AA 128 64 20 BA 21 single C4 x4 |
| manitou | 0:e5c9fd5789d7 | 489 | // Micron MT25QL02GC 256 64 20 BA 22 70 C4 x2 |
| manitou | 0:e5c9fd5789d7 | 490 | // SST SST25WF010 1/8 ? BF 25 02 |
| manitou | 0:e5c9fd5789d7 | 491 | // SST SST25WF020 1/4 ? BF 25 03 |
| manitou | 0:e5c9fd5789d7 | 492 | // SST SST25WF040 1/2 ? BF 25 04 |
| manitou | 0:e5c9fd5789d7 | 493 | // SST SST25VF016B 1 ? BF 25 41 |
| manitou | 0:e5c9fd5789d7 | 494 | // SST26VF016 ? BF 26 01 |
| manitou | 0:e5c9fd5789d7 | 495 | // SST26VF032 ? BF 26 02 |
| manitou | 0:e5c9fd5789d7 | 496 | // SST25VF032 4 64 BF 25 4A |
| manitou | 0:e5c9fd5789d7 | 497 | // SST26VF064 8 ? BF 26 43 |
| manitou | 0:e5c9fd5789d7 | 498 | // LE25U40CMC 1/2 64 62 06 13 |
| manitou | 0:e5c9fd5789d7 | 499 | |
| manitou | 0:e5c9fd5789d7 | 500 | SerialFlashChip SerialFlash; |
