Step 1: Update the dataflash source code
I pulled the source for the dataflash library from the asynclabs github. I could read the manufacturer's ID, but that was about it. After several failed attempts to read and write anything to flash, I pulled up the datasheet. It appeared that the source used old deprecated versions of some of the opcodes and the status register. These changes needed to be made to dataflash.h
- Code: Select all
-#define FlashPageRead 0x52 // Main memory page read
+#define FlashPageRead 0xD2 // Main memory page read
-#define Buf1Read 0x54 // Buffer 1 read
+#define Buf1Read 0xD4 // Buffer 1 read
-#define Buf2Read 0x56 // Buffer 2 read
-#define StatusReg 0x57 // Status register
+#define Buf2Read 0xD6 // Buffer 2 read
+#define StatusReg 0xD7 // Status register
-#define ContArrayRead 0x68 // Continuous Array Read (Note : Only A/B-parts supported)
+#define ContArrayRead 0x03 // Continuous Array Read (Note : Only A/B-parts supported) low freq
Another issue is that the class assumes that the static variables PageBits and PageSize are initialized before calling anything that reads or writes to flash memory (buffer memory was ok). These are only set when reading the status, so that meant that the first of every flash read / write would have undefined results. To alleviate this I moved a call to Read_DF_Status() to the init routine.
Cont_Flash_Read_Enable() really didn't do anything, because there was no Cont_Read() function. Continuous read is like streaming data directly from flash, each byte you read increments the address pointer, which will roll from one page to the next; it is a pretty useful read method, so I changed Cont_Flash_Read_Enable() to use the low-frequency opcode and stop discarding the first four bytes of data, as well as added a Cont_Flash_Read() to get the streamed data.
Finally I added Sizes() function for returning the size of the page address bits and page size, for use in an uploader program I wrote that page aligned the start of each file.
Step 2: Understanding the SPI bus
I could read and write to flash and buffer all I wanted now, but using it with WiFi caused the Arduino to stop responding to web requests as soon as it started to serve the first flash file. The issue here is simple. The SPI bus is basically a chain of devices who all share the same clock and rx/tx lines but each device on the bus has its own unique line that is called Slave Select (SS#). When this signal is high, the device ignores all data on the bus. When this line is low, the device is "selected" to be the recipient of the SPI bus data. What I noticed is that when you use the dataflash, it enables the DF's slaveselect, does its business, and returns, leaving the slave line low. When the WiFi tries to send the data you're pulling from the flash, the data it is sending is now going to both the dataflash and wifi, which means both of them can respond too. This of course is a problem considering you've got two devices with different communication protocols sending and receiving at the same time with no arbitration. The solution is to enable the dataflash chip when you're using it and disable it using once you've done your business.
Sample Code!
Serving a static page from dataflash.
- Code: Select all
const char FNAME000[] PROGMEM = "favicon.ico";
const char FNAME001[] PROGMEM = "fire.png";
const char FNAME002[] PROGMEM = "";
const struct flash_file {
const char *fname;
const unsigned int page;
const unsigned int size;
} FLASHFILES[] PROGMEM = {
{ FNAME000, 3, 1625 },
{ FNAME001, 7, 11824 },
{ FNAME002, 30, 4218 },
{ 0, 0, 0},
};
boolean sendPage(char* URL)
{
++URL; // WARNING: URL no longer has leading '/'
const struct flash_file *file = FLASHFILES;
while (pgm_read_word(&file->fname))
{
if (strcmp_P(URL, (const prog_char *)pgm_read_word(&file->fname)) == 0)
{
sendFlashFile(file);
return true;
}
++file;
}
void sendFlashFile(const struct flash_file *file)
{
unsigned int size = pgm_read_word(&file->size);
dflash.Cont_Flash_Read_Enable(pgm_read_word(&file->page), 0);
while (size-- > 0)
WiServer.write(dflash.Cont_Flash_Read());
dflash.DF_CS_inactive();
}
Note that this is largely inefficient for files over ~350 bytes because the whole file is read from flash every time even though the WiFi can only transfer 400 at a time. It works though.
But your comment about needing to 'unselect' the dataflash chip on the SPI bus makes me wonder if you've also put a DF_CS_inactive() call at the end of the dflash.Cont_Flash_Read() method. Like just inside Cont_Flash_Read() so it will release the dataflash chip just as you said. Is this the case?
Actually no. The WiServer.write() method only writes the byte to the buffer, not actually out the device. If you toggle the slave select during the continuous read operation, the dataflash chip will stop sending because it is expecting you to send a command. When the sendPage() returns, the wiserver will activate the wifi and send the data you've written with write(), which is ok because you've deactivated the SS# (called CS for some reason in the code) before returning from your sendPage() method.
My edited version of dataflash I use is available from my website.