SPIMemory library - Formerly SPIFlash - now supports SPI FRAM as well! :)

Hello,
Firstly want to thank you very much for your hard work, it's very helpful and useful.
Personally I have a wish. To be honest, main usage scenario of your library is to READ and WRITE SPI EEPROM. Mainly it means doing full backup and restore or upgrade some firmware or bios. That what programmer do. So, I think your library could be more popular if you supply with it crossplatform app to identify, read and flash ROMs. To turn Arduino into powerfull and easy to use programmer.

Hi,
I try to change from SD to SPIFlash and all is working fine so far. Absolutly great Job!
I try to load some picture (bmp) into the flash. I imagine to convert the image content in a string and then store into the flash.

think this is basically possible :roll_eyes:

Open requested file on SD card

  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }

Convert to string

 String SD_Read;
  while (bmpFile.available()) {
    char ltr = bmpFile.read();
    SD_Read += ltr;
  }

Store to flash

  while (!flash.eraseSector(17, 0));


  if (flash.writeStr(17, 0, SD_Read)) {
    Serial.println("SAVED");
  } else {
    Serial.println("ERROR");
  }

then to display on the TFT, I have to read from Flash convert from String to File. :confused:

Did some one try this already?

pasaf:
Hello,
Firstly want to thank you very much for your hard work, it's very helpful and useful.
Personally I have a wish. To be honest, main usage scenario of your library is to READ and WRITE SPI EEPROM. Mainly it means doing full backup and restore or upgrade some firmware or bios. That what programmer do. So, I think your library could be more popular if you supply with it crossplatform app to identify, read and flash ROMs. To turn Arduino into powerfull and easy to use programmer.

Hi @pasaf, Thank you for the kind words. Your idea is a very interesting one. However, as much as I'd like to write code for a living, my main job actually involves dealing with stuff in a biology lab - I'm a geneticist by trade, so this library and the support I provide it are things I do in my free time. But, that is the beauty of Open Access/Free software - you can always take my code and turn it into whatever you wish :slight_smile: The main aim of this library is to provide you with the basic tools to build an app with any function you want, yourself. The upcoming release might help with what you want to do.

Release v3.0.0 will support read/write from/to multiple SPI Flash chips connected to one Arduino board. Using that version of the library it should be reasonably easy to put together a program that reads some data from Flash chip A and saves to to Flash chip B; repeating the process till all the data on chip A is on chip B. Afterward, the same program can be used to transfer the data from chip B to chip C. Easy backup & clone! ???

I know you want to read of EEPROMs - as long as the EEPROMs communicate over SPI, getting them to work with this library is only a matter of modifying it slightly. Take a look at the opcodes in 'defines.h' and see if any need changing - then change the JEDEC ID (specifically the manufacturer ID) that the library recognises, to the one one from your EEPROM. Everything else should just work.

(If you wait till v3.0.0 comes out, you should be able to do this straight from your code, release 3.0.0 will come with the ability to define custom chip size/ID in user code. :slight_smile: )

Feel free to play around with the code in the developer branch on the Github repo. Its not stable yet and is a work in progress, but it already has some of the features you'll need.

mysource:
Hi,
I try to change from SD to SPIFlash and all is working fine so far. Absolutly great Job!
I try to load some picture (bmp) into the flash. I imagine to convert the image content in a string and then store into the flash.

think this is basically possible :roll_eyes:

then to display on the TFT, I have to read from Flash convert from String to File. :confused:

Did some one try this already?

