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

wsteelmanj:
@Okio is that also the correct wiring for the W25Q256FV with an ATMega328p? And couldn't you save a pin by wiring the Chip Select (Pin 1) straight to GND? There's no harm in always keep the chip selected right? The project I am working on requires a lot of pins and I'm trying to conserve if possible.

No,
you must toggle the CS pin at the start and end of each transaction. the SPI interface is just a bit stream, the CS pin is used to alert the device that It should start watching the SCK and MOSI pins.

Without an active CS pin, you can only do ONE command either a READ or WRITE, then you would have to power down and start over.

This is because the chip (W25xx) expects a command byte, possibly address bytes, then an unknown number of data bytes. the digitalWrite(CS,HIGH) terminates the transmission which causes the chip to recycle it's machine state back to where it is expecting a command byte.

If you have many SPI devices you can use a 2:4 decoder which allows 2 Arduino Pins to select one of three SPI devices. Or you can use a 3:8 decode which allows 3 arduino pins to control seven SPI devices.

Here is a schematic of how I use a 3:8 decoder to control 5 devices with 3 pins.

With this circuit, D8..D10 are my CS pins, of the 8 possible value 0x07 is reserved to mean NONE are selected. I keep D8..D10 high to unselect all SPI devices I can Select my SDCard, FLASH, or RAM devices by changing only one pin to low, to Select my RS232 or RS485 I have to do two or three digitalWrite() commands. Between the digitalWrite() commands, the WRONG device is selected. After I set D8..D10 to the correct value, I delay for short time(500ns) to let the chips settle out.

Chuck.

Thanks Chuck. I got my chip all wired up and now I have another question about programming.

I am using a W25Q128FVSIG which is 16MB. So how would I go about putting say 5MB of data on the chip? I'm thinking I want to read the data from a text file on my pc via the serial port, but I don't want it to take several hours if possible. Is there a way for the arduino to access and read a file on my computer? Or should I be writing some code on my computer which sends the data through the serial monitor? And if the latter than what is the best software to do that with? Thanks guys.

wsteelmanj:
Thanks Chuck. I got my chip all wired up and now I have another question about programming.

I am using a W25Q128FVSIG which is 16MB. So how would I go about putting say 5MB of data on the chip? I'm thinking I want to read the data from a text file on my pc via the serial port, but I don't want it to take several hours if possible. Is there a way for the arduino to access and read a file on my computer? Or should I be writing some code on my computer which sends the data through the serial monitor? And if the latter than what is the best software to do that with? Thanks guys.

You'll have to write software, That is what I am doing. I have a similar setup. AtMega2560, SPIram(64kB) SPIFlash(8MB). I am writing a simple block transmission client and server. It is part of a bigger project.
the client (Mega2560) watches for for single byte control characters, all data is transmitted as ASCII coded HEX with using the Intel Hex format. with a few extra commands to specify chip,type, offset.

Chuck.

I've been trying to use an array in the first page of a block to indicate which pages in a block are available for use. To do this I've attempted to set the array elements to 1 or 2 depending on usage and then change to 0 to say that page is now junk.

On EVERY NOR flash I've used in the last 20 years, any '1' can be programmed down to a '0' but is it really true that for the Winbond W25Q series (and I quote the spec):

"The Page Program instruction allows from one byte to 256 bytes (a page) of data to be programmed at
previously erased (FFh) memory locations. "

which means that an arbitrary value can't be changed to a '0' and my whole design is stuffed (I was hoping to use 'spiffs' at some point which relies on this property of NOR flash).

I've tried it and it certainly appears to be the case that a byte MUST be 0xff to be programmed :frowning:

frackers:
On EVERY NOR flash I've used in the last 20 years, any '1' can be programmed down to a '0' but is it really true that for the Winbond W25Q series (and I quote the spec):

It appears that I misinterpreted how "SPIFlash::_notPrevWritten" is supposed to work - by putting the code into "HIGHSPEED" mode all my problems go away. Can't help thinking that this function is dangerous!!

Found another potential bug - if the address is 0x0000000 then _prepRead & _prepWrite return this value which just happens to be 'false' !!

Just pushed through an update to v2.3.0. This one is for everyone who has been asking for ESP8266 compatibility! :slight_smile:

  • ESP8266 compatibility added
  • Compatible with Arduino Fio and Micro
  • Now compatible with Arduino IDE <= v1.6.9
  • Compatible with Arduino-Makefile (Thanks Raphael!)

As always, you can find this version on Github here --> SPIFlash Library for Arduino v2.3.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 v2.3.0 :slight_smile:

