42x11 LED matrix

Hello folks. I'm working with someone on making a 42x11 LED matrix. I put the matrix together and he put the boards and code together. We're using TLC5926 chips to control the low end and A2982 chips to control the high side. I'm testing with 1 high side board (8 rows) and 1 low side board (16 columns) before hooking everything up. The sketch that we have should just scroll HELLO. He set up a couple shiftregister libraries, one for 8 and one for 16 bit.

The problem comes in that, when the sketch is run, there is no scrolling. All of the lights just come on. I've attached the libraries and the sketch is below. Could someone please point out where we may have gone wrong? Any help is appreciated.

/*
 * Show messages on an 8x8 led matrix,
 * scrolling from right to left.
 *
 * Uses FrequencyTimer2 library to
 * constantly run an interrupt routine
 * at a specified frequency. This
 * refreshes the display without the
 * main loop having to do anything.
 *
 */

#include <FrequencyTimer2.h>
#include <ShiftRegister8.h>
#include <ShiftRegister16.h>

#define NUM_ROWS             8
#define NUM_COLUMNS          16
#define NUM_LOW_SIDE_BOARDS  1
#define NUM_HIGH_SIDE_BOARDS 1
#define NUM_PATTERN_ROWS     8
#define NUM_PATTERN_COLUMNS  8

ShiftRegister8 high_side(3, 4, 5); //data, clock, latch
ShiftRegister16 low_side(6, 7, 8); //data, clock, latch
const uint8_t low_side_enable_pin = 9; // enable to low side drivers

void set_column(uint8_t ii);
void clear_column(uint8_t ii);
void set_row(uint8_t ii);
void clear_row(uint8_t ii);

#define SPACE { \
    {0, 0, 0, 0, 0, 0, 0, 0},  \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0} \
}

#define H { \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}  \
}

#define E  { \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define L { \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define O { \
    {0, 0, 0, 1, 1, 0, 0, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 0, 0, 1, 1, 0, 0, 0}  \
}

byte col = 0;
byte leds[NUM_COLUMNS][NUM_ROWS];

uint16_t columns[NUM_LOW_SIDE_BOARDS] = {0};
uint8_t rows[NUM_HIGH_SIDE_BOARDS] = {0};

const int numPatterns = 6;
byte patterns[numPatterns][NUM_PATTERN_COLUMNS][NUM_PATTERN_ROWS] = {
  H,E,L,L,O,SPACE
};

int pattern = 0;

void setup() {  
  clearLeds();

  // Turn off toggling of pin 11
  FrequencyTimer2::disable();
  // Set refresh rate (interrupt timeout period)
  FrequencyTimer2::setPeriod(2000);
  // Set interrupt routine to be called
  FrequencyTimer2::setOnOverflow(display);

  setPattern(pattern);
  
  pinMode(low_side_enable_pin, OUTPUT);
  digitalWrite(low_side_enable_pin, LOW); // enable the display
}

void loop() {
    pattern = ++pattern % numPatterns;
    slidePattern(pattern, 60);
}

void clearLeds() {
  // Clear display array
  for (int i = 0; i < NUM_COLUMNS; i++) {
    for (int j = 0; j < NUM_ROWS; j++) {
      leds[i][j] = 0;
    }
  }
}

void setPattern(int pattern) {
  for (int i = 0; i < NUM_PATTERN_COLUMNS; i++) {
    for (int j = 0; j < NUM_PATTERN_ROWS; j++) {
      leds[i][j] = patterns[pattern][i][j];
    }
  }
}

void slidePattern(int pattern, int del) {
  for (int l = 0; l < NUM_ROWS; l++) {
    for (int i = 0; i < NUM_ROWS - 1; i++) {
      for (int j = 0; j < NUM_COLUMNS; j++) {
        leds[j][i] = leds[j][i+1];
      }
    }
    for (int j = 0; j < NUM_PATTERN_COLUMNS; j++) {
      leds[j][NUM_ROWS - 1] = patterns[pattern][j][0 + l];
    }
    delay(del);
  }
}

