From 22c55aff29cc281b766f2ccded7149faa3660daf Mon Sep 17 00:00:00 2001 From: Yveaux <> Date: Sat, 30 Sep 2023 10:46:18 +0200 Subject: [PATCH 1/4] Fix using different SPI devices --- SerialFlashChip.cpp | 176 ++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/SerialFlashChip.cpp b/SerialFlashChip.cpp index a07618b..d92f98a 100644 --- a/SerialFlashChip.cpp +++ b/SerialFlashChip.cpp @@ -39,7 +39,7 @@ uint8_t SerialFlashChip::busy = 0; static volatile IO_REG_TYPE *cspin_basereg; static IO_REG_TYPE cspin_bitmask; -static SPIClass& SPIPORT = SPI; +static SPIClass* SPIPORT = &SPI; #define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address #define FLAG_STATUS_CMD70 0x02 // requires special busy flag check @@ -53,23 +53,23 @@ void SerialFlashChip::wait(void) uint32_t status; //Serial.print("wait-"); while (1) { - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); if (flags & FLAG_STATUS_CMD70) { // some Micron chips require this different // command to detect program and erase completion - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x70); + status = SPIPORT->transfer(0); CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); //Serial.printf("b=%02x.", status & 0xFF); if ((status & 0x80)) break; } else { // all others work by simply reading the status reg - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x05); + status = SPIPORT->transfer(0); CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); //Serial.printf("b=%02x.", status & 0xFF); if (!(status & 1)) break; } @@ -85,18 +85,18 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) memset(p, 0, len); f = flags; - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); b = busy; if (b) { // read status register ... chip may no longer be busy CSASSERT(); if (flags & FLAG_STATUS_CMD70) { - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x70); + status = SPIPORT->transfer(0); if ((status & 0x80)) b = 0; } else { - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x05); + status = SPIPORT->transfer(0); if (!(status & 1)) b = 0; } CSRELEASE(); @@ -108,37 +108,37 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) // which apparently have 2 different suspend // commands, for program vs erase CSASSERT(); - SPIPORT.transfer(0x06); // write enable (Micron req'd) + SPIPORT->transfer(0x06); // write enable (Micron req'd) CSRELEASE(); delayMicroseconds(1); cmd = 0x75; //Suspend program/erase for almost all chips // but Spansion just has to be different for program suspend! if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; CSASSERT(); - SPIPORT.transfer(cmd); // Suspend command + SPIPORT->transfer(cmd); // Suspend command CSRELEASE(); if (f & FLAG_STATUS_CMD70) { // Micron chips don't actually suspend until flags read CSASSERT(); - SPIPORT.transfer(0x70); + SPIPORT->transfer(0x70); do { - status = SPIPORT.transfer(0); + status = SPIPORT->transfer(0); } while (!(status & 0x80)); CSRELEASE(); } else { CSASSERT(); - SPIPORT.transfer(0x05); + SPIPORT->transfer(0x05); do { - status = SPIPORT.transfer(0); + status = SPIPORT->transfer(0); } while ((status & 0x01)); CSRELEASE(); } } else { // chip is busy with an operation that can not suspend - SPIPORT.endTransaction(); // is this a good idea? + SPIPORT->endTransaction(); // is this a good idea? wait(); // should we wait without ending b = 0; // the transaction?? - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); } } do { @@ -151,14 +151,14 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) CSASSERT(); // TODO: FIFO optimize.... if (f & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0x03); - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); + SPIPORT->transfer(0x03); + SPIPORT->transfer16(addr >> 16); + SPIPORT->transfer16(addr); } else { - SPIPORT.transfer16(0x0300 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); + SPIPORT->transfer16(0x0300 | ((addr >> 16) & 255)); + SPIPORT->transfer16(addr); } - SPIPORT.transfer(p, rdlen); + SPIPORT->transfer(p, rdlen); CSRELEASE(); p += rdlen; addr += rdlen; @@ -166,16 +166,16 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) } while (len > 0); if (b) { CSASSERT(); - SPIPORT.transfer(0x06); // write enable (Micron req'd) + SPIPORT->transfer(0x06); // write enable (Micron req'd) CSRELEASE(); delayMicroseconds(1); cmd = 0x7A; if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; CSASSERT(); - SPIPORT.transfer(cmd); // Resume program/erase + SPIPORT->transfer(cmd); // Resume program/erase CSRELEASE(); } - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); } void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) @@ -186,10 +186,10 @@ void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) //Serial.printf("WR: addr %08X, len %d\n", addr, len); do { if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); // write enable command - SPIPORT.transfer(0x06); + SPIPORT->transfer(0x06); CSRELEASE(); max = 256 - (addr & 0xFF); pagelen = (len <= max) ? len : max; @@ -197,21 +197,21 @@ void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) delayMicroseconds(1); // TODO: reduce this, but prefer safety first CSASSERT(); if (flags & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0x02); // program page command - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); + SPIPORT->transfer(0x02); // program page command + SPIPORT->transfer16(addr >> 16); + SPIPORT->transfer16(addr); } else { - SPIPORT.transfer16(0x0200 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); + SPIPORT->transfer16(0x0200 | ((addr >> 16) & 255)); + SPIPORT->transfer16(addr); } addr += pagelen; len -= pagelen; do { - SPIPORT.transfer(*p++); + SPIPORT->transfer(*p++); } while (--pagelen > 0); CSRELEASE(); busy = 4; - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); } while (len > 0); } @@ -234,32 +234,32 @@ void SerialFlashChip::eraseAll() if (die_index >= die_count) return; // all dies erased :-) uint8_t die_size = 2; // in 16 Mbyte units if (id[2] == 0x22) die_size = 8; - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0x06); // write enable command + SPIPORT->transfer(0x06); // write enable command CSRELEASE(); delayMicroseconds(1); CSASSERT(); // die erase command - SPIPORT.transfer(0xC4); - SPIPORT.transfer16((die_index * die_size) << 8); - SPIPORT.transfer16(0x0000); + SPIPORT->transfer(0xC4); + SPIPORT->transfer16((die_index * die_size) << 8); + SPIPORT->transfer16(0x0000); CSRELEASE(); //Serial.printf("Micron erase begin\n"); flags |= (die_index + 1) << 6; } else { // All other chips support the bulk erase command - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); // write enable command - SPIPORT.transfer(0x06); + SPIPORT->transfer(0x06); CSRELEASE(); delayMicroseconds(1); CSASSERT(); // bulk erase command - SPIPORT.transfer(0xC7); + SPIPORT->transfer(0xC7); CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); } busy = 3; } @@ -268,22 +268,22 @@ void SerialFlashChip::eraseBlock(uint32_t addr) { uint8_t f = flags; if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0x06); // write enable command + SPIPORT->transfer(0x06); // write enable command CSRELEASE(); delayMicroseconds(1); CSASSERT(); if (f & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0xD8); - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); + SPIPORT->transfer(0xD8); + SPIPORT->transfer16(addr >> 16); + SPIPORT->transfer16(addr); } else { - SPIPORT.transfer16(0xD800 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); + SPIPORT->transfer16(0xD800 | ((addr >> 16) & 255)); + SPIPORT->transfer16(addr); } CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); busy = 2; } @@ -292,23 +292,23 @@ bool SerialFlashChip::ready() { uint32_t status; if (!busy) return true; - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); if (flags & FLAG_STATUS_CMD70) { // some Micron chips require this different // command to detect program and erase completion - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x70); + status = SPIPORT->transfer(0); CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); //Serial.printf("ready=%02x\n", status & 0xFF); if ((status & 0x80) == 0) return false; } else { // all others work by simply reading the status reg - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); + SPIPORT->transfer(0x05); + status = SPIPORT->transfer(0); CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); //Serial.printf("ready=%02x\n", status & 0xFF); if ((status & 1)) return false; } @@ -336,7 +336,7 @@ bool SerialFlashChip::ready() bool SerialFlashChip::begin(SPIClass& device, uint8_t pin) { - SPIPORT = device; + SPIPORT = &device; return begin(pin); } @@ -348,7 +348,7 @@ bool SerialFlashChip::begin(uint8_t pin) cspin_basereg = PIN_TO_BASEREG(pin); cspin_bitmask = PIN_TO_BITMASK(pin); - SPIPORT.begin(); + SPIPORT->begin(); pinMode(pin, OUTPUT); CSRELEASE(); readID(id); @@ -360,23 +360,23 @@ bool SerialFlashChip::begin(uint8_t pin) if (size > 16777216) { // more than 16 Mbyte requires 32 bit addresses f |= FLAG_32BIT_ADDR; - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); if (id[0] == ID0_SPANSION) { // spansion uses MSB of bank register CSASSERT(); - SPIPORT.transfer16(0x1780); // bank register write + SPIPORT->transfer16(0x1780); // bank register write CSRELEASE(); } else { // micron & winbond & macronix use command CSASSERT(); - SPIPORT.transfer(0x06); // write enable + SPIPORT->transfer(0x06); // write enable CSRELEASE(); delayMicroseconds(1); CSASSERT(); - SPIPORT.transfer(0xB7); // enter 4 byte addr mode + SPIPORT->transfer(0xB7); // enter 4 byte addr mode CSRELEASE(); } - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; } if (id[0] == ID0_SPANSION) { @@ -401,51 +401,51 @@ bool SerialFlashChip::begin(uint8_t pin) void SerialFlashChip::sleep() { if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0xB9); // Deep power down command + SPIPORT->transfer(0xB9); // Deep power down command CSRELEASE(); } void SerialFlashChip::wakeup() { - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0xAB); // Wake up from deep power down command + SPIPORT->transfer(0xAB); // Wake up from deep power down command CSRELEASE(); } void SerialFlashChip::readID(uint8_t *buf) { if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0x9F); - buf[0] = SPIPORT.transfer(0); // manufacturer ID - buf[1] = SPIPORT.transfer(0); // memory type - buf[2] = SPIPORT.transfer(0); // capacity + SPIPORT->transfer(0x9F); + buf[0] = SPIPORT->transfer(0); // manufacturer ID + buf[1] = SPIPORT->transfer(0); // memory type + buf[2] = SPIPORT->transfer(0); // capacity if (buf[0] == ID0_SPANSION) { - buf[3] = SPIPORT.transfer(0); // ID-CFI - buf[4] = SPIPORT.transfer(0); // sector size + buf[3] = SPIPORT->transfer(0); // ID-CFI + buf[4] = SPIPORT->transfer(0); // sector size } CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); } void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes { if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); + SPIPORT->beginTransaction(SPICONFIG); CSASSERT(); - SPIPORT.transfer(0x4B); - SPIPORT.transfer16(0); - SPIPORT.transfer16(0); + SPIPORT->transfer(0x4B); + SPIPORT->transfer16(0); + SPIPORT->transfer16(0); for (int i=0; i<8; i++) { - buf[i] = SPIPORT.transfer(0); + buf[i] = SPIPORT->transfer(0); } CSRELEASE(); - SPIPORT.endTransaction(); + SPIPORT->endTransaction(); // 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]); } From e8ec0f8650420f2696a0845a9f5831a326f4e0f5 Mon Sep 17 00:00:00 2001 From: Yveaux Date: Sat, 30 Sep 2023 11:05:35 +0200 Subject: [PATCH 2/4] Allow changing SPI settings (e.g. clockspeed) at runtime --- SerialFlash.h | 1 + SerialFlashChip.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/SerialFlash.h b/SerialFlash.h index f51dc21..de56b25 100644 --- a/SerialFlash.h +++ b/SerialFlash.h @@ -38,6 +38,7 @@ class SerialFlashChip public: static bool begin(SPIClass& device, uint8_t pin = 6); static bool begin(uint8_t pin = 6); + static void changeSettings(SPISettings& settings); static uint32_t capacity(const uint8_t *id); static uint32_t blockSize(); static void sleep(); diff --git a/SerialFlashChip.cpp b/SerialFlashChip.cpp index d92f98a..3ce351f 100644 --- a/SerialFlashChip.cpp +++ b/SerialFlashChip.cpp @@ -30,7 +30,7 @@ #define CSASSERT() DIRECT_WRITE_LOW(cspin_basereg, cspin_bitmask) #define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) -#define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0) +static SPISettings SPICONFIG(50000000, MSBFIRST, SPI_MODE0); uint16_t SerialFlashChip::dirindex = 0; uint8_t SerialFlashChip::flags = 0; @@ -396,6 +396,11 @@ bool SerialFlashChip::begin(uint8_t pin) return true; } +void SerialFlashChip::changeSettings(SPISettings& settings) +{ + SPICONFIG = settings; +} + // chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992 // void SerialFlashChip::sleep() From 3fc6be8a61b2fac3417c34cfab47df4da041755e Mon Sep 17 00:00:00 2001 From: Yveaux Date: Sun, 1 Oct 2023 09:17:40 +0200 Subject: [PATCH 3/4] Added support for sector erase --- SerialFlash.h | 1 + SerialFlashChip.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/SerialFlash.h b/SerialFlash.h index de56b25..253693a 100644 --- a/SerialFlash.h +++ b/SerialFlash.h @@ -50,6 +50,7 @@ class SerialFlashChip static void wait(); static void write(uint32_t addr, const void *buf, uint32_t len); static void eraseAll(); + static void eraseSector(uint32_t addr); static void eraseBlock(uint32_t addr); static SerialFlashFile open(const char *filename); diff --git a/SerialFlashChip.cpp b/SerialFlashChip.cpp index 3ce351f..ac9224f 100644 --- a/SerialFlashChip.cpp +++ b/SerialFlashChip.cpp @@ -264,6 +264,29 @@ void SerialFlashChip::eraseAll() busy = 3; } +void SerialFlashChip::eraseSector(uint32_t addr) +{ + uint8_t f = flags; + if (busy) wait(); + SPIPORT->beginTransaction(SPICONFIG); + CSASSERT(); + SPIPORT->transfer(0x06); // write enable command + CSRELEASE(); + delayMicroseconds(1); + CSASSERT(); + if (f & FLAG_32BIT_ADDR) { + SPIPORT->transfer(0x20); + SPIPORT->transfer16(addr >> 16); + SPIPORT->transfer16(addr); + } else { + SPIPORT->transfer16(0x2000 | ((addr >> 16) & 255)); + SPIPORT->transfer16(addr); + } + CSRELEASE(); + SPIPORT->endTransaction(); + busy = 2; +} + void SerialFlashChip::eraseBlock(uint32_t addr) { uint8_t f = flags; From f72125642682f7265ef4d37e349696dccd7d38c7 Mon Sep 17 00:00:00 2001 From: Yveaux Date: Wed, 18 Oct 2023 16:56:15 +0200 Subject: [PATCH 4/4] Fix supporting multiple instances --- SerialFlash.h | 64 ++++++++++++++++++++++++++------------------- SerialFlashChip.cpp | 20 +++++++------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/SerialFlash.h b/SerialFlash.h index 253693a..070fc42 100644 --- a/SerialFlash.h +++ b/SerialFlash.h @@ -30,43 +30,53 @@ #include #include +#include "util/SerialFlash_directwrite.h" class SerialFlashFile; class SerialFlashChip { public: - static bool begin(SPIClass& device, uint8_t pin = 6); - static bool begin(uint8_t pin = 6); - static void changeSettings(SPISettings& settings); - static uint32_t capacity(const uint8_t *id); - static uint32_t blockSize(); - static void sleep(); - static void wakeup(); - static void readID(uint8_t *buf); - static void readSerialNumber(uint8_t *buf); - static void read(uint32_t addr, void *buf, uint32_t len); - static bool ready(); - static void wait(); - static void write(uint32_t addr, const void *buf, uint32_t len); - static void eraseAll(); - static void eraseSector(uint32_t addr); - static void eraseBlock(uint32_t addr); + SerialFlashChip(); + bool begin(SPIClass& device, uint8_t pin = 6); + bool begin(uint8_t pin = 6); + void changeSettings(SPISettings& settings); + uint32_t capacity(const uint8_t *id); + uint32_t blockSize(); + void sleep(); + void wakeup(); + void readID(uint8_t *buf); + void readSerialNumber(uint8_t *buf); + void read(uint32_t addr, void *buf, uint32_t len); + bool ready(); + void wait(); + void write(uint32_t addr, const void *buf, uint32_t len); + void eraseAll(); + void eraseSector(uint32_t addr); + void eraseBlock(uint32_t addr); - static SerialFlashFile open(const char *filename); - static bool create(const char *filename, uint32_t length, uint32_t align = 0); - static bool createErasable(const char *filename, uint32_t length) { + SerialFlashFile open(const char *filename); + bool create(const char *filename, uint32_t length, uint32_t align = 0); + bool createErasable(const char *filename, uint32_t length) { return create(filename, length, blockSize()); } - static bool exists(const char *filename); - static bool remove(const char *filename); - static bool remove(SerialFlashFile &file); - static void opendir() { dirindex = 0; } - static bool readdir(char *filename, uint32_t strsize, uint32_t &filesize); + bool exists(const char *filename); + bool remove(const char *filename); + bool remove(SerialFlashFile &file); + void opendir() { dirindex = 0; } + bool readdir(char *filename, uint32_t strsize, uint32_t &filesize); +protected: + SPISettings SPICONFIG; + + volatile IO_REG_TYPE *cspin_basereg; + IO_REG_TYPE cspin_bitmask; + + SPIClass* SPIPORT; + private: - static uint16_t dirindex; // current position for readdir() - static uint8_t flags; // chip features - static uint8_t busy; // 0 = ready + uint16_t dirindex; // current position for readdir() + uint8_t flags; // chip features + uint8_t busy; // 0 = ready // 1 = suspendable program operation // 2 = suspendable erase operation // 3 = busy for realz!! diff --git a/SerialFlashChip.cpp b/SerialFlashChip.cpp index ac9224f..d8fb178 100644 --- a/SerialFlashChip.cpp +++ b/SerialFlashChip.cpp @@ -26,20 +26,9 @@ */ #include "SerialFlash.h" -#include "util/SerialFlash_directwrite.h" #define CSASSERT() DIRECT_WRITE_LOW(cspin_basereg, cspin_bitmask) #define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) -static SPISettings SPICONFIG(50000000, MSBFIRST, SPI_MODE0); - -uint16_t SerialFlashChip::dirindex = 0; -uint8_t SerialFlashChip::flags = 0; -uint8_t SerialFlashChip::busy = 0; - -static volatile IO_REG_TYPE *cspin_basereg; -static IO_REG_TYPE cspin_bitmask; - -static SPIClass* SPIPORT = &SPI; #define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address #define FLAG_STATUS_CMD70 0x02 // requires special busy flag check @@ -357,6 +346,15 @@ bool SerialFlashChip::ready() //#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands //#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks +SerialFlashChip::SerialFlashChip() + : SPICONFIG(50000000, MSBFIRST, SPI_MODE0), + dirindex(0), + flags(0), + busy(0), + SPIPORT(&SPI) +{ +} + bool SerialFlashChip::begin(SPIClass& device, uint8_t pin) { SPIPORT = &device;