Scrolling LED Matrix code

Hi All!

I have an 8x8 LED Matrix but I have no idea how to construct code to make it scroll. Below is the code I have to simply display a letter, but thats all I can figure out at this point. I'll also attach the schematic. Any help or guidance would be appreciated!

const int rClock = 2; //chip pin 7
const int rData = 0; //chip pin 5
const int cData = 4; //chip pin 3
const int latch = 1; //chip pin 6 
const int cClock = 3; //chip pin 2

//byte rowActivator[] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001};
byte rowActivator[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
byte letters[27][8] =
{
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100}, //A0
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000100, B01000100, B01111000}, //B1
  {B00000000, B00111000, B01000100, B01000000, B01000000, B01000000, B01000100, B00111000}, //C2
  {B00000000, B01110000, B01001000, B01000100, B01000100, B01000100, B01001000, B01110000}, //D3
  {B00000000, B01111100, B01000000, B01000000, B01111100, B01000000, B01000000, B01111100}, //E4
  {B00000000, B01111100, B01000000, B01000000, B01111000, B01000000, B01000000, B01000000}, //F5
  {B00000000, B01111000, B01000100, B01000000, B01011100, B01000100, B01000100, B00111100}, //G6
  {B00000000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100, B01000100}, //H7
  {B00000000, B01110000, B00100000, B00100000, B00100000, B00100000, B00100000, B01110000}, //I8
  {B00000000, B00011100, B00001000, B00001000, B00001000, B00001000, B01001000, B00110000}, //J9
  {B00000000, B01000100, B01001000, B01010000, B01100000, B01010000, B01001000, B01000100}, //K10
  {B00000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01111100}, //L11
  {B00000000, B01000100, B01101100, B01010100, B01010100, B01000100, B01000100, B01000100}, //M12
  {B00000000, B01000100, B01000100, B01100100, B01010100, B01001100, B01000100, B01000100}, //N13
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //O14
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000000, B01000000, B01000000}, //P15
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01010100, B01001000, B00110100}, //Q16
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01010000, B01001000, B01000100}, //R17
  {B00000000, B00111100, B01000000, B01000000, B00111000, B00000100, B00000100, B01111000}, //S18
  {B00000000, B01111100, B00010000, B00010000, B00010000, B00010000, B00010000, B00010000}, //T19
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //U20
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B00101000, B00010000}, //V21
  {B00000000, B01000100, B01000100, B01000100, B01010100, B01010100, B01010100, B00101000}, //W22
  {B00000000, B01000100, B01000100, B00101000, B00010000, B00101000, B01000100, B01000100}, //X23
  {B00000000, B01000100, B01000100, B01000100, B00101000, B00010000, B00010000, B00010000}, //Y24
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01111100}, //Z25
  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}, // Space to shift 32
};



void setup() {
  //Serial.begin(9600); Isn't supported on the ATtiny85
  pinMode(rClock, OUTPUT);
  pinMode(rData, OUTPUT);
  pinMode(cData, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cClock, OUTPUT);
}

void loop() {
  displayLetter('A'); 
}

void displayLetter(char letter)
{
  for(int i=0; i<8; i++)
  {
    clrData();
    digitalWrite(latch, LOW);
    shiftOut(rData, rClock, LSBFIRST, rowActivator[i]); // change to just 'i' to fip image
    shiftOut(cData, cClock, LSBFIRST, letters[letter - 65][i]);
    digitalWrite(latch, HIGH);
    delay(1);
  }
}

//function yo clear the row registers preventing "ghosting"
void clrData() 
{
  digitalWrite(latch, LOW);
  shiftOut(rData, rClock, LSBFIRST, B00000000);
  digitalWrite(latch, HIGH); 
}

Are you looking for a library to make this easy, or do you want to code this yourself as a learning exercise?

Either way, google is your friend.
I did a quick check and "arduino scrolling text led matrix" turned up a large number of examples, with code.

Not an answer to your question, but a couple of little tips.

You can remove this:

byte rowActivator[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};

and change:

shiftOut(rData, rClock, LSBFIRST, rowActivator[i]);

to:

shiftOut(rData, rClock, LSBFIRST, bit(i));

Also this:

byte letters[27][8] = { ... }

