From ac05a7827c99d6eace8017e84d2ee7668f75a1d9 Mon Sep 17 00:00:00 2001 From: Eero af Heurlin Date: Tue, 2 Oct 2018 20:26:20 +0300 Subject: [PATCH 01/25] Make is possible to configure SPI clock right from the beginning Jumper wires and breadboards (especially solderless ones) have very poor high-frequency characteristics leading to weird problems in high-speed communications. --- README.md | 2 +- src/SPIFlash.cpp | 13 ++++++++++++- src/SPIFlash.h | 7 +++++++ src/SPIFlashIO.cpp | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bfe4211..07d734d 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ The library enables the following functions: Must be called at the start in setup(). This function detects the type of chip being used and sets parameters accordingly. An optional CHIPSIZE parameter can be declared as an argument with this function. For supported CHIPSIZE values, please refer to the appropriate [wiki section](https://github.com/Marzogh/SPIMemory/wiki/Chipsize) or look at defines.h . ###### `setClock(clockSpeed)` -Must be called straight after begin(). This function takes a 32-bit number as replacement for the default maximum clock speed (104MHz for Winbond NOR flash) thereby initiating future SPI transactions with the user-defined clock speed. Use with care. +Must be called right before begin(). If `SPI_HAS_TRANSACTION` is defined This function takes a 32-bit number as replacement for the default maximum clock speed (104MHz for Winbond NOR flash). Otherwise takes `SPI_CLOCK_DIVx`. In either case sets the clock speed for SPI. Use with care. ###### `error(_verbosity)` Returns the (8-bit) error code generated by the function called immediately before this is called. If the optional VERBOSE argument is used, a verbose troubleshooting report is printed to Serial. Refer to the [Error reporting](https://github.com/Marzogh/SPIMemory/wiki/Error-reporting) section the Wiki for further reference. diff --git a/src/SPIFlash.cpp b/src/SPIFlash.cpp index 1dcfe4d..6d94b56 100644 --- a/src/SPIFlash.cpp +++ b/src/SPIFlash.cpp @@ -77,7 +77,13 @@ bool SPIFlash::begin(uint32_t flashChipSize) { BEGIN_SPI #ifdef SPI_HAS_TRANSACTION //Define the settings to be used by the SPI bus - _settings = SPISettings(SPI_CLK, MSBFIRST, SPI_MODE0); + if (!_settings_set) { + _settings = SPISettings(SPI_CLK, MSBFIRST, SPI_MODE0); + } +#else + if (!_clockdiv) { + _clockdiv = SPI_CLOCK_DIV2; + } #endif bool retVal = _chipID(flashChipSize); _endSPI(); @@ -90,6 +96,11 @@ bool SPIFlash::begin(uint32_t flashChipSize) { #ifdef SPI_HAS_TRANSACTION void SPIFlash::setClock(uint32_t clockSpeed) { _settings = SPISettings(clockSpeed, MSBFIRST, SPI_MODE0); + _settings_set = true; +} +#else +void SPIFlash::setClock(uint8_t clockdiv) { + _clockdiv = clockdiv; } #endif diff --git a/src/SPIFlash.h b/src/SPIFlash.h index bff1393..6e4099b 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -43,7 +43,11 @@ class SPIFlash { #endif //----------------------------- Initial / Chip Functions ------------------------------// bool begin(uint32_t flashChipSize = 0); + #ifdef SPI_HAS_TRANSACTION void setClock(uint32_t clockSpeed); + #else + void setClock(uint8_t clockdiv); + #endif bool libver(uint8_t *b1, uint8_t *b2, uint8_t *b3); bool sfdpPresent(void); uint8_t error(bool verbosity = false); @@ -153,6 +157,9 @@ class SPIFlash { //-------------------------------- Private variables ----------------------------------// #ifdef SPI_HAS_TRANSACTION SPISettings _settings; + bool _settings_set = false; + #else + uint8_t _clockdiv; #endif //If multiple SPI ports are available this variable is used to choose between them (SPI, SPI1, SPI2 etc.) SPIClass *_spi; diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index 7611c20..d1a9180 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -175,7 +175,7 @@ #ifdef SPI_HAS_TRANSACTION SPI.beginTransaction(_settings); #else - SPI.setClockDivider(SPI_CLOCK_DIV4); + SPI.setClockDivider(_clockdiv); SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); #endif From 3e8b18028535469cea281ac35feae33c47f606cc Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Sat, 20 Apr 2019 13:21:31 +0000 Subject: [PATCH 02/25] Add Gitter badge --- README.md | 350 +++++++++++++++++++++++++++++------------------------- 1 file changed, 190 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index f289f01..c55165f 100644 --- a/README.md +++ b/README.md @@ -1,254 +1,284 @@ +# SPIMemory [![Build Status](https://travis-ci.org/Marzogh/SPIMemory.svg?branch=master)](https://travis-ci.org/Marzogh/SPIMemory) [![DOI](https://zenodo.org/badge/35823047.svg)](https://zenodo.org/badge/latestdoi/35823047) +[![GitHub release](https://img.shields.io/github/release/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory) +[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.2.1.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.2.1...v3.3.0) +[![GitHub issues](https://img.shields.io/github/issues/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/issues) +[![GitHub pull requests](https://img.shields.io/github/issues-pr/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/pulls) +[![license](https://img.shields.io/github/license/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/blob/master/LICENSE) [![Join the chat at https://gitter.im/SPIMemory/community](https://badges.gitter.im/SPIMemory/community.svg)](https://gitter.im/SPIMemory/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -# SPIFlash [![Build Status](https://travis-ci.org/Marzogh/SPIFlash.svg?branch=master)](https://travis-ci.org/Marzogh/SPIFlash) [![DOI](https://zenodo.org/badge/18908/Marzogh/SPIFlash.svg)](https://zenodo.org/badge/latestdoi/18908/Marzogh/SPIFlash) -### Arduino library for Winbond Flash Memory Chips - Download the latest stable release (v2.6.0) from here. Please report any bugs in issues. +## Arduino library for Flash & FRAM Memory Chips (SPI based only) + Download the latest stable release from [here](https://github.com/Marzogh/SPIMemory/releases/latest). Please report any bugs in [issues](https://github.com/Marzogh/SPIMemory/issues/new). -This Arduino library is for use with Winbond serial flash memory chips. In its current form it supports identifying the flash chip and its various features; automatic address allocation and management; reading and writing bytes/chars/ints/longs/floats/Strings from and to various locations; reading and writing pages of bytes; continous reading/writing of data from/to arrays of bytes/chars; sector, block and chip erase; and powering down for low power operation. +This Arduino library is for use with flash and FRAM memory chips that communicate using the SPI protocol. In its current form it supports identifying the flash/FRAM chip and its various features; automatic address allocation and management; writing and reading a number of different types of data, ranging from 8-bit to 32-bit (signed and unsigned) values, floats, Strings, arrays of bytes/chars and structs to and from various locations; sector, block and chip erase; and powering down for low power operation. -- For details of the Winbond Flash chips compatible with this library please refer to the Excel spreadsheet in the Extras folder. +
+ +### IDE Compatibility (actually tested with) + +- Arduino IDE v1.5.x +- Arduino IDE v1.6.x +- Arduino IDE v1.8.x -#### Compatibility +
+ +### Platform compatibility -##### Arduino IDEs supported (actually tested with) -- IDE v1.5.x -- IDE v1.6.0-v1.6.5 -- IDE v1.6.9-v1.6.12 -- IDE v1.8.2 +| Micro controllers | Dev boards tested with | Notes | +| ----------------- | ---------------------- | ----- | +| ATmega328P | Arduino Uno, Arduino Micro,
Arduino Fio, Arduino Nano | - | +| ATmega32u4 | Arduino Leonardo, Arduino Fio v3 | - | +| ATmega2560 | Arduino Mega | - | +| ATSAMD21G18 (ARM Cortex M0+) | Adafruit Feather M0,
Adafruit Feather M0 Express,
Adafruit ItsyBitsy M0 Express | - | +| AT91SAM3X8E (ARM Cortex M3) | Arduino Due | - | +| ATSAMD51J19 (ARM Cortex M4) | Adafruit Metro M4 | - | +| STM32F091RCT6 | Nucleo-F091RC | | +| ESP8266 | Adafruit ESP8266 Feather,
Sparkfun ESP8266 Thing | - | +| ESP32 | Adafruit ESP32 Feather,
Sparkfun ESP32 Thing | Onboard flash memory. Refer to footnote£ below. | +| Simblee | Sparkfun Simblee | - | -##### Boards +£ ESP32 boards usually have an SPI Flash already attached to their default SS pin, so the user has to explicitly declare the `ChipSelect` pin being used with the constructor -###### Completely supported -- Arduino Uno -- Arduino Leonardo -- Arduino Micro -- Arduino Fio -- Arduino Mega -- Arduino Due -- ESP8266 Boards (On the Arduino IDE) -- Simblee Boards (On the Arduino IDE) +
+ +### Flash memory compatibility - Actually tested with + +| Manufacturer | Flash IC | Notes | +| ------------ | -------- | ----- | +| Winbond | W25Q16BV
W25Q64FV
W25Q64JV
W25Q80BV
W25Q256FV | Should work with the W25QXXXBV, W25QXXXFV &
W25QXXXJV families | +| Microchip | SST25VF064C
SST26VF016B
SST26VF032B
SST26VF064B | Should work with the SST25 & SST26 families | +| Cypress/Spansion | S25FL032P
S25FL116K
S25FL127S | Should work with the S25FL family | +| ON Semiconductor | LE25U40CMC | | +| AMIC| A25L512A0 | | +| Micron| M25P40 | | +| Adesto| AT25SF041 | | +| Giga devices| GD25Q16C | (Used on the Adafruit ItsyBitsy M0 Express) | + +##### Should work with any flash memory that is compatible with the SFDP standard as defined in JESD216B + +
-###### In BETA -- ESP32 Boards (Tested on the Sparkfun ESP32 thing) The library is known to work with the ESP32 core as of the current commit 7d0968c on 16.04.2017. ```ESP32 support will remain in beta till the ESP32 core can be installed via the Arduino boards manager.``` +### FRAM memory compatibility - Actually tested with -#### Installation +| Manufacturer | Flash IC | Notes | +| ------------ | -------- | ----- | +| Cypress/Spansion | FM25W256 | Should work with the FM25W family | -##### Option 1 +
+ +### Installation + +#### Option 1 - Open the Arduino IDE. - Go to Sketch > Include Library > Manage libraries. -- Search for SPIFlash. +- Search for SPIMemory. - Install the latest version. -##### Option 2 -- Click on the 'Clone or download' button above the list of files on this page . +#### Option 2 +- Click on the 'Clone or download' button above the list of files on this page . - Select Download ZIP. A .zip file will download to your computer. -- Unzip the archive and rename resulting folder to 'SPIFlash' +- Unzip the archive and rename resulting folder to 'SPIMemory' - Move the folder to your libraries folder (~/sketches/libraries) -#### Usage +### Usage + +#### SPIFlash + +- The library is called by declaring the```SPIFlash flash(csPin*)``` constructor where 'flash' can be replaced by a user constructor of choice and 'csPin' is the Chip Select pin for the flash module. + + * Optional. Do not include csPin if using the default slave select pin for your board. +- Every version of the library >= v3.0.0 supports the ability to use any of multiple SPI interfaces (if your micro-controller supports them). Switching to use another SPI interface is done by calling ```SPIFlash flash(csPin, &SPI1);``` (or &SPI2 and so on), instead of ```SPIFlash flash(csPin)```. + + * NOTE: This is currently only supported on the SAMD and STM32 architectures. +- Also make sure to include ```flash.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of flash chip installed and load the right parameters. + + * 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.) -The library is called by declaring the```SPIFLASH flash(csPin)``` constructor where 'flash' can be replaced by a user constructor of choice and 'csPin' is the Chip Select pin for the flash module. -Make sure to include ```#include``` when you include ```#include```. -Also make sure to include ```flash.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of flash chip installed and load the right parameters. -* Optional -###### 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 setting the optional last 'overflow' argument in the constructor to NOOVERFLOW - For eg. call the constructor ```SPIFlash(csPin, NOOVERFLOW)``` instead of ```SPIFlash(csPin)```. (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)```. +#### SPIFram ^ + +- The library is called by declaring the```SPIFram fram(csPin*)``` constructor where 'flash' can be replaced by a user constructor of choice and 'csPin' is the Chip Select pin for the flash module. + + * Optional. Do not include csPin if using the default slave select pin for your board. +- Every version of the library >= v3.0.0 supports the ability to use any of multiple SPI interfaces (if your micro-controller supports them). Switching to use another SPI interface is done by calling ```SPIFram fram(csPin, &SPI1);``` (or &SPI2 and so on), instead of ```SPIFram fram(csPin)```. + + * NOTE: This is currently only officially supported on the SAMD and STM32 architectures. +- Also make sure to include ```fram.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of flash chip installed and load the right parameters. + +^ Currently in BETA. The methods in SPIFram are not final and subject to change over the next few revisions. + The library enables the following functions:
-##### Primary commands +#### Non-Read/Write functions +
-###### begin(_chipsize*) -Must be called at the start in setup(). This function detects the type of chip being used and sets parameters accordingly. An optional CHIPSIZE parameter can be declared as an argument with this function. For supported CHIPSIZE values, please refer to the appropriate [wiki section](https://github.com/Marzogh/SPIFlash/wiki/begin()) or look at defines.h . +###### `begin(_chipsize)` -###### setClock(clockSpeed) -Must be called straight after begin(). This function takes a 32-bit number as replacement for the default maximum clock speed (104MHz for Winbond NOR flash) thereby initiating future SPI transactions with the user-defined clock speed. Use with care. +###### `setClock(clockSpeed)` -###### error(_verbosity) -Returns the (8-bit) error code generated by the function called immediately before this is called. If the optional VERBOSE argument is used, a verbose troubleshooting report is printed to Serial. Refer to the [Error reporting](https://github.com/Marzogh/SPIFlash/wiki/Error-reporting) section the Wiki for further reference. +###### `error(_verbosity)` -###### getMANID() -Returns the Manufacturer ID as a 16-bit value. +###### `getManID()` -###### getJEDECID() -Returns the JEDEC ID as a 32-bit value. +###### `getJEDECID()` -###### getCapacity() -Returns the capacity in bytes as a 32-bit value. +###### `getUniqueID()` -###### getmaxPage() -Returns the maximum number of pages in the flash memory as a 32-bit value. +###### `getAddress(sizeOfData)` -###### getAddress() -Gets the next available address for use. Has two variants: -* Takes the size of the data as an argument and returns a 32-bit address -* Takes a three variables, the size of the data and two other variables to return a page number value & an offset into. +###### `sizeofStr()` -All addresses in the in the sketch must be obtained via this function or not at all. -###### sizeofStr() -Use this function to find the size of a String to use as the argument in getAddress(size). Using size = sizeof(String) will cause the getAddress(size) function to fail. +###### `getCapacity()` -size = sizeof(variable) can be used for all types of data but String objects. -
+###### `getMaxPage()` + +###### `functionRunTime()` -###### All read/write commands can take a 32-bit address variable in the place of the 16-bit page number & 8-bit offset variables.
##### Read commands +
+ All read commands take a last boolean argument 'fastRead'. This argument defaults to FALSE, but when set to TRUE carries out the Fast Read instruction so data can be read at up to the memory's maximum frequency. + -All read commands can take a 32-bit address variable instead of the 16-bit page number & 8-bit offset variables + +All read commands only take a 32-bit address variable instead of the optional 16-bit page number & 8-bit offset variables in previous versions of the library (< v3.0.0) + -###### readAnything(page_number, offset, value) -Reads _any type of variable/struct_ (any sized value) from a specific location on a page. Takes the page number (0-maxPage), the offset of the data within page (0-255) and the variable/struct to write the data back to, as arguments. +###### `readAnything(address, value)` -###### readByte(page_number, offset) -Reads a _byte_ (unsigned 8 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the byte within page (0-255) as arguments. +###### `readByte(address)` -###### readChar(page_number, offset) -Reads a _char_ (signed 8 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the char within page (0-255) as arguments. +###### `readChar(address)` -###### readWord(page_number, offset) -Reads a _word_ (unsigned 16 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the word within page (0-255) as arguments. +###### `readWord(address)` -###### readShort(page_number, offset) -Reads a _short_ (signed 16 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the short within page (0-255) as arguments. +###### `readShort(address)` -###### readULong(page_number, offset) -Reads an _unsigned long_ (unsigned 32 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the unsigned long within page (0-255) as arguments. +###### `readULong(address)` -###### readLong(page_number, offset) -Reads a _long_ (signed 32 bit value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the long within page (0-255) as arguments. +###### `readLong(address)` -###### readFloat(page_number, offset) -Reads a _float_ (decimal value) from a specific location on a page. Takes the page number (0-maxPage) and offset of the float within page (0-255) as arguments. +###### `readFloat(address)` -###### readStr(page_number, offset, outputStr) -Reads a _string_ (String Object) to a specific location on a page to an outputStr variable. Takes the page number (0-maxPage), the offset of the String within page (0-255) and a String as arguments. +###### `readStr(address, outputStr)` -###### readPage(page_number, *data_buffer) -Reads a page worth of data into a data buffer array for further use. ```uint8_t data_buffer[256];``` The data buffer **must** be an array of 256 bytes. - -###### readAnything(page_number, offset, value) -Reads _any type of variable/struct_ (any sized value) from a specific location on a page. Takes the page number (0-maxPage), the offset of the data within page (0-255) and the variable/struct to write the data to, as arguments. +###### `readAnything(address, value)`
##### Write commands +
+ All write commands take a boolean last argument 'errorCheck'. This argument defaults to TRUE, but when set to FALSE will more than double the writing speed. This however comes at the cost of checking for writing errors. Use with care. + -All write commands can take a 32-bit address variable instead of the 16-bit page number & 8-bit offset variables + All write commands only take a 32-bit address variable instead of the optional 16-bit page number & 8-bit offset variables in previous versions of the library (< v3.0.0) -###### writeByte(page, offset, data) -Writes a byte of data to a specific location on a page. Takes the page number (0-maxPage), offset of data byte within page (0-255) and one byte of data as arguments. +###### `writeByte(address, data)` -###### writeChar(page_number, offset, data) -Writes a _char_ (signed 8 bit value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the char within page (0-255) and one char of data as arguments. +###### `writeChar(address, data)` -###### writeWord(page_number, offset, data) -Writes a _word_ (unsigned 16 bit value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the word within page (0-255) and one word of data as arguments. +###### `writeWord(address, data)` -###### writeShort(page_number, offset, data) -Writes a _short_ (signed 16 bit value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the short within page (0-255) and one short of data as arguments. +###### `writeShort(address, data)` -###### writeULong(page_number, offset, data) -Writes an _unsigned long_ (unsigned 32 bit value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the unsigned long within page (0-255) and one unsigned long of data as arguments. +###### `writeULong(address, data)` -###### writeLong(page_number, offset, data) -Writes a _long_ (signed 32 bit value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the long within page (0-255) and one long of data as arguments. +###### `writeLong(address, data)` -###### writeFloat(page_number, offset, data) -Writes a _float_ (decimal value) to a specific location on a page. Takes the page number (0-maxPage), the offset of the float within page (0-255) and one float of data as arguments. +###### `writeFloat(address, data)` -###### writeStr(page_number, offset, data) -Writes a _string_ (String Object) to a specific location on a page. Takes the page number (0-maxPage), the offset of the String within page (0-255) and a String as arguments. +###### `writeStr(address, data)` -###### writePage(page_number, *data_buffer) -Writes a page worth of data into a data buffer array for further use. ```uint8_t data_buffer[256];``` The data buffer **must** be an array of 256 bytes. +###### `writeAnything(address, value)` -###### writeAnything(page_number, offset, value) -Writes _any type of variable/struct_ (any sized value) from a specific location on a page. Takes the page number (0-maxPage), the offset of the data within page (0-255) and the variable/struct to write the data from, as arguments.
##### Continuous read/write commands + All write commands take a boolean last argument 'errorCheck'. This argument defaults to TRUE, but when set to FALSE will more than double the writing speed. This however comes at the cost of checking for writing errors. Use with care. + +
+ +###### `readByteArray(address, *data_buffer, bufferSize)` -###### readByteArray(page_number, offset, *data_buffer, bufferSize) -Reads an array of bytes starting from a specific location in a page. Takes the page number (0-maxPage), offset of data byte within page (0-255), a data_buffer - i.e. an array of bytes to be read from the flash memory - and size of the array as arguments. ```uint8_t data_buffer[n];``` The data buffer **must** be an array of n **bytes**. 'n' is determined by the amount of RAM available on the Arduino board. +###### `writeByteArray(address, *data_buffer, bufferSize)` -###### writeByteArray(page_number, offset, *data_buffer, bufferSize) -Writes an array of bytes starting from a specific location in a page. Takes the page number (0-maxPage), offset of data byte within page (0-255), a data_buffer - i.e. an array of bytes to be written to the flash memory - and size of the array as arguments. ```uint8_t data_buffer[n];``` The data buffer **must** be an array of 'n' **bytes**. The number of bytes - 'n' - is determined by the amount of RAM available on the Arduino board. +###### `readCharArray(address, *data_buffer, bufferSize)` -###### readCharArray(page_number, offset, *data_buffer, bufferSize) -Reads an array of chars starting from a specific location in a page. Takes the page number (0-maxPage), offset of data byte within page (0-255), a data_buffer - i.e. an array of chars to be read from the flash memory - and size of the array as arguments. ```char data_buffer[n];``` The data buffer **must** be an array of n **chars**. 'n' is determined by the amount of RAM available on the Arduino board. +###### `writeCharArray(address, *data_buffer, bufferSize)` -###### writeCharArray(page_number, offset, *data_buffer, bufferSize) -Writes an array of chars starting from a specific location in a page. Takes the page number (0-maxPage), offset of data byte within page (0-255), a data_buffer - i.e. an array of chars to be written to the flash memory - and size of the array as arguments. ```char data_buffer[n];``` The data buffer **must** be an array of 'n' **chars**. The number of chars - 'n' - is determined by the amount of RAM available on the Arduino board.
##### Erase commands -All erase commands can take a 32-bit address variable instead of the 16-bit page number & 8-bit offset variables + +All erase commands only take a 32-bit address variable instead of the optional 16-bit page number & 8-bit offset variables in previous versions of the library (< v3.0.0) + +
+ +###### `eraseSector(address)` + +###### `eraseBlock32K(address)` -###### eraseSector(page_number, offset) -Erases one 4KB sector - 16 pages - containing the page to be erased. The sectors are numbered 0 - 255 containing 16 pages each. -Page 0-15 --> Sector 0; Page 16-31 --> Sector 1;......Page 4080-4095 --> Sector 255, and so on... +###### `eraseBlock64K(address)` -###### eraseBlock32K(page_number, offset) -Erases one 32KB block - 128 pages - containing the page to be erased. The blocks are numbered 0 - 31 containing 128 pages each. -Page 0-127 --> Block 0; Page 128-255 --> Block 1;......Page 3968-4095 --> Block 31, and so on... +###### `eraseChip()` -###### eraseBlock64K(page_number, offset) -Erases one 64KB block - 256 pages - containing the page to be erased. The blocks are numbered 0 - 15 containing 256 pages each. -// Page 0-255 --> Block 0; Page 256-511 --> Block 1;......Page 3840-4095 --> Block 15, and so on... +###### `eraseSection(address, sizeOfData)` -###### eraseChip() -Erases entire chip. Use with care.
##### Suspend/Resume commands +
-###### suspendProg() -Suspends current Block Erase/Sector Erase/Page Program. Does not suspend chipErase(). Page Program, Write Status Register, Erase instructions are not allowed. Erase suspend is only allowed during Block/Sector erase. Program suspend is only allowed during Page/Quad Page Program +###### `suspendProg()` -###### resumeProg() -Resumes previously suspended Block Erase/Sector Erase/Page Program. +###### `resumeProg()`
##### Power operation commands +
+ +###### `powerDown()` -###### powerDown() -Puts device in low power state. Useful for battery powered operations. Typical current consumption during power-down is 1mA with a maximum of 5mA. (Datasheet 7.4). In powerDown() the chip will only respond to powerUp() +###### `powerUp()` -###### powerUp() -Wakes chip from low power state.
##### Error codes explained -* **0x00** - Action completed successfully. No Error. -* **0x01** - *constructor_of_choice*.begin() was not called in void setup() -* **0x02** - Unable to identify chip. Are you sure this is a Winbond Flash chip? Please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with your chip type and I will try to add support to your chip -* **0x03** - Unable to identify capacity. Please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with your chip type and I will work on adding support to your chip -* **0x04** - Chip is busy. Make sure all pins have been connected properly. If it still doesn't work,please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with the details of what your were doing when this error occurred -* **0x05** - Page overflow has been disabled and the address called exceeds the memory -* **0x06** - Unable to Enable Writing to chip. Please make sure the HOLD & WRITEPROTECT pins are connected properly to VCC & GND respectively. If you are still facing issues, please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with the details of what your were doing when this error occurred -* **0x07** - This sector already contains data. Please make sure the sectors being written to are erased. If you are still facing issues, please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with the details of what your were doing when this error occurred. -* **0x08** - You are running low on SRAM. Please optimise your program for better RAM usage -* **0x09** - Unable to suspend/resume operation. -* **0x0A** - This function is not supported by the current flash IC. -* **0x0B** - Write Function has failed errorcheck. -* **0x0C** - Check your wiring. Flash chip is non-responsive. -* **0xFE** - Unknown error. Please raise an issue [here](http://www.github.com/Marzogh/SPIFlash/issues) with the details of what your were doing when this error occurred -
- -###### How to get data off Flash memory via Serial -(Works only for Unix based Operating Systems) - - - Make sure you include code to let you dump entire flash memory/specific page's content to Serial (Refer to the code in the _TestFlash.h_ for details on how to do this) - - Connect your Arduino board to the computer. - - Open the Arduino IDE, the IDE's Serial Monitor, and an OSX POSIX terminal. - - Type the following command into the terminal window. ```% tail -f /dev/tty.usbmodem1411 > FlashDump.txt``` Make sure to replace the _/dev/tty.usbmodem1411_ with the port your arduino is connected to. (You can find this in Tools --> Ports in Arduino IDE 1.6.x) - - Then type the command to read all pages into the Serial console. If you use my code from the example file the command is ```read_all_pages``` - - Wait a few seconds before typing ```Ctrl+C``` to end the tail process - - Check that you have actually recieved all the data by typing ```% cat FlashDump.txt```. This should output the entire textfile into your terminal window. +Note: If you are unable to fix the error/s, please raise an issue [here](http://www.github.com/Marzogh/SPIMemory/issues) with the details of your flash chip and what you were doing when this error occurred. **Please follow the issue template that show up at the link** + +| Error code | Explanation | +| :-----------: | ------------- | +| **0x00** | Function executed successfully | +| **0x01** | `*constructor_of_choice*.begin()` was not called in `void setup()` | +| **0x02** | Unable to identify chip.
Is this chip officially supported? | +| **0x03** | Unable to identify capacity.
Is this chip officially supported?
If not, please define a `CAPACITY` constant and include it in `flash.begin(CAPACITY)`. | +| **0x04** | Chip is busy. Make sure all pins have been connected properly. | +| **0x05** | Page overflow has been disabled and the address called exceeds the memory | +| **0x06** | Unable to Enable Writing to chip.
Please make sure the HOLD & WRITEPROTECT pins are pulled up to VCC. | +| **0x07** | This sector already contains data.
Please make sure the sectors being written to are erased. | +| **0x08** | You are running low on RAM.
Please optimise your code for better RAM usage | +| **0x09** | Unable to suspend/resume operation.
Please raise an issue. | +| **0x0A** | Write Function has failed errorcheck.
Please raise an issue. | +| **0x0B** | Check your wiring. Flash chip is non-responsive. | +| **0x0C** | This function is not supported by the memory hardware. | +| **0x0D** | Unable to enable 4-byte addressing.
Please raise an issue. | +| **0x0E** | Unable to disable 4-byte addressing.
Please raise an issue. | +| **0x0F** | The chip is currently powered down. | +| **0x10** | The Flash chip does not support SFDP. | +| **0x11** | Unable to read Erase Parameters from chip.
Reverting to library defaults. | +| **0x12** | Unable to read erase times from flash memory.
Reverting to library defaults. | +| **0x13** | Unable to read program times from flash memory.
Reverting to library defaults. | +| **0xFE** | Unknown error.
Please raise an issue. | + +
From c50f7243d20795141f3276a2d6c2d5f5d408044a Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:38:41 +1000 Subject: [PATCH 03/25] Updated library speed comparisons doc --- extras/Library speed comparisons.xlsx | Bin 47585 -> 48428 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/extras/Library speed comparisons.xlsx b/extras/Library speed comparisons.xlsx index 902eda6b9a0cd0c6f6d11a0b246af5b71ef02adc..30d2b76719fc22c1f449bdbcb5ec0d6b7500dc06 100644 GIT binary patch literal 48428 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 N?A6CWsdfyE{{WAm$;$u$ literal 47585 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 From 444fde0f0d87af6f4d60630d72310d10e9757273 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:47:46 +1000 Subject: [PATCH 04/25] - An alternate version of the constructor ``` SPIFlash flash(int8_t *SPIPinsArray); ``` can be used (only with ESP32 boards as of now) to enable the use of custom SPI pins. ```SPIPinsArray``` has to be a 4 element array containing the custom SPI pin numbers (as signed integers - int8_t) in the following order - sck, miso, mosi, ss. Refer to the wiki for more information. Resolves #113 - Created new error code `NOCHIPSELECTDECLARED` for errors with declaring custom SPI pins. - Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #117 --- src/SPIFlash.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++--- src/SPIFlash.h | 17 ++++++++++++++- src/defines.h | 6 ++++- src/diagnostics.cpp | 4 ++++ src/diagnostics.h | 1 + 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/SPIFlash.cpp b/src/SPIFlash.cpp index cc0e663..172bdb6 100644 --- a/src/SPIFlash.cpp +++ b/src/SPIFlash.cpp @@ -27,36 +27,75 @@ #include "SPIFlash.h" // Constructor -//If board has multiple SPI interfaces, this constructor lets the user choose between them // Adding Low level HAL API to initialize the Chip select pinMode on RTL8195A - @boseji 2nd March 2017 #if defined (ARDUINO_ARCH_AVR) SPIFlash::SPIFlash(uint8_t cs) { + _SPIInUse = STDSPI; csPin = cs; cs_mask = digitalPinToBitMask(csPin); pinMode(csPin, OUTPUT); CHIP_DESELECT } -#elif defined (ARDUINO_ARCH_SAMD) || defined (ARCH_STM32) +#elif defined (ARDUINO_ARCH_SAMD) || defined (ARCH_STM32) || defined(ARDUINO_ARCH_ESP32) SPIFlash::SPIFlash(uint8_t cs, SPIClass *spiinterface) { _spi = spiinterface; //Sets SPI interface - if no user selection is made, this defaults to SPI + if (_spi == &SPI) { + _SPIInUse = STDSPI; + } + else { + _SPIInUse = ALTSPI; + } csPin = cs; pinMode(csPin, OUTPUT); CHIP_DESELECT } + #elif defined (BOARD_RTL8195A) SPIFlash::SPIFlash(PinName cs) { + _SPIInUse = STDSPI; gpio_init(&csPin, cs); gpio_dir(&csPin, PIN_OUTPUT); gpio_mode(&csPin, PullNone); gpio_write(&csPin, 1); CHIP_DESELECT } + #else +//#elif defined (ALTSPI) +//If board has multiple SPI interfaces, this overloaded constructor lets the user choose between them. Currently only works with ESP32 +SPIFlash::SPIFlash(int8_t *SPIPinsArray) { + _nonStdSPI.sck = SPIPinsArray[0]; + _nonStdSPI.miso = SPIPinsArray[1]; + _nonStdSPI.mosi = SPIPinsArray[2]; + _nonStdSPI.ss = SPIPinsArray[3]; + if (_nonStdSPI.ss == -1) + { + _troubleshoot(NOCHIPSELECTDECLARED, PRINTOVERRIDE); + return; + } + else { + csPin = _nonStdSPI.ss; + } + + if (_nonStdSPI.sck == -1 || _nonStdSPI.sck == -1 || _nonStdSPI.sck == -1) { + _SPIInUse = STDSPI; + } + else { + _SPIInUse = ALTSPI; + } + + pinMode(csPin, OUTPUT); + CHIP_DESELECT +} + +//#else SPIFlash::SPIFlash(uint8_t cs) { + _SPIInUse = STDSPI; csPin = cs; pinMode(csPin, OUTPUT); CHIP_DESELECT } + #endif //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// @@ -73,7 +112,15 @@ bool SPIFlash::begin(uint32_t flashChipSize) { Serial.println(F("Highspeed mode initiated.")); Serial.println(); #endif - BEGIN_SPI + if (_SPIInUse == ALTSPI) { + #if defined (ARDUINO_ARCH_ESP32) + SPI.begin(_nonStdSPI.sck, _nonStdSPI.miso, _nonStdSPI.mosi, _nonStdSPI.ss); + #endif + } + else { + BEGIN_SPI + } + #ifdef SPI_HAS_TRANSACTION //Define the settings to be used by the SPI bus _settings = SPISettings(SPI_CLK, MSBFIRST, SPI_MODE0); diff --git a/src/SPIFlash.h b/src/SPIFlash.h index d3ba888..9f6c0d2 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -33,12 +33,13 @@ class SPIFlash { public: //------------------------------------ Constructor ------------------------------------// //New Constructor to Accept the PinNames as a Chip select Parameter - @boseji 02.03.17 - #if defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32) + #if defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32) || defined(ARDUINO_ARCH_ESP32) SPIFlash(uint8_t cs = CS, SPIClass *spiinterface=&SPI); #elif defined (BOARD_RTL8195A) SPIFlash(PinName cs = CS); #else SPIFlash(uint8_t cs = CS); + SPIFlash(int8_t *SPIPinsArray); #endif //----------------------------- Initial / Chip Functions ------------------------------// bool begin(uint32_t flashChipSize = 0); @@ -153,14 +154,28 @@ class SPIFlash { #ifdef SPI_HAS_TRANSACTION SPISettings _settings; #endif + //If multiple SPI ports are available this variable is used to choose between them (SPI, SPI1, SPI2 etc.) SPIClass *_spi; + #if !defined (BOARD_RTL8195A) uint8_t csPin; #else // Object declaration for the GPIO HAL type for csPin - @boseji 02.03.17 gpio_t csPin; #endif + + // Variables specific to using non-standard SPI (currently only tested with ESP32) + struct _SPIPins { + int8_t sck = -1; + int8_t miso = -1; + int8_t mosi = -1; + int8_t ss = -1; + }; + _SPIPins _nonStdSPI; + //_SPIPins _stdSPI; + uint8_t _SPIInUse; + volatile uint8_t *cs_port; bool pageOverflow; bool SPIBusState = false; diff --git a/src/defines.h b/src/defines.h index 253be62..2d09178 100644 --- a/src/defines.h +++ b/src/defines.h @@ -42,11 +42,13 @@ #define BEGIN_SPI SPI.begin(); // Defines and variables specific to SAMD architecture - #elif defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32) + #elif defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32)|| defined(ARDUINO_ARCH_ESP32) #define CHIP_SELECT digitalWrite(csPin, LOW); #define CHIP_DESELECT digitalWrite(csPin, HIGH); #define xfer(n) _spi->transfer(n) #define BEGIN_SPI _spi->begin(); + + // Defines and variables not specific to any architecture #else #define CHIP_SELECT digitalWrite(csPin, LOW); #define CHIP_DESELECT digitalWrite(csPin, HIGH); @@ -174,6 +176,8 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// #define BUSY 0x01 +#define STDSPI 0x0A +#define ALTSPI 0x0B #if defined (ARDUINO_ARCH_ESP32) #define SPI_CLK 20000000 //Hz equivalent of 20MHz #else diff --git a/src/diagnostics.cpp b/src/diagnostics.cpp index e15fe0b..37ff058 100644 --- a/src/diagnostics.cpp +++ b/src/diagnostics.cpp @@ -144,6 +144,10 @@ void Diagnostics::troubleshoot(uint8_t _code, bool printoverride) { Serial.println(F("Unable to read program times from flash memory. Reverting to library defaults.")); break; + case NOCHIPSELECTDECLARED: + Serial.println(F("No Chip Select pin defined in the custom SPI Array.")); + break; + default: Serial.println(F("Unknown error")); break; diff --git a/src/diagnostics.h b/src/diagnostics.h index b22db91..2580b63 100644 --- a/src/diagnostics.h +++ b/src/diagnostics.h @@ -51,6 +51,7 @@ #define NOSFDPERASEPARAM 0x11 #define NOSFDPERASETIME 0x12 #define NOSFDPPROGRAMTIMEPARAM 0x13 + #define NOCHIPSELECTDECLARED 0x14 #define UNKNOWNERROR 0xFE //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// From 97a5037e5419ed2ff68090b981d6fa8b6d6d2c06 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:48:25 +1000 Subject: [PATCH 05/25] Update README.md with latest changes to constructor (re. custom SPI pins) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index de7b91c..f13bfa5 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ This Arduino library is for use with flash and FRAM memory chips that communicat - Every version of the library >= v3.0.0 supports the ability to use any of multiple SPI interfaces (if your micro-controller supports them). Switching to use another SPI interface is done by calling ```SPIFlash flash(csPin, &SPI1);``` (or &SPI2 and so on), instead of ```SPIFlash flash(csPin)```. * NOTE: This is currently only supported on the SAMD and STM32 architectures. +- An alternate version ```SPIFlash flash (SPIPinsArray)``` of the constructor can be used (only with ESP32 board as of now) to enable the use of custom SPI pins. ```SPIPinsArray``` has to be a 4 element array containing the custom SPI pin numbers (as signed integers - int8_t) in the following order - sck, miso, mosi, ss. - Also make sure to include ```flash.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of flash chip installed and load the right parameters. * Optional @@ -280,6 +281,7 @@ Note: If you are unable to fix the error/s, please raise an issue [here](http:// | **0x11** | Unable to read Erase Parameters from chip.
Reverting to library defaults. | | **0x12** | Unable to read erase times from flash memory.
Reverting to library defaults. | | **0x13** | Unable to read program times from flash memory.
Reverting to library defaults. | +| **0x14** | No Chip Select pin defined in the custom SPI Array.
Refer to section about Constructor for information on how to use custom SPI pins. | | **0xFE** | Unknown error.
Please raise an issue. |
From 8071dc36aca9ac8fddb4586ffcfdd9e4cf2b2e4d Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:49:29 +1000 Subject: [PATCH 06/25] Updated FlashDiagnostics.ino to work with latest changes. --- examples/FlashDiagnostics/FlashDiagnostics.ino | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/FlashDiagnostics/FlashDiagnostics.ino b/examples/FlashDiagnostics/FlashDiagnostics.ino index 031ba47..771ffa6 100644 --- a/examples/FlashDiagnostics/FlashDiagnostics.ino +++ b/examples/FlashDiagnostics/FlashDiagnostics.ino @@ -2,10 +2,10 @@ |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| | FlashDiagnostics.ino | | SPIMemory library | - | v 3.2.0 | + | v 3.4.0 | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| | Marzogh | - | 02.05.2018 | + | 01.06.2019 | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| | | | For a full diagnostics rundown - with error codes and details of the errors | @@ -37,15 +37,20 @@ #define TRUE 1 #define FALSE 0 +int8_t SPIPins[4] = {-1, -1, -1, 33}; + SPIFlash flash; -//SPIFlash flash(11); +//SPIFlash flash(SPIPins); +//SPIFlash flash(33); //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) || defined(NRF5) +#if defined (ARDUINO_ARCH_SAMD) || defined (__AVR_ATmega32U4__) || defined (ARCH_STM32) || defined (NRF5) || defined (ARDUINO_ARCH_ESP32) while (!Serial) ; // Wait for Serial monitor to open #endif + delay(50); //Time to terminal get connected Serial.print(F("Initialising")); for (uint8_t i = 0; i < 10; ++i) @@ -54,6 +59,7 @@ void setup() { } Serial.println(); randomSeed(analogRead(RANDPIN)); + Serial.println(flash.error(VERBOSE)); flash.begin(); //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)); From f44c3d1cc6075e866d2a4b5d70eaaa572b533057 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:49:44 +1000 Subject: [PATCH 07/25] Removed changes.log --- extras/Changes.log | 391 --------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 extras/Changes.log diff --git a/extras/Changes.log b/extras/Changes.log deleted file mode 100644 index a14eebd..0000000 --- a/extras/Changes.log +++ /dev/null @@ -1,391 +0,0 @@ -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// SPIMemory Library // -// Changes log // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 3.3.0 // -// Author: Prajwal Bhattaram // -// 20.04.2019 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Library now supports FRAM memory (In BETA). Documentation for this is in progress. - -Bugs squashed: ---> Fixed a major bug causing issues with page boundaries in writeAnything and writeStr. Thanks for your help @jacky4566 (Issue #151) ---> Fixed a major bug with reading Strings (Issue #143) ---> @incorvia fixed a bug by initialising the SPIBusState to false ---> Fixes bug that causes ballooning of pagesize when SFDP is not read. This is because no default _pageSize value was set in this scenario. This has now been fixed. ---> Fixed a major bug in how SFDP data is used to calculate chip capacity. The previous version of the library returned a wrong value. This has now been fixed. Refer to comments in _getSFDPFlashParam(void) for further details. - -Enhancements: ---> Reduce SRAM footprint when running Diagnostics.ino. Thanks @rambo (PR #157) ---> Officially supports the ESP32 (tested on the ESP32 Feather) ---> Now supports FRAM memory chips via the `SPIFram` constructor ---> Changed Diagnostics output in _chipID() to match situation better. ---> Fixed _chipID() to run more efficiently. - -New flash memory chips supported: ---> GD25Q16C from Giga devices (Used on the Adafruit ItsyBitsy M0 Express) ---> W25Q64JV from Winbond ---> SST26VF016B & SST26VF032B from Microchip. (Thanks @mix86) - -New FRAM memory chips supported: ---> FM25W256 from Cypress -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// 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 // -// 13.04.2018 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// ---> Library name changed to SPIMemory - ---> The library now gathers sfdp information through _chipID(). SFDP information is collected when available, otherwise the library defaults to the pre v3.2.0 version. - -New flash memory chips supported ---> All flash memories compatible with the SFDP standard (as at JESD216B) should be compatible with the library - ---> LE25U40CMC from ON Semiconductor ---> A25L512A0 from AMIC ---> M25P40 from Micron ---> AT25SF041 from Adesto ---> S25FL032P from Cypress/Spansion - -New Microcontrollers supported ---> ATSAMD51J19 ARM Cortex M4 (Adafruit Metro M4) - -Bugs squashed - -Enhancements -The library now does the following - if the flash memory chip is compatible with the SFDP standard: ---> Identifies the flash memory chip using the Serial Flash Discoverable Parameters standard. (JESD216B) ---> Identifies the following opcodes from the SFDP tables and uses them to read/write data: - - eraseSector, eraseBlock32K, eraseBlock64K ---> Reads timing information from SFDP to accurately time the following operations: - - eraseSector, eraseBlock32K, eraseBlock64K -- Fixes #125 in commit 10c2b86. The library automatically uses the alternate chip erase code (0xC7) as required (only on fully supported chips) - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 3.1.0 // -// Author: Prajwal Bhattaram // -// 24.02.2018 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// ---> Library name change notice inserted into flash.begin(). This notice can be dismissed by commenting out the instance of `#define PRINTNAMECHANGEALERT` in SPIFlash.h. Please refer to the Readme file for further details. - -Bugs squashed ---> An error with how _addressCheck() works with data that spans the memory boundary - when rolling over from address '_chip.capacity' to address '0x00' - has been fixed. In previous versions this caused issues with writing complex data structures across the memory boundary and led to many _writeErrorCheck() failures. Fixes issue #112 - ---> An error with how _writeErrorCheck() worked has been resolved. Writing structs is now as stable as other functions are. Fixes #106 - ---> All compilation-time, non-critical errors have been fixed. - -Enhancements: ---> A new function - 'flash.eraseSection(address, size)' - has been introduced in this version. When a user requires a large and variable (between writes) amount of data to be written to the flash memory on the fly and to have the correct amount of space erased to fit the data, this function will automatically calculate and erase the right amount of space to fit the data. Please note that if the the amount of data being written is consistently the same size, the pre-existing 'flash.eraseSector()', 'flash.eraseBlock32K()' and 'flash.eraseBlock64K()' functions will operate a lot faster. - ---> Updated Diagnostics.ino to include eraseSection(). - ---> All I/O functions now check to see if the flash chip is powered down. If it is, they prevent the function from running and returns an error. A new error code 'CHIPISPOWEREDDOWN' will be returned upon calling flash.error(). - - -New flash memory chips supported: ---> S25FL127S -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 3.0.1 // -// Author: Prajwal Bhattaram // -// 10.12.2017 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs squashed: -**************** -Thanks for catching and fixing the following @hanyazou (PR #101) ---> Added missing break statements in SPIFlash::_beginSPI() because that lacks some essential break statement in switch/case. (commit 1: 1de2c10) ---> The library did not wait until the busy bit in SR1 was cleared. Timeout argument of _notBusy() must be in micro-second. 500L means 500 micro seconds which is too short to wait for completion of erase command. Added " * 1000" to the arguments. (commit 2: 9633c2f) ---> Modified TestFlash.ino to check error of erase commands execution. (commit 3: a179306) -**************** ---> Fixes a major bug that was causing input Arrays to be overwritten by zeros during the writeByteArray() / writeCharArray() process. Thanks for catching this @hanyazou. (Issue #102) - -New flash memory chips supported: ---> S25FL127S -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 3.0.0 // -// Author: Prajwal Bhattaram // -// 09.08.2017 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs Squashed: ---> The writeByteArray() & writeCharArray() bug that occurred when writing arrays that spanned page boundaries (squashed in v2.5.0), stayed around to haunt the other functions. Writing any data larger than a single byte that spanned page boundaries would cause the data to wrap around to the beginning of the page. The likelihood of this occurring was slim - no one has reported it to date. However, just in case, this has now been squashed in this release. - -Deprecations: ---> Going forward the ATTiny85 is no longer officially supported. ---> The library no longer supports using the page number + offset combination instead of addresses. If your code requires you to use a page number + offset combination, use the following code to help - address = (pagenumber << 8) + offset. - _____________________________________ - (32 bit) | (16 bit) | (8 bit) ---> The constructor no longer takes the pageOverflow variable as an argument. Page overflow is globally enabled by default and can be disabled by including a "#define DISABLEOVERFLOW" at the beginning of SPIFlash.h. - -New Boards supported: ---> Nucleo-F091RC ---> Adafruit Feather M0 - -New Flash manufacturers supported: ---> Microchip (SST25 & SST26 series) ---> Cypress ---> Spansion - -Enhancements: ---> Confirmed to work with SPANSION/CYPRESS & MICROCHIP (Both SST25 & SST26 series). ---> If not using an officially supported chip, use the following variation of flash.begin() : - flash.begin(flashChipSize); - - where flashChipSize is indicated in Bytes, Kilobytes or Megabytes. (Refer to the next two items in this change log) ---> Including 'flashChipSize' in flash.begin() compiles more efficiently than in previous versions. ---> The way memory size is indicated by the users in flash.begin(flashChipSize) has changed - please refer to defines.h or the wiki for further information. The new method enables users to define any custom size unlike the previous version where only a limited number of predetermined sizes were available to use. ---> Library faster than before (Refer to Library speed comparison in the extras folder for timing details): - - Improvements in speed in v3.0.0 when compared to v2.7.0 (values in percentage of time v3.0.0 is faster than v2.7.0) - (+) writeByte -> +3% - (+) writeChar -> +6% - (+) writeWord -> +3% - (+) writeShort -> +5% - (+) writeULong -> +7% - (+) writeLong | readLong -> +7% | +7% - (+) writeFloat -> +7% - (+) writeStr | readStr -> +57% | +61% - (+) writeAnything | readAnything (struct) -> +25% | +20% - (+) writeByteArray | readByteArray (256 bytes) -> +18% | +45% - (+) eraseSector -> +99% - (+) eraseBlock32K -> +99.4% - (+) eraseBlock64K -> +99.5% - (+) eraseChip -> +12% - ---> Constructor changed to enable user to choose one of multiple SPI ports - if available. Look at wiki for further info ---> When RUNDIAGNOSTIC is uncommented in SPIFlash.h, users now have access to a new function called flash.functionRunTime() which can be called after any library I/O function is run. flash.functionRunTime() returns the time taken by the previous function to run, in microseconds (as a float). An example use case can be found when the FlashDiagnostics sketch is run with RUNDIAGNOSTIC uncommented. ---> _notBusy() is faster ---> Completely re-written FlashDiagnostics - uses fewer resources, compatible with more chips and boards ---> All functions except the Byte/Char Array Read/Write functions now call an internal _write/_read function for increased ease of compilation, execution and troubleshooting ---> Restructured the internal _troubleshoot() function to be better human readable and faster to compile. ---> Added a function `getUniqueID()` which returns the unique ID of the flash memory chip as a 64-bit integer. ---> Added an internal _readStat3() function to enable 4-byte addressing when using flash memory larger than 128 MB ---> 4-byte addressing enabled in flash memory larger than 128 MB. This is currently only supported on the W25Q256 and W25Q512. More chips will be supported on a case-by-case basis ---> getAddress() function now works anytime it is called - even if there is preexisting data on the chip -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.7.0 // -// Author: Prajwal Bhattaram // -// 17.04.2017 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs Squashed: ---> ATTiny85 support available again. - -Deletions: ---> - -New Boards supported: ---> Arduino Zero now fully supported. - -Enhancements: ---> Compatible with the ESP32 core for Arduino as of the current commit
9618eec on 02.08.2017. For some unknown reason, SPI clock speeds higher than board speed/4 are not stable and so, the clock speed for ESP32 dev boards has currently been throttled to 20MHz. -NOTE: ESP32 boards usually have an SPIFlash already attached to their SS pin, so the user has to declare the ChipSelect pin being used when the constructor is declared - for example -``` -SPIFlash flash(33); -``` -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.6.0 // -// Author: Prajwal Bhattaram // -// 16.04.2017 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs Squashed: ---> Fixed issue with reading status register 2 and the Suspend operation where the library was not checking the suspend status correctly. - -Deletions: ---> flash.getChipName() has been removed as it is superfluous now that the library is now on its way to multi-chip compatibility. ---> Library is not compatible with the ATTiny85 anymore. Currently working on a fix which will hopefully be rolled out in v2.7.0 - -New Boards supported: ---> RTL8195A compatibility tested and enabled by @boseji on 02.03.17. Code modified to fit with the library structure by Prajwal Bhattaram on 14.04.17. ---> Compatible with the ESP32 core for Arduino as of the current commit 7d0968c on 16.04.2017. The ESP32 core currently does not support analogRead and so randomSeed(AnalogRead(A0)) cannot be used. Also, for some unknown reason, SPI clock speeds higher than board speed/4 are not stable and so, the clock speed for ESP32 dev boards has currently been throttled to 20MHz. ---> Added support for the Simblee module. - -Enhancements: ---> flash.error() now takes an optional argument 'VERBOSE'. By default the verbosity argument is set to 'false' and calling flash.error() still returns the latest error code as an unsigned byte. However, running flash.error(VERBOSE) will not only return the latest error code, but will also print it to serial. This - especially in boards with resources to spare - will result in a detailed error report printed to serial. ---> flash.begin() now returns a boolean value to indicate establishment of successful comms with the flash chip. Usercode can be made more efficient now by calling ```if (!flash.begin()) { Serial.println(flash.error(VERBOSE)); }``` to identify a problem as soon as the library code is run. ---> The internal _addressCheck() function now locks up usercode with appropriate error codes if - a) flash.begin() has not been called (or) - b) There is a possible issue with the wiring - i.e. the flash chip is non-responsive (or) - c) If the chip's capacity either cannot be identified & the user has not defined a chipSize in flash.begin(). ---> Fixed powerDown() to be more efficient. The chip now powers down much faster than before. ---> Added a new error code. Library can now detect non-responsive chips - bad wiring or otherwise. ---> Streamlined variables to make code structure better. ---> Changed library structure to enable the addition of multi-flash compatibility in the near future. ---> moved `#define RUNDIAGNOSTIC` & `#define HIGHSPEED` to SPIFlash.h from SPIFlash.cpp. ---> Now works with other Winbond modules (not in the official supported module list) (beta) by taking the flash memory size in bits as an argument in flash.begin(_chipSize); ---> Formatted code to be better human readable. ---> Changed the internal _troubleshoot() function to use fewer resources. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.5.0 // -// Author: Prajwal Bhattaram // -// 13.11.2016 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs Squashed: ---> Errorchecking on writeByteArray() and writeCharArray() was broken in 2.4.0 and is fixed now. ---> Previous versions (< 2.5.0) suffered from the inability to write datasets that spanned page boundaries. This has now been fixed (Ref. Enhancements section below) - -Deletions: ---> flash.writePage() & flash.readPage() have been retired as they can be duplicated - more effectively - by other functions. Refer to pageDiag() in Diagnostics.ino to see how the same results can be obtained by using flash.writeByteArray() & flash.readByteArray(). - -Enhancements: ---> Added support for SPI Transactions. ---> Added an upgrade for Arduino Due users to read and write large arrays of data at very high speeds - previously reading 60kb of data would take ~108ms - it now takes ~525uS. In this mode, the time between consecutive reads has been reduced to <12ms (needs more work!). ---> Completely re-written writeByteArray() and writeCharArray() functions. They no longer breakdown when writing arrays that span multiple pages. ---> All functions that write more than 1 byte of data now take into account page boundaries when writing across pages. In previous versions writing across a page boundary would cause the remainder of the data to wrap around to the beginning of the page. Now the data is written seamlessly from one page over to the next. ---> Calling flash.begin() now instantiates the SPI bus. Comms with other SPI slaves do not need to call SPI.begin() if flash.begin() has already been called first in the user's code. ---> Sped up _beginSPI(). Code in _beginSPI() instantiates the SPI bus if another SPI comms library has shut it down. ---> Added function allowing setting SPI clock speeds - call the function ```flash.setClock(uint32_t clockSpeed);``` straight after ```begin();```. ---> Made _beginSPI() & _nextByte() shorter and faster. ---> Replaced all calls to SPI.transfer() with xfer() to make the code more human readable and to enable simplicity when switching platforms. ---> Added the ability to change Chip Erase wait times depending on the chip (supported chips only). ---> Don't need to ```#include ``` in user code anymore. Just ```#include``` will do. Examples are now updated. ---> Now supports & uses SPI.transfer(*buf, count) along with the standard SPI.transfer(), speeding up the read/write of anything larger than an byte (AVR Boards only) ---> Reduces the program storage space in the uC's flash (AVR Boards) - -To-do ---> Need to find a way to better identify _notPrevWritten(); ---> Need to fix up eraseBlock32K, eraseBlock64K and eraseSector times -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.4.0 // -// Author: Prajwal Bhattaram // -// 11.09.2016 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -Bugs Squashed: ---> Fixed bug preventing writing to Address 0x00 ---> Fixed bug in _notPrevWritten() which did did not perform a thorough enough check to see if a location had been previously written to. ---> Fixed errorchecking bug - it now works for all data types - not just byte/char as previously. -Enhancements & Optimizations: ---> Added a function 'error()' to enable users to check for errors generated at any point in their program. ---> Optimized writePage() so it doesn't depend on other functions and so runs faster than before. ---> Diagnostics.ino now outputs time taken for each function as a part of its and provides additional diagnostic data. ---> Now have a common set of private functions - _prep(), _beginSPI(), _nextByte() & _endSPI() - to replace _prepWrite(), _prepRead(), _beginRead(), _readNextByte(), _beginWrite(), _writeNextByte() & _endProcess(); ---> Changed the way _addressCheck() works. It now checks all addresses involved in writing the data rather than one address block at a time and returns the correct address to write to in case overflow is enabled. Also, this function is now built into _prep(). ---> Reading and writing using page numbers + offset has been optimised to be faster than before. ---> Optimized the way address and error checking is done so its more efficient and uses few system resources and runs faster. ---> Using this library with an ESP8266 board defaults to using GPIO15 as the Slave Select - unless something different is explicitly specified in the constructor. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.3.1 // -// Author: Prajwal Bhattaram // -// 19.06.2016 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> W25Q256FV support added. (Thanks Stanislav-Povolotsky!) ---> Cleaned up redundant code in TestFlash.ino and SPIFlash.h -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.3.0 // -// Author: Prajwal Bhattaram // -// 04.06.2016 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> ESP8266 compatibility added ---> Compatible with Arduino Fio and Micro ---> Now compatible with Arduino IDE v1.6.9 ---> Compatible with Arduino-Makefile. (Thanks Raphael!) -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.2.0 // -// Author: Prajwal Bhattaram // -// 24.11.2015 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> When #RUNDIAGNOSTIC is uncommented, the _troubleshoot() function is now optimised for different µCs ---> Added the ability to check if the address has been previously written to before initiating a write - operation. ---> Added a sizeofStr() function to get sizes of String objects, to use with the getAddress() function ---> Fixed a bug with getAddress() ---> Added the ability get the chip's name via getChipName() ---> Diagnostics.ino has been made more and efficient and provides a cleaner Serial output ---> Added getAddressEx.ino to show how getAdress() works. - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.1.1 // -// Author: Prajwal Bhattaram // -// 24.10.2015 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> Fixed bugs that prevented the following functions from working properly - A. powerDown() - B. powerUp() - C. suspendProg() - D. resumeProg() - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.1.0 // -// Author: Prajwal Bhattaram // -// 18.10.2015 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> Arduino Due compatible - refer to wiki for further details ---> Fixed bug with write/readByteArray ---> Added write/readCharArray ---> Added a proper error checking function that gets called when #ifdef RUNDIAGNOSTIC is uncommented in SPIFlash.cpp. - This function returns a verbose error message to the Serial console instead of the terse error codes of the previous version. ---> The following functions have been changed to enable bug fixes and uniformity in coding style. - A. writeBytes() - B. readBytes() - They have been replaced with readByteArray() and writeByteArray(). - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 2.0.0 // -// Author: Prajwal Bhattaram // -// 12.10.2015 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> Sped up all functions atleast 25x ---> Compatible with ATTiny85 ---> All Read/Write/Erase functions can now take either (page number & offset) or (address) as arguments - (Except readPage() & printPage()) ---> getAddress() can now return either a 32-bit address or a page number & offset - Refer to Readme.md ---> Error codes explained: - 0x00 SUCCESS Operation successful. - 0x01 CALLBEGIN Please make sure .begin() has been called in setup(). - 0x02 UNKNOWNCHIP Unknown chip manufacturer. - 0x03 UNKNOWNCAP Unknown chip capacity. - 0x04 CHIPBUSY Chip busy. - 0x05 OUTOFBOUNDS Address out of bounds. Please check if .begin() has been called in setup(). - 0x06 CANTENWRITE Unable to _writeEnable. Check wiring/chip. - 0x07 OUTOFMEM Pagenumber outside maximum. - 0x08 OUTOFPAGE Offset is outside page. ---> The following functions are deprecated to enable compatibility with other AVR chips. - - A. _printPageBytes() - B. printPage() - C. printAllPages() - D. readSerialStr() - - They can be used by uncommenting them in the SPIFlash.cpp file. However, be warned, this particular block of code has only - been tested with the Arduino IDE (1.6.5) and only with 8-bit AVR based Arduino boards and will not be supported any further. - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Version 1.3.2 // -// Author: Prajwal Bhattaram // -// 09.10.2015 // -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - ---> Added the ability to read and write String objects with a simple high level function readStr() & writeStr() ---> Added the ability to getAddress() ---> Added the ability to fastRead to every read function as the last boolean argument (defaults to FALSE) ---> Changed the example code as given below: - --> Modified TestFlash.ino to use 25% less memory - --> Struct_writer.ino now writes struct to a random location on the Flash Memory chip. - --> Added instructions for real world data storage to Struct_writer.ino - --> Diagnostics.ino now provides a cleaner diagnostic readout From fe6d262f1c16d6299d5b445b8104b626c2484328 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:51:46 +1000 Subject: [PATCH 08/25] Created, markdown formatted and cleaned new change log file - Change log.md - and moved it to root folder of the library. --- Change log.md | 434 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 Change log.md diff --git a/Change log.md b/Change log.md new file mode 100644 index 0000000..55c15a6 --- /dev/null +++ b/Change log.md @@ -0,0 +1,434 @@ +# SPIMemory +##### Change log +[![Build Status](https://travis-ci.org/Marzogh/SPIMemory.svg?branch=master)](https://travis-ci.org/Marzogh/SPIMemory) [![DOI](https://zenodo.org/badge/35823047.svg)](https://zenodo.org/badge/latestdoi/35823047) +[![GitHub release](https://img.shields.io/github/release/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory) +[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.3.0.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.3.0...development) +[![GitHub issues](https://img.shields.io/github/issues/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/issues) +[![GitHub pull requests](https://img.shields.io/github/issues-pr/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/pulls) +[![license](https://img.shields.io/github/license/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/blob/master/LICENSE) +[![Join the chat at https://gitter.im/SPIMemory/community](https://badges.gitter.im/SPIMemory/community.svg)](https://gitter.im/SPIMemory/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +>This Arduino library is for use with flash and FRAM memory chips that communicate using the SPI protocol. In its current form it supports identifying the flash/FRAM chip and its various features; automatic address allocation and management; writing and reading a number of different types of data, ranging from 8-bit to 32-bit (signed and unsigned) values, floats, Strings, arrays of bytes/chars and structs to and from various locations; sector, block and chip erase; and powering down for low power operation. + +
+## Version 3.4.0 (In progress) +#### Release date TBA + +###### Bugs squashed: + +###### Enhancements: +- An alternate version of the constructor +``` +SPIFlash flash(int8_t *SPIPinsArray); +``` +can be used (only with ESP32 boards as of now) to enable the use of custom SPI pins. ```SPIPinsArray``` has to be a 4 element array containing the custom SPI pin numbers (as signed integers - int8_t) in the following order - sck, miso, mosi, ss. Refer to the wiki for more information. Resolves #113 +- Created new error code `NOCHIPSELECTDECLARED` for errors with declaring custom SPI pins. +- Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #117 + +###### New flash memory chips supported: + +###### New FRAM memory chips supported: + + +
+## Version 3.3.0 +#### Release date 20.04.2019 + +>Library now supports FRAM memory (In BETA). Documentation for this is in progress. + +###### Bugs squashed: +- Fixed a major bug causing issues with page boundaries in writeAnything and writeStr. Thanks for your help @jacky4566 (Issue #151) +- Fixed a major bug with reading Strings (Issue #143) +- @incorvia fixed a bug by initialising the SPIBusState to false +- Fixes bug that causes ballooning of pagesize when SFDP is not read. This is because no default `_pageSize` value was set in this scenario. This has now been fixed +- Fixed a major bug in how SFDP data is used to calculate chip capacity. The previous version of the library returned a wrong value. This has now been fixed. Refer to comments in `_getSFDPFlashParam(void)` for further details. + +###### Enhancements: +- Reduce SRAM footprint when running Diagnostics.ino. Thanks @rambo (PR #157) +- Officially supports the ESP32 (tested on the ESP32 Feather) +- Now supports FRAM memory chips via the `SPIFram` constructor +- Changed Diagnostics output in `_chipID()` to match situation better. +- Fixed `_chipID()` to run more efficiently. + +###### New flash memory chips supported: +- GD25Q16C from Giga devices (Used on the Adafruit ItsyBitsy M0 Express) +- W25Q64JV from Winbond +- SST26VF016B & SST26VF032B from Microchip. (Thanks @mix86) + +###### New FRAM memory chips supported: +- FM25W256 from Cypress + +
+## Version 3.2.1 +#### Release date 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 | Test platform | +|:-----:|:-----:|:-----:| +|FlashDiagnostics.ino from v3.2.0
with #RUNDIAGNOSTIC commented out | Arduino Pro Mini 8MHz 3.3V | + + +| Library version | Compiled code size
(Bytesb | SFDP status | Size compared to v3.1.0 | +| :------: | :------: | :------: | :-----: | +| v3.1.0 | 17652 | SFDP discovery not supported | Same size | +| v3.2.0 | 20104 | SFDP discovery supported & on by default | 13.9% larger than v3.1.0 | +| v3.2.1 | 15316 | SFDP discovery turned off | 13.75% smaller than v3.1.0 | +| v3.2.1 | 17854 bytes | SFDP discovery turned on | 1.1% larger than v3.1.0 | + +
+## Version 3.2.0 +#### Release date 24.01.2018 + +- Library name changed to SPIMemory + +- The library now gathers sfdp information through `_chipID()`. SFDP information is collected when available, otherwise the library defaults to the pre v3.2.0 version. + +###### New flash memory chips supported +- All flash memories compatible with the SFDP standard (as at JESD216B) should be compatible with the library + +- LE25U40CMC from ON Semiconductor +- A25L512A0 from AMIC +- M25P40 from Micron +- AT25SF041 from Adesto +- S25FL032P from Cypress/Spansion + +###### New Microcontrollers supported +--> ATSAMD51J19 ARM Cortex M4 (Adafruit Metro M4) + +###### Enhancements +The library now does the following - if the flash memory chip is compatible with the SFDP standard: +- Identifies the flash memory chip using the Serial Flash Discoverable Parameters standard. (JESD216B) +- Identifies the following opcodes from the SFDP tables and uses them to read/write data: + - `eraseSector` + - `eraseBlock32K` + - `eraseBlock64K` +- Reads timing information from SFDP to accurately time the following operations: + - `eraseSector` + - `eraseBlock32K` + - `eraseBlock64K` +- Fixes #125 in commit 10c2b86. The library automatically uses the alternate chip erase code (0xC7) as required (only on fully supported chips) + +
+## Version 3.1.0 +#### Release date 24.01.2018 +- Library name change notice inserted into `flash.begin()`. This notice can be dismissed by commenting out the instance of `#define PRINTNAMECHANGEALERT` in SPIFlash.h. Please refer to the Readme file for further details. + +Bugs squashed +- An error with how `_addressCheck()` works with data that spans the memory boundary - when rolling over from address `_chip.capacity` to address `0x00` - has been fixed. In previous versions this caused issues with writing complex data structures across the memory boundary and led to many `_writeErrorCheck()` failures. Fixes issue #112 + +- An error with how `_writeErrorCheck()` worked has been resolved. Writing structs is now as stable as other functions are. Fixes #106 + +- All compilation-time, non-critical errors have been fixed. + +###### Enhancements: +- A new function - `flash.eraseSection(address, size)` - has been introduced in this version. When a user requires a large and variable (between writes) amount of data to be written to the flash memory on the fly and to have the correct amount of space erased to fit the data, this function will automatically calculate and erase the right amount of space to fit the data. Please note that if the the amount of data being written is consistently the same size, the pre-existing `flash.eraseSector()`, `flash.eraseBlock32K()` and `flash.eraseBlock64K()` functions will operate a lot faster. +- Updated `Diagnostics.ino` to include `eraseSection()`. + +- All I/O functions now check to see if the flash chip is powered down. If it is, they prevent the function from running and returns an error. A new error code 'CHIPISPOWEREDDOWN' will be returned upon calling `flash.error()`. + + +###### New flash memory chips supported: +- S25FL127S + +
+## Version 3.0.1 +#### Release date 10.12.2017 + +###### Bugs squashed: + +- Thanks for catching and fixing the following @hanyazou (PR #101) + - Added missing break statements in `_beginSPI()` because that lacks some essential break statement in switch/case. (commit 1: 1de2c10) + - The library did not wait until the busy bit in SR1 was cleared. Timeout argument of `_notBusy()` must be in micro-second. 500L means 500 micro seconds which is too short to wait for completion of erase command. Added " * 1000" to the arguments. (commit 2: 9633c2f) + - Modified `TestFlash.ino` to check error of erase commands execution. (commit 3: a179306) + +- Fixes a major bug that was causing input Arrays to be overwritten by zeros during the writeByteArray() / writeCharArray() process. Thanks for catching this @hanyazou. (Issue #102) + +New flash memory chips supported: +- S25FL127S + + +
+## Version 3.0.0 +#### Release date 09.08.2017 + +###### Bugs Squashed: +- The `writeByteArray()` & `writeCharArray()` bug that occurred when writing arrays that spanned page boundaries (squashed in v2.5.0), stayed around to haunt the other functions. Writing any data larger than a single byte that spanned page boundaries would cause the data to wrap around to the beginning of the page. This has now been squashed in this release. + +###### Deprecations: +- Going forward the ATTiny85 is no longer officially supported. +- The library no longer supports using the page number + offset combination instead of addresses. If your code requires you to use a page number + offset combination, use the following code to help +``` +address = (pagenumber << 8) + offset. +(32 bit) | (16 bit) | (8 bit) +``` +- The constructor no longer takes the pageOverflow variable as an argument. Page overflow is globally enabled by default and can be disabled by including a "#define DISABLEOVERFLOW" at the beginning of SPIFlash.h. + +###### New Boards supported: +- Nucleo-F091RC +- Adafruit Feather M0 + +###### New Flash manufacturers supported: +- Microchip (SST25 & SST26 series) +- Cypress +- Spansion + +###### Enhancements: +- Confirmed to work with SPANSION/CYPRESS & MICROCHIP (Both SST25 & SST26 series). +- If not using an officially supported chip, use the following variation of flash.begin() : +``` +flash.begin(flashChipSize) +``` + + where flashChipSize is indicated in Bytes, Kilobytes or Megabytes. (Refer to the next two items in this change log) + +- Including 'flashChipSize' in flash.begin() compiles more efficiently than in previous versions. +- The way memory size is indicated by the users in flash.begin(flashChipSize) has changed - please refer to defines.h or the wiki for further information. The new method enables users to define any custom size unlike the previous version where only a limited number of predetermined sizes were available to use. +- Library faster than before (Refer to Library speed comparison in the extras folder for timing details): + +> Improvements in speed in v3.0.0 when compared to v2.7.0 (values in percentage of time v3.0.0 is faster than v2.7.0) +> +>| Function | Change in speed | +>| :-----: | :-----: | +>| writeByte | +3% | +>| writeChar | +6% | +>| writeWord | +3% | +>| writeShort | +5% | +>| writeULong | +7% | +>| writeLong | +7% | +>| readLong | +7% | +>| writeFloat | +7% | +>| writeStr | +57% | +>| readStr | +61% | +>| writeAnything
(struct) | +25% | +>| readAnything
(struct) | +20% | +>| writeByteArray
(256 bytes) | +18% | +>| readByteArray
(256 bytes) | +45% | +>| eraseSector | +99% | +>| eraseBlock32K | +99.4% | +>| eraseBlock64K | +99.5% | +>| eraseChip | +12% | + +- Constructor changed to enable user to choose one of multiple SPI ports - if available. Look at wiki for further info +- When `RUNDIAGNOSTIC` is uncommented in SPIFlash.h, users now have access to a new function called `flash.functionRunTime()` which can be called after any library I/O function is run. `flash.functionRunTime()` returns the time taken by the previous function to run, in microseconds (as a float). An example use case can be found when the FlashDiagnostics sketch is run with `RUNDIAGNOSTIC` uncommented. +- `_notBusy()` is faster +- Completely re-written `FlashDiagnostics.ino` - uses fewer resources, compatible with more chips and boards +- All functions except the Byte/Char Array Read/Write functions now call an internal `_write`/`_read` function for increased ease of compilation, execution and troubleshooting +- Restructured the internal `_troubleshoot()` function to be better human readable and faster to compile. +- Added a function `getUniqueID()` which returns the unique ID of the flash memory chip as a 64-bit integer. +- Added an internal `_readStat3()` function to enable 4-byte addressing when using flash memory larger than 128 MB +- 4-byte addressing enabled in flash memory larger than 128 MB. This is currently only supported on the W25Q256 and W25Q512. More chips will be supported on a case-by-case basis +- `getAddress()` function now works anytime it is called - even if there is preexisting data on the chip + +
+## Version 2.7.0 +#### Release date 17.04.2017 + +###### Bugs Squashed: +- ATTiny85 support available again. + +###### New Boards supported: +- Arduino Zero now fully supported. + +###### Enhancements: +- Compatible with the ESP32 core for Arduino as of the current commit [9618eec](https://github.com/espressif/arduino-esp32/tree/9618eec19ea470bb59b9a25ebeb8c0c516ca89cc) on 02.08.2017. For some unknown reason, SPI clock speeds higher than board speed/4 are not stable and so, the clock speed for ESP32 dev boards has currently been throttled to 20MHz. + + _NOTE: ESP32 boards usually have an SPIFlash already attached to their SS pin, so the user has to declare the ChipSelect pin being used when the constructor is declared - for example +`SPIFlash flash(33);`_ + +
+## Version 2.6.0 +#### Release date 16.04.2017 + +###### Bugs Squashed: +- Fixed issue with reading status register 2 and the Suspend operation where the library was not checking the suspend status correctly. + +###### Deletions: +- `flash.getChipName()` has been removed as it is superfluous now that the library is now on its way to multi-chip compatibility. +- Library is not compatible with the ATTiny85 anymore. Currently working on a fix which will hopefully be rolled out in v2.7.0 + +###### New Boards supported: +- RTL8195A compatibility tested and enabled by @boseji on 02.03.17. Code modified to fit with the library structure by Prajwal Bhattaram on 14.04.17. +- Compatible with the ESP32 core for Arduino as of the current commit [7d0968c](https://github.com/espressif/arduino-esp32/tree/7d0968c10dc540709d69a28eab2457075c1e4fa8) as on 16.04.2017. The ESP32 core currently does not support analogRead and so randomSeed(AnalogRead(A0)) cannot be used. + + For an unknown reason, SPI clock speeds higher than board speed/4 are not stable and so, the clock speed for ESP32 dev boards has currently been throttled to 20MHz. +- Added support for the Simblee module. + +###### Enhancements: +- `flash.error()` now takes an optional argument 'VERBOSE'. By default the verbosity argument is set to 'false' and calling `flash.error()` still returns the latest error code as an unsigned byte. However, running `flash.error(VERBOSE)` will not only return the latest error code, but will also print it to serial. This - especially in boards with resources to spare - will result in a detailed error report printed to serial. +- `flash.begin()` now returns a boolean value to indicate establishment of successful comms with the flash chip. Usercode can be made more efficient now by calling ```if (!flash.begin()) { Serial.println(flash.error(VERBOSE)); }``` to identify a problem as soon as the library code is run. +- The internal `_addressCheck()` function now locks up usercode with appropriate error codes if + 1. `flash.begin()` has not been called (or) + 2. There is a possible issue with the wiring - i.e. the flash chip is non-responsive (or) + 3. If the chip's capacity either cannot be identified & the user has not defined a chipSize in `flash.begin()`. +- Fixed `powerDown()`to be more efficient. The chip now powers down much faster than before. +- Added a new error code. Library can now detect non-responsive chips - bad wiring or otherwise. +- Streamlined variables to make code structure better. +- Changed library structure to enable the addition of multi-flash compatibility in the near future. +- moved `#define RUNDIAGNOSTIC` & `#define HIGHSPEED` to `SPIFlash.h` from `SPIFlash.cpp`. +- Now works with other Winbond modules (not in the official supported module list) (beta) by taking the flash memory size in bits as an argument in `flash.begin(_chipSize)`; +- Formatted code to be better human readable. +- Changed the internal `_troubleshoot()` function to use fewer resources. + + +
+## Version 2.5.0 +#### Release date 13.11.2016 + +###### Bugs Squashed: +- Errorchecking on `writeByteArray()` and `writeCharArray()` was broken in 2.4.0 and is fixed now. +- Previous versions (< 2.5.0) suffered from the inability to write datasets that spanned page boundaries. This has now been fixed (Ref. Enhancements section below) + +###### Deletions: +- `flash.writePage()` & `flash.readPage()` have been retired as they can be duplicated - more effectively - by other functions. Refer to `pageDiag()` in `Diagnostics.ino` to see how the same results can be obtained by using `flash.writeByteArray()` & `flash.readByteArray()`. + +###### Enhancements: +- Added support for SPI Transactions. +- Added an upgrade for Arduino Due users to read and write large arrays of data at very high speeds - previously reading 60kb of data would take ~108ms - it now takes ~525uS. In this mode, the time between consecutive reads has been reduced to <12ms (needs more work!). +- Completely re-written `writeByteArray()` and `writeCharArray()` functions. They no longer breakdown when writing arrays that span multiple pages. +- All functions that write more than 1 byte of data now take into account page boundaries when writing across pages. In previous versions writing across a page boundary would cause the remainder of the data to wrap around to the beginning of the page. Now the data is written seamlessly from one page over to the next. +- Calling `flash.begin()` now instantiates the SPI bus. Comms with other SPI slaves do not need to call `SPI.begin()` if `flash.begin()` has already been called first in the user's code. +- Sped up `_beginSPI()`. Code in `_beginSPI()` instantiates the SPI bus if another SPI comms library has shut it down. +- Added function allowing setting SPI clock speeds - call the function `flash.setClock(uint32_t clockSpeed)` straight after `begin()`. +- Made `_beginSPI()` & `_nextByte()` shorter and faster. +- Replaced all calls to `SPI.transfer()` with `xfer()` to make the code more human readable and to enable simplicity when switching platforms. +- Added the ability to change Chip Erase wait times depending on the chip (supported chips only). +- Don't need to `#include ` in user code anymore. Just `#include` will do. Examples are now updated. +- Now supports & uses `SPI.transfer(*buf, count)` along with the standard `SPI.transfer()`, speeding up the read/write of anything larger than an byte (AVR Boards only) +--> Reduces the program storage space in the uC's flash (AVR Boards only) + +
+## Version 2.4.0 +#### Release date 11.09.2016 + +###### Bugs Squashed: +- Fixed bug preventing writing to Address 0x00 +- Fixed bug in `_notPrevWritten()` which did did not perform a thorough enough check to see if a location had been previously written to. +- Fixed errorchecking bug - it now works for all data types - not just byte/char as previously. + +###### Enhancements & Optimizations: +- Added a function 'error()' to enable users to check for errors generated at any point in their program. +- Optimized writePage() so it doesn't depend on other functions and so runs faster than before. +- Diagnostics.ino now outputs time taken for each function as a part of its and provides additional diagnostic data. +- Now have a common set of private functions: + * `_prep()` + * `_beginSPI()` + * `_nextByte()` + * `_endSPI()` + + to replace + + * `_prepWrite()` + * `_prepRead()` + * `_beginRead()` + * `_readNextByte()` + * `_beginWrite()` + * `_writeNextByte()` & + * `_endProcess()` +- Changed the way `_addressCheck()` works. It now checks all addresses involved in writing the data rather than one address block at a time and returns the correct address to write to in case overflow is enabled. Also, this function is now built into `_prep()`. +- Reading and writing using page numbers + offset has been optimised to be faster than before. +- Optimized the way address and error checking is done so its more efficient and uses few system resources and runs faster. +- Using this library with an ESP8266 board defaults to using GPIO15 as the Slave Select - unless something different is explicitly specified in the constructor. + +
+## Version 2.3.1 +#### Release date 19.06.2016 +- W25Q256FV support added. (Thanks [Stanislav-Povolotsky](https://github.com/Marzogh/SPIMemory/pull/26)!) +- Cleaned up redundant code in TestFlash.ino and SPIFlash.h + +
+## Version 2.3.0 +#### Release date 04.06.2016 +- ESP8266 compatibility added +- Compatible with Arduino Fio and Micro +- Now compatible with Arduino IDE v1.6.9 +- Compatible with Arduino-Makefile. (Thanks [Raphael](https://github.com/Marzogh/SPIMemory/pull/21) !) + +
+## Version 2.2.0 +#### Release date 24.11.2015 +- When `#RUNDIAGNOSTIC` is uncommented, the `_troubleshoot()` function is now optimised for different µCs +- Added the ability to check if the address has been previously written to before initiating a write + operation. +- Added a `sizeofStr()` function to get sizes of String objects, to use with the `getAddress()` function +- Fixed a bug with `getAddress()` +- Added the ability get the chip's name via `getChipName()` +- `Diagnostics.ino` has been made more and efficient and provides a cleaner Serial output +- Added `getAddressEx.ino` to show how `getAdress()` works. + +
+## Version 2.1.1 +#### Release date 24.10.2015 + +- Fixed bugs that prevented the following functions from working properly + + * `powerDown()` + * `powerUp()` + * `suspendProg()` + * `resumeProg()` + +
+## Version 2.1.0 +#### Release date 18.10.2015 + +- Arduino Due compatible - refer to wiki for further details +- Fixed bug with write/readByteArray +- Added write/readCharArray +- Added a proper error checking function that gets called when #ifdef RUNDIAGNOSTIC is uncommented in SPIFlash.cpp. + This function returns a verbose error message to the Serial console instead of the terse error codes of the previous version. +- The following functions have been changed to enable bug fixes and uniformity in coding style. + + * `writeBytes()` + * `readBytes()` + + They have been replaced with + + * `readByteArray()` + * `writeByteArray()` + + respectively + +
+## Version 2.0.0 +#### Release date 12.10.2015 +- Sped up all functions atleast 25x +- Compatible with ATTiny85 +- All Read/Write/Erase functions can now take either (page number & offset) or (address) as arguments (Except `readPage()` & `printPage()`) +- `getAddress()` can now return either a 32-bit address or a page number & offset - Refer to Readme.md +- Error codes explained: + 0x00 SUCCESS Operation successful. + 0x01 CALLBEGIN Please make sure `begin()` has been called in `setup()`. + 0x02 UNKNOWNCHIP Unknown chip manufacturer. + 0x03 UNKNOWNCAP Unknown chip capacity. + 0x04 CHIPBUSY Chip busy. + 0x05 OUTOFBOUNDS Address out of bounds. Please check if `begin()` has been called in `setup()`. + 0x06 CANTENWRITE Unable to `_writeEnable()`. Check wiring/chip. + 0x07 OUTOFMEM Pagenumber outside maximum. + 0x08 OUTOFPAGE Offset is outside page. +- The following functions are deprecated to enable compatibility with other AVR chips. + + * `_printPageBytes()` + * `printPage()` + * `printAllPages()` + * `readSerialStr()` + + They can be used by uncommenting them in the SPIFlash.cpp file. However, be warned, this particular block of code has only been tested with the Arduino IDE (1.6.5) and only with 8-bit AVR based Arduino boards and will not be supported any further. + +
+## Version 1.3.2 +#### Release date 09.10.2015 + + +- Added the ability to read and write String objects with a simple high level function `readStr()` & `writeStr()` +- Added the ability to `getAddress()` +- Added the ability to fastRead to every read function as the last boolean argument (defaults to FALSE) +- Changed the example code as given below: + - Modified `TestFlash.ino` to use 25% less memory + - `Struct_writer.ino` now writes struct to a random location on the Flash Memory chip. + - Added instructions for real world data storage to `Struct_writer.ino` + - `Diagnostics.ino` now provides a cleaner diagnostic readout From b4676cb54d971cfaac18538e40b8de481e6a422a Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sat, 1 Jun 2019 22:57:45 +1000 Subject: [PATCH 09/25] Updated change log to reflect error from previous commit. The change resolves #177 not #117 --- Change log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Change log.md b/Change log.md index 55c15a6..01eca8a 100644 --- a/Change log.md +++ b/Change log.md @@ -22,7 +22,7 @@ SPIFlash flash(int8_t *SPIPinsArray); ``` can be used (only with ESP32 boards as of now) to enable the use of custom SPI pins. ```SPIPinsArray``` has to be a 4 element array containing the custom SPI pin numbers (as signed integers - int8_t) in the following order - sck, miso, mosi, ss. Refer to the wiki for more information. Resolves #113 - Created new error code `NOCHIPSELECTDECLARED` for errors with declaring custom SPI pins. -- Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #117 +- Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #177 ###### New flash memory chips supported: From 6ca5d1f8993ee6238c189624173d87adbd8f700f Mon Sep 17 00:00:00 2001 From: Marzogh Date: Sat, 1 Jun 2019 23:19:00 +1000 Subject: [PATCH 10/25] Fixed formatting issues --- Change log.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/Change log.md b/Change log.md index 01eca8a..1c4f51c 100644 --- a/Change log.md +++ b/Change log.md @@ -10,7 +10,9 @@ >This Arduino library is for use with flash and FRAM memory chips that communicate using the SPI protocol. In its current form it supports identifying the flash/FRAM chip and its various features; automatic address allocation and management; writing and reading a number of different types of data, ranging from 8-bit to 32-bit (signed and unsigned) values, floats, Strings, arrays of bytes/chars and structs to and from various locations; sector, block and chip erase; and powering down for low power operation.
+ ## Version 3.4.0 (In progress) + #### Release date TBA ###### Bugs squashed: @@ -30,7 +32,9 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p
+ ## Version 3.3.0 + #### Release date 20.04.2019 >Library now supports FRAM memory (In BETA). Documentation for this is in progress. @@ -58,7 +62,9 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p - FM25W256 from Cypress
+ ## Version 3.2.1 + #### Release date 13.04.2018 ###### Bugs squashed @@ -82,7 +88,9 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p | v3.2.1 | 17854 bytes | SFDP discovery turned on | 1.1% larger than v3.1.0 |
+ ## Version 3.2.0 + #### Release date 24.01.2018 - Library name changed to SPIMemory @@ -99,7 +107,7 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p - S25FL032P from Cypress/Spansion ###### New Microcontrollers supported ---> ATSAMD51J19 ARM Cortex M4 (Adafruit Metro M4) +- ATSAMD51J19 ARM Cortex M4 (Adafruit Metro M4) ###### Enhancements The library now does the following - if the flash memory chip is compatible with the SFDP standard: @@ -115,9 +123,12 @@ The library now does the following - if the flash memory chip is compatible with - Fixes #125 in commit 10c2b86. The library automatically uses the alternate chip erase code (0xC7) as required (only on fully supported chips)
+ ## Version 3.1.0 + #### Release date 24.01.2018 -- Library name change notice inserted into `flash.begin()`. This notice can be dismissed by commenting out the instance of `#define PRINTNAMECHANGEALERT` in SPIFlash.h. Please refer to the Readme file for further details. + +>Library name change notice inserted into `flash.begin()`. This notice can be dismissed by commenting out the instance of `#define PRINTNAMECHANGEALERT` in SPIFlash.h. Please refer to the Readme file for further details. Bugs squashed - An error with how `_addressCheck()` works with data that spans the memory boundary - when rolling over from address `_chip.capacity` to address `0x00` - has been fixed. In previous versions this caused issues with writing complex data structures across the memory boundary and led to many `_writeErrorCheck()` failures. Fixes issue #112 @@ -137,7 +148,9 @@ Bugs squashed - S25FL127S
+ ## Version 3.0.1 + #### Release date 10.12.2017 ###### Bugs squashed: @@ -149,12 +162,14 @@ Bugs squashed - Fixes a major bug that was causing input Arrays to be overwritten by zeros during the writeByteArray() / writeCharArray() process. Thanks for catching this @hanyazou. (Issue #102) -New flash memory chips supported: +###### New flash memory chips supported: - S25FL127S
+ ## Version 3.0.0 + #### Release date 09.08.2017 ###### Bugs Squashed: @@ -226,7 +241,9 @@ flash.begin(flashChipSize) - `getAddress()` function now works anytime it is called - even if there is preexisting data on the chip
+ ## Version 2.7.0 + #### Release date 17.04.2017 ###### Bugs Squashed: @@ -242,7 +259,9 @@ flash.begin(flashChipSize) `SPIFlash flash(33);`_
+ ## Version 2.6.0 + #### Release date 16.04.2017 ###### Bugs Squashed: @@ -277,7 +296,9 @@ flash.begin(flashChipSize)
+ ## Version 2.5.0 + #### Release date 13.11.2016 ###### Bugs Squashed: @@ -303,7 +324,9 @@ flash.begin(flashChipSize) --> Reduces the program storage space in the uC's flash (AVR Boards only)
+ ## Version 2.4.0 + #### Release date 11.09.2016 ###### Bugs Squashed: @@ -336,22 +359,31 @@ flash.begin(flashChipSize) - Using this library with an ESP8266 board defaults to using GPIO15 as the Slave Select - unless something different is explicitly specified in the constructor.
+ ## Version 2.3.1 + #### Release date 19.06.2016 -- W25Q256FV support added. (Thanks [Stanislav-Povolotsky](https://github.com/Marzogh/SPIMemory/pull/26)!) + +- W25Q256FV support added. (Thanks [@Stanislav-Povolotsky](https://github.com/Marzogh/SPIMemory/pull/26)!) - Cleaned up redundant code in TestFlash.ino and SPIFlash.h
+ ## Version 2.3.0 + #### Release date 04.06.2016 + - ESP8266 compatibility added - Compatible with Arduino Fio and Micro - Now compatible with Arduino IDE v1.6.9 -- Compatible with Arduino-Makefile. (Thanks [Raphael](https://github.com/Marzogh/SPIMemory/pull/21) !) +- Compatible with Arduino-Makefile. (Thanks [@rnestler](https://github.com/Marzogh/SPIMemory/pull/21) !)
+ ## Version 2.2.0 + #### Release date 24.11.2015 + - When `#RUNDIAGNOSTIC` is uncommented, the `_troubleshoot()` function is now optimised for different µCs - Added the ability to check if the address has been previously written to before initiating a write operation. @@ -362,7 +394,9 @@ flash.begin(flashChipSize) - Added `getAddressEx.ino` to show how `getAdress()` works.
+ ## Version 2.1.1 + #### Release date 24.10.2015 - Fixed bugs that prevented the following functions from working properly @@ -373,7 +407,9 @@ flash.begin(flashChipSize) * `resumeProg()`
+ ## Version 2.1.0 + #### Release date 18.10.2015 - Arduino Due compatible - refer to wiki for further details @@ -394,8 +430,11 @@ flash.begin(flashChipSize) respectively
+ ## Version 2.0.0 + #### Release date 12.10.2015 + - Sped up all functions atleast 25x - Compatible with ATTiny85 - All Read/Write/Erase functions can now take either (page number & offset) or (address) as arguments (Except `readPage()` & `printPage()`) @@ -420,7 +459,9 @@ flash.begin(flashChipSize) They can be used by uncommenting them in the SPIFlash.cpp file. However, be warned, this particular block of code has only been tested with the Arduino IDE (1.6.5) and only with 8-bit AVR based Arduino boards and will not be supported any further.
+ ## Version 1.3.2 + #### Release date 09.10.2015 From 0c55c30c1da60916bbbed281a4d80cec9b8a450e Mon Sep 17 00:00:00 2001 From: Marzogh Date: Sat, 1 Jun 2019 23:20:04 +1000 Subject: [PATCH 11/25] Fixed table formatting issue in section v3.2.1 --- Change log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Change log.md b/Change log.md index 1c4f51c..99b1262 100644 --- a/Change log.md +++ b/Change log.md @@ -76,7 +76,7 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p - 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 | Test platform | -|:-----:|:-----:|:-----:| +|:-----:|:-----:| |FlashDiagnostics.ino from v3.2.0
with #RUNDIAGNOSTIC commented out | Arduino Pro Mini 8MHz 3.3V | From de3c71f7ca202662cbe8d62a52cd876346eeb42e Mon Sep 17 00:00:00 2001 From: Marzogh Date: Sat, 1 Jun 2019 23:21:31 +1000 Subject: [PATCH 12/25] Fixed formatting error in v3.0.0 --- Change log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Change log.md b/Change log.md index 99b1262..82f8691 100644 --- a/Change log.md +++ b/Change log.md @@ -200,7 +200,7 @@ address = (pagenumber << 8) + offset. flash.begin(flashChipSize) ``` - where flashChipSize is indicated in Bytes, Kilobytes or Megabytes. (Refer to the next two items in this change log) + where flashChipSize is indicated in Bytes, Kilobytes or Megabytes. (Refer to the next two items in this change log) - Including 'flashChipSize' in flash.begin() compiles more efficiently than in previous versions. - The way memory size is indicated by the users in flash.begin(flashChipSize) has changed - please refer to defines.h or the wiki for further information. The new method enables users to define any custom size unlike the previous version where only a limited number of predetermined sizes were available to use. From e8b8262ab6a928a3efaf0fcc9f2f145b403d54b7 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 09:07:56 +1000 Subject: [PATCH 13/25] Changed name of variable from Pull #156 to better match library's design. --- src/SPIFlash.cpp | 4 ++-- src/SPIFlash.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SPIFlash.cpp b/src/SPIFlash.cpp index 0ed88f5..6ef3359 100644 --- a/src/SPIFlash.cpp +++ b/src/SPIFlash.cpp @@ -123,7 +123,7 @@ bool SPIFlash::begin(uint32_t flashChipSize) { #ifdef SPI_HAS_TRANSACTION //Define the settings to be used by the SPI bus - if (!_settings_set) { + if (!_SPISettingsSet) { _settings = SPISettings(SPI_CLK, MSBFIRST, SPI_MODE0); } #else @@ -143,7 +143,7 @@ bool SPIFlash::begin(uint32_t flashChipSize) { #ifdef SPI_HAS_TRANSACTION void SPIFlash::setClock(uint32_t clockSpeed) { _settings = SPISettings(clockSpeed, MSBFIRST, SPI_MODE0); - _settings_set = true; + _SPISettingsSet = true; } #else void SPIFlash::setClock(uint8_t clockdiv) { diff --git a/src/SPIFlash.h b/src/SPIFlash.h index e6b5221..d111888 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -157,7 +157,7 @@ class SPIFlash { //-------------------------------- Private variables ----------------------------------// #ifdef SPI_HAS_TRANSACTION SPISettings _settings; - bool _settings_set = false; + bool _SPISettingsSet = false; #else uint8_t _clockdiv; #endif From fd151a7950b9e176d0d9eb5505715a288df436ef Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 09:12:10 +1000 Subject: [PATCH 14/25] Updated change log with chnages about setClock(). --- Change log.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Change log.md b/Change log.md index 82f8691..58fec79 100644 --- a/Change log.md +++ b/Change log.md @@ -25,6 +25,7 @@ SPIFlash flash(int8_t *SPIPinsArray); can be used (only with ESP32 boards as of now) to enable the use of custom SPI pins. ```SPIPinsArray``` has to be a 4 element array containing the custom SPI pin numbers (as signed integers - int8_t) in the following order - sck, miso, mosi, ss. Refer to the wiki for more information. Resolves #113 - Created new error code `NOCHIPSELECTDECLARED` for errors with declaring custom SPI pins. - Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #177 +- Formatted and merged pull request #153. This changes the way [setClock()](https://github.com/Marzogh/SPIMemory/wiki/Library-instantiation-functions#setclockclockspeed) works and allows for the definition of clock speed before the SPI Bus is instantiated. Refer to [wiki](https://github.com/Marzogh/SPIMemory/wiki/Library-instantiation-functions#setclockclockspeed) for further details. Thanks @rambo. ###### New flash memory chips supported: From 1d8e0b77151625a752cd2dc87d23832ed79f1efb Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:20:08 +1000 Subject: [PATCH 15/25] Fixed change log formatting --- Change log.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Change log.md b/Change log.md index 58fec79..88bfd98 100644 --- a/Change log.md +++ b/Change log.md @@ -81,12 +81,12 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p |FlashDiagnostics.ino from v3.2.0
with #RUNDIAGNOSTIC commented out | Arduino Pro Mini 8MHz 3.3V | -| Library version | Compiled code size
(Bytesb | SFDP status | Size compared to v3.1.0 | +| Library version | Compiled code size
(Bytes) | SFDP status | Size compared to v3.1.0 | | :------: | :------: | :------: | :-----: | | v3.1.0 | 17652 | SFDP discovery not supported | Same size | | v3.2.0 | 20104 | SFDP discovery supported & on by default | 13.9% larger than v3.1.0 | | v3.2.1 | 15316 | SFDP discovery turned off | 13.75% smaller than v3.1.0 | -| v3.2.1 | 17854 bytes | SFDP discovery turned on | 1.1% larger than v3.1.0 | +| v3.2.1 | 17854 | SFDP discovery turned on | 1.1% larger than v3.1.0 |
From 05ff8f7e38eae7ba52f6d3dc42d498f65a1edd0c Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:20:19 +1000 Subject: [PATCH 16/25] Fixed errors in Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f13bfa5..77af580 100644 --- a/README.md +++ b/README.md @@ -105,13 +105,13 @@ As of v3.2.1, SFDP parameter discovery is an user controlled option. To get the #### SPIFram ^ -- The library is called by declaring the```SPIFram fram(csPin*)``` constructor where 'flash' can be replaced by a user constructor of choice and 'csPin' is the Chip Select pin for the flash module. +- The library is called by declaring the```SPIFram fram(csPin*)``` constructor where 'fram' can be replaced by a user constructor of choice and 'csPin' is the Chip Select pin for the fram module. * Optional. Do not include csPin if using the default slave select pin for your board. - Every version of the library >= v3.0.0 supports the ability to use any of multiple SPI interfaces (if your micro-controller supports them). Switching to use another SPI interface is done by calling ```SPIFram fram(csPin, &SPI1);``` (or &SPI2 and so on), instead of ```SPIFram fram(csPin)```. * NOTE: This is currently only officially supported on the SAMD and STM32 architectures. -- Also make sure to include ```fram.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of flash chip installed and load the right parameters. +- Also make sure to include ```fram.begin(CHIPSIZE*)``` in ```void setup()```. This enables the library to detect the type of fram chip installed and load the right parameters. ^ Currently in BETA. The methods in SPIFram are not final and subject to change over the next few revisions. From 1b319cdf7a3fc38d63e694350f2f720475e2cf51 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:39:37 +1000 Subject: [PATCH 17/25] The program now checks for errors in the library constructor and prints them to Serial when RUNDIAGNOSTICS is enabled --- examples/FlashDiagnostics/Diagnostics_functions.ino | 1 - examples/FlashDiagnostics/FlashDiagnostics.ino | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/FlashDiagnostics/Diagnostics_functions.ino b/examples/FlashDiagnostics/Diagnostics_functions.ino index 00b4b1d..6aa3745 100644 --- a/examples/FlashDiagnostics/Diagnostics_functions.ino +++ b/examples/FlashDiagnostics/Diagnostics_functions.ino @@ -556,4 +556,3 @@ void eraseChipTest() { } } - diff --git a/examples/FlashDiagnostics/FlashDiagnostics.ino b/examples/FlashDiagnostics/FlashDiagnostics.ino index 771ffa6..38bc3a8 100644 --- a/examples/FlashDiagnostics/FlashDiagnostics.ino +++ b/examples/FlashDiagnostics/FlashDiagnostics.ino @@ -45,7 +45,7 @@ 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) || defined (__AVR_ATmega32U4__) || defined (ARCH_STM32) || defined (NRF5) || defined (ARDUINO_ARCH_ESP32) while (!Serial) ; // Wait for Serial monitor to open @@ -59,10 +59,14 @@ void setup() { } Serial.println(); randomSeed(analogRead(RANDPIN)); - Serial.println(flash.error(VERBOSE)); + + if (flash.error()) { + Serial.println(flash.error(VERBOSE)); + } + flash.begin(); //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)); + //flash.begin(MB(32)); if (getID()) { @@ -78,7 +82,7 @@ void setup() { Serial.print(" Runtime"); printLine(); - powerDownTest(); + //powerDownTest(); Serial.println(); powerUpTest(); Serial.println(); From aa1554fdf9c4c17c7b13628e47bf7abbadef6eb5 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:40:15 +1000 Subject: [PATCH 18/25] Updated library version --- library.properties | 2 +- src/SPIMemory.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.properties b/library.properties index f4aa52a..0c0c46e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SPIMemory -version=3.3.0 +version=3.4.0 author=Prajwal Bhattaram maintainer=Prajwal Bhattaram sentence=SPI Memory library for Arduino. (Formerly SPIFlash) diff --git a/src/SPIMemory.h b/src/SPIMemory.h index 89bb45d..3f1c425 100644 --- a/src/SPIMemory.h +++ b/src/SPIMemory.h @@ -115,7 +115,7 @@ #endif #define LIBVER 3 -#define LIBSUBVER 3 +#define LIBSUBVER 4 #define BUGFIXVER 0 class SPIMemory { From 0f6b540703c3007e4f3c356246e9105d1ceb88c3 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:40:26 +1000 Subject: [PATCH 19/25] Added ENFASTREAD to defines --- src/defines.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/defines.h b/src/defines.h index 2d09178..39f7e69 100644 --- a/src/defines.h +++ b/src/defines.h @@ -183,6 +183,7 @@ #else #define SPI_CLK 104000000 //Hz equivalent of 104MHz #endif +#define ENFASTREAD 0x01 #define WRTEN 0x02 #define SUS 0x80 #define WSE 0x04 @@ -193,11 +194,11 @@ #define NULLINT 0x0000 #define NO_CONTINUE 0x00 #define NOVERBOSE 0x00 +#define VERBOSE 0x01 #define PASS 0x01 #define FAIL 0x00 #define NOOVERFLOW false #define NOERRCHK false -#define VERBOSE true #define PRINTOVERRIDE true #define ERASEFUNC 0xEF #define BUSY_TIMEOUT 1000000000L From 7e0825a40d8b67899855b75a1e4cb8b04877dad9 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 17:41:18 +1000 Subject: [PATCH 20/25] Fixed bug that prevented FastRead from working as it was meant to. Resolves #173 --- src/SPIFlashIO.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index e1f47c1..bb2f4af 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -202,8 +202,8 @@ case FASTREAD: _nextByte(WRITE, opcode); - _nextByte(WRITE, DUMMYBYTE); _transferAddress(); + _nextByte(WRITE, DUMMYBYTE); break; case SECTORERASE: @@ -576,7 +576,7 @@ else { if (_chip.sfdpAvailable) { #ifdef RUNDIAGNOSTIC - Serial.println("SFDP ID finished."); + Serial.println(F("SFDP ID finished.")); #endif return true; } @@ -588,7 +588,9 @@ } if (!_chip.capacity) { - + #ifdef RUNDIAGNOSTIC + Serial.println(F("Chip capacity cannot be identified")); + #endif if (flashChipSize) { // If a custom chip size is defined #ifdef RUNDIAGNOSTIC From 2924cf1b2892dd2ad2098e10756fce2fa0169fae Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Sun, 2 Jun 2019 20:22:52 +1000 Subject: [PATCH 21/25] Added support for the nRF52 platform, tested changes and updated docs. --- Change log.md | 3 +++ README.md | 1 + examples/FlashDiagnostics/FlashDiagnostics.ino | 2 +- library.properties | 2 +- src/SPIMemory.h | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Change log.md b/Change log.md index 88bfd98..855915c 100644 --- a/Change log.md +++ b/Change log.md @@ -27,6 +27,9 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p - Using other SPI ports (HSPI, VSPI, SPI1, SPI2 etc.) is now also supported on ESP32 boards - along with SAMD and STM32 boards (supported since v3.0.0). Resolves #177 - Formatted and merged pull request #153. This changes the way [setClock()](https://github.com/Marzogh/SPIMemory/wiki/Library-instantiation-functions#setclockclockspeed) works and allows for the definition of clock speed before the SPI Bus is instantiated. Refer to [wiki](https://github.com/Marzogh/SPIMemory/wiki/Library-instantiation-functions#setclockclockspeed) for further details. Thanks @rambo. +###### New Microcontrollers supported: +- nRF52832 ARM Cortex M4F (Adafruit nRF52 Feather) + ###### New flash memory chips supported: ###### New FRAM memory chips supported: diff --git a/README.md b/README.md index 77af580..4ffd2d7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This Arduino library is for use with flash and FRAM memory chips that communicat | ATmega2560 | Arduino Mega | - | | ATSAMD21G18 (ARM Cortex M0+) | Adafruit Feather M0,
Adafruit Feather M0 Express,
Adafruit ItsyBitsy M0 Express | - | | AT91SAM3X8E (ARM Cortex M3) | Arduino Due | - | +| nRF52832 (ARM Cortex M4F) | Adafruit nRF52 Feather | - | | ATSAMD51J19 (ARM Cortex M4) | Adafruit Metro M4 | - | | STM32F091RCT6 | Nucleo-F091RC | | | ESP8266 | Adafruit ESP8266 Feather,
Sparkfun ESP8266 Thing | - | diff --git a/examples/FlashDiagnostics/FlashDiagnostics.ino b/examples/FlashDiagnostics/FlashDiagnostics.ino index 38bc3a8..973cb71 100644 --- a/examples/FlashDiagnostics/FlashDiagnostics.ino +++ b/examples/FlashDiagnostics/FlashDiagnostics.ino @@ -82,7 +82,7 @@ void setup() { Serial.print(" Runtime"); printLine(); - //powerDownTest(); + powerDownTest(); Serial.println(); powerUpTest(); Serial.println(); diff --git a/library.properties b/library.properties index 0c0c46e..d0a1d3b 100644 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ sentence=SPI Memory library for Arduino. (Formerly SPIFlash) paragraph=This library enables read, write, erase and power functions on number of compatible flash and fram 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 +architectures=avr,sam,samd,esp8266,esp32,Simblee,stm32, nrf52 includes=SPIMemory.h diff --git a/src/SPIMemory.h b/src/SPIMemory.h index 3f1c425..3a96fc7 100644 --- a/src/SPIMemory.h +++ b/src/SPIMemory.h @@ -107,7 +107,7 @@ #define ARCH_STM32 #endif #endif -#if defined (ARDUINO_ARCH_SAM) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_ESP8266) || defined (SIMBLEE) || defined (ARDUINO_ARCH_ESP32) || defined (BOARD_RTL8195A) || defined(ARCH_STM32) || defined(ESP32) || defined(NRF5) +#if defined (ARDUINO_ARCH_SAM) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_ESP8266) || defined (SIMBLEE) || defined (ARDUINO_ARCH_ESP32) || defined (BOARD_RTL8195A) || defined(ARCH_STM32) || defined(ESP32) || defined(NRF52) // RTL8195A included - @boseji 02.03.17 #define _delay_us(us) delayMicroseconds(us) #else From e7d4a4b4371a4a0802a0a502e361151efac47c28 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Mon, 3 Jun 2019 10:39:38 +1000 Subject: [PATCH 22/25] Added support for STM32L0 ARM Cortex M0+ (Nucleo-L031K6) --- Change log.md | 1 + README.md | 1 + src/SPIMemory.h | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Change log.md b/Change log.md index 855915c..c5497e2 100644 --- a/Change log.md +++ b/Change log.md @@ -29,6 +29,7 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p ###### New Microcontrollers supported: - nRF52832 ARM Cortex M4F (Adafruit nRF52 Feather) +- STM32L0 ARM Cortex M0+ (Nucleo-L031K6) ###### New flash memory chips supported: diff --git a/README.md b/README.md index 4ffd2d7..b44762f 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This Arduino library is for use with flash and FRAM memory chips that communicat | nRF52832 (ARM Cortex M4F) | Adafruit nRF52 Feather | - | | ATSAMD51J19 (ARM Cortex M4) | Adafruit Metro M4 | - | | STM32F091RCT6 | Nucleo-F091RC | | +| STM32L0 | Nucleo-L031K6 | | | ESP8266 | Adafruit ESP8266 Feather,
Sparkfun ESP8266 Thing | - | | ESP32 | Adafruit ESP32 Feather,
Sparkfun ESP32 Thing | Onboard flash memory. Refer to footnote£ below. | | Simblee | Sparkfun Simblee | - | diff --git a/src/SPIMemory.h b/src/SPIMemory.h index 3a96fc7..01f6e32 100644 --- a/src/SPIMemory.h +++ b/src/SPIMemory.h @@ -103,7 +103,7 @@ #endif #ifndef ARCH_STM32 - #if defined(ARDUINO_ARCH_STM32) || defined(__STM32F1__) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F0xx) + #if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32L0) || defined(__STM32F1__) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F0xx) #define ARCH_STM32 #endif #endif From 9b71d57fe5a9eff806bbe4b03ca7aef587fad492 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Mon, 3 Jun 2019 10:57:38 +1000 Subject: [PATCH 23/25] Added support for MX25L4005 & MX25L4005 from Macronix --- Change log.md | 2 ++ README.md | 11 ++++++----- src/SPIFlash.h | 2 +- src/SPIFlashIO.cpp | 16 ++++++++++++++++ src/defines.h | 16 ++++++++++------ 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/Change log.md b/Change log.md index c5497e2..f8698f4 100644 --- a/Change log.md +++ b/Change log.md @@ -16,6 +16,7 @@ #### Release date TBA ###### Bugs squashed: +- FastRead works properly now. All previous versions of the library has a FastRead bug that prevent it from doing what it was supposed to. ###### Enhancements: - An alternate version of the constructor @@ -32,6 +33,7 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p - STM32L0 ARM Cortex M0+ (Nucleo-L031K6) ###### New flash memory chips supported: +- MX25L4005 & MX25L4005 from Macronix ###### New FRAM memory chips supported: diff --git a/README.md b/README.md index b44762f..eab1283 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,12 @@ This Arduino library is for use with flash and FRAM memory chips that communicat | Winbond | W25Q16BV
W25Q64FV
W25Q64JV
W25Q80BV
W25Q256FV | Should work with the W25QXXXBV, W25QXXXFV &
W25QXXXJV families | | Microchip | SST25VF064C
SST26VF016B
SST26VF032B
SST26VF064B | Should work with the SST25 & SST26 families | | Cypress/Spansion | S25FL032P
S25FL116K
S25FL127S | Should work with the S25FL family | -| ON Semiconductor | LE25U40CMC | | -| AMIC| A25L512A0 | | -| Micron| M25P40 | | -| Adesto| AT25SF041 | | -| Giga devices| GD25Q16C | (Used on the Adafruit ItsyBitsy M0 Express) | +| ON Semiconductor | LE25U40CMC | | +| AMIC| A25L512A0 | | +| Micron| M25P40 | | +| Adesto| AT25SF041 | | +| Giga devices| GD25Q16C | (Used on the Adafruit ItsyBitsy M0 Express) | +| Macronix| MX25L4005
MX25L4005 | | ##### Should work with any flash memory that is compatible with the SFDP standard as defined in JESD216B diff --git a/src/SPIFlash.h b/src/SPIFlash.h index d111888..daf13c8 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -222,7 +222,7 @@ class SPIFlash { {KB(64), KB(128), KB(256), KB(512), MB(1), MB(2), MB(4), MB(8), MB(16), MB(32), MB(2), MB(4), MB(8), MB(8), KB(256), KB(512), MB(4), KB(512)}; // To understand the _memSize definitions check defines.h - const uint8_t _supportedManID[8] = {WINBOND_MANID, MICROCHIP_MANID, CYPRESS_MANID, ADESTO_MANID, MICRON_MANID, ON_MANID, GIGA_MANID, AMIC_MANID}; + const uint8_t _supportedManID[9] = {WINBOND_MANID, MICROCHIP_MANID, CYPRESS_MANID, ADESTO_MANID, MICRON_MANID, ON_MANID, GIGA_MANID, AMIC_MANID, MACRONIX_MANID}; const uint8_t _altChipEraseReq[3] = {A25L512, M25P40, SST26}; }; diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index bb2f4af..0d5bded 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -562,6 +562,22 @@ Serial.println(F("No Chip size defined by user. Checking library support.")); #endif //Identify capacity + if(_chip.manufacturerID == MACRONIX_MANID) + { + switch(_chip.capacityID) + { + case MX25L4005: + _chip.capacity = MB(4); + break; + + case MX25L8005: + _chip.capacity = MB(8); + break; + + default: + _troubleshoot(UNKNOWNCHIP); //Error code for unidentified capacity + } //TODO - Set up other manufaturerIDs in a similar pattern. + } for (uint8_t j = 0; j < sizeof(_capID); j++) { if (_chip.capacityID == _capID[j]) { _chip.capacity = (_memSize[j]); diff --git a/src/defines.h b/src/defines.h index 39f7e69..423da46 100644 --- a/src/defines.h +++ b/src/defines.h @@ -156,21 +156,25 @@ #define RAMTRON_FRAM_MANID 0xC2 //~~~~~~~~~~~~~~~~~~~~~~~~ Adesto ~~~~~~~~~~~~~~~~~~~~~~~~// - #define ADESTO_MANID 0x1F + #define ADESTO_MANID 0x1F //~~~~~~~~~~~~~~~~~~~~~~~~ Micron ~~~~~~~~~~~~~~~~~~~~~~~~// - #define MICRON_MANID 0x20 - #define M25P40 0x20 + #define MICRON_MANID 0x20 + #define M25P40 0x20 //~~~~~~~~~~~~~~~~~~~~~~~~ ON ~~~~~~~~~~~~~~~~~~~~~~~~// - #define ON_MANID 0x62 + #define ON_MANID 0x62 //~~~~~~~~~~~~~~~~~~~~~~~~ Giga ~~~~~~~~~~~~~~~~~~~~~~~~// #define GIGA_MANID 0xC8 //~~~~~~~~~~~~~~~~~~~~~~~~ AMIC ~~~~~~~~~~~~~~~~~~~~~~~~// - #define AMIC_MANID 0x37 - #define A25L512 0x30 + #define AMIC_MANID 0x37 + #define A25L512 0x30 +//~~~~~~~~~~~~~~~~~~~~~~~~ AMIC ~~~~~~~~~~~~~~~~~~~~~~~~// + #define MACRONIX_MANID 0xC2 + #define MX25L4005 0x13 + #define MX25L8005 0x14 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Definitions // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// From 4fb70364ca909778893aa7966af59477b31556a9 Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Mon, 3 Jun 2019 11:11:49 +1000 Subject: [PATCH 24/25] Code clean up --- src/SPIFlashIO.cpp | 3 +++ src/defines.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index 0d5bded..093be87 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -259,7 +259,10 @@ //Reads/Writes next data buffer. Should be called after _beginSPI() void SPIFlash::_nextBuf(uint8_t opcode, uint8_t *data_buffer, uint32_t size) { + #if !defined(ARDUINO_ARCH_SAM) && !defined(ARDUINO_ARCH_SAMD) && !defined(ARDUINO_ARCH_AVR) uint8_t *_dataAddr = &(*data_buffer); + #endif + switch (opcode) { case READDATA: #if defined (ARDUINO_ARCH_SAM) diff --git a/src/defines.h b/src/defines.h index 423da46..f7080c1 100644 --- a/src/defines.h +++ b/src/defines.h @@ -56,6 +56,12 @@ #define BEGIN_SPI SPI.begin(); #endif + #ifdef RUNDIAGNOSTIC + #if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL) + #define Serial SERIAL_PORT_USBVIRTUAL + #endif + #endif + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Common Instructions // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// From 106ae3f8b8f8c4ea04ec0d2332484c37c3d9b57a Mon Sep 17 00:00:00 2001 From: Prajwal Bhattaram Date: Mon, 3 Jun 2019 11:30:36 +1000 Subject: [PATCH 25/25] Final code for v3.4.0 pending final tests --- Change log.md | 8 +++----- README.md | 5 +++-- library.properties | 2 +- src/DMASAM.cpp | 2 +- src/DMASAM.h | 2 +- src/SPIFlash.cpp | 10 +++++----- src/SPIFlash.h | 4 ++-- src/SPIFlashIO.cpp | 6 +++--- src/SPIFlashSFDP.cpp | 2 +- src/SPIFram.cpp | 9 +++++---- src/SPIFram.h | 2 +- src/SPIFramIO.cpp | 2 +- src/SPIMemory.cpp | 2 +- src/SPIMemory.h | 14 +++++++++----- src/defines.h | 4 ++-- src/diagnostics.cpp | 4 ++-- src/diagnostics.h | 4 ++-- 17 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Change log.md b/Change log.md index f8698f4..43e9634 100644 --- a/Change log.md +++ b/Change log.md @@ -2,7 +2,7 @@ ##### Change log [![Build Status](https://travis-ci.org/Marzogh/SPIMemory.svg?branch=master)](https://travis-ci.org/Marzogh/SPIMemory) [![DOI](https://zenodo.org/badge/35823047.svg)](https://zenodo.org/badge/latestdoi/35823047) [![GitHub release](https://img.shields.io/github/release/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory) -[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.3.0.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.3.0...development) +[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.4.0.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.3.0...v3.4.0) [![GitHub issues](https://img.shields.io/github/issues/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/issues) [![GitHub pull requests](https://img.shields.io/github/issues-pr/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/pulls) [![license](https://img.shields.io/github/license/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/blob/master/LICENSE) @@ -11,9 +11,9 @@
-## Version 3.4.0 (In progress) +## Version 3.4.0 -#### Release date TBA +#### Release date 03.06.2019 ###### Bugs squashed: - FastRead works properly now. All previous versions of the library has a FastRead bug that prevent it from doing what it was supposed to. @@ -35,8 +35,6 @@ can be used (only with ESP32 boards as of now) to enable the use of custom SPI p ###### New flash memory chips supported: - MX25L4005 & MX25L4005 from Macronix -###### New FRAM memory chips supported: -
diff --git a/README.md b/README.md index eab1283..f730003 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SPIMemory [![Build Status](https://travis-ci.org/Marzogh/SPIMemory.svg?branch=master)](https://travis-ci.org/Marzogh/SPIMemory) [![DOI](https://zenodo.org/badge/35823047.svg)](https://zenodo.org/badge/latestdoi/35823047) [![GitHub release](https://img.shields.io/github/release/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory) -[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.2.1.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.2.1...v3.3.0) +[![GitHub commits](https://img.shields.io/github/commits-since/Marzogh/SPIMemory/v3.3.0.svg)](https://github.com/Marzogh/SPIMemory/compare/v3.3.0...v3.4.0) [![GitHub issues](https://img.shields.io/github/issues/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/issues) [![GitHub pull requests](https://img.shields.io/github/issues-pr/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/pulls) [![license](https://img.shields.io/github/license/Marzogh/SPIMemory.svg)](https://github.com/Marzogh/SPIMemory/blob/master/LICENSE) @@ -53,8 +53,9 @@ This Arduino library is for use with flash and FRAM memory chips that communicat | AMIC| A25L512A0 | | | Micron| M25P40 | | | Adesto| AT25SF041 | | -| Giga devices| GD25Q16C | (Used on the Adafruit ItsyBitsy M0 Express) | | Macronix| MX25L4005
MX25L4005 | | +| Giga devices| GD25Q16C | (Used on the Adafruit ItsyBitsy M0 Express) | + ##### Should work with any flash memory that is compatible with the SFDP standard as defined in JESD216B diff --git a/library.properties b/library.properties index d0a1d3b..25391af 100644 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ sentence=SPI Memory library for Arduino. (Formerly SPIFlash) paragraph=This library enables read, write, erase and power functions on number of compatible flash and fram 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, nrf52 +architectures=avr,sam,samd,esp8266,esp32,Simblee,stm32,nrf52 includes=SPIMemory.h diff --git a/src/DMASAM.cpp b/src/DMASAM.cpp index 98c2e2d..8aa7f05 100644 --- a/src/DMASAM.cpp +++ b/src/DMASAM.cpp @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 30/09/2016 * Original code from @manitou48 diff --git a/src/DMASAM.h b/src/DMASAM.h index 3f26a64..3ec9f88 100644 --- a/src/DMASAM.h +++ b/src/DMASAM.h @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/04/2018 * Modified by Prajwal Bhattaram - 19/06/2018 diff --git a/src/SPIFlash.cpp b/src/SPIFlash.cpp index 6ef3359..94a5e91 100644 --- a/src/SPIFlash.cpp +++ b/src/SPIFlash.cpp @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 20/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 20/04/2019 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, @@ -183,9 +183,9 @@ float SPIFlash::functionRunTime(void) { //Returns the library version as three bytes bool SPIFlash::libver(uint8_t *b1, uint8_t *b2, uint8_t *b3) { - *b1 = LIBVER; - *b2 = LIBSUBVER; - *b3 = BUGFIXVER; + *b1 = SPIFLASH_LIBVER; + *b2 = SPIFLASH_LIBSUBVER; + *b3 = SPIFLASH_REVVER; return true; } diff --git a/src/SPIFlash.h b/src/SPIFlash.h index daf13c8..3a0c380 100644 --- a/src/SPIFlash.h +++ b/src/SPIFlash.h @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 20/04/2019 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, diff --git a/src/SPIFlashIO.cpp b/src/SPIFlashIO.cpp index 093be87..083ed29 100644 --- a/src/SPIFlashIO.cpp +++ b/src/SPIFlashIO.cpp @@ -1,8 +1,8 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 - * Modified by Prajwal Bhattaram - 20/04/2019 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, @@ -262,7 +262,7 @@ #if !defined(ARDUINO_ARCH_SAM) && !defined(ARDUINO_ARCH_SAMD) && !defined(ARDUINO_ARCH_AVR) uint8_t *_dataAddr = &(*data_buffer); #endif - + switch (opcode) { case READDATA: #if defined (ARDUINO_ARCH_SAM) diff --git a/src/SPIFlashSFDP.cpp b/src/SPIFlashSFDP.cpp index 1f22d83..0184db8 100644 --- a/src/SPIFlashSFDP.cpp +++ b/src/SPIFlashSFDP.cpp @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 18/04/2018 * Modified by Prajwal Bhattaram - 19/06/2018 diff --git a/src/SPIFram.cpp b/src/SPIFram.cpp index 947cdff..ea164c3 100644 --- a/src/SPIFram.cpp +++ b/src/SPIFram.cpp @@ -1,6 +1,7 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/06/2018 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, @@ -117,9 +118,9 @@ float SPIFram::functionRunTime(void) { //Returns the library version as three bytes bool SPIFram::libver(uint8_t *b1, uint8_t *b2, uint8_t *b3) { - *b1 = LIBVER; - *b2 = LIBSUBVER; - *b3 = BUGFIXVER; + *b1 = SPIFRAM_LIBVER; + *b2 = SPIFRAM_LIBSUBVER; + *b3 = SPIFRAM_REVVER; return true; } diff --git a/src/SPIFram.h b/src/SPIFram.h index 4eb6669..ee900f8 100644 --- a/src/SPIFram.h +++ b/src/SPIFram.h @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/06/2018 * diff --git a/src/SPIFramIO.cpp b/src/SPIFramIO.cpp index 60dd4b4..859301c 100644 --- a/src/SPIFramIO.cpp +++ b/src/SPIFramIO.cpp @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/06/2018 * diff --git a/src/SPIMemory.cpp b/src/SPIMemory.cpp index 7788c42..92ddfaf 100644 --- a/src/SPIMemory.cpp +++ b/src/SPIMemory.cpp @@ -1,4 +1,4 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 * Modified by @boseji - 02/03/2017 diff --git a/src/SPIMemory.h b/src/SPIMemory.h index 01f6e32..cd1fcd9 100644 --- a/src/SPIMemory.h +++ b/src/SPIMemory.h @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2017 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 18/04/2018 - * Modified by Prajwal Bhattaram - 02/04/2019 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, @@ -114,9 +114,13 @@ #include #endif -#define LIBVER 3 -#define LIBSUBVER 4 -#define BUGFIXVER 0 +#define SPIFLASH_LIBVER 3 +#define SPIFLASH_LIBSUBVER 4 +#define SPIFLASH_REVVER 0 + +#define SPIFRAM_LIBVER 0 +#define SPIFRAM_LIBSUBVER 0 +#define SPIFRAM_REVVER 1 class SPIMemory { public: diff --git a/src/defines.h b/src/defines.h index f7080c1..7d8ee19 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 - * Modified by Prajwal Bhattaram - 20/04/2019 + * Modified by Prajwal Bhattaram - 03/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, diff --git a/src/diagnostics.cpp b/src/diagnostics.cpp index 37ff058..2dfe86f 100644 --- a/src/diagnostics.cpp +++ b/src/diagnostics.cpp @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 14/11/2016 - * Modified by Prajwal Bhattaram - 19/06/2018 + * Modified by Prajwal Bhattaram - 03/06/2018 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading, diff --git a/src/diagnostics.h b/src/diagnostics.h index 2580b63..85d0601 100644 --- a/src/diagnostics.h +++ b/src/diagnostics.h @@ -1,7 +1,7 @@ -/* Arduino SPIMemory Library v.3.3.0 +/* Arduino SPIMemory Library v.3.4.0 * Copyright (C) 2019 by Prajwal Bhattaram * Created by Prajwal Bhattaram - 19/05/2015 - * Modified by Prajwal Bhattaram - 20/04/2019 + * Modified by Prajwal Bhattaram - 02/06/2019 * * This file is part of the Arduino SPIMemory Library. This library is for * Flash and FRAM memory modules. In its current form it enables reading,