Hello, I have slight trouble with W25Q64. When I using Arduino board switched to 3.3V everything is working well. But when I test this same type of memory on my own datalogger board the memory return inconsistent output as shown below. Board has main power supply rail 5V and second one 3.3V and for data there is also logic level converter (unipolar transistors). I also tried different frequency dividers to slow things down but nothing really helps.

Schematic was inspired by sparkfun bi-directional logic level converter. There are no other devices on SPI bus. Everything was tested for shorts and bad connections. Also on the schamatic there is no 0.1uF cap across VCC and GND, on the actual board there is one but with no difference when present.

Initialising Flash memory..........
----------------------------------------------------------------------------------------------------------------------------------
                                                           Get ID                                                                 
----------------------------------------------------------------------------------------------------------------------------------
                                                       Winbond W25Q64BV
----------------------------------------------------------------------------------------------------------------------------------
	JEDEC ID: ef4017h
	Manufacturer ID: efh
	Memory Type: 16h
	Capacity: 4017h
	Maximum pages: 32768
----------------------------------------------------------------------------------------------------------------------------------
                                                          Write Data                                                              
----------------------------------------------------------------------------------------------------------------------------------
	Data written without errors
----------------------------------------------------------------------------------------------------------------------------------
                                                          Data Check                                                              
----------------------------------------------------------------------------------------------------------------------------------
	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
	inputStruct	||		outputStruct		||		Pass
	0 - 255		||		0 - 255			||		Pass
----------------------------------------------------------------------------------------------------------------------------------
                                                     Check Other Functions                                                        
----------------------------------------------------------------------------------------------------------------------------------
			Function		||		Result
----------------------------------------------------------------------------------------------------------------------------------
			powerDown		||		Error code: 0x04Pass
			powerUp			||		Pass
			sectorErase		||		Pass
			chipErase		||		

Initialising Flash memory..........
Error code: 0x04
Error code: 0x02

but sometimes when i try this test i have 5 times error and then normal output with errors.

Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........

----------------------------------------------------------------------------------------------------------------------------------
                                                           Get ID                                                                 
----------------------------------------------------------------------------------------------------------------------------------
                                                       Winbond W25Q64BV
----------------------------------------------------------------------------------------------------------------------------------
	JEDEC ID: ef4017h
	Manufacturer ID: efh
	Memory Type: 16h
	Capacity: 4017h
	Maximum pages: 32768
----------------------------------------------------------------------------------------------------------------------------------
                                                          Write Data                                                              
----------------------------------------------------------------------------------------------------------------------------------
	Data written without errors
----------------------------------------------------------------------------------------------------------------------------------
                                                          Data Check                                                              
----------------------------------------------------------------------------------------------------------------------------------
	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
	inputStruct	||		outputStruct		||		Pass
	0 - 255		||		0 - 255			||		Pass
----------------------------------------------------------------------------------------------------------------------------------
                                                     Check Other Functions                                                        
----------------------------------------------------------------------------------------------------------------------------------
			Function		||		Result
----------------------------------------------------------------------------------------------------------------------------------
			powerDown		||		Error code: 0x04
Pass
			powerUp			||		Pass
			sectorErase		||		Pass
			chipErase		||		Error code: 0x04
Fail
----------------------------------------------------------------------------------------------------------------------------------

For some reason it often tellign that there is 0x04 error, but I can't find the reason. Could it be a error like slow logic converter? Thank you for any advice! Michal

kozlik-cze:
Hello, I have slight trouble with W25Q64. When I using Arduino board switched to 3.3V everything is working well. But when I test this same type of memory on my own datalogger board the memory return inconsistent output as shown below. Board has main power supply rail 5V and second one 3.3V and for data there is also logic level converter (unipolar transistors). I also tried different frequency dividers to slow things down but nothing really helps.

Schematic was inspired by sparkfun bi-directional logic level converter. There are no other devices on SPI bus. Everything was tested for shorts and bad connections. Also on the schamatic there is no 0.1uF cap across VCC and GND, on the actual board there is one but with no difference when present.

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

Get ID

Winbond W25Q64BV

JEDEC ID: ef4017h
Manufacturer ID: efh
Memory Type: 16h
Capacity: 4017h
Maximum pages: 32768

Write Data

Data written without errors

Data Check

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
inputStruct || outputStruct || Pass
0 - 255 || 0 - 255 || Pass

Check Other Functions

Function || Result

powerDown || Error code: 0x04Pass
powerUp || Pass
sectorErase || Pass
chipErase ||