will be using 216 bytes of ram memory. Tiny85 only has 512 bytes. So if you expand your "font" to include lower case letters, digits and a few symbols, and want to scroll some long messages, there's a good chance you will run out of ram. You can keep this data in Flash instead of using ram. You have 8192 bytes of Flash memory. The font patterns in your sketch are already part of the sketch, and get copied to ram on power-up/reset, before setup() runs. So you won't be using any more Flash than is used now.

@PaulRb Thanks for that suggestion! and I'd like to code it myself as a learning experience, but I also want my project to be working right now. So if there was a library that you knew of for an LED matrix using 2 shift registers I'd much appreciate it. But if you had an explanation of the idea behind the code to have scrolling letters I'd also appreciate it!

@vinceherman I've been trying to search google but have only been able to find code for matrices using the MAX7219 :confused:

To scroll a message, you will need to store the text of the message like this:

char message[] = "HELLO ";

Notice I put a space at the end of the message. This will help make the code a little easier to write.

You will need to put a loop in your code to display the characters of the message one at a time:

for (byte c = 0; c < sizeof(message)-1; c++) { ... }

Here I put "-1" because we don't want to include that extra space at this point.

Inside that for loop, you will need another for loop to scroll the current character off the display and scroll the next character onto the display in 8 steps:

for (byte s = 0; s < 8; s++) { ... }

Inside that loop, you will need another loop to repeat the multiplex code you already have. The number of repetitions will control how fast the message scrolls. More repetitions will give a slower scroll. Try around 10 to 15 and see if that is OK.

Inside your multiplex code, you will need to create a new bit pattern which is part of the current message character and part of the next character. First, get the pattern for the current row of the current character:

byte x = letters[message[c]- 65][i];

Then get the pattern for the current row of the next character:

byte y = letters[message[c+1]- 65][i];

(Note: when c is the last character of the message, the "O" of "HELLO", the character c+1 is that extra space I added earlier.)
Then combine the two patterns, depending on which of the 8 scroll steps is currently displayed:

byte z = x << s | y >> (7-s);

The "<<" and ">>" operators perform bit-wise shifts to the left and right. The "|" operator performs bit-wise logical-OR. Look them up on the language reference page to understand how they work.
Finally, you can shift out the combined pattern:

shiftOut(cData, cClock, LSBFIRST, z);

@PaulRB I've been trying to use your guidance to create a code but have come up with nothing. Surprisingly, I posted about LED matrices some time ago and you actually gave me a code to use which I've attached below. For some reason after a single iteration of the message, everything after is simply a jumbled mess. I feel as though theres a tiny problem that I'm missing, any thoughts?

void scrollMessage(String message)
{
  
  //Array to hold scrolling message 
  byte scrollArray[300];
  int scrollDelay = 150;
  int y = 0;
  int messageLength = message.length();

  //puts message into the scroll array
  for(int C=0; C<messageLength; C++)
  {

    //places spaces into scroll array
    if(message.charAt(C) == ' ') 
    {
      for(int D=0; D<8; D++)
      {        
        scrollArray[(C*8) + D] = letters[26][D];
      }
    }  

    //places letters into scroll array  
    else
    {
      for(int D=0; D<8; D++)
      {
        // "- 'A' " is to make the ascii value of A equal to 0
        scrollArray[(C*8) + D] = letters[(message.charAt(C)) - 'A'][D];
      }
    }
  }

  // scrolls out the scroll array
  int X = 0;
  for(int E=0; E<=(messageLength*8); E++)
  {
    int Xl = X % 8;
    int Xr = 8 - Xl;
    byte rowData[8];
    for(int F=0; F<8; F++)
    {
      rowData[F] = scrollArray[F + X - Xl] << Xl | scrollArray[F + X + Xr] >> Xr;
    }
    unsigned long timer = millis();
    while((timer + scrollDelay) > millis())
    {
      for(int F=0; F<8; F++)
      {
        clrData();
        digitalWrite(latch, LOW);
        shiftOut(rData, rClock, LSBFIRST, bit(((F-7)*-1))); //Use 'F' to flip upside down
        shiftOut(cData, cClock, LSBFIRST, rowData[F]);
        digitalWrite(latch, HIGH);        
      }
    }
    X++;
  }
}

Post the complete sketch always please.