// Interrupt routine
void display() {
  clear_column(col); // Turn whole previous column off
  col++;
  if (col == NUM_COLUMNS) {
    col = 0;
  }
  
  for (int row = 0; row < NUM_ROWS; row++) {
    if (leds[col][NUM_ROWS - 1 - row] == 1) {
      set_row(row);  // Turn on this led
    }
    else {
      clear_row(row); // Turn off this led
    }
  }
  high_side.loadValues(rows, NUM_HIGH_SIDE_BOARDS);
  set_column(col); // Turn whole column on at once (for equal lighting times)
}

void set_column(uint8_t ii){
  uint8_t entry_index = ii / 16;
  uint8_t bit_index = ii % 16;
  columns[entry_index] |= ((uint16_t) 1) << bit_index;
  low_side.loadValues(columns, NUM_LOW_SIDE_BOARDS);  
}

void clear_column(uint8_t ii){
  uint8_t entry_index = ii / 16;
  uint8_t bit_index = ii % 16;
  columns[entry_index] &= ~(((uint16_t) 1) << bit_index);
  low_side.loadValues(columns, NUM_LOW_SIDE_BOARDS);  
}

void set_row(uint8_t ii){
  uint8_t entry_index = ii / 8;
  uint8_t bit_index = ii % 8;
  rows[entry_index] |= 1 << bit_index;  
}

void clear_row(uint8_t ii){
  uint8_t entry_index = ii / 8;
  uint8_t bit_index = ii % 8;
  rows[entry_index] &= ~(1 << bit_index);    
}

ShiftRegister-1[1].zip (4.42 KB)

http://www.allegromicro.com/en/Products/Part_Numbers/2981/2981.pdf

Too convoluted for me to follow.

I'm sorry? Is it my explanation that's not clear?

OK.... I've made some "progress" in that I can make a few changes that are visible in the results. However, I'm still not there.

  1. I changed setperiod to an obscenely high number (999999) and got 2 results. The first was that it showed me that the columns were actually flashing and scrolling instead of just being on constantly. However, it looked like it was just turning a column on and then dimming it. Also, if I just send "SPACE", the lights all just stay on but are dim. Not sure why setperiod would control brightness.

  2. The line byte leds[NUM_ROWS][NUM_COLUMNS]; was backwards. Fixing this allowed me to have the scroll go across all 16 columns that I'm testing with. But, as mentioned, still unreadable.

Any thoughts would be helpful.

I just went through each LED with a 6v battery, resistor, and a couple of wires. Each bulb worked individually. So maybe it's an issue with the libraries and/or the clear rows functions?

Convoluted as in you have 8 functions, and 64 bytes for a letter when they could be defined in just 8. I just have a hard time following what your code is doing.

OK. I don't even count as competent most of the time, so maybe it would be best to explain what the goal is.

  1. Use the 8 bit board to control the anodes and the 16 bit board to control the cathodes.
  2. Scroll text from left to right and overflow onto the next board in the sequence.
  3. The 2 boards are running off of separate latch, clock, and data pins.
  4. The 16 bit board has its OE pin plugged directly into ground.

I'm not married to any of this code. I just want something that's functional, even if that means throwing this out.

From the examples that I've seen online, I'm thinking that maybe we should just be using a version of the SPI library but, again, I barely register as competent most days.

The code was built off of this example off of arduino playground. That should hopefully clear up the starting point http://playground.arduino.cc/Main/DirectDriveLEDMatrix

/*
 * Show messages on an 8x8 led matrix,
 * scrolling from right to left.
 *
 * Uses FrequencyTimer2 library to
 * constantly run an interrupt routine
 * at a specified frequency. This
 * refreshes the display without the
 * main loop having to do anything.
 *
 */

#include <FrequencyTimer2.h>

#define SPACE { \
    {0, 0, 0, 0, 0, 0, 0, 0},  \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0} \
}

#define H { \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}  \
}

#define E  { \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define L { \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define O { \
    {0, 0, 0, 1, 1, 0, 0, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 0, 0, 1, 1, 0, 0, 0}  \
}

