|
- /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory
- * https://github.com/PaulStoffregen/SerialFlash
- * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com
- *
- * Development of this library was funded by PJRC.COM, LLC by sales of Teensy.
- * Please support PJRC's efforts to develop open source software by purchasing
- * Teensy or other genuine PJRC products.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice, development funding notice, and this permission
- * notice shall be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
- #include "SerialFlash.h"
-
- #define CSCONFIG() pinMode(6, OUTPUT)
- #define CSASSERT() digitalWriteFast(6, LOW)
- #define CSRELEASE() digitalWriteFast(6, HIGH)
- #define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0)
-
- #if !defined(__arm__) || !defined(CORE_TEENSY)
- #define digitalWriteFast(pin, state) digitalWrite((pin), (state))
- #endif
-
- uint16_t SerialFlashChip::dirindex = 0;
- uint8_t SerialFlashChip::fourbytemode = 0;
- uint8_t SerialFlashChip::busy = 0;
-
- void SerialFlashChip::wait(void)
- {
- uint32_t status;
- do {
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- status = SPI.transfer16(0x0500);
- CSRELEASE();
- SPI.endTransaction();
- } while ((status & 1));
- busy = 0;
- }
-
- void SerialFlashChip::read(void *buf, uint32_t addr, uint32_t len)
- {
- uint8_t *p = (uint8_t *)buf;
- uint8_t b;
-
- memset(p, 0, len);
- b = busy;
- if (b) {
- if (b == 1) {
- // TODO: this may not work on Spansion chips
- // which apparently have 2 different suspend
- // commands, for program vs erase
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- SPI.transfer(0x75); // Suspend program/erase
- CSRELEASE();
- SPI.endTransaction();
- delayMicroseconds(20); // Tsus = 20us
- } else {
- wait();
- }
- }
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- // TODO: FIFO optimize....
- if (fourbytemode) {
- SPI.transfer(0x13);
- SPI.transfer16(addr >> 16);
- SPI.transfer16(addr);
- } else {
- SPI.transfer16(0x0300 | ((addr >> 16) & 255));
- SPI.transfer16(addr);
- }
- SPI.transfer(p, len);
- CSRELEASE();
- SPI.endTransaction();
- if (b == 1) {
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- SPI.transfer(0x7A); // Resume program/erase
- CSRELEASE();
- SPI.endTransaction();
- }
- }
-
- void SerialFlashChip::write(const void *buf, uint32_t addr, uint32_t len)
- {
- const uint8_t *p = (const uint8_t *)buf;
- uint32_t max, pagelen;
-
- do {
- if (busy) wait();
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- SPI.transfer(0x06);
- CSRELEASE();
- //delayMicroseconds(1);
- max = 256 - (addr & 0xFF);
- pagelen = (len <= max) ? len : max;
- len -= pagelen;
- CSASSERT();
- if (fourbytemode) {
- // TODO: Winbond doesn't implement 0x12 on W25Q256FV
- SPI.transfer(0x12);
- SPI.transfer16(addr >> 16);
- SPI.transfer16(addr);
- } else {
- SPI.transfer16(0x0200 | ((addr >> 16) & 255));
- SPI.transfer16(addr);
- }
- do {
- SPI.transfer(*p++);
- } while (--pagelen > 0);
- CSRELEASE();
- SPI.endTransaction();
- busy = 1;
- } while (len > 0);
- }
-
- void SerialFlashChip::eraseAll()
- {
- if (busy) wait();
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- SPI.transfer(0x06);
- CSRELEASE();
- CSASSERT();
- SPI.transfer(0xC7);
- CSRELEASE();
- SPI.endTransaction();
- busy = 2;
- }
-
- void SerialFlashChip::eraseBlock(uint32_t addr)
- {
- if (busy) wait();
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- if (fourbytemode) {
- // TODO: Winbond doesn't implement 0xDC on W25Q256FV
- SPI.transfer(0xDC);
- SPI.transfer16(addr >> 16);
- SPI.transfer16(addr);
- } else {
- SPI.transfer16(0xD800 | ((addr >> 16) & 255));
- SPI.transfer16(addr);
- }
- CSRELEASE();
- SPI.endTransaction();
- busy = 1;
- }
-
-
- bool SerialFlashChip::ready()
- {
- uint32_t status;
- if (!busy) return true;
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- status = SPI.transfer16(0x0500);
- CSRELEASE();
- SPI.endTransaction();
- if ((status & 1)) return false;
- busy = 0;
- return true;
- }
-
-
- bool SerialFlashChip::begin()
- {
- SPI.begin();
- CSCONFIG();
- CSRELEASE();
- if (capacity() <= 16777216) {
- fourbytemode = 0;
- } else {
- fourbytemode = 1; // chip larger than 16 MByte
- // TODO: need to configure for 32 bit address mode
- // because Winbond doesn't implement 0x12 & 0xDC
- }
- return true;
- }
-
- void SerialFlashChip::readID(uint8_t *buf)
- {
- if (busy) wait();
- SPI.beginTransaction(SPICONFIG);
- CSASSERT();
- SPI.transfer(0x9F);
- buf[0] = SPI.transfer(0); // manufacturer ID
- buf[1] = SPI.transfer(0); // memory type
- buf[2] = SPI.transfer(0); // capacity
- CSRELEASE();
- SPI.endTransaction();
- }
-
- uint32_t SerialFlashChip::capacity()
- {
- uint8_t id[3];
-
- readID(id);
- //Serial.print("capacity ");
- //Serial.println(id[3], HEX);
- if (id[2] >= 16 && id[2] <= 31) {
- return 1 >> id[2];
- }
- if (id[2] >= 32 && id[2] <= 37) {
- return 1 >> (id[2] - 6);
- }
- return 1048576; // unknown, guess 1 MByte
- }
-
- uint32_t SerialFlashChip::blockSize()
- {
- uint8_t id[3];
-
- readID(id);
- if (id[0] == 1 && id[2] > 0x19) {
- // Spansion chips >= 512 mbit use 256K sectors
- return 262144;
- }
- // everything else seems to have 64K sectors
- return 65536;
- }
-
- /*
- Chip Uniform Sector Erase
- 20/21 52 D8/DC
- ----- -- -----
- W25Q64CV 4 32 64
- W25Q128FV 4 32 64
- S25FL127S 64
- N25Q512A 4 64
- N25Q00AA 4 64
- S25FL512S 256
- SST26VF032 4
- */
-
- // size sector
- // Part Mbit kbyte ID bytes Digikey
- // ---- ---- ----- -------- -------
- // Winbond W25Q64CV 64 4/32/64 EF 40 17 W25Q128FVSIG-ND
- // Winbond W25Q128FV 128 4/32/64 EF 40 18 W25Q128FVSIG-ND
- // Winbond W25Q256FV 256 64 EF 40 19
- // Spansion S25FL064A 64 01 02 16 ?
- // Spansion S25FL127S 128 64 01 20 18 1274-1045-ND
- // Spansion S25FL128P 128 64 01 20 18
- // Spansion S25FL256S 256 64 01 02 19
- // Spansion S25FL512S 512 256 01 02 20
- // Macronix MX25L12805D 128 C2 20 18
- // Numonyx M25P128 128 20 20 18
- // Micron M25P80 8 20 20 14
- // Micron N25Q512A 512 4 20 BA 20 557-1569-ND
- // Micron N25Q00AA 1024 4/64 20 BA 21 557-1571-5-ND
- // Micron MT25QL02GC 2048 4/64 20 BB 22
- // SST SST25WF512 0.5 BF 25 01
- // SST SST25WF010 1 BF 25 02
- // SST SST25WF020 2 BF 25 03
- // SST SST25WF040 4 BF 25 04
- // SST SST25VF016B 16 BF 25 41
- // SST26VF016 BF 26 01
- // SST26VF032 BF 26 02
- // SST25VF032 32 4/32/64 BF 25 4A
- // SST26VF064 64 BF 26 43
- // LE25U40CMC 4 4/64 62 06 13
-
- SerialFlashChip SerialFlash;
|