What Arduino was that sketch running on before? Remember that tiny85 has only 512 bytes of ram Vs 2048 bytes on Uno. Your font data is using almost half of that. The above code uses 300 bytes for the message buffer. I suspect the tiny has run out of ram.

Sorry, heres the full code.

const int rClock = 2; //chip pin 7
const int rData = 0; //chip pin 5
const int cData = 4; //chip pin 3
const int latch = 1; //chip pin 6 
const int cClock = 3; //chip pin 2

//replaced with " bit(i) "
//byte rowActivator[] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001};
//byte rowActivator[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
byte letters[27][8] =
{
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100}, //A0
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000100, B01000100, B01111000}, //B1
  {B00000000, B00111000, B01000100, B01000000, B01000000, B01000000, B01000100, B00111000}, //C2
  {B00000000, B01110000, B01001000, B01000100, B01000100, B01000100, B01001000, B01110000}, //D3
  {B00000000, B01111100, B01000000, B01000000, B01111100, B01000000, B01000000, B01111100}, //E4
  {B00000000, B01111100, B01000000, B01000000, B01111000, B01000000, B01000000, B01000000}, //F5
  {B00000000, B01111000, B01000100, B01000000, B01011100, B01000100, B01000100, B00111100}, //G6
  {B00000000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100, B01000100}, //H7
  {B00000000, B01110000, B00100000, B00100000, B00100000, B00100000, B00100000, B01110000}, //I8
  {B00000000, B00011100, B00001000, B00001000, B00001000, B00001000, B01001000, B00110000}, //J9
  {B00000000, B01000100, B01001000, B01010000, B01100000, B01010000, B01001000, B01000100}, //K10
  {B00000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01111100}, //L11
  {B00000000, B01000100, B01101100, B01010100, B01010100, B01000100, B01000100, B01000100}, //M12
  {B00000000, B01000100, B01000100, B01100100, B01010100, B01001100, B01000100, B01000100}, //N13
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //O14
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000000, B01000000, B01000000}, //P15
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01010100, B01001000, B00110100}, //Q16
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01010000, B01001000, B01000100}, //R17
  {B00000000, B00111100, B01000000, B01000000, B00111000, B00000100, B00000100, B01111000}, //S18
  {B00000000, B01111100, B00010000, B00010000, B00010000, B00010000, B00010000, B00010000}, //T19
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //U20
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B00101000, B00010000}, //V21
  {B00000000, B01000100, B01000100, B01000100, B01010100, B01010100, B01010100, B00101000}, //W22
  {B00000000, B01000100, B01000100, B00101000, B00010000, B00101000, B01000100, B01000100}, //X23
  {B00000000, B01000100, B01000100, B01000100, B00101000, B00010000, B00010000, B00010000}, //Y24
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01111100}, //Z25
  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}, // Space to shift 32
};



void setup() {
  //Serial.begin(9600); Isn't supported on the ATtiny85
  pinMode(rClock, OUTPUT);
  pinMode(rData, OUTPUT);
  pinMode(cData, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cClock, OUTPUT);
}

void loop() {
  scrollMessage("HELLO WORLD"); 
}

//function to clear the row registers preventing "ghosting"
void clrData() 
{
  digitalWrite(latch, LOW);
  shiftOut(rData, rClock, LSBFIRST, B00000000);
  digitalWrite(latch, HIGH); 
}


void displayLetter(char letter)
{
  for(int i=0; i<8; i++)
  {
    clrData();
    digitalWrite(latch, LOW);
    shiftOut(rData, rClock, LSBFIRST, bit(i)); //bit(i) used to be row activator 
    shiftOut(cData, cClock, LSBFIRST, letters[letter - 65][i]);
    digitalWrite(latch, HIGH);
    delay(0);
  }
}