Initialising Flash memory..........
Error code: 0x04
Error code: 0x02





but sometimes when i try this test i have 5 times error and then normal output with errors. 



Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........
Error code: 0x04
Error code: 0x02
Initialising Flash memory..........


Get ID

Winbond W25Q64BV

JEDEC ID: ef4017h
Manufacturer ID: efh
Memory Type: 16h
Capacity: 4017h
Maximum pages: 32768

Write Data

Data written without errors

Data Check

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
inputStruct || outputStruct || Pass
0 - 255 || 0 - 255 || Pass

Check Other Functions

Function || Result

powerDown || Error code: 0x04
Pass
powerUp || Pass
sectorErase || Pass
chipErase || Error code: 0x04
Fail




For some reason it often tellign that there is 0x04 error, but I can't find the reason. Could it be a error like slow logic converter? Thank you for any advice! Michal

More than likely, your N-Mosfet level converter using 10k pullups is too slow.

How fast are you running the SPI bus?
Depending on the capacitance of the bus, it may take too long for the voltage to rise from 0v to 5v. This would cause transmission errors.

There are two things you can try:

  1. Reduce the resistance to 1K.
  2. Reduce the speed of the SPI bus SPI.beginTransaction(SPISettings(100000L,MSBFIRST,SPI_MODE0));
    this would set the SPI bus to 100Kbits from the 4Mbits default.

Chuck.

chucktodd Thank you for your answer! This is what I tought it can be, because when I measure logic transistor voltage on low side just with 1second pulse it took little too much time to settle the switched value, but I cannot tell exactly because I don't have an oscilloscope :frowning: Unfortunately I can't find any 1k resistor net, bud I desolder 2 4.7k from other device to try it out. Same issue, so i have to try order 1k nets.

By the SPIlib library default, there was no actual divider, so I think the frequency was actual maximum 4MHz. But even if I try all available dividers to 2,4,8,...,128 nothing realy solve the issue, on some frequencies there was even stranger errors like 0x07, 0x06 etc..

I hope this is not circuit design issue, the winbond memory is placed like inch from MCU and on the way there is logic shifter. So i will order 1k nets and try it out again next week. Thank you, cheers :slight_smile:

ADD: maybe this is good link for everyone who is solving same issue insane level shifter

kozlik-cze:
chucktodd Thank you for your answer! This is what I tought it can be, because when I measure logic transistor voltage on low side just with 1second pulse it took little too much time to settle the switched value, but I cannot tell exactly because I don't have an oscilloscope :frowning: Unfortunately I can't find any 1k resistor net, bud I desolder 2 4.7k from other device to try it out. Same issue, so i have to try order 1k nets.

By the SPIlib library default, there was no actual divider, so I think the frequency was actual maximum 4MHz. But even if I try all available dividers to 2,4,8,...,128 nothing realy solve the issue, on some frequencies there was even stranger errors like 0x07, 0x06 etc..

I hope this is not circuit design issue, the winbond memory is placed like inch from MCU and on the way there is logic shifter. So i will order 1k nets and try it out again next week. Thank you, cheers :slight_smile:

ADD: maybe this is good link for everyone who is solving same issue insane level shifter

I use the Texas Instruments TXS104E as a SPI levelshifter. It's low side can go down to 1.65V and the High side up to 5.5V. With the SPI bus's Push/Pull this translator is rated at 24Mhz, way above the possible Arduino Uno/Mega SPI frequencies.

The TXS104E is 4 bit, TXS108 is 8 bit. The choice depending on how many 3.3V SPI devices you have. You can also use these chips for I2C at reduced speed (2Mhz). You can use the TXS108 for both you SPI bus and the I2C bus at the same time.

With shared usage, 2 bits for I2C, 6 bits for SPI, you can have 3 SPI slaves.

Chuck.

Dear Marzogh,

First of all, many thanks for this great library. I have started to work with your library using ATMega1284p and Winbond 25Q40BVNIG.
I works fine except one strange problem. If I understand everything correctly, proper addresses starts at 0, which means I can execute code below and flash.readByte(0) should return value 17.

flash.begin();
flash.eraseBlock32K(0);
flash.writeByte(0, 17);
flash.readByte(0); //returns 0, why ?

Unfortunately my tests shows that everything is working starting from address 1. Writes for address 0 are ok, but further reads returns 0. Do you have any idea what might be the reason behind this ?

Regards,
Piotr

miduchp:
It works fine except one strange problem. If I understand everything correctly, proper addresses starts at 0, which means I can execute code below and flash.readByte(0) should return value 17.