Hi mate, rather than working with a String object (especially on an 8-bit AVR, which I assume is what you're using?), how about you try and read the data on your SD into a byte array? Your code will be a lot more efficient and it'll be much easier to just feed an array to your TFT. Gary - HERE - was using the readByteArray() function to read data off his flash chip and display it on a TFT.

There are two points to note though.

  • If the size of the image file is larger than the free RAM on your MCU, you will not be able to read the entire file off the SD card before you transfer it across to the SPI Flash. You'll have to buffer it somehow
  • The latency will be an issue. Reading an array from Flash and displaying it on a TFT will take time and there will be a noticeable delay in refreshing the image. If you want to be able to read data very quickly off the Flash memory, I'd recommend using the Arduino Due - the library supports DMA on the Due and your latency should be as low as is possible. If not, Using a faster clocked chip like an ESP8266 at 160MHz or an ESP32 @ 80MHz should work in a pinch.

Marzogh:
Hi mate, rather than working with a String object (especially on an 8-bit AVR, which I assume is what you're using?), how about you try and read the data on your SD into a byte array?

This is just a "one Time" action. I will store the pic in the flash instead of the SD card. Then to read the pic from flash to display on the TFT.
OK first I will tray to store the pic content in an array.
something like that. bmpFile.read() returns an integer.

  char *arrayOfChars;
  uint16_t index = 0;
  while (bmpFile.available()) {
    arrayOfChars[index] = bmpFile.read();
    index++;
  }

Will see.....
After thet is done will check then -> TFT. Gary - HERE
THX for your info.

ok ...

Serial.println(read16(bmpFile), HEX);

  // pic size: 49208 bytes
  uint16_t _index = 0;
  uint8_t _page = 16;
  for (int i = _page; i < 209; i++) {
    flash.eraseSector(i, 0);
  }

  while (bmpFile.available()) {
    if (_index == 255) {
      _page++;
      _index = 0;
    }
    flash.writeByte(_page, _index, (byte)bmpFile.read());
    _index++;
  }

  Serial.println(read16(bmpFile), HEX);

Serial.println(read16(bmpFile), HEX); => 4D42 => BMP signature (read LSB and MSB) that is OK. but after flash erase I get C038 and after the while loop I get FFFF. :cold_sweat:... but I'm not changing the file handler so far.

New start :smiley:

some calluclation about the image size.....
image size: 49208 bytes
1 page = 256 bytes
1 sector = 4k / 4096 bytes
1 sector = 16 pages
4096 bytes * 256 = 1'048'576 = 1MB

PIC Size = 49208/4096 = 12.01... 13 sectors
13 sectors * 16 Pages = 208 total Pages / 16..207
208 pages and the last page (208 )just 56 bytes.

Clear the Flash from page 16..208

  uint16_t _pageOfset = 16; // Start from page 16. First 16 pages are reserved for configuration
  for (uint16_t p = _pageOfset; p < _pageOfset + 208; p++) {
  while (!flash.eraseSector(p, 0));
  }

Read the file:

File bmpFileTmp;
  // Open requested file on SD card
  if ((bmpFileTmp = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }

Write to flash:

 uint16_t _index = 0;
  uint8_t _page = _pageOfset;
  uint16_t maxPage = 0;
  uint16_t lastMaxIndex = 0;
  if (bmpFileTmp) {
    while (bmpFileTmp.available()) {
      if (_index == 256) {
        _page++;
        _index = 0;
      }
      flash.writeByte(_page, _index, (byte)bmpFileTmp.read());
      _index++;
    }
    bmpFileTmp.close();
  }  
maxPage = _page - _pageOfset;
lastMaxIndex = _index;

Check to first page (BMP signature) in the flash:

  printPage(16, 2);
  printPage(16, 1);
Reading page (0010)
066,077,056,192,000,000,000,000,000,000,054,000,000,000,040,000,
000,000,128,000,000,000,128,000,000,000,001,000,024,000,000,000,
000,000,002,192,000,000,229,076,000,000,229,076,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
Reading page (0010)
424d38c0000000000000360000002800
00008000000080000000010018000000
000002c00000e54c0000e54c00000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000

First two bytes are signature for BMP.
066,077 or 0x424d

So far all ok.

Read back from flash to file handler:

  Serial.print("Page to loop: ");
  Serial.println(_pageOfset + maxPage);

  uint32_t _indexTotal = 0;
  for (uint16_t p = _pageOfset; p <= _pageOfset + maxPage; p++) {
    if (p == _pageOfset + maxPage) {
      for (uint16_t i = 0; i <= lastMaxIndex; i++) {
        Serial.print(p);
        Serial.print("\t");
        Serial.print(i);
        Serial.print("\t");
        bmpFile.write((byte)flash.readByte(p, i));
        Serial.println(_indexTotal);
        _indexTotal++;
      }
    } else {
      for (uint16_t i = 0; i <= 255; i++) {
        Serial.print(p);
        Serial.print("\t");
        Serial.print(i);
        Serial.print("\t");
        Serial.println(_indexTotal);
        bmpFile.write((byte)flash.readByte(p, i));
        _indexTotal++;
      }
    }
  }

last output...
pages bytes index
208 56 49208

but when I check first two bytes from the new file handler I get wrong values.

  uint16_t checkBMP = read16(bmpFile);
  Serial.println(checkBMP);


uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

Here I expect 0x424D (d16973) but I get 65535. :roll_eyes:
The Bytes in the Flash looks good. In any case, the signature is correct.

Some Idea?

Hi, guys, please help me. I dont understand one simple thing.

I'm using W25Q64FVSSIG chip and trying to write a single byte, but i can't do it without preceding erasing the block.

my code (full sketch here SPI Flash Test · GitHub):

void loop() {
sensorValue = byte(analogRead(A0));

flash.eraseSector(page, 0); //<-- Does not work without this line (WTF?)
flash.writeByte(page, offset, byte(sensorValue));

dataByte = flash.readByte(page, offset);
Serial.println(dataByte, HEX);
}

So, without erasing the block dataByte != sensorValue.

I know, that NAND flash needed to be erased before writing, but W25Q64FV is a NOR according to datasheet

Question: is it correct behaviour or i has a trouble in my code/connections?

Thank You)

mixael:
Hi, guys, please help me. I dont understand one simple thing.

I'm using W25Q64FVSSIG chip and trying to write a single byte, but i can't do it without preceding erasing the block.

my code (full sketch here SPI Flash Test · GitHub):

void loop() {
sensorValue = byte(analogRead(A0));

flash.eraseSector(page, 0); //<-- Does not work without this line (WTF?)
flash.writeByte(page, offset, byte(sensorValue));

dataByte = flash.readByte(page, offset);
Serial.println(dataByte, HEX);
}

So, without erasing the block dataByte != sensorValue.

I know, that NAND flash needed to be erased before writing, but W25Q64FV is a NOR according to datasheet

Question: is it correct behaviour or i has a trouble in my code/connections?

Thank You)

You need to read the data sheet.

The W25Q64FV has a 4k sector erase AND can "program" up to 256 bytes at a time. The Erase operation sets all of the sector's bits to 1. A program operation can change one's to Zero's. If you are programing the same memory byte multiple times, you can only change One's to Zero's UNLESS you erase the SECTOR (4k).

So, you can change from 255 to 127 but you cannot change a 127 to 128 without Erasing the whole 4KB sector.

It is a hassle, but that is how it is. I am working on a FAT file system using theses 8MB and 16MB devices. The 4k sector erase is difficult to work around with 8KB total RAM (MEGA:).

Chuck.

Thank you very much, Chuck. This is exhaustive explanation)
I'll try to read datasheets more thoroughly.
Thank you again)