byte col = 0;
byte leds[8][8];

// pin[xx] on led matrix connected to nn on Arduino (-1 is dummy to make array start at pos 1)
int pins[17]= {-1, 5, 4, 3, 2, 14, 15, 16, 17, 13, 12, 11, 10, 9, 8, 7, 6};

// col[xx] of leds = pin yy on led matrix
int cols[8] = {pins[13], pins[3], pins[4], pins[10], pins[06], pins[11], pins[15], pins[16]};

// row[xx] of leds = pin yy on led matrix
int rows[8] = {pins[9], pins[14], pins[8], pins[12], pins[1], pins[7], pins[2], pins[5]};

const int numPatterns = 6;
byte patterns[numPatterns][8][8] = {
  H,E,L,L,O,SPACE
};

int pattern = 0;

void setup() {
  // sets the pins as output
  for (int i = 1; i <= 16; i++) {
    pinMode(pins[i], OUTPUT);
  }

  // set up cols and rows
  for (int i = 1; i <= 8; i++) {
    digitalWrite(cols[i - 1], LOW);
  }

  for (int i = 1; i <= 8; i++) {
    digitalWrite(rows[i - 1], LOW);
  }

  clearLeds();

  // Turn off toggling of pin 11
  FrequencyTimer2::disable();
  // Set refresh rate (interrupt timeout period)
  FrequencyTimer2::setPeriod(2000);
  // Set interrupt routine to be called
  FrequencyTimer2::setOnOverflow(display);

  setPattern(pattern);
}

void loop() {
    pattern = ++pattern % numPatterns;
    slidePattern(pattern, 60);
}

void clearLeds() {
  // Clear display array
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      leds[i][j] = 0;
    }
  }
}

void setPattern(int pattern) {
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      leds[i][j] = patterns[pattern][i][j];
    }
  }
}

void slidePattern(int pattern, int del) {
  for (int l = 0; l < 8; l++) {
    for (int i = 0; i < 7; i++) {
      for (int j = 0; j < 8; j++) {
        leds[j][i] = leds[j][i+1];
      }
    }
    for (int j = 0; j < 8; j++) {
      leds[j][7] = patterns[pattern][j][0 + l];
    }
    delay(del);
  }
}

// Interrupt routine
void display() {
  digitalWrite(cols[col], LOW);  // Turn whole previous column off
  col++;
  if (col == 8) {
    col = 0;
  }
  for (int row = 0; row < 8; row++) {
    if (leds[col][7 - row] == 1) {
      digitalWrite(rows[row], LOW);  // Turn on this led
    }
    else {
      digitalWrite(rows[row], HIGH); // Turn off this led
    }
  }
  digitalWrite(cols[col], HIGH); // Turn whole column on at once (for equal lighting times)
}

I prefer SPI.transfer for sending to shift registers.
I'm no software genius either, I tend to approach things from the hardware point of view.
You can put your message into an array and arrange it so that a byte, or two in your design, are sent out. Since you're using a current control output chip for the cathodes, blink without delay with added counter can be used to display a row at a time. I guess you're doing that in your interrupt routine.
Say your message is in a 48 byte array, with 6 letters going across. You have two time periods. One is the multiplex time period, the other is the scroll period.
Every multiplex time period, you turn off the anodes, write the cathode data for the next row, and turn on the next anode.
Every scroll period, you move the pointer in the array to the next column of data.

column #s of the array, say 0 to 49 (can be hundreds of bytes long)
00000000001111111111222222222233333333334444444444
01234567890123456789012345678901234567890123456789
HooHoEEEEoLooooLooooOOOOo row 7  small o used for spaces here for clarity
HooHoEooooLooooLooooOooOo row 6  in reality the capital letters would be 1
HooHoEooooLooooLooooOooOo row 5  and the o's a 0
HooHoEooooLooooLooooOooOo row 4  
HHHHoEEEEoLooooLooooOooOo row 3  Just hard to think of the data horizontally.
HooHoEooooLooooLooooOooOo row 2  Much easier (for me) to picture each column
HooHoEooooLooooLooooOooOo row 1  as a byte of data.
HooHoEEEEoLLLLoLLLLoOOOOo row 0

