Streaming from SD card to SPI display

Hi all,

After many days of troubleshooting I am making steady progress in my project, but again I have hit a wall.

I’m using an OLED display, and so far I have succeeded in getting the display working. To test the display, I have been thus far converting images to hex arrays and loading them into the onboard flash memory. One at a time, so far. However, now that the display is working OK I need a better solution for image storage. Each array is 8kB and I will have perhaps 10-20 images total to display - far in excess of the 328 memory.

So I am instead going to use an SD card to store the images and read them from the SD card. Even a small SD card is overkill for my data, but I like that I can quickly insert the card into the computer to modify or upload new images. I had considered using an I2C EEPROM but then I would have to load all images onto the chip “through” the Arduino, which seems tedious.

However there is now a problem of how to implement this function. Ideally I would like to “stream” the data, byte by byte, from the SD card to the display, but they are both on the same SPI bus! This presents the large problem of avoiding interference (which so far has not been successful), and the smaller problem that it is slow - with many clock cycles going to selecting/deselecting chips, modifying SPI settings, etc… between each byte.

Here is my sketch as of now:

/* This is the main code for the control system. It has been modified to work with the Arduino
Pro Mini 3.3V board. Check that the pin assignments match the actual circuit. 

4-10-12
*/


#include <SPI.h>
#include <SD.h>

const int valve = 3;           // Output to SSR that switches valve
const int button = 4;          // 3.3V button signal
const int displayDC = 5;       // OLED display data control
const int displayReset = 6;    // OLED display reset 
const int displaySelect = 7;   // OLED display SPI slave select
const int interlock = 8;       // Input from interlock 
const int lightSensor = A0;    // Input from ambient light sensor
const int sdSelect = 10;       // SD Card select pin

int brightness = 0;
int fadeamount = 1;  

File myFile;

void setup(){
  
  Serial.begin(9600);  
   // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
   digitalWrite(sdSelect,HIGH);
   pinMode(sdSelect, OUTPUT);
   
  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  
  
  digitalWrite(displaySelect,HIGH);
  digitalWrite(valve,HIGH);
  pinMode(valve, OUTPUT);
  pinMode(displaySelect, OUTPUT);
  pinMode(displayReset, OUTPUT);
  pinMode(displayDC, OUTPUT);

  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  SPI.setDataMode(SPI_MODE3);
  SPI.begin();
  analogReference(DEFAULT);
  OLED_Init(); 
  
  clear_Screen();


myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
    while (myFile.available()){
      Serial.write(myFile.read());
    }
  }
    
/* This code sends a full image to the display. Because each byte
contains two 4 bit grayscale pixels, 8192 bytes are needed to fill the
complete 16384 pixel screen. 

SPI.transfer is explicitly called because the "oled_Data()" function
toggles the display select and data control lines before and after each byte.
The greatly reduces the transfer speed and results in a noticeable "wipe" 
as the data is loaded to RAM. */

myFile = SD.open("test.txt");
  unsigned int i;
	
	Set_Column_Address(0x1C,0x5B);
	Set_Row_Address(0x00,0x3F);
	Set_Write_RAM();
        digitalWrite(displaySelect,LOW);
        digitalWrite(displayDC,HIGH);
//	for(i=0;i<8192;i++)
        while(myFile.available())
       	{
          SPI.transfer(myFile.read());
	}
        digitalWrite(displaySelect,HIGH);
        myFile.close();
}

void loop(){
    if (digitalRead(button)){
        clear_Screen();
    }
}

I would like to avoid delving into the inner workings of the SD library, but I now have many questions about using it in conjunction with the display.

  • Do I need to change the settings of the SPI bus for each transfer?
  • Does either the SD library or the SPI library override the settings of the other? Or are the settings simply that which the most recent program changed them to?
  • If I use the SD library, do I have to explicitly define the SPI settings for the other device before using that one as well?

This structure in particular is one that worries me:

myFile = SD.open("sia.txt");
  unsigned int i;
	
	Set_Column_Address(0x1C,0x5B);
	Set_Row_Address(0x00,0x3F);
	Set_Write_RAM();
        digitalWrite(displaySelect,LOW);
        digitalWrite(displayDC,HIGH);
//	for(i=0;i<8192;i++)
        while(myFile.available())
       	{
          SPI.transfer(myFile.read());
	}
        digitalWrite(displaySelect,HIGH);
        myFile.close();
}

I am using the SPI.transfer function to read from the SD card. Now, I am not surprised that this is giving me gibberish, but I’m also unsure of what to do about it. I had considered copying data from the SD card to FLASH, and then reading THAT data. This would seem to avoid the aforementioned problems, but I decided that that was unacceptable. The number of write cycles for flash memory will be exceeded many times over during the life of this device.

So, essentially I need a way to either stream data from SD to the display over the same bus, and either do it byte-by-byte, or in small chunks that can all reside in SRAM. I.e. read 128kB from SD card, transfer to display, etc…

One final question and I will end this post: I have my data on the SD card in a .txt format. So for example my data in my test image is like this:

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
...

This is what you see when you open the file in Notepad. Now the obvious problem, the “read()” command in the SD library will not return these values the same as if that same array was in the Arduino IDE. Rather, my feeling is that it will return something like:
0xFF
comma
space
0xFF
comma
space

or even break down each character into its ASCII code. I.e.
ASCII code for 0
ASCII code for x
ASCII code for F
ASCII code for F
ASCII code for comma
ASCII code for space

etc…

When what I really need, for my above code to work, is for it to return the values as they are written in the text file. Like this:
0xFF
0xFF
0xFF
so that these values can be passed directly over the SPI bus without translation.

I hope that makes sense, I am dreading the thought of what to do with 8000+ commas for each image if that is not possible.

Anyway that is all. As always I am grateful for any help, and I’d be glad to do some more reading once I figure out the correct approach to take.

You will probably need to handle the Slave Select for the display. The SD library handles the SS for it, so this is what I would try:

        while(myFile.available())
       	{
          // get byte from SD card
          // this library function handles the SD SS line for you
          byte thisByte = myFile.read();

          // enable display SS
          digitalWrite(displaySelect,LOW);

          // write to display
          SPI.transfer(thisByte);

          // disable display SS
          digitalWrite(displaySelect,HIGH):

	}

This is basically the code the SD card uses in its low level read/write functions.