//function to scroll a message from left to right
void scrollMessage(String message)
{
  
  //Array to hold scrolling message 
  byte scrollArray[300];
  int scrollDelay = 150;
  int y = 0;
  int messageLength = message.length();

  //puts message into the scroll array
  for(int C=0; C<messageLength; C++)
  {

    //places spaces into scroll array
    if(message.charAt(C) == ' ') 
    {
      for(int D=0; D<8; D++)
      {        
        scrollArray[(C*8) + D] = letters[26][D];
      }
    }  

    //places letters into scroll array  
    else
    {
      for(int D=0; D<8; D++)
      {
        // "- 'A' " is to make the ascii value of A equal to 0
        scrollArray[(C*8) + D] = letters[(message.charAt(C)) - 'A'][D];
      }
    }
  }

  // scrolls out the scroll array
  int X = 0;
  for(int E=0; E<=(messageLength*8); E++)
  {
    int Xl = X % 8;
    int Xr = 8 - Xl;
    byte rowData[8];
    for(int F=0; F<8; F++)
    {
      rowData[F] = scrollArray[F + X - Xl] << Xl | scrollArray[F + X + Xr] >> Xr;
    }
    unsigned long timer = millis();
    while((timer + scrollDelay) > millis())
    {
      for(int F=0; F<8; F++)
      {
        clrData();
        digitalWrite(latch, LOW);
        shiftOut(rData, rClock, LSBFIRST, bit(((F-7)*-1))); //Use 'F' to flip upside down
        shiftOut(cData, cClock, LSBFIRST, rowData[F]);
        digitalWrite(latch, HIGH);        
      }
    }
    X++;
  }
}

And it previously ran on an Arduino uno. Would you have any suggestions to save on ram?

Yes, forget that Uno sketch and see post #6. This time, try to come up with more than nothing. Post your attempt and I will help.

@PaulRb I've been able to make a code that scrolls out letters individually, but I can't seem to figure out how to connect the letters.

const int rClock = 2; //chip pin 7
const int rData = 0; //chip pin 5
const int cData = 4; //chip pin 3
const int latch = 1; //chip pin 6 
const int cClock = 3; //chip pin 2

//replaced with " bit(i) "
//byte rowActivator[] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001};
//byte rowActivator[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
byte letters[27][8] =
{
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100}, //A0
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000100, B01000100, B01111000}, //B1
  {B00000000, B00111000, B01000100, B01000000, B01000000, B01000000, B01000100, B00111000}, //C2
  {B00000000, B01110000, B01001000, B01000100, B01000100, B01000100, B01001000, B01110000}, //D3
  {B00000000, B01111100, B01000000, B01000000, B01111100, B01000000, B01000000, B01111100}, //E4
  {B00000000, B01111100, B01000000, B01000000, B01111000, B01000000, B01000000, B01000000}, //F5
  {B00000000, B01111000, B01000100, B01000000, B01011100, B01000100, B01000100, B00111100}, //G6
  {B00000000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100, B01000100}, //H7
  {B00000000, B01110000, B00100000, B00100000, B00100000, B00100000, B00100000, B01110000}, //I8
  {B00000000, B00011100, B00001000, B00001000, B00001000, B00001000, B01001000, B00110000}, //J9
  {B00000000, B01000100, B01001000, B01010000, B01100000, B01010000, B01001000, B01000100}, //K10
  {B00000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01111100}, //L11
  {B00000000, B01000100, B01101100, B01010100, B01010100, B01000100, B01000100, B01000100}, //M12
  {B00000000, B01000100, B01000100, B01100100, B01010100, B01001100, B01000100, B01000100}, //N13
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //O14
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000000, B01000000, B01000000}, //P15
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01010100, B01001000, B00110100}, //Q16
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01010000, B01001000, B01000100}, //R17
  {B00000000, B00111100, B01000000, B01000000, B00111000, B00000100, B00000100, B01111000}, //S18
  {B00000000, B01111100, B00010000, B00010000, B00010000, B00010000, B00010000, B00010000}, //T19
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //U20
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B00101000, B00010000}, //V21
  {B00000000, B01000100, B01000100, B01000100, B01010100, B01010100, B01010100, B00101000}, //W22
  {B00000000, B01000100, B01000100, B00101000, B00010000, B00101000, B01000100, B01000100}, //X23
  {B00000000, B01000100, B01000100, B01000100, B00101000, B00010000, B00010000, B00010000}, //Y24
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01111100}, //Z25
  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}, // Space to shift 32
};



void setup() {
  //Serial.begin(9600); Isn't supported on the ATtiny85
  pinMode(rClock, OUTPUT);
  pinMode(rData, OUTPUT);
  pinMode(cData, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cClock, OUTPUT);
}