Its finally here!!! Just pushed through an update to v3.0.0. This update is a major overhaul of the library and brings with it support for more flash memory manufacturers, more µC platforms, squashes a major bug, loses ATTiny85 support, and has a large number of optimizations for speed, stability & ease of development.

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.

Deletions

  • 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 pseudocode to help

address = ( pagenumber << 8 ) + offset;

New Boards supported

  • Nucleo-F091RC
  • Adafruit Feather M0
  • Adafruit Feather M0 Express (On board flash and external flash)

Flash memory chips supported

  • Winbond (All SPI Flash chips)
  • Microchip (SST25 & SST26 series)
  • Cypress/Spansion (S25FL series)

Enhancements & Optimizations

  • 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() (where flashChipSize is indicated in Bytes, Kilobytes or Megabytes. (Refer to the next two items in this change log): flash.begin(flashChipSize);
  • 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 release notes for details)
  • 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

As always, you can find this version on Github here --> SPIFlash Library for Arduino v3.0.0
A ZIP file is also attached to the first post on this thread. The easiest way, as always, is to open up Library Manager on your Arduino IDE and update the libary to v3.0.0 :slight_smile:

Is the Arduino Nano known to not work, or is it a matter of testing?

@liolau: The Nano is just a matter of testing. It uses the same ATMega328 µC as in the Uno so it should work with no issues.

