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

Hi all, I’ve just written up a library with a bunch of basic and advanced functions for the SPI Flash modules.

I’m looking at adding more functionality to the library in time. Any suggestions/improvements are welcome.

Update 03.06.2019:

Library is now called SPIMemory not SPIFlash.

  • Just updated to v 3.4.0 with a number of bug fixes and enhancements. Refer to change log for further details. .

As always, You can find the latest version on Github here → SPIMemory Library for Arduino. v3.4.0 is also attached to this post as a ZIP file. :slight_smile: (All the old versions are still attached)

SPIFlash-1.3.2.zip (69.6 KB)

SPIFlash-2.0.0.zip (74.8 KB)

SPIFlash-2.1.0.zip (75.5 KB)

SPIFlash-2.2.0.zip (80.3 KB)

SPIFlash-2.3.0.zip (79.4 KB)

SPIFlash-2.4.0.zip (81.1 KB)

SPIFlash-2.5.0.zip (110 KB)

SPIFlash-2.6.0.zip (125 KB)

SPIFlash-2.7.0.zip (141 KB)

SPIFlash-3.0.0.zip (138 KB)

SPIFlash-3.0.1.zip (140 KB)

SPIFlash-3.1.0.zip (145 KB)

SPIMemory-3.2.0.zip (171 KB)

SPIMemory-3.2.1.zip (173 KB)

SPIMemory-3.3.0.zip (185 KB)

SPIMemory-3.4.0.zip (192 KB)

Just updated to v 1.0.1 and added the ability to output data to Serial as CSV decimal values.

As always, You can find the latest version on Github here → SPI Flash library for Arduino. V1.0.1 is attached to this post. :slight_smile:

SPIFlash.zip (23.6 KB)

Nice work, but it is not specific to the W25Q08! not 80... they all share the same command structure, as such will also work for 2M, 4M, 8M & 16M :-

#define CHIP_SIZE128     16L * 1024L * 1024L  //W25Q128
#define CHIP_SIZE64       8L * 1024L * 1024L  //W25Q64
#define CHIP_SIZE32       4L * 1024L * 1024L  //W25Q32
#define CHIP_SIZE16       2L * 1024L * 1024L  //W25Q16
#define CHIP_SIZE8        1L * 1024L * 1024L  //W25Q08

Regards,

Graham

How does your Library handle the 4k Erase sector size? do you use a triangle buffer? The Uno only has 2k of ram. If you need to change a value in the flash you have to erase an entire 4KB sector.

I wrote a similar Library, but I added a 64KB SPI Ram chip to use as a temporary buffer to copy the sector data into. My library first verifies that the write is possible (memory bits can be programed to '0' only), if a bit is already '0' and needs to be a '1', it copies the corresponding sector to the SPI RAM, initiates a Sector Erase, merges the new data into the SPI RAM image, then after the Sector Erase Completes writes all non '0xff' bytes back into the FLASH by 256byte pages.

Chuck.

check out my Kickstarter Memory Panes a 1MB RAM expansion Shield for MEGA2560 projects.

Regarding your kickstarter project, NICE!! But why is it specific to the MEGA? Same form factor as the DUE, SPI pinout virtually the same, consider making the necessary modifications to work with MEGA AND DUE and I can see your project will do well or at least increase the potential number of devices pledged.

Regards,

Graham

The DUE doesn't express all of the necessary pins from the Atmel processor. The SAM3x does not multiplex A0-A7 and D0-D7. The Processor has D0-D15 (PC2-PC17) A0-A22 (PC21-PC30,PD0-PD9)

But the DUE only expresses

PC 2-9,12-17,21-26,28,30 Missing Data D8,9 Address A6,A8 PD 0-3,6-9 Missing Address A14,15

So, it is not possible unless a NEW Mega DUE is created and all of the necessary pins are expressed.

Chuck.

Had a very quick look, and I noticed writepage does check the pagenumber but readpage doesn't. shouldn't that be checked too?