void loop() {
  scrollMessage("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
}

//function to clear the row registers preventing "ghosting"
void clrData() 
{
  digitalWrite(latch, LOW);
  shiftOut(rData, rClock, LSBFIRST, B00000000);
  digitalWrite(latch, HIGH); 
}


void displayLetter(char letter)
{
  for(int i=0; i<8; i++)
  {
    clrData();
    digitalWrite(latch, LOW);
    shiftOut(rData, rClock, LSBFIRST, bit(i)); //bit(i) used to be row activator 
    shiftOut(cData, cClock, LSBFIRST, letters[letter - 65][i]);
    digitalWrite(latch, HIGH);
    delay(0);
  }
}


void scrollMessage(String message)
{
  int scrollDelay = 150;
  for(int i = 0; i < message.length(); i++) 
  {
    for(int j=0; j<8; j++)
    {
      unsigned long timer = millis();
      while((timer + scrollDelay) > millis())
      {        
        for(int k=0; k<8; k++)
        {
          byte b = letters[message.charAt(i) - 65][k] << j;
          clrData();
          digitalWrite(latch, LOW);
          shiftOut(rData, rClock, LSBFIRST, bit(k)); //bit(i) used to be row activator 
          shiftOut(cData, cClock, LSBFIRST, b);
          digitalWrite(latch, HIGH);
          delay(0);
        }
      }
    }
  }
}

I have been trying to follow your instructions in post 6, but am getting a little confused. From what I have so far is it a quick road to having a fully functional scroll function?

Try this. I can't test it for you, so if it doesn't work, don't just say "it doesn't work"!

const int rClock = 2; //chip pin 7
const int rData = 0; //chip pin 5
const int cData = 4; //chip pin 3
const int latch = 1; //chip pin 6
const int cClock = 3; //chip pin 2

byte letters[27][8] =
{
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100}, //A0
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000100, B01000100, B01111000}, //B1
  {B00000000, B00111000, B01000100, B01000000, B01000000, B01000000, B01000100, B00111000}, //C2
  {B00000000, B01110000, B01001000, B01000100, B01000100, B01000100, B01001000, B01110000}, //D3
  {B00000000, B01111100, B01000000, B01000000, B01111100, B01000000, B01000000, B01111100}, //E4
  {B00000000, B01111100, B01000000, B01000000, B01111000, B01000000, B01000000, B01000000}, //F5
  {B00000000, B01111000, B01000100, B01000000, B01011100, B01000100, B01000100, B00111100}, //G6
  {B00000000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100, B01000100}, //H7
  {B00000000, B01110000, B00100000, B00100000, B00100000, B00100000, B00100000, B01110000}, //I8
  {B00000000, B00011100, B00001000, B00001000, B00001000, B00001000, B01001000, B00110000}, //J9
  {B00000000, B01000100, B01001000, B01010000, B01100000, B01010000, B01001000, B01000100}, //K10
  {B00000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01111100}, //L11
  {B00000000, B01000100, B01101100, B01010100, B01010100, B01000100, B01000100, B01000100}, //M12
  {B00000000, B01000100, B01000100, B01100100, B01010100, B01001100, B01000100, B01000100}, //N13
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //O14
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000000, B01000000, B01000000}, //P15
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01010100, B01001000, B00110100}, //Q16
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01010000, B01001000, B01000100}, //R17
  {B00000000, B00111100, B01000000, B01000000, B00111000, B00000100, B00000100, B01111000}, //S18
  {B00000000, B01111100, B00010000, B00010000, B00010000, B00010000, B00010000, B00010000}, //T19
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //U20
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B00101000, B00010000}, //V21
  {B00000000, B01000100, B01000100, B01000100, B01010100, B01010100, B01010100, B00101000}, //W22
  {B00000000, B01000100, B01000100, B00101000, B00010000, B00101000, B01000100, B01000100}, //X23
  {B00000000, B01000100, B01000100, B01000100, B00101000, B00010000, B00010000, B00010000}, //Y24
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01111100}, //Z25
  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}, // Space to shift 32
};



void setup() {
  //Serial.begin(9600); Isn't supported on the ATtiny85
  pinMode(rClock, OUTPUT);
  pinMode(rData, OUTPUT);
  pinMode(cData, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cClock, OUTPUT);
}

