++
Fork of SerialFlash by
Embed:
(wiki syntax)
Show/hide line numbers
SerialFlash.cpp
00001 /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory 00002 * https://github.com/PaulStoffregen/SerialFlash 00003 * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com 00004 * 00005 * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. 00006 * Please support PJRC's efforts to develop open source software by purchasing 00007 * Teensy or other genuine PJRC products. 00008 * 00009 * Permission is hereby granted, free of charge, to any person obtaining a copy 00010 * of this software and associated documentation files (the "Software"), to deal 00011 * in the Software without restriction, including without limitation the rights 00012 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00013 * copies of the Software, and to permit persons to whom the Software is 00014 * furnished to do so, subject to the following conditions: 00015 * 00016 * The above copyright notice, development funding notice, and this permission 00017 * notice shall be included in all copies or substantial portions of the Software. 00018 * 00019 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00020 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00021 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00022 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00023 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00024 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00025 * THE SOFTWARE. 00026 */ 00027 #include "mbed.h" 00028 #include "SerialFlash.h" 00029 00030 static SPI spi(PB_15,PB_14,PB_13); // mosi, miso, sclk 00031 DigitalOut cspin(PB_12); 00032 00033 #define CSASSERT() cspin = 0 00034 #define CSRELEASE() cspin = 1 00035 00036 uint16_t SerialFlashChip::dirindex = 0; 00037 uint8_t SerialFlashChip::flags = 0; 00038 uint8_t SerialFlashChip::busy = 0; 00039 00040 00041 #define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address 00042 #define FLAG_STATUS_CMD70 0x02 // requires special busy flag check 00043 #define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands 00044 #define FLAG_MULTI_DIE 0x08 // multiple die, don't read cross 32M barrier 00045 #define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks 00046 #define FLAG_DIE_MASK 0xC0 // top 2 bits count during multi-die erase 00047 00048 void SerialFlashChip::wait(void) 00049 { 00050 uint32_t status; 00051 //Serial.print("wait-"); 00052 while (1) { 00053 CSASSERT(); 00054 if (flags & FLAG_STATUS_CMD70) { 00055 // some Micron chips require this different 00056 // command to detect program and erase completion 00057 spi.write(0x70); 00058 status = spi.write(0); 00059 CSRELEASE(); 00060 //Serial.printf("b=%02x.", status & 0xFF); 00061 if ((status & 0x80)) break; 00062 } else { 00063 // all others work by simply reading the status reg 00064 spi.write(0x05); 00065 status = spi.write(0); 00066 CSRELEASE(); 00067 //Serial.printf("b=%02x.", status & 0xFF); 00068 if (!(status & 1)) break; 00069 } 00070 } 00071 busy = 0; 00072 //Serial.println(); 00073 } 00074 00075 void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) 00076 { 00077 uint8_t *p = (uint8_t *)buf; 00078 uint8_t b, f, status, cmd; 00079 00080 memset(p, 0, len); 00081 f = flags; 00082 b = busy; 00083 if (b) { 00084 // read status register ... chip may no longer be busy 00085 CSASSERT(); 00086 if (flags & FLAG_STATUS_CMD70) { 00087 spi.write(0x70); 00088 status = spi.write(0); 00089 if ((status & 0x80)) b = 0; 00090 } else { 00091 spi.write(0x05); 00092 status = spi.write(0); 00093 if (!(status & 1)) b = 0; 00094 } 00095 CSRELEASE(); 00096 if (b == 0) { 00097 // chip is no longer busy :-) 00098 busy = 0; 00099 } else if (b < 3) { 00100 // TODO: this may not work on Spansion chips 00101 // which apparently have 2 different suspend 00102 // commands, for program vs erase 00103 CSASSERT(); 00104 spi.write(0x06); // write enable (Micron req'd) 00105 CSRELEASE(); 00106 wait_us(1); 00107 cmd = 0x75; //Suspend program/erase for almost all chips 00108 // but Spansion just has to be different for program suspend! 00109 if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; 00110 CSASSERT(); 00111 spi.write(cmd); // Suspend command 00112 CSRELEASE(); 00113 if (f & FLAG_STATUS_CMD70) { 00114 // Micron chips don't actually suspend until flags read 00115 CSASSERT(); 00116 spi.write(0x70); 00117 do { 00118 status = spi.write(0); 00119 } while (!(status & 0x80)); 00120 CSRELEASE(); 00121 } else { 00122 CSASSERT(); 00123 spi.write(0x05); 00124 do { 00125 status = spi.write(0); 00126 } while ((status & 0x01)); 00127 CSRELEASE(); 00128 } 00129 } else { 00130 // chip is busy with an operation that can not suspend 00131 wait(); // should we wait without ending 00132 b = 0; // the transaction?? 00133 } 00134 } 00135 do { 00136 uint32_t rdlen = len; 00137 if (f & FLAG_MULTI_DIE) { 00138 if ((addr & 0xFE000000) != ((addr + len - 1) & 0xFE000000)) { 00139 rdlen = 0x2000000 - (addr & 0x1FFFFFF); 00140 } 00141 } 00142 CSASSERT(); 00143 // TODO: FIFO optimize.... 00144 if (f & FLAG_32BIT_ADDR) { 00145 spi.write(0x03); 00146 spi.write(addr >> 24); 00147 spi.write(addr >> 16); 00148 spi.write(addr >> 8); 00149 spi.write(addr); 00150 } else { 00151 spi.write(3); 00152 spi.write(addr >> 16); 00153 spi.write(addr >> 8); 00154 spi.write(addr); 00155 } 00156 uint32_t i = rdlen; // need block transfer 00157 while(i>0){ 00158 *p++ = spi.write(0); 00159 i--; 00160 } 00161 CSRELEASE(); 00162 addr += rdlen; 00163 len -= rdlen; 00164 } while (len > 0); 00165 if (b) { 00166 CSASSERT(); 00167 spi.write(0x06); // write enable (Micron req'd) 00168 CSRELEASE(); 00169 wait_us(1); 00170 cmd = 0x7A; 00171 if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; 00172 CSASSERT(); 00173 spi.write(cmd); // Resume program/erase 00174 CSRELEASE(); 00175 } 00176 } 00177 00178 void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) 00179 { 00180 const uint8_t *p = (const uint8_t *)buf; 00181 uint32_t max, pagelen; 00182 00183 //Serial.printf("WR: addr %08X, len %d\n", addr, len); 00184 do { 00185 if (busy) wait(); 00186 CSASSERT(); 00187 // write enable command 00188 spi.write(0x06); 00189 CSRELEASE(); 00190 max = 256 - (addr & 0xFF); 00191 pagelen = (len <= max) ? len : max; 00192 //Serial.printf("WR: addr %08X, pagelen %d\n", addr, pagelen); 00193 wait_us(1); // TODO: reduce this, but prefer safety first 00194 CSASSERT(); 00195 if (flags & FLAG_32BIT_ADDR) { 00196 spi.write(0x02); // program page command 00197 spi.write(addr >> 24); 00198 spi.write(addr >> 16); 00199 spi.write(addr >> 8); 00200 spi.write(addr); 00201 } else { 00202 spi.write(2); 00203 spi.write(addr >> 16); 00204 spi.write(addr >> 8); 00205 spi.write(addr); 00206 } 00207 addr += pagelen; 00208 len -= pagelen; 00209 do { 00210 spi.write(*p++); 00211 } while (--pagelen > 0); 00212 CSRELEASE(); 00213 busy = 4; 00214 } while (len > 0); 00215 } 00216 00217 void SerialFlashChip::eraseAll() 00218 { 00219 if (busy) wait(); 00220 uint8_t id[5]; 00221 readID(id); 00222 //Serial.printf("ID: %02X %02X %02X\n", id[0], id[1], id[2]); 00223 if (id[0] == 0x20 && id[2] >= 0x20 && id[2] <= 0x22) { 00224 // Micron's multi-die chips require special die erase commands 00225 // N25Q512A 20 BA 20 2 dies 32 Mbyte/die 65 nm transitors 00226 // N25Q00AA 20 BA 21 4 dies 32 Mbyte/die 65 nm transitors 00227 // MT25QL02GC 20 BA 22 2 dies 128 Mbyte/die 45 nm transitors 00228 uint8_t die_count = 2; 00229 if (id[2] == 0x21) die_count = 4; 00230 uint8_t die_index = flags >> 6; 00231 //Serial.printf("Micron die erase %d\n", die_index); 00232 flags &= 0x3F; 00233 if (die_index >= die_count) return; // all dies erased :-) 00234 uint8_t die_size = 2; // in 16 Mbyte units 00235 if (id[2] == 0x22) die_size = 8; 00236 CSASSERT(); 00237 spi.write(0x06); // write enable command 00238 CSRELEASE(); 00239 wait_us(1); 00240 CSASSERT(); 00241 // die erase command 00242 spi.write(0xC4); 00243 spi.write((die_index * die_size) ); 00244 spi.write(0); 00245 spi.write(0); 00246 spi.write(0); 00247 CSRELEASE(); 00248 //Serial.printf("Micron erase begin\n"); 00249 flags |= (die_index + 1) << 6; 00250 } else { 00251 // All other chips support the bulk erase command 00252 CSASSERT(); 00253 // write enable command 00254 spi.write(0x06); 00255 CSRELEASE(); 00256 wait_us(1); 00257 CSASSERT(); 00258 // bulk erase command 00259 spi.write(0xC7); 00260 CSRELEASE(); 00261 } 00262 busy = 3; 00263 } 00264 00265 void SerialFlashChip::eraseBlock(uint32_t addr) 00266 { 00267 uint8_t f = flags; 00268 if (busy) wait(); 00269 CSASSERT(); 00270 spi.write(0x06); // write enable command 00271 CSRELEASE(); 00272 wait_us(1); 00273 CSASSERT(); 00274 if (f & FLAG_32BIT_ADDR) { 00275 spi.write(0xD8); 00276 spi.write(addr >> 24); 00277 spi.write(addr >> 16); 00278 spi.write(addr >> 8); 00279 spi.write(addr); 00280 } else { 00281 spi.write(0xD8); 00282 spi.write(addr >> 16); 00283 spi.write(addr >> 8); 00284 spi.write(addr); 00285 } 00286 CSRELEASE(); 00287 busy = 2; 00288 } 00289 00290 00291 bool SerialFlashChip::ready() 00292 { 00293 uint32_t status; 00294 if (!busy) return true; 00295 CSASSERT(); 00296 if (flags & FLAG_STATUS_CMD70) { 00297 // some Micron chips require this different 00298 // command to detect program and erase completion 00299 spi.write(0x70); 00300 status = spi.write(0); 00301 CSRELEASE(); 00302 //Serial.printf("ready=%02x\n", status & 0xFF); 00303 if ((status & 0x80) == 0) return false; 00304 } else { 00305 // all others work by simply reading the status reg 00306 spi.write(0x05); 00307 status = spi.write(0); 00308 CSRELEASE(); 00309 //Serial.printf("ready=%02x\n", status & 0xFF); 00310 if ((status & 1)) return false; 00311 } 00312 busy = 0; 00313 if (flags & 0xC0) { 00314 // continue a multi-die erase 00315 eraseAll(); 00316 return false; 00317 } 00318 return true; 00319 } 00320 00321 00322 #define ID0_WINBOND 0xEF 00323 #define ID0_SPANSION 0x01 00324 #define ID0_MICRON 0x20 00325 #define ID0_MACRONIX 0xC2 00326 #define ID0_SST 0xBF 00327 00328 //#define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address 00329 //#define FLAG_STATUS_CMD70 0x02 // requires special busy flag check 00330 //#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands 00331 //#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks 00332 00333 bool SerialFlashChip::begin(uint8_t pin) 00334 { 00335 uint8_t id[5]; 00336 uint8_t f; 00337 uint32_t size; 00338 00339 spi.frequency(30000000); // max 00340 CSRELEASE(); 00341 readID(id); 00342 f = 0; 00343 size = capacity(id); 00344 if (size > 16777216) { 00345 // more than 16 Mbyte requires 32 bit addresses 00346 f |= FLAG_32BIT_ADDR; 00347 if (id[0] == ID0_SPANSION) { 00348 // spansion uses MSB of bank register 00349 CSASSERT(); 00350 spi.write(0x17); // bank register write 00351 spi.write(0x80); 00352 CSRELEASE(); 00353 } else { 00354 // micron & winbond & macronix use command 00355 CSASSERT(); 00356 spi.write(0x06); // write enable 00357 CSRELEASE(); 00358 wait_us(1); 00359 CSASSERT(); 00360 spi.write(0xB7); // enter 4 byte addr mode 00361 CSRELEASE(); 00362 } 00363 if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; 00364 } 00365 if (id[0] == ID0_SPANSION) { 00366 // Spansion has separate suspend commands 00367 f |= FLAG_DIFF_SUSPEND; 00368 if (!id[4]) { 00369 // Spansion chips with id[4] == 0 use 256K sectors 00370 f |= FLAG_256K_BLOCKS; 00371 } 00372 } 00373 if (id[0] == ID0_MICRON) { 00374 // Micron requires busy checks with a different command 00375 f |= FLAG_STATUS_CMD70; // TODO: all or just multi-die chips? 00376 } 00377 flags = f; 00378 readID(id); 00379 return true; 00380 } 00381 00382 // chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992 00383 // 00384 void SerialFlashChip::sleep() 00385 { 00386 if (busy) wait(); 00387 CSASSERT(); 00388 spi.write(0xB9); // Deep power down command 00389 CSRELEASE(); 00390 } 00391 00392 void SerialFlashChip::wakeup() 00393 { 00394 CSASSERT(); 00395 spi.write(0xAB); // Wake up from deep power down command 00396 CSRELEASE(); 00397 } 00398 00399 void SerialFlashChip::readID(uint8_t *buf) 00400 { 00401 if (busy) wait(); 00402 CSASSERT(); 00403 spi.write(0x9F); 00404 buf[0] = spi.write(0); // manufacturer ID 00405 buf[1] = spi.write(0); // memory type 00406 buf[2] = spi.write(0); // capacity 00407 if (buf[0] == ID0_SPANSION) { 00408 buf[3] = spi.write(0); // ID-CFI 00409 buf[4] = spi.write(0); // sector size 00410 } 00411 CSRELEASE(); 00412 //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); 00413 } 00414 00415 void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes 00416 { 00417 if (busy) wait(); 00418 CSASSERT(); 00419 spi.write(0x4B); 00420 spi.write(0); 00421 spi.write(0); 00422 spi.write(0); 00423 spi.write(0); 00424 for (int i=0; i<8; i++) { 00425 buf[i] = spi.write(0); 00426 } 00427 CSRELEASE(); 00428 // 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]); 00429 } 00430 00431 uint32_t SerialFlashChip::capacity(const uint8_t *id) 00432 { 00433 uint32_t n = 1048576; // unknown chips, default to 1 MByte 00434 00435 if (id[2] >= 16 && id[2] <= 31) { 00436 n = 1ul << id[2]; 00437 } else 00438 if (id[2] >= 32 && id[2] <= 37) { 00439 n = 1ul << (id[2] - 6); 00440 } 00441 //Serial.printf("capacity %lu\n", n); 00442 return n; 00443 } 00444 00445 uint32_t SerialFlashChip::blockSize() 00446 { 00447 // Spansion chips >= 512 mbit use 256K sectors 00448 if (flags & FLAG_256K_BLOCKS) return 262144; 00449 // everything else seems to have 64K sectors 00450 return 65536; 00451 } 00452 00453 00454 00455 00456 /* 00457 Chip Uniform Sector Erase 00458 20/21 52 D8/DC 00459 ----- -- ----- 00460 W25Q64CV 4 32 64 00461 W25Q128FV 4 32 64 00462 S25FL127S 64 00463 N25Q512A 4 64 00464 N25Q00AA 4 64 00465 S25FL512S 256 00466 SST26VF032 4 00467 */ 00468 00469 00470 00471 // size sector busy pgm/erase chip 00472 // Part Mbyte kbyte ID bytes cmd suspend erase 00473 // ---- ---- ----- -------- --- ------- ----- 00474 // Winbond W25Q64CV 8 64 EF 40 17 00475 // Winbond W25Q128FV 16 64 EF 40 18 05 single 60 & C7 00476 // Winbond W25Q256FV 32 64 EF 40 19 00477 // Spansion S25FL064A 8 ? 01 02 16 00478 // Spansion S25FL127S 16 64 01 20 18 05 00479 // Spansion S25FL128P 16 64 01 20 18 00480 // Spansion S25FL256S 32 64 01 02 19 05 60 & C7 00481 // Spansion S25FL512S 64 256 01 02 20 00482 // Macronix MX25L12805D 16 ? C2 20 18 00483 // Macronix MX66L51235F 64 C2 20 1A 00484 // Numonyx M25P128 16 ? 20 20 18 00485 // Micron M25P80 1 ? 20 20 14 00486 // Micron N25Q128A 16 64 20 BA 18 00487 // Micron N25Q512A 64 ? 20 BA 20 70 single C4 x2 00488 // Micron N25Q00AA 128 64 20 BA 21 single C4 x4 00489 // Micron MT25QL02GC 256 64 20 BA 22 70 C4 x2 00490 // SST SST25WF010 1/8 ? BF 25 02 00491 // SST SST25WF020 1/4 ? BF 25 03 00492 // SST SST25WF040 1/2 ? BF 25 04 00493 // SST SST25VF016B 1 ? BF 25 41 00494 // SST26VF016 ? BF 26 01 00495 // SST26VF032 ? BF 26 02 00496 // SST25VF032 4 64 BF 25 4A 00497 // SST26VF064 8 ? BF 26 43 00498 // LE25U40CMC 1/2 64 62 06 13 00499 00500 SerialFlashChip SerialFlash;
Generated on Sun Jul 17 2022 03:51:41 by 1.7.2