0123456789abcdef  these show the current 16 columns being displayed
-0123456789abcdef  moving along as scroll time goes by
--0123456789abcdef  
---0123456789abcdef
----0123456789abcdef
-----0123456789abcdef
------0123456789abcdef
-------0123456789abcdef

So:
Time period m0 (multiplex 0) data for row0 columns 0-15 go out
Time period m1 (multiplex 1) data for row1 columns 0-15 go out
Time period m2 (multiplex 2) data for row2 columns 0-15 go out
Time period m3 (multiplex 3) data for row3 columns 0-15 go out
Time period m4 (multiplex 4) data for row4 columns 0-15 go out
Time period m5 (multiplex 5) data for row5 columns 0-15 go out
Time period m6 (multiplex 6) data for row6 columns 0-15 go out
Time period m7 (multiplex 7) data for row7 columns 0-15 go out
repeat:
Time period m0 (multiplex 0) data for row0 columns 0-15 go out
Time period m1 (multiplex 1) data for row1 columns 0-15 go out
etc.
Every 3mS, the next multiplex data goes out.

Then every 100mS, the data is shifted.
Columns 0-15,
100mS later, 1-16
100mS later, 2-17

etc. until sometime later, you get to the end, the data starts to wrap around:

35-49, 0
36-49, 0,1
37-49, 0,1,2
38-49, 0-4
etc.

Then the times when you're just waiting for 3mS to go by, or 100mS, other stuff can occurring - updating the array contents, responding to serial messages, button pushes, etc.

Desribe it like that, I can follow it easily, it's easy to see where the data is coming from, it's easy to see what's happening.
With 8 functions and each LED needing a whole byte to describe it, it's just too messy.

OK, thank you. I'm sorry that I wasn't clear before. Now, if someone could translate that to code that will work with my setup, I can finally get out of your hair! :smiley:

PROGRESS! I loaded up the LoadPattern sketch from the shiftregister16 library, plugged everything into the same clock, data, and latch pins, and, lo and behold, patterns are cycling on my display!

So I went back to the problem child sketch and took out references to the shiftregister8 library and changed the references to high_side to low_side. I'm now looking at a diagonal line from the top left of my display to the bottom right (1 over, 1 down) that's 8 bulbs wide. It's not much, but it's a start I think.

IT'S ALIVE!!!! I had to re-write the libraries to combine to use the same clock and data pin while adding a latch pin. Below is a video, the new sketch, and the new library (shiftregister16 is gone).

Now I just need help turning this thing 90 degrees.

/*
 * Show messages on an 8x8 led matrix,
 * scrolling from right to left.
 *
 * Uses FrequencyTimer2 library to
 * constantly run an interrupt routine
 * at a specified frequency. This
 * refreshes the display without the
 * main loop having to do anything.
 *
 */

#include <FrequencyTimer2.h>
#include <ShiftRegister8.h>


#define NUM_ROWS             8
#define NUM_COLUMNS          16
#define NUM_LOW_SIDE_BOARDS  1
#define NUM_HIGH_SIDE_BOARDS 1
#define NUM_PATTERN_ROWS     8
#define NUM_PATTERN_COLUMNS  8

ShiftRegister8 high_side(5, 7, 9, 11); //data, clock, latch, latch2

const uint8_t low_side_enable_pin = 10; // enable to low side drivers

void set_column(uint8_t ii);
void clear_column(uint8_t ii);
void set_row(uint8_t ii);
void clear_row(uint8_t ii);

#define SPACE { \
    {0, 0, 0, 0, 0, 0, 0, 0},  \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0} \
}

#define H { \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}  \
}

#define E  { \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define L { \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 0, 0}, \
    {0, 1, 1, 1, 1, 1, 1, 0}  \
}

