diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 13f7e32..52d64e8 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,6 @@ -![GitHub issue state](https://img.shields.io/github/issues/detail/s/Marzogh/SPIFlash/14.svg) ![GitHub issue title](https://img.shields.io/github/issues/detail/title/Marzogh/SPIFlash/14.svg) ![GitHub issue author](https://img.shields.io/github/issues/detail/u/Marzogh/SPIFlash/14.svg) ![GitHub issue label](https://img.shields.io/github/issues/detail/label/Marzogh/SPIFlash/14.svg) ![GitHub issue comments](https://img.shields.io/github/issues/detail/comments/Marzogh/SPIFlash/14.svg) ![GitHub issue age](https://img.shields.io/github/issues/detail/age/Marzogh/SPIFlash/14.svg) ![GitHub issue last update](https://img.shields.io/github/issues/detail/last-update/Marzogh/SPIFlash/14.svg) +![GitHub issue state](https://img.shields.io/github/issues/detail/s/Marzogh/SPIFlash/14.svg) ![GitHub issue title](https://img.shields.io/github/issues/detail/title/Marzogh/SPIFlash/14.svg) +![GitHub issue author](https://img.shields.io/github/issues/detail/u/Marzogh/SPIFlash/14.svg) ![GitHub issue label](https://img.shields.io/github/issues/detail/label/Marzogh/SPIFlash/14.svg) ![GitHub issue comments](https://img.shields.io/github/issues/detail/comments/Marzogh/SPIFlash/14.svg) +![GitHub issue age](https://img.shields.io/github/issues/detail/age/Marzogh/SPIFlash/14.svg) ![GitHub issue last update](https://img.shields.io/github/issues/detail/last-update/Marzogh/SPIFlash/14.svg) ###### ---------------------------- **DO NOT DELETE OR EDIT** anything above this line ---------------------------- ###### ---------------------------- **DO NOT DELETE OR EDIT** anything above this line ---------------------------- diff --git a/README.md b/README.md index e9c4035..bfe4211 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,9 @@ SPIFlash flash(33); * Optional +##### Note on SFDP discovery +As of v3.2.1, SFDP parameter discovery is an user controlled option. To get the library to work with SFDP compatible flash memory chips that are not officially supported by the library, the user must uncomment '//#define USES_SFDP' in 'SPIMemory.h'. + ##### Notes on Address overflow and Error checking - The library has Address overflow enabled by default - i.e. if the last address read/written from/to, in any function, is 0xFFFFF then, the next address read/written from/to is 0x00000. This can be disabled by uncommenting ```#define DISABLEOVERFLOW``` in SPIMemory.h. (Address overflow only works for Read / Write functions. Erase functions erase only a set number of blocks/sectors irrespective of overflow.) - All write functions have Error checking turned on by default - i.e. every byte written to the flash memory will be checked against the data stored on the Arduino. Users who require greater write speeds can disable this function by setting an optional last 'errorCheck' argument in any write function to NOERRCHK - For eg. call the function ```writeByte(address, *data_buffer, NOERRCHK)``` instead of ```writeByte(address, *data_buffer)```. diff --git a/examples/FlashDiagnostics/Diagnostics_functions.ino b/examples/FlashDiagnostics/Diagnostics_functions.ino index 10f17f0..00b4b1d 100644 --- a/examples/FlashDiagnostics/Diagnostics_functions.ino +++ b/examples/FlashDiagnostics/Diagnostics_functions.ino @@ -29,7 +29,7 @@ void printTab(uint8_t _times) { } void printTime(uint32_t _wTime, uint32_t _rTime) { - printTab(2); + printTab(2); if (_rTime != 0) { printTimer(_wTime); printTab(3); @@ -118,7 +118,7 @@ bool getID() { Serial.print("Max Pages: "); Serial.println(flash.getMaxPage()); printUniqueID(); - + } return true; } @@ -150,7 +150,7 @@ void byteTest() { pass(FALSE); } printTime(wTime, rTime); - + } void charTest() { @@ -179,7 +179,7 @@ void charTest() { pass(FALSE); } printTime(wTime, rTime); - + } void wordTest() { @@ -208,7 +208,7 @@ void wordTest() { pass(FALSE); } printTime(wTime, rTime); - + } void shortTest() { @@ -237,7 +237,7 @@ void shortTest() { pass(FALSE); } printTime(wTime, rTime); - + } void uLongTest() { @@ -266,7 +266,7 @@ void uLongTest() { pass(FALSE); } printTime(wTime, rTime); - + } void longTest() { @@ -295,7 +295,7 @@ void longTest() { pass(FALSE); } printTime(wTime, rTime); - + } void floatTest() { @@ -324,7 +324,7 @@ void floatTest() { pass(FALSE); } printTime(wTime, rTime); - + } void stringTest() { @@ -353,7 +353,7 @@ void stringTest() { pass(FALSE); } printTime(wTime, rTime); - + } void structTest() { @@ -402,7 +402,7 @@ void structTest() { pass(FALSE); } printTime(wTime, rTime); - + } void arrayTest() { @@ -430,11 +430,13 @@ void arrayTest() { for (uint16_t i = 0; i < 256; i++) { if (_data[i] != i) { pass(FALSE); + Serial.print(i); + Serial.print(", "); } } pass(TRUE); printTime(wTime, rTime); - + } void powerDownTest() { @@ -452,7 +454,7 @@ void powerDownTest() { printTab(2); Serial.print("Not all chips support power down. Check your datasheet."); } - + } void powerUpTest() { @@ -468,7 +470,7 @@ void powerUpTest() { else { pass(FALSE); } - + } void eraseSectorTest() { @@ -485,7 +487,7 @@ void eraseSectorTest() { else { pass(FALSE); } - + } void eraseSectionTest() { @@ -502,7 +504,7 @@ void eraseSectionTest() { else { pass(FALSE); } - + } void eraseBlock32KTest() { @@ -519,7 +521,7 @@ void eraseBlock32KTest() { else { pass(FALSE); } - + } void eraseBlock64KTest() { @@ -536,7 +538,7 @@ void eraseBlock64KTest() { else { pass(FALSE); } - + } void eraseChipTest() { @@ -552,6 +554,6 @@ void eraseChipTest() { else { pass(FALSE); } - + } diff --git a/examples/TestFlash/TestFlash.ino b/examples/TestFlash/TestFlash.ino index 36edf1d..1914216 100644 --- a/examples/TestFlash/TestFlash.ino +++ b/examples/TestFlash/TestFlash.ino @@ -87,30 +87,37 @@ String inputString, outputString; #if defined (SIMBLEE) #define BAUD_RATE 250000 +#define RANDPIN 1 #else #define BAUD_RATE 115200 +#if defined(ARCH_STM32) +#define RANDPIN PA0 +#else +#define RANDPIN A0 +#endif #endif -SPIFlash flash(11); +SPIFlash flash; //SPIFlash flash(SS1, &SPI1); //Use this constructor if using an SPI bus other than the default SPI. Only works with chips with more than one hardware SPI bus void setup() { + Serial.begin(BAUD_RATE); - #if defined (ARDUINO_ARCH_SAMD) || (__AVR_ATmega32U4__) || defined(ARCH_STM32) - while (!Serial) ; // Wait for Serial monitor to open - #endif +#if defined (ARDUINO_ARCH_SAMD) || (__AVR_ATmega32U4__) || defined(ARCH_STM32) || defined(NRF5) + while (!Serial) ; // Wait for Serial monitor to open +#endif delay(50); //Time to terminal get connected - Serial.print(F("Initialising Flash memory")); - for (int i = 0; i < 10; ++i) + Serial.print(F("Initialising")); + for (uint8_t i = 0; i < 10; ++i) { Serial.print(F(".")); } Serial.println(); -#if defined (CHIPSIZE) - flash.begin(CHIPSIZE); //use flash.begin(CHIPSIZE) if using non-Winbond flash (Refer to '#define CHIPSIZE' above) -#else + randomSeed(analogRead(RANDPIN)); flash.begin(); -#endif + //To use a custom flash memory size (if using memory from manufacturers not officially supported by the library) - declare a size variable according to the list in defines.h + //flash.begin(MB(1)); + Serial.println(); Serial.println(); commandList(); @@ -503,7 +510,7 @@ void printPage(uint32_t _address, uint8_t outputType) { Serial.begin(115200); char buffer[24]; - sprintf(buffer, "Reading address (%04x)", _address); + sprintf(buffer, "Reading address 0x(%04x)", _address); Serial.println(buffer); uint8_t data_buffer[SPI_PAGESIZE]; diff --git a/extras/Changes.log b/extras/Changes.log index 0892d8e..cd9deec 100644 --- a/extras/Changes.log +++ b/extras/Changes.log @@ -1,6 +1,27 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // SPIMemory Library // // Changes log // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Version 3.2.1 // +// Author: Prajwal Bhattaram // +// 13.04.2018 // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +Bugs squashed +--> Fixes issue #135 : The addition of the SFDP checking to _chipID resulted in a sudden (very large) increase in compiled code size. As of the current version (v3.2.1), SFDP checking is an user controlled option. To get the library to work with SFDP compatible flash memory that is not officially supported, the user must uncomment '//#define USES_SFDP' in 'SPIMemory.h'. + +--> Moved `bool _loopedOver` from being a local variable in `getAddress()` to a global one. Now it actually does what it was meant to do - i.e. prevent looping over data a second time. + +Enhancements +--> The way the basic functions execute has been modified to keep the function runtime the same (or improved) while reducing their memory footprint. + + Test sketch - FlashDiagnostics.ino from v3.2.0 with #RUNDIAGNOSTIC commented out + Test platform - Arduino Pro Mini 8MHz 3.3V + Library version: v3.1.0 Compiled code size - 17652 bytes (SFDP discovery not supported) + Library version: v3.2.0 Compiled code size - 20104 bytes (SFDP discovery supported & on by default) (13.9% larger than v3.1.0) + Library version: v3.2.1 Compiled code size - 15316 bytes (SFDP discovery turned off) (13.75% smaller than v3.1.0) + Library version: v3.2.1 Compiled code size - 17854 bytes (SFDP discovery turned on) (1.1% larger than v3.1.0) + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Version 3.2.0 // // Author: Prajwal Bhattaram // diff --git a/library.properties b/library.properties index 5b3d75f..c2b4978 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=SPIMemory -version=3.2.0 +version=3.2.1 author=Prajwal Bhattaram maintainer=Prajwal Bhattaram sentence=SPI Memory library for Arduino. (Formerly SPIFlash) -paragraph=Starting v3.2.0, this library enables read, write, erase and power functions on all SFDP compatible Flash chips. Older versions are only compatible with specific chips as listed here - < v2.6.0 is compatible with W25X05**, W25X10**, W25X20**, W25X40**, W25Q80**, W25Q16**, W25Q32** & W25Q64**. All other Winbond flash chips can also be used with this library from v2.6.0 onwards. A number of Microchip, Cypress & Spansion chips - W25Q128**, W25Q256**, SST25VF064C, SST26VF064B & S25FL116K - can be used with with the library from v3.0.0 onwards. Refer to change log for further information about this release. +paragraph=This library enables read, write, erase and power functions on number of compatible flash memory chips. Refer to change log for further information about this release. To download any version of this library pre v3.2.0 (pre name-change from SPIFlash please visit https://github.com/Marzogh/SPIMemory/releases?after=v3.2.0 ) category=Data Storage url=https://github.com/Marzogh/SPIMemory architectures=avr,sam,samd,esp8266,esp32,Simblee,stm32 diff --git a/src/SPIFlash.cpp b/src/SPIFlash.cpp index cbede21..1dcfe4d 100644 --- a/src/SPIFlash.cpp +++ b/src/SPIFlash.cpp @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.1.0 +/* Arduino SPIMemory Library v.3.2.1 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 24/02/2018 + * Modified by Prajwal Bhattaram - 21/05/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Winbond NOR flash memory modules. In its current form it enables reading @@ -184,7 +184,6 @@ uint64_t SPIFlash::getUniqueID(void) { // Takes the size of the data as an argument and returns a 32-bit address // All addresses in the in the sketch must be obtained via this function or not at all. uint32_t SPIFlash::getAddress(uint16_t size) { - bool _loopedOver = false; if (!_addressCheck(currentAddress, size)){ return false; } @@ -360,7 +359,49 @@ bool SPIFlash::readStr(uint32_t _addr, String &data, bool fastRead) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeByte(uint32_t _addr, uint8_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _BYTE_); + //return _write(_addr, data, sizeof(data), errorCheck, _BYTE_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + _nextByte(WRITE, data); + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + if (data != _nextByte(READ)) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + else { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + } + return true; } // Writes a char of data to a specific location in a page. @@ -371,7 +412,49 @@ bool SPIFlash::writeByte(uint32_t _addr, uint8_t data, bool errorCheck) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeChar(uint32_t _addr, int8_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _CHAR_); + //return _write(_addr, data, sizeof(data), errorCheck, _CHAR_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + _nextByte(WRITE, data); + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + if (data != (int8_t)_nextByte(READ)) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + else { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + } + return true; } // Writes an array of bytes starting from a specific location in a page. @@ -389,7 +472,7 @@ bool SPIFlash::writeByteArray(uint32_t _addr, uint8_t *data_buffer, size_t buffe if (!_prep(PAGEPROG, _addr, bufferSize)) { return false; } - uint16_t maxBytes = _pageSize-(_addr % _pageSize); // Force the first set of bytes to stay within the first page + uint16_t maxBytes = SPI_PAGESIZE-(_addr % SPI_PAGESIZE); // Force the first set of bytes to stay within the first page if (bufferSize <= maxBytes) { CHIP_SELECT @@ -412,6 +495,7 @@ bool SPIFlash::writeByteArray(uint32_t _addr, uint8_t *data_buffer, size_t buffe CHIP_SELECT _nextByte(WRITE, PAGEPROG); _transferAddress(); + //_nextBuf(PAGEPROG, &data_buffer[data_offset], writeBufSz); for (uint16_t i = 0; i < writeBufSz; ++i) { _nextByte(WRITE, data_buffer[data_offset + i]); } @@ -422,7 +506,7 @@ bool SPIFlash::writeByteArray(uint32_t _addr, uint8_t *data_buffer, size_t buffe length -= writeBufSz; maxBytes = 256; // Now we can do up to 256 bytes per loop - if(!_notBusy(10000L) || !_writeEnable()){ + if(!_notBusy() || !_writeEnable()){ return false; } @@ -441,15 +525,10 @@ bool SPIFlash::writeByteArray(uint32_t _addr, uint8_t *data_buffer, size_t buffe return false; } _currentAddress = _addr; - //uint8_t _inByte; CHIP_SELECT _nextByte(WRITE, READDATA); _transferAddress(); for (uint16_t j = 0; j < bufferSize; j++) { - //_inByte = _nextByte(READ); - //if (_inByte != data_buffer[j]) { - //Serial.print("inByte = "); - //Serial.println(_inByte); if (_nextByte(READ) != data_buffer[j]) { return false; } @@ -477,13 +556,13 @@ bool SPIFlash::writeCharArray(uint32_t _addr, char *data_buffer, size_t bufferSi if (!_prep(PAGEPROG, _addr, bufferSize)) { return false; } - uint16_t maxBytes = _pageSize-(_addr % _pageSize); // Force the first set of bytes to stay within the first page + uint16_t maxBytes = SPI_PAGESIZE-(_addr % SPI_PAGESIZE); // Force the first set of bytes to stay within the first page if (bufferSize <= maxBytes) { CHIP_SELECT _nextByte(WRITE, PAGEPROG); _transferAddress(); - //_nextBuf(PAGEPROG, (uint8_t*) &data_buffer[0], bufferSize); + //_nextBuf(PAGEPROG, &data_buffer[0], bufferSize); for (uint16_t i = 0; i < bufferSize; ++i) { _nextByte(WRITE, data_buffer[i]); } @@ -533,7 +612,7 @@ bool SPIFlash::writeCharArray(uint32_t _addr, char *data_buffer, size_t bufferSi _nextByte(WRITE, READDATA); _transferAddress(); for (uint16_t j = 0; j < bufferSize; j++) { - if (_nextByte(READ) != data_buffer[j]) { + if ((char)_nextByte(READ) != data_buffer[j]) { return false; } } @@ -553,7 +632,54 @@ bool SPIFlash::writeCharArray(uint32_t _addr, char *data_buffer, size_t bufferSi // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeWord(uint32_t _addr, uint16_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _WORD_); + //return _write(_addr, data, sizeof(data), errorCheck, _WORD_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + for (uint8_t i = 0; i < sizeof(data); i++) { + _nextByte(WRITE, data >> (8*i)); + } + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + union { + uint8_t byte[2]; + uint16_t word; + } dataIn; + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + for (uint8_t i = 0; i < sizeof(data); i++) { + dataIn.byte[i] = _nextByte(READ); + } + _endSPI(); + if (dataIn.word != data) { + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + } + return true; } // Writes a signed int as two bytes starting from a specific location in a page @@ -564,7 +690,54 @@ bool SPIFlash::writeWord(uint32_t _addr, uint16_t data, bool errorCheck) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeShort(uint32_t _addr, int16_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _SHORT_); + //return _write(_addr, data, sizeof(data), errorCheck, _SHORT_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + for (uint8_t i = 0; i < sizeof(data); i++) { + _nextByte(WRITE, data >> (8*i)); + } + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + union { + uint8_t byte[2]; + int16_t short_; + } dataIn; + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + for (uint8_t i = 0; i < sizeof(data); i++) { + dataIn.byte[i] = _nextByte(READ); + } + _endSPI(); + if (dataIn.short_ != data) { + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + } + return true; } // Writes an unsigned long as four bytes starting from a specific location in a page. @@ -575,7 +748,54 @@ bool SPIFlash::writeShort(uint32_t _addr, int16_t data, bool errorCheck) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeULong(uint32_t _addr, uint32_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _ULONG_); + //return _write(_addr, data, sizeof(data), errorCheck, _ULONG_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + for (uint8_t i = 0; i < sizeof(data); i++) { + _nextByte(WRITE, data >> (8*i)); + } + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + union { + uint8_t byte[4]; + uint32_t uLong; + } dataIn; + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + for (uint8_t i = 0; i < sizeof(data); i++) { + dataIn.byte[i] = _nextByte(READ); + } + _endSPI(); + if (dataIn.uLong != data) { + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + } + return true; } // Writes a signed long as four bytes starting from a specific location in a page @@ -586,7 +806,54 @@ bool SPIFlash::writeULong(uint32_t _addr, uint32_t data, bool errorCheck) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeLong(uint32_t _addr, int32_t data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _LONG_); + //return _write(_addr, data, sizeof(data), errorCheck, _LONG_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + _beginSPI(PAGEPROG); + for (uint8_t i = 0; i < sizeof(data); i++) { + _nextByte(WRITE, data >> (8*i)); + } + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + union { + uint8_t byte[4]; + int32_t Long; + } dataIn; + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + for (uint8_t i = 0; i < sizeof(data); i++) { + dataIn.byte[i] = _nextByte(READ); + } + _endSPI(); + if (dataIn.Long != data) { + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + } + return true; } // Writes a float as four bytes starting from a specific location in a page @@ -597,7 +864,60 @@ bool SPIFlash::writeLong(uint32_t _addr, int32_t data, bool errorCheck) { // WARNING: You can only write to previously erased memory locations (see datasheet). // Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs) bool SPIFlash::writeFloat(uint32_t _addr, float data, bool errorCheck) { - return _write(_addr, data, sizeof(data), errorCheck, _FLOAT_); + //return _write(_addr, data, sizeof(data), errorCheck, _FLOAT_); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros(); + #endif + if(!_prep(PAGEPROG, _addr, sizeof(data))) { + return false; + } + + union { + float Float; + uint8_t byte[sizeof(float)]; + } dataOut; + dataOut.Float = data; + + _beginSPI(PAGEPROG); + for (uint8_t i = 0; i < sizeof(data); i++) { + _nextByte(WRITE, dataOut.byte[i]); + } + CHIP_DESELECT + + if (!errorCheck) { + _endSPI(); + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return true; + } + else { + if (!_notBusy()) { + return false; + } + union { + uint8_t byte[4]; + float Float; + } dataIn; + _currentAddress = _addr; + CHIP_SELECT + _nextByte(WRITE, READDATA); + _transferAddress(); + for (uint8_t i = 0; i < sizeof(data); i++) { + dataIn.byte[i] = _nextByte(READ); + } + _endSPI(); + if (dataIn.Float != data) { + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + return false; + } + #ifdef RUNDIAGNOSTIC + _spifuncruntime = micros() - _spifuncruntime; + #endif + } + return true; } // Writes a string to a specific location on a page @@ -611,6 +931,8 @@ bool SPIFlash::writeStr(uint32_t _addr, String &data, bool errorCheck) { return _write(_addr, data, sizeof(data), errorCheck, _STRING_); } + + // Erases a number of sectors or blocks as needed by the data being input. // Takes an address and the size of the data being input as the arguments and erases the block/s of memory containing the address. bool SPIFlash::eraseSection(uint32_t _addr, uint32_t _sz) { @@ -687,7 +1009,7 @@ bool SPIFlash::eraseSection(uint32_t _addr, uint32_t _sz) { _timeFactor = kb4Erase.time; } if(!_notBusy(_timeFactor * 1000L)) { - return false; //Datasheet says erasing a sector takes 400ms max + return false; } if (j == noOfEraseRunsB4Boundary) { if (!_prep(ERASEFUNC, (_addr + (_sz - _addressOverflow)), _sz)) { diff --git a/src/SPIFlash.h b/src/SPIFlash.h index d7dca2b..bff1393 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.1.0 +/* Arduino SPIMemory Library v.3.2.1 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 24/02/2018 + * Modified by Prajwal Bhattaram - 21/05/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Winbond NOR flash memory modules. In its current form it enables reading @@ -166,13 +166,14 @@ class SPIFlash { bool pageOverflow, SPIBusState; bool chipPoweredDown = false; bool address4ByteEnabled = false; + bool _loopedOver = false; uint8_t cs_mask, errorcode, stat1, stat2, stat3, _SPCR, _SPSR, _a0, _a1, _a2; char READ = 'R'; char WRITE = 'W'; float _spifuncruntime = 0; struct chipID { bool supported; - bool JEDECsupport; + bool supportedMan; bool sfdpAvailable; uint8_t manufacturerID; uint8_t memoryTypeID; @@ -198,6 +199,10 @@ class SPIFlash { const uint32_t _memSize[16] = {KB(64), KB(128), KB(256), KB(512), MB(1), MB(2), MB(4), MB(8), MB(16), MB(32), MB(8), MB(8), KB(256), KB(512), MB(4), KB(512)}; // To understand the _memSize definitions check defines.h + + const uint8_t _supportedManID[7] = {WINBOND_MANID, MICROCHIP_MANID, CYPRESS_MANID, ADESTO_MANID, MICRON_MANID, ON_MANID, AMIC_MANID}; + + const uint8_t _altChipEraseReq[3] = {A25L512, M25P40, SST26}; }; //--------------------------------- Public Templates ------------------------------------// @@ -229,7 +234,7 @@ template bool SPIFlash::_writeErrorCheck(uint32_t _addr, const T& valu return false; } const uint8_t* p = (const uint8_t*)(const void*)&value; - if (_dataType == _STRUCT_) { + /*if (_dataType == _STRUCT_) { uint8_t _inByte[_sz]; _beginSPI(READDATA); _nextBuf(READDATA, &(*_inByte), _sz); @@ -244,7 +249,7 @@ template bool SPIFlash::_writeErrorCheck(uint32_t _addr, const T& valu } } } - else { + else {*/ CHIP_SELECT _nextByte(WRITE, READDATA); _transferAddress(); @@ -256,7 +261,7 @@ template bool SPIFlash::_writeErrorCheck(uint32_t _addr, const T& valu } } _endSPI(); - } + //} return true; } @@ -283,57 +288,51 @@ template bool SPIFlash::_write(uint32_t _addr, const T& value, uint32_ //Serial.print("_addrIn: "); //Serial.println(_addrIn, HEX); const uint8_t* p = ((const uint8_t*)(const void*)&value); -//Serial.print(F("Address being written to: ")); -//Serial.println(_addr); + //Serial.print(F("Address being written to: ")); + //Serial.println(_addr); + uint32_t length = _sz; + uint16_t maxBytes = SPI_PAGESIZE-(_addrIn % SPI_PAGESIZE); // Force the first set of bytes to stay within the first page + if (!SPIBusState) { _startSPIBus(); } CHIP_SELECT _nextByte(WRITE, PAGEPROG); _transferAddress(); - //If data is only one byte (8 bits) long - if (_sz == 0x01) { - _nextByte(WRITE, *p); + + if (maxBytes > length) { + for (uint16_t i = 0; i < length; ++i) { + _nextByte(WRITE, *p++); + } CHIP_DESELECT } - else { //If data is longer than one byte (8 bits) - uint32_t length = _sz; - uint16_t maxBytes = SPI_PAGESIZE-(_addrIn % SPI_PAGESIZE); // Force the first set of bytes to stay within the first page + else { + uint32_t writeBufSz; + uint16_t data_offset = 0; + + do { + writeBufSz = (length<=maxBytes) ? length : maxBytes; - if (maxBytes > length) { - for (uint16_t i = 0; i < length; ++i) { + for (uint16_t i = 0; i < writeBufSz; ++i) { _nextByte(WRITE, *p++); } CHIP_DESELECT - } - else { - uint32_t writeBufSz; - uint16_t data_offset = 0; - - do { - writeBufSz = (length<=maxBytes) ? length : maxBytes; - - for (uint16_t i = 0; i < writeBufSz; ++i) { - _nextByte(WRITE, *p++); - } - CHIP_DESELECT - if (!_addressOverflow) { - _currentAddress += writeBufSz; - } - else { - if (data_offset >= _addressOverflow) { - _currentAddress = 0x00; - _addressOverflow = false; - } - } - data_offset += writeBufSz; - length -= writeBufSz; - maxBytes = SPI_PAGESIZE; // Now we can do up to 256 bytes per loop - if(!_notBusy() || !_writeEnable()) { - return false; + if (!_addressOverflow) { + _currentAddress += writeBufSz; + } + else { + if (data_offset >= _addressOverflow) { + _currentAddress = 0x00; + _addressOverflow = false; } - } while (length > 0); - } + } + data_offset += writeBufSz; + length -= writeBufSz; + maxBytes = SPI_PAGESIZE; // Now we can do up to 256 bytes per loop + if(!_notBusy() || !_writeEnable()) { + return false; + } + } while (length > 0); } if (!errorCheck) { diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index 4d64974..7611c20 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.1.0 +/* Arduino SPIMemory Library v.3.2.1 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 24/02/2018 + * Modified by Prajwal Bhattaram - 21/05/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Winbond NOR flash memory modules. In its current form it enables reading @@ -427,7 +427,7 @@ { return true; } - _time++; + } while ((micros() - _time) < timeout); if (timeout <= (micros() - _time)) { _troubleshoot(CHIPBUSY); @@ -462,22 +462,23 @@ //Checks the device ID to establish storage parameters bool SPIFlash::_getManId(uint8_t *b1, uint8_t *b2) { - if(!_notBusy()) - return false; - _beginSPI(MANID); + if(!_notBusy()) { + return false; + } + _beginSPI(MANID); _nextByte(READ); _nextByte(READ); _nextByte(READ); *b1 = _nextByte(READ); *b2 = _nextByte(READ); CHIP_DESELECT - return true; + return true; } //Checks for presence of chip by requesting JEDEC ID bool SPIFlash::_getJedecId(void) { if(!_notBusy()) { - return false; + return false; } _beginSPI(JEDECID); _chip.manufacturerID = _nextByte(READ); // manufacturer id @@ -519,85 +520,82 @@ //Identifies the chip bool SPIFlash::_chipID(uint32_t flashChipSize) { + //set some default values + kb4Erase.supported = kb32Erase.supported = kb64Erase.supported = chipErase.supported = true; + kb4Erase.opcode = SECTORERASE; + kb32Erase.opcode = BLOCK32ERASE; + kb64Erase.opcode = BLOCK64ERASE; + kb4Erase.time = BUSY_TIMEOUT; + kb32Erase.time = kb4Erase.time * 8; + kb64Erase.time = kb32Erase.time * 4; + kb256Erase.supported = false; + chipErase.opcode = CHIPERASE; + chipErase.time = kb64Erase.time * 100L; + _getJedecId(); - if (_chip.manufacturerID == MICROCHIP_MANID) { - _disableGlobalBlockProtect(); + for (uint8_t i = 0; i < sizeof(_supportedManID); i++) { + if (_chip.manufacturerID == _supportedManID[i]) { + _chip.supportedMan = true; + break; + } } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Begin SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - _checkForSFDP(); - if (_chip.sfdpAvailable) { - _getSFDPFlashParam(); - } - else { - kb4Erase.supported = kb32Erase.supported = kb64Erase.supported = chipErase.supported = true; - kb4Erase.opcode = SECTORERASE; - kb32Erase.opcode = BLOCK32ERASE; - kb64Erase.opcode = BLOCK64ERASE; - kb4Erase.time = BUSY_TIMEOUT; - kb32Erase.time = kb4Erase.time * 8; - kb64Erase.time = kb32Erase.time * 4; - kb256Erase.supported = false; - chipErase.opcode = CHIPERASE; - chipErase.time = kb64Erase.time * 100L; + for (uint8_t i = 0; i < sizeof(_altChipEraseReq); i++) { + if (_chip.memoryTypeID == _altChipEraseReq[i]) { + chipErase.opcode = ALT_CHIPERASE; + break; + } } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - if (!_chip.capacity) { - if (!flashChipSize) { - #ifdef RUNDIAGNOSTIC - Serial.println("No Chip size defined by user. Automated identification initiated."); - #endif + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Begin SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + #ifdef USES_SFDP + if (_checkForSFDP()) { + _getSFDPFlashParam(); + } + #endif + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - if (_chip.manufacturerID == WINBOND_MANID || _chip.manufacturerID == MICROCHIP_MANID || _chip.manufacturerID == CYPRESS_MANID || _chip.manufacturerID == ADESTO_MANID || _chip.manufacturerID == MICRON_MANID || _chip.manufacturerID == ON_MANID || _chip.manufacturerID == AMIC_MANID) { - if (_chip.manufacturerID == AMIC_MANID) { - _chip.capacityID |= _chip.manufacturerID; - } - //Identify capacity - for (uint8_t i = 0; i < sizeof(_capID); i++) { - if (_chip.capacityID == _capID[i]) { - _chip.capacity = (_memSize[i]); - _chip.JEDECsupport = true; - _chip.supported = true; - #ifdef RUNDIAGNOSTIC - Serial.println("Chip identified. This chip is fully supported by the library."); - #endif - } - } - _getJedecId(); - if (!_chip.capacity) { - _troubleshoot(UNKNOWNCAP); - return false; - } - } - else { - _troubleshoot(UNKNOWNCHIP); //Error code for unidentified capacity - return false; + if (_chip.supportedMan) { + #ifdef RUNDIAGNOSTIC + Serial.println("No Chip size defined by user. Automated identification initiated."); + #endif + //Identify capacity + for (uint8_t j = 0; j < sizeof(_capID); j++) { + if (_chip.capacityID == _capID[j]) { + _chip.capacity = (_memSize[j]); + _chip.supported = true; + #ifdef RUNDIAGNOSTIC + Serial.println("Chip identified. This chip is fully supported by the library."); + #endif + return true; } } - else { + } + else { + _troubleshoot(UNKNOWNCHIP); //Error code for unidentified capacity + return false; + } + + if (!_chip.capacity) { + + if (flashChipSize) { // If a custom chip size is defined #ifdef RUNDIAGNOSTIC Serial.println("Custom Chipsize defined"); #endif _chip.capacity = flashChipSize; _chip.supported = false; + return true; } - } - if (_chip.manufacturerID == CYPRESS_MANID) { - setClock(SPI_CLK/4); // Cypress/Spansion chips appear to perform best at SPI_CLK/4 - } + else { + _troubleshoot(UNKNOWNCAP); + return false; + } - if ( ((_chip.manufacturerID == MICROCHIP_MANID) && (_chip.memoryTypeID == SST26))\ - || ((_chip.manufacturerID == MICRON_MANID) && (_chip.memoryTypeID == M25P40))\ - || ((_chip.manufacturerID == AMIC_MANID) && (_chip.memoryTypeID == A25L512)) ) { - chipErase.opcode = ALT_CHIPERASE; } - - return true; } //Troubleshooting function. Called when #ifdef RUNDIAGNOSTIC is uncommented at the top of this file. diff --git a/src/SPIMemory.h b/src/SPIMemory.h index 293c39d..67d7160 100644 --- a/src/SPIMemory.h +++ b/src/SPIMemory.h @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.2.0 +/* Arduino SPIMemory Library v.3.2.1 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 18/04/2018 - * Modified by Prajwal Bhattaram - 20/04/2018 + * Modified by Prajwal Bhattaram - 21/05/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Winbond NOR flash memory modules. In its current form it enables reading @@ -26,13 +26,21 @@ #ifndef SPIMEMORY_H #define SPIMEMORY_H +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Uncomment the code below to enable SFDP discovery - especially // +// if using an unsupported chip // +// // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +//#define USES_SFDP // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Uncomment the code below to run a diagnostic if your flash // // does not respond // // // // Error codes will be generated and returned on functions // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -#define RUNDIAGNOSTIC // +//#define RUNDIAGNOSTIC // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// @@ -43,6 +51,7 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// //#define HIGHSPEED // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Uncomment the code below to disable overflow and force data // // to only be written to the last address of the flash memory // @@ -53,7 +62,7 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Comment out the code below to disable DMA mode on SAMD based // -// platforms // +// platforms (In ALPHA) // // // // Change the ZERO_SPISERCOM define below to use other SPI ports // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// @@ -107,7 +116,7 @@ #define LIBVER 3 #define LIBSUBVER 2 -#define BUGFIXVER 0 +#define BUGFIXVER 1 class SPIMemory { public: @@ -115,6 +124,8 @@ class SPIMemory { SPIMemory(void) {}; ~SPIMemory(void) {}; //------------------------------- Public functions -----------------------------------// + //------------------------------- Public variables -----------------------------------// + }; diff --git a/src/defines.h b/src/defines.h index b67f51e..0be893e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.1.0 +/* Arduino SPIMemory Library v.3.2.1 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 - * Modified by Prajwal Bhattaram - 24/02/2018 + * Modified by Prajwal Bhattaram - 21/05/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Winbond NOR flash memory modules. In its current form it enables reading @@ -191,7 +191,7 @@ #define VERBOSE true #define PRINTOVERRIDE true #define ERASEFUNC 0xEF -#define BUSY_TIMEOUT 10000000L +#define BUSY_TIMEOUT 1000000000L #define arrayLen(x) (sizeof(x) / sizeof(*x)) #define lengthOf(x) (sizeof(x))/sizeof(byte) #define BYTE 1L diff --git a/src/diagnostics.cpp b/src/diagnostics.cpp index 3ccbf3a..a6c57ba 100644 --- a/src/diagnostics.cpp +++ b/src/diagnostics.cpp @@ -48,7 +48,7 @@ void Diagnostics::troubleshoot(uint8_t _code, bool printoverride) { #elif defined (RUNDIAGNOSTIC) && defined (ARDUINO_ARCH_AVR) _printErrorCode(); #endif -#if !defined (RUNDIAGNOSTIC) +#if !defined (RUNDIAGNOSTIC) _printoverride = printoverride; #endif if (_printoverride) {