void loop() {
  scrollMessage("ABCDEFGHIJKLMNOPQRSTUVWXYZ ");
}

//function to clear the row registers preventing "ghosting"
void clrData()
{
  digitalWrite(latch, LOW);
  shiftOut(rData, rClock, LSBFIRST, B00000000);
  digitalWrite(latch, HIGH);
}


void displayLetter(char letter)
{
  for(int i=0; i<8; i++)
  {
    clrData();
    digitalWrite(latch, LOW);
    shiftOut(rData, rClock, LSBFIRST, bit(i)); //bit(i) used to be row activator
    shiftOut(cData, cClock, LSBFIRST, letters[letter - 65][i]);
    digitalWrite(latch, HIGH);
    delay(0);
  }
}


void scrollMessage(char* message)
{
  int scrollDelay = 150;
  for(int i = 0; i < strlen(message)-1; i++)
  {
    for(int j=0; j<8; j++)
    {
      unsigned long timer = millis();
      while(millis() - timer < scrollDelay)
      {       
        for(int k=0; k<8; k++)
        {
          byte b = letters[message[i] - 'A'][k] << j | letters[message[i+1] - 'A'][k] >> (8-j);
          clrData();
          digitalWrite(latch, LOW);
          shiftOut(rData, rClock, LSBFIRST, bit(k));
          shiftOut(cData, cClock, LSBFIRST, b);
          digitalWrite(latch, HIGH);
          delay(1);
        }
      }
    }
  }
}

Notice that I removed use of the "String" class. Its not a good idea on an mcu with only 512 bytes of ram!

A tip for you:

//Serial.begin(9600); Isn't supported on the ATtiny85

If you use DrAzzy's tiny cores, Serial.print is supported. Of course, you will need a serial-USB adaptor to see the output on Serial Monitor.

Thanks so much for the help! It worked perfectly!

I'm extremely thankfu! But I was wondering if you could help me understand the following line you provided?

byte b = letters[message[i] - 'A'][k] << j | letters[message[i+1] - 'A'][k] >> (8-j);

Pick values for I, j & k and work though an example on paper. Use the reference page to find out about the "|" and ">>" operators.

@PaulRb I've made sense of that line of code and edited it a little so that spaces work properly. The only problem I have is that I'd like to make the space a shorter length. It's too much waiting currently, but I can't seem to figure out how to shorten it without messing up everything else. Would you happen to know how to do this?

const int rClock = 2; //chip pin 7
const int rData = 0; //chip pin 5
const int cData = 4; //chip pin 3
const int latch = 1; //chip pin 6 
const int cClock = 3; //chip pin 2

//replaced with " bit(i) "
//byte rowActivator[] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001};
//byte rowActivator[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
byte letters[27][8] =
{
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100}, //A0
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000100, B01000100, B01111000}, //B1
  {B00000000, B00111000, B01000100, B01000000, B01000000, B01000000, B01000100, B00111000}, //C2
  {B00000000, B01110000, B01001000, B01000100, B01000100, B01000100, B01001000, B01110000}, //D3
  {B00000000, B01111100, B01000000, B01000000, B01111100, B01000000, B01000000, B01111100}, //E4
  {B00000000, B01111100, B01000000, B01000000, B01111000, B01000000, B01000000, B01000000}, //F5
  {B00000000, B01111000, B01000100, B01000000, B01011100, B01000100, B01000100, B00111100}, //G6
  {B00000000, B01000100, B01000100, B01000100, B01111100, B01000100, B01000100, B01000100}, //H7
  {B00000000, B01110000, B00100000, B00100000, B00100000, B00100000, B00100000, B01110000}, //I8
  {B00000000, B00011100, B00001000, B00001000, B00001000, B00001000, B01001000, B00110000}, //J9
  {B00000000, B01000100, B01001000, B01010000, B01100000, B01010000, B01001000, B01000100}, //K10
  {B00000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01000000, B01111100}, //L11
  {B00000000, B01000100, B01101100, B01010100, B01010100, B01000100, B01000100, B01000100}, //M12
  {B00000000, B01000100, B01000100, B01100100, B01010100, B01001100, B01000100, B01000100}, //N13
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //O14
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01000000, B01000000, B01000000}, //P15
  {B00000000, B00111000, B01000100, B01000100, B01000100, B01010100, B01001000, B00110100}, //Q16
  {B00000000, B01111000, B01000100, B01000100, B01111000, B01010000, B01001000, B01000100}, //R17
  {B00000000, B00111100, B01000000, B01000000, B00111000, B00000100, B00000100, B01111000}, //S18
  {B00000000, B01111100, B00010000, B00010000, B00010000, B00010000, B00010000, B00010000}, //T19
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B01000100, B00111000}, //U20
  {B00000000, B01000100, B01000100, B01000100, B01000100, B01000100, B00101000, B00010000}, //V21
  {B00000000, B01000100, B01000100, B01000100, B01010100, B01010100, B01010100, B00101000}, //W22
  {B00000000, B01000100, B01000100, B00101000, B00010000, B00101000, B01000100, B01000100}, //X23
  {B00000000, B01000100, B01000100, B01000100, B00101000, B00010000, B00010000, B00010000}, //Y24
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01111100}, //Z25
  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}, // Space to shift 32
};



