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.
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(D11,D12,D13); // mosi, miso, sclk 00031 DigitalOut cspin(D6); 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 Tue Jul 12 2022 20:36:33 by