boolean  SPIFlash::_readPage(uint16_t page_number, uint8_t *page_buffer) {
     if(!_notBusy())
        return false;
...

boolean SPIFlash::_writePage(uint16_t page_number, uint8_t *page_buffer) {
    if(!_notBusy() || page_number >= 4096 || !_writeEnable())
        return false;

page_number >= 4096 part

debugging refactored

//Prints hex/dec formatted data from page reads - for debugging
void SPIFlash::_printPageBytes(uint8_t *page_buffer, uint8_t outputType) 
{
  char buffer[10];
  if (outPutType == 1)
  {
    for (int idx = 0; idx < 256; ++idx)
    {
      sprintf(buffer, "%02x,", page_buffer[idx]);
      Serial.print(buffer);
    }
  }
  else   // if (outPutType == 2)
  {
    for (int idx = 0; idx < 256; ++idx)
    {
      int x = page_buffer[idx];
      if (x < 10) Serial.print("0");
      if (x < 100) Serial.print("0");
      Serial.print(x);
      Serial.print(',');
    }
  }
}

using the return values of the private function result in faster failing.

boolean SPIFlash::writePage(uint16_t page_number, uint8_t *page_buffer) 
{
  uint8_t temp_buffer[256];
  char buffer[28];  // <<<<<<< 80 is overkill, wastes heap space (more places)

  if (false == _writePage(page_number, page_buffer)) return false;
  if (false == _readPage(page_number, temp_buffer)) return false;

  for (int a=0; a<256; ++a) 
  {
    if (!temp_buffer[a] == page_buffer[a]) return false;
  }
  sprintf(buffer, "Writing page (%04x) done", page_number);
  Serial.println(buffer);

  return true;
}

my 2 cents

Just updated to v 1.2.1 with a bunch of optimizations and improvements .

As always, You can find the latest version on Github here → SPIFlash Library for Arduino v1.2.1 A ZIP file is also attached to this post. :slight_smile:

My apologies for not getting on here in a while. It was a very busy month at Uni with the semester coming to an end and field trip season starting up. I’ve only just managed to get back on now.

@Graham I haven’t played around with the other chips or looked at their datasheets yet, so thank you for pointing it out. It’ll be the next thing I get on to. :slight_smile:

@Chuck Unfortunately all the erase functions just erase, thanks to the Uno’s 2K RAM. I can’t think of any way to get around it without resorting to what you’ve done with external SPI RAM. The onboard RAM also limits the use of the readBytes() and writeBytes() functions in the latest version of the library. :frowning:

I’ve been considering tossing in some FRAM. I just got some samples from Fujitsu and I’m waiting for a few components to arrive from Element14 before I cook up some breakout boards in the toaster. I’ll test them out and post the results ASAP. :slight_smile:

Good luck with the Kickstarter! :slight_smile:

@Rob Thanks for the feedback. :slight_smile: The latest version of the library does not have _readPage() and _writePage() anymore. It also has page_number and offset checking built in where ever they are used. Also, Address rollover is the default action for when the end of the memory bank is reached during any operation - rollover can be disabled globally with an optional argument in the constructor. (Now that I think of it, perhaps I should make it a local argument in any function that requires it - it might give a user more flexibility?).

Thank you for your suggestion on the debug code. My version is a bit clumsy I admit, but it outputs the code in a neat 16/16 grid which makes it easy to read. I’m still trying to see if there’s a cleaner way of formatting the output, but I’m pretty much a self-taught programmer (I’m a geneticist by trade) and I’d love to learn a new way of doing things if you have any suggestions. :slight_smile:

I’ve used some of the code from your suggestion (below) in the latest version of the library. Thank you for that!

uint8_t x = data_buffer[a * 16 + b];
if (x < 10) Serial.print("0");
if (x < 100) Serial.print("0");
Serial.print(x);
Serial.print(',');

I’ve actually re-written writePage() completely so it no longer uses _writePage(). My current version checks the written data byte by byte without actually having to read it all into a buffer first. Saves a ton of time in the process and catches errors exactly where they happen. I’m toying with the idea of including a subroutine in my error checking protocol to return the exact location of the error so a user can ID any faulty registers.

SPIFlash-1.2.1.zip (25.6 KB)

Marzogh: Just updated to v 1.2.1 with a bunch of optimizations and improvements .

@Chuck Unfortunately all the erase functions just erase, thanks to the Uno's 2K RAM. I can't think of any way to get around it without resorting to what you've done with external SPI RAM. The onboard RAM also limits the use of the readBytes() and writeBytes() functions in the latest version of the library. :(

I've been considering tossing in some FRAM. I just got some samples from Fujitsu and I'm waiting for a few components to arrive from Element14 before I cook up some breakout boards in the toaster. I'll test them out and post the results ASAP. :)

Good luck with the Kickstarter! :)

I was hoping you had came up with some magical way, that did not encounter this issue. But I guess magic is rare :money_mouth_face: .

When you say you work with 'software' your really mean 'Wetware?' :)

Well, I'm up to 14 backers, only Need 55 :(

Chuck.


Check out my Kickstarter Project Memory Panes an expansion RAM Shield for Mega2560's. It adds 1MB of RAM for those projects where 8KB is not enough.

Just updated to v1.3.0 with a serious set of improvements including writeAnything() & readAnything(), support for writing & reading most supported variable types (String support to come in future updates) and built in error checking. :)

As always, You can find the latest version on Github here -->SPIFlash Library for Arduino v1.3.0 A ZIP file is also attached to the first post on this thread. :)

@Chuck: Too bad the Kickstarter didn't go through. :( I was a backer. Are you going to open source your design?

Hi Marzogh

Brilliant library, got a W25Q128FV working with my Mega board. Tried to wire it up to my Due board but encountered some problems. Commenting out the #include <util/delay.h> in the SPIFlash.cpp file gets it to compile, but my Due board becomes unresponsive after calling the constructor. It seems to occur after the SPI.begin() call. Any ideas for a quick fix?

Hi Jtewell,