#define O { \
    {0, 0, 0, 1, 1, 0, 0, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 1, 0, 0, 0, 0, 1, 0}, \
    {0, 0, 1, 0, 0, 1, 0, 0}, \
    {0, 0, 0, 1, 1, 0, 0, 0}  \
}

byte col = 0;
byte leds[NUM_COLUMNS][NUM_ROWS];

uint16_t columns[NUM_LOW_SIDE_BOARDS] = {0};
uint8_t rows[NUM_HIGH_SIDE_BOARDS] = {0};

const int numPatterns = 6;
byte patterns[numPatterns][NUM_PATTERN_COLUMNS][NUM_PATTERN_ROWS] = {
  H,E,L,L,O,SPACE
};

int pattern = 0;

void setup() {  
  clearLeds();

  // Turn off toggling of pin 11
  FrequencyTimer2::disable();
  // Set refresh rate (interrupt timeout period)
  FrequencyTimer2::setPeriod(200);
  // Set interrupt routine to be called
  FrequencyTimer2::setOnOverflow(display);

  setPattern(pattern);
  
  pinMode(low_side_enable_pin, OUTPUT);
  digitalWrite(low_side_enable_pin, LOW); // enable the display
}

void loop() {
    pattern = ++pattern % numPatterns;
    slidePattern(pattern, 60);
}

void clearLeds() {
  // Clear display array
  for (int i = 0; i < NUM_COLUMNS; i++) {
    for (int j = 0; j < NUM_ROWS; j++) {
      leds[i][j] = 0;
    }
  }
}

void setPattern(int pattern) {
  for (int i = 0; i < NUM_PATTERN_COLUMNS; i++) {
    for (int j = 0; j < NUM_PATTERN_ROWS; j++) {
      leds[i][j] = patterns[pattern][i][j];
    }
  }
}

void slidePattern(int pattern, int del) {
  for (int l = 0; l < NUM_ROWS; l++) {
    for (int i = 0; i < NUM_ROWS - 1; i++) {
      for (int j = 0; j < NUM_COLUMNS; j++) {
        leds[j][i] = leds[j][i+1];
      }
    }
    for (int j = 0; j < NUM_PATTERN_COLUMNS; j++) {
      leds[j][NUM_ROWS - 1] = patterns[pattern][j][0 + l];
    }
    delay(del);
  }
}

// Interrupt routine
void display() {
  clear_column(col); // Turn whole previous column off
  col++;
  if (col == NUM_COLUMNS) {
    col = 0;
  }
  
  for (int row = 0; row < NUM_ROWS; row++) {
    if (leds[col][NUM_ROWS - 1 - row] == 1) {
      set_row(row);  // Turn on this led
    }
    else {
      clear_row(row); // Turn off this led
    }
  }
  high_side.loadValues(rows, NUM_HIGH_SIDE_BOARDS);
  set_column(col); // Turn whole column on at once (for equal lighting times)
}

void set_column(uint8_t ii){
  uint8_t entry_index = ii / 16;
  uint8_t bit_index = ii % 16;
  columns[entry_index] |= ((uint16_t) 1) << bit_index;
  high_side.loadVals(columns, NUM_LOW_SIDE_BOARDS);  
}

void clear_column(uint8_t ii){
  uint8_t entry_index = ii / 16;
  uint8_t bit_index = ii % 16;
  columns[entry_index] &= ~(((uint16_t) 1) << bit_index);
  high_side.loadVals(columns, NUM_LOW_SIDE_BOARDS);  
}

void set_row(uint8_t ii){
  uint8_t entry_index = ii / 8;
  uint8_t bit_index = ii % 8;
  rows[entry_index] |= 1 << bit_index;  
}

void clear_row(uint8_t ii){
  uint8_t entry_index = ii / 8;
  uint8_t bit_index = ii % 8;
  rows[entry_index] &= ~(1 << bit_index);    
}

ShiftRegister8.zip (1.35 KB)

Alright... I've run a test of taking out slidepattern and I get a sideways H on the display. So that makes me think that the display function is what's causing the issue. I've tried to reverse it, but I can't get it to compile.