void setup() {
  //Serial.begin(9600); Isn't supported on the ATtiny85
  pinMode(rClock, OUTPUT);
  pinMode(rData, OUTPUT);
  pinMode(cData, OUTPUT);
  pinMode(latch, OUTPUT);
  pinMode(cClock, OUTPUT);
}

void loop() {
  scrollMessage(" HELLO WORLD "); 
}

//function to clear the row registers preventing "ghosting"
void clrData() 
{
  digitalWrite(latch, LOW);
  shiftOut(rData, rClock, LSBFIRST, B00000000);
  digitalWrite(latch, HIGH); 
}


void displayLetter(char letter)
{
  for(int i=0; i<8; i++)
  {
    clrData();
    digitalWrite(latch, LOW);
    shiftOut(rData, rClock, LSBFIRST, bit(i)); //bit(i) used to be row activator 
    shiftOut(cData, cClock, LSBFIRST, letters[letter - 65][i]);
    digitalWrite(latch, HIGH);
    delay(0);
  }
}

//add a space to the end of the message
void scrollMessage(char* message)
{  
  int scrollDelay = 130;
  for(int i = 0; i < strlen(message)-1; i++)
  {
    for(int j=0; j<8; j++)
    {
      unsigned long timer = millis();
      while(millis() - timer < scrollDelay)
      { 
        for(int m=0; m<8; m++)
        {
          byte b;

          //Allows spaces to work correctly
          if(message[i+1] == ' ') {
            b = letters[message[i] - 'A'][m] << j | letters[26][m] >> (8-j);
          }
          else if(message[i] == ' ') {
            b = letters[26][m] << j | letters[message[i +1] - 'A'][m] >> (8-j);
          }else {
            b = letters[message[i] - 'A'][m] << j | letters[message[i+1] - 'A'][m] >> (8-j);
          }
          clrData();
          digitalWrite(latch, LOW);
          shiftOut(rData, rClock, LSBFIRST, bit(m));
          shiftOut(cData, cClock, LSBFIRST, b);
          digitalWrite(latch, HIGH);
          delay(1);
         }
      }
    }
  }
}

Yes, I see what you mean about spaces and I did not think of that. The ASCII code for a space is 32, so when you subtract the code for an 'A', 65, the answer is negative. But then it gets assigned to a byte, which is an unsigned type, so we get some value over 220, which is outside the bounds of the 'letter' array. Well done for spoting that and for figuring out a solution. Your solution could have been simpler, because

letters[26][m] >> (8-j)

will always be zero for any value of m or j, so you might as well delete those parts of the formula.

What I don't understand is that you say that space takes too long to scroll. Are you saying it takes longer than any other letter? I think it should take the same time as any other letter, and that sounds ok to me.

Thanks for that way to simplify the space code. The problem I am having is that though the space works correctly, I personally think it is too long. I was wondering if there was a way to make the space 6 pixels wide instead of 8, or any width I'd like.

Not easily. You would then have the situation where 3 characters would be partially visible on the matrix instead of two: the space and the letter before and the letter after.

What if you just increase the overall scroll speed for the message? Find a balance where you can still read the message but the space scrolling does not seem so slow.