Hi all, just pushed through a major bug-fix release v3.0.1

@hanyazou identified a major bug (detailed here in issue 102) which has been fixed with v3.0.1

v3.0.1 also adds support for the S25FL127S from Spansion/Cypress.

As always, you can find this version on Github here --> SPIFlash Library for Arduino v3.0.1
A ZIP file is also attached to the first post on this thread. The easiest way, as always, is to open up Library Manager on your Arduino IDE and update the library to v3.0.1 :slight_smile:

Hi,

Does this library support QSPI ? I have wired W25Q128JV in QSPI mode and was wondering if I could try that with this llibrary

Hi,
I get some error on save to flash. flash.writeAnything with char variables in struct fails.

I have two structs

struct Configuration {
  uint8_t tempHigh;         // store higher value for temperature
  uint8_t tempLow;          // store lower value for temperature
  bool parkingMode;         // store Parking Battery value boolean
  uint8_t chargingRange;    // store charging range status
  uint8_t chargingHigh;     // store charging higher value
  uint8_t chargingLow;      // store charging lower value
  boolean sendData;         // store send data flag
} configuration;

struct NetworkConfig {
  char ssid[50];            // Store network ssid from client
  char pwd[50];             // Store network password from client
  char userid[50];          // Store identification ID from client
} networkConfig;

set some values on both struct, and save to flash:

  Serial.print(">");
  Serial.print(networkConfig.ssid);
  Serial.println("<");
  Serial.print(">");
  Serial.print(networkConfig.pwd);
  Serial.println("<");
  Serial.print(">");
  Serial.print(networkConfig.userid);
  Serial.println("<");
  
  flash.eraseSector(0);
  if (flash.writeAnything(0, configuration)) {
    Serial.println ("Data write successful");
  } else {
    Serial.println ("Data write failed");
  }

  if (flash.writeAnything(1, networkConfig)) {
    Serial.println ("Data write successful");
  } else {
    Serial.println ("Data write failed");
  }
  flash.readAnything(0, configuration);
  flash.readAnything(1, networkConfig);

  Serial.println(networkConfig.ssid);
  Serial.println(networkConfig.pwd);
  Serial.println(networkConfig.userid);

Serial Monitor:

MYSSID<
thePassword<
599bcc60e4942d4469d1c961<
Data write successful
Data write failed
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮$⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮$⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮$⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮

All the values from struct "configuration" are OK.
Some has an Idea please.

after changing struct to nested struct, all is good.

Hi - thanks for a fantastic library! I know this is a pretty big ask but maybe someone here can help out. I'd love to use this library on an STM32 MCU in the Particle ecosystem. Particle is a fork of Arduino/Wiring, in my understanding, so I don't think a ton would need to change but it's hard to tell. Has anyone tried this or perhaps have any advice for where I could start? Thank you!

Hi,
is there a conflict between EEPROM (mega2560 internal flash) and SPIFLASH Lib?
I tried to adjust the bootloader, then I noticed that the data on the winbond is gone, and the bootloader is not working as expected.

I tried this one.

Any idea?

@a7ashh14: I'm afraid it does not yet support QSPI. What µC are you using with your W25Q128JV?

@supscientist: I've yet to play around with the Particle/Electron ecosystem. Feel free to play around with the code in the 'stable' branch of the library on Github and see if you can make it compatible with the Particle ecosystem. :slight_smile:

@mysource: What pins are you connecting your Flash memory to? I don't think that the conflict is between the EEPROM and the Flash - its the probably because you use the SPI bus to program your Mega. If somehow, during the programming the Slave Select pin (that you have your flash connected to) is a) pulled low or b) the voltage drops and the µC enters Brown-out mode - then the data that's being sent into your Mega might a) get piped into your Flash memory as well or b) be interrupted unexpectedly. This could very easily cause corruption of the data on both the Mega's internal flash and the external SPI Flash you are using.