Thanks for that. I'm glad it works well with the Mega. :) It has been throwing errors with the Leonardo and I haven't had time to fix that yet. :( Unfortunately I have close to zero experience working with ARM processors and can't help with the issue you are facing. I'm still working on learning more about the Due and plan on including support once I figure out where I stand.

If you do find a way to add Due support, feel free to fork the github repo and submit any changes - I'm always open to learning something new. :)

Just updated to v1.3.2 which supports storage and retrieval of Strings, along with a raft of other features. :)

As always the latest version can be found at here.

Just pushed through a big update to v2.0.0.

  • BIG update in terms of speed. 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())
  • getAddress() can now return either a 32-bit address or a page number & offset.

As always, you can find this version on Github here -->SPIFlash Library for Arduino v2.0.0 A ZIP file is also attached to the first post on this thread. :)

Just pushed through an update to v2.1.0

  • The library now supports the Arduino Due!
  • Lots more optimizations + even better ATTiny85 support

As always, you can find this version on Github here -->SPIFlash Library for Arduino v2.1.0 A ZIP file is also attached to the first post on this thread. :)

P.S. jtewell : This might be the version you are looking for!

Help please! I am trying to get the W25Q80BV Winbond 1Mbyte Spi Flash DIP chip to work with the Arduino Due, but it gets stuck in function flash.begin. I am using example programms fron Marzogh/SPIFlash V2.1.1 with Arduino V1.6.5 with an Arduino due. The examples compile fine and upload to the Due board fine but all I see is “Initialising Flash memory……….” Nothing else is written to the screen. Further investigation shows it get stuck in flash.begin(); I have wired the chip to the Due as follows: Chip pin 1 -> Due pin 10 (Chip Select) Chip pin 2 -> Due pin MISO (108) (SPI Pins) Chip pin 3 -> Vcc 3.3V Chip pin 4 -> GND (Common GND for chip and Due) Chip pin 5 -> Due pin MOSI (109) (SPI Pins) Chip pin 6 -> Due pin SCK (110) (SPI Pins) Chip pin 7 -> Vcc 3.3V Chip pin 8 -> Vcc 3.3V Any help will be much appreciated.

That is unexpected. v2.1.1 was a bug fix release for the powerUp & powerDown functions so I didn't realise I'd broken Due support. Thanks for the heads up - I'll test and get back to you with a fix shortly, however, for the time being, using v2.1.0 should solve the problem.

Update:

I've just tested my Due with v2.1.1 and the only issue I have with it is that powerDown() returns a false negative (This is being fixed in the upcoming v2.2.0). Everything else is functioning properly and returns the following:

Initialising Flash memory..........


----------------------------------------------------------------------------------------------------------------------------------
                                                           Get ID                                                                 
----------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------
Manufacturer ID: efh
Memory Type: 13h
Capacity: 4014h
JEDEC ID: ef4014h
----------------------------------------------------------------------------------------------------------------------------------
                                                          Write Data                                                              
----------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------
    Data Written    ||      Data Read       ||      Result
----------------------------------------------------------------------------------------------------------------------------------
    35      ||      35          ||      Pass
    -110        ||      -110            ||      Pass
    4520        ||      4520            ||      Pass
    -1250       ||      -1250           ||      Pass
    876532      ||      876532          ||      Pass
    -10959      ||      -10959          ||      Pass
    3.1415      ||      3.1415          ||      Pass
    123 Test !@#    ||      123 Test !@#        ||      Pass

Struct data written successfully
31325, 4.84, 880932, 1, 5
Saved!
Local values set to 0
After reading
31325, 4.84, 880932, 1, 5

Values from 0 to 255 have been written to the page 484
These values have been read back from page 484
Reading page (01e4)
000,001,002,003,004,005,006,007,008,009,010,011,012,013,014,015,
016,017,018,019,020,021,022,023,024,025,026,027,028,029,030,031,
032,033,034,035,036,037,038,039,040,041,042,043,044,045,046,047,
048,049,050,051,052,053,054,055,056,057,058,059,060,061,062,063,
064,065,066,067,068,069,070,071,072,073,074,075,076,077,078,079,
080,081,082,083,084,085,086,087,088,089,090,091,092,093,094,095,
096,097,098,099,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,

Have you tried running Diagnostic.ino - included in the examples after uncommenting #define RUNDIAGNOSTIC from the top of SPIFlash.cpp? (Make sure you save SPIFlash.cpp before you recompile and upload Diagnostics.ino) If you have, what errors did it print to your serial?

Can you post a photo of your wiring?

Also, if all fails have you tried using a different chip? I have previously found the chip returns 'busy' if the breadboard connections are not properly made or if the chip is faulty and not responding.

Thankyou for this reply. I am working on this today. I will use another chip just in case and will send you a photo and let you know how I get on. I have noticed some people use lots of resistors and a 0.1uf capcitor between VCC and GND but I simply connect direct and use the Due's 3.3V for VCC. (Trust my connections above are OK for the Due) Excelent Library you have. I am sure I had something simple wrong the first time I tried it.