Unfortunately my tests shows that everything is working starting from address 1. Writes for address 0 are ok, but further reads returns 0. Do you have any idea what might be the reason behind this ?

Uh oh! ::slight_smile: That shouldn't be happening. I'll take a look at it and sort it out asap and let you know when I've fixed it. Thanks for bringing this to my attention.

I'm glad you find the library useful!

Cheers,

Prajwal

Hi Piotr,

The Address 0x00 bug's been squashed. However, it is a part of the changes a release that isn't completely ready yet - even though its fully functional for now. Feel free to download the v2.4.0-w.i.p branch for now - it should work for all intents and purposes. Just remember tis is still a work-in-progress, so, things might not be as polished as in a final release.

Cheers,

Prajwal

Hi,

This is a great implementation. Thanks for all the work. i am using it to access a w25q32dw and it is
working fine. The issue I run into is when I try to read address 0x00000000. because of the way
_prepRead() is coded it returns false if the address is bad or "address" if it is okay. Therefore if you
pass in address 0x00000000 it returns 0 (false) and the read operation fails.

I can't believe this hasn't come up before so I am assuming I am missing something? The fix is easy
since no routines currently take advantage of the returned address so simply having _prepRead() return
true instead of address seems to do the trick.

Please let me know and thanks again for this great library.

tozz88:
The issue I run into is when I try to read address 0x00000000. because of the way
_prepRead() is coded it returns false if the address is bad or "address" if it is okay. Therefore if you
pass in address 0x00000000 it returns 0 (false) and the read operation fails.

Hi mate,

I'm glad you find the library useful. :slight_smile: I'm afraid the bug's a part of the v2.3.0-w.i.p release - it has been fixed in the v2.4.0-w.i.p release that's available on Github. Please use that and you won't have this problem anymore. I will make some time over the next couple of weekends to clean & polish up v2.4.0 and push it through as a proper release.

Cheers!

Praj

Just pushed through an update to v2.4.0. This one has had a bunch of bugs squashed and another bunch of optimizations performed on it

Bugs Squashed

  • Fixed bug preventing writing to Address 0x00
  • Fixed bug in _notPrevWritten() which 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 in 2.3.0 & 2.3.1.

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 fewer 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.

As always, you can find this version on Github here --> SPIFlash Library for Arduino v2.4.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 v2.4.0 :slight_smile:

Hi,
I have tried your library (V2.3) for reading data on my Due board.
That's really friendly to use with my W25Q16.

Because of mass data needed to be read, readByteArray function is what I use.
About 95ms for reading 60,000 byte data, which is the fastest condition for me so far.
But it still too slow to use for data reading in my project (image plotting).

The frequency measure by scope in SPI SCK pin is only 330KHz in my the test condition.
Is it correct for getting this number, which is much less Due' working frequency?
I have set SPI devider to 2 (in SPIFlash.cpp, line 704) to get better performance.

Is there any way to speed up for reading data with your library?

Any suggestion is appreciated.

Hi Gary,

I'm glad you're finding the library useful. Try out the pre-release [SPI-Transactions-w.i.p] branch. I've been getting consistent clock speeds of 44MHz with the Due, using the code committed to this branch as of today. That should - on paper at least - bring down the read time for 60kb of data to ~10.90 ms. :slight_smile:

Hope that helps!

Cheers,

Praj

P.S. Don't worry about SPI_DIV2 any more. This branch has SPI Transactions support and will run as fast as the MCU allows it to. :slight_smile:

Hi Marzogh,
Thanks for your rapid reply and new library.
After trying [SPI-Transactions-w.i.p] you provided, the processing time increase to 132 ms in my test sketch :o
I just replace your library (V2.3) with the new one(w.i.p) without other modification.
Is there anything I should do to use it correctly?

BRs

Both of the SCK frequency in SPI are about 42MHz measured by scope.
But the interval between two"readByteArray" command is slightly longer in [SPI-Transactions-w.i.p] library.
That should be the reason why it takes more processing time for running my test sketch.
Is it possible to shorten that duration?

void loop()
{
  StartTime = millis();

  DrawImage(0);

  EndTime = millis();
  Duration = EndTime - StartTime;
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(255, 255, 255);
  myGLCD.printNumI(Duration, 130, 110);
  //delay(5000);
}
void DrawImage(int header)
{
  uint8_t bufrow[60000];
  flashread.readByteArray(16 + header, bufrow, 60000);
  //flashread.readByteArray(16 + header, bufrow, 60000);
 }