Cascading Four 74HC595 Shift Registers To Control a 16x16 LED Matrix

Hi all,

I've been working on a project involving the use of four 74HC595's to control four 8x8 LED matrices in order to form a 16x16 matrix. I was able to create a successful prototype using a single 8x8 matrix but I'm getting a lot of strange things happening when I moved to the cascading shift registers.

My code is below. I'm using the following array to contain the image that is to displayed, unsigned long drawing[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // LED control array where the first eight bits of the long int contain the information for the first LED matrix, the second eight bits for the second LED matrix, and so on. For the purpose of this post and for my own debugging I have been hard coding values into drawing[0] in order to try and figure out what is going on.

Here's where it get strange, consider the code segment: unsigned long drawing[8] = {32768, 0, 0, 0, 0, 0, 0, 0}; As one would expect this lights a single LED on the matrix in the location it should be. However, if I do this: unsigned long drawing[8] = {1<<15, 0, 0, 0, 0, 0, 0, 0}; the same LED lights up in addition to all the LEDs in the next two shift registers. (1<<15 == 32768). It's as if I wrote a 0xFFFF8000 into the shift registers. And if I shift one any more than fifteen, nothing at all is displayed.

In addition, I've discovered that if I use any number, as long as it does not contain a bit shift operator, it will be displayed correctly. I believe that this means I've got the cascading aspect of it down.

I have tried several different methods of rectifying the problem and have quadruple checked that everything is connected correctly; I just cannot seem to figure out what is going on. I'm hoping that one of you has some insights as to what may be happening and how I can solve this problem. If anymore clarification is needed please let me know, and thanks in advance for your help!

Entire Code:

#include <avr/io.h>
#include <avr/interrupt.h>

#define DIMENSION 16 //length and width of Matrix
#define DIVISOR (int)(1024/DIMENSION) //Poteniometer increment value

// Variables
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

int rowPot = 1; //Right side potentiometer
int colPot = 5; //Left side pot
int currentRow = 0;
int cRow = 0, cCol = 0; //Cursor row and col
int soft_prescaler = 0;
unsigned long drawing[8] = {1<<8, 0, 0, 0, 0, 0, 0, 0}; // LED control array

void setup()
{
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  DDRD = B11111111; //Sets digital pins 0-7 as outputs
  
  // Calculation for timer 2
  // 16 MHz / 8 = 2 MHz (prescaler 8)
  // 2 MHz / 256 = 7812 Hz
  // soft_prescaler = 15 ==> 520.8 updates per second
  // 520.8 / 8 rows ==> 65.1 Hz for the complete display
  TCCR2A = 0;           // normal operation
  TCCR2B = (1<<CS21);   // prescaler 8
  TIMSK2 = (1<<TOIE2);  // enable overflow interrupt
  
  // Sets initial cursor row and column
  //cRow = analogRead(rowPot) / DIVISOR;
  //cCol = analogRead(colPot) / DIVISOR;
  
  //drawing[0] = (unsigned long)(1<<16);
}

/**
 * ISR TIMER2_OVF_vect
 * This is the timer interrupt service routine.
 * It gets called at 7812 times per second.
 */
ISR(TIMER2_OVF_vect)
{
  soft_prescaler++;
  if (soft_prescaler == 15)
  {
    //Turn off currentRow
    //PORTD = (0<<currentRow);
    currentRow = (currentRow+1) % 8;
    
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, (drawing[currentRow])>>24);
    shiftOut(dataPin, clockPin, LSBFIRST, (drawing[currentRow])>>16);
    shiftOut(dataPin, clockPin, LSBFIRST, (drawing[currentRow])>>8);
    shiftOut(dataPin, clockPin, LSBFIRST, (drawing[currentRow])>>0);
    digitalWrite(latchPin, HIGH);
    
    PORTD = (1<<currentRow);   
    
    soft_prescaler = 0;
  }	
}

void loop()
{}

Strange. Sounds like a variation of sign-extension, only the "wrong way". << - Arduino Reference
Since you use constants, I'm guessing something to do with the complier (at compile time), or maybe it's supposed to be like that, I'm not really sure. Have you tried casting it to either unsigned or signed long?

unsigned long drawing[8] = { (unsigned long)1<<15, 0, 0, 0, 0, 0, 0, 0};

EDIT: Come to think about it, you define constant types by letters after the numbers, like 1234L for long, or 1234UL for unsigned long.

unsigned long drawing[8] = { 1UL<<15, 0, 0, 0, 0, 0, 0, 0};

Otherwise it is treated like a signed integer (Integer Constants - Arduino Reference)

raron:
Strange. Sounds like a variation of sign-extension, only the "wrong way".

It's a quirk of C and C++: Type conversions do length, then 'signedness'.

If you take an int 1 and shift it left 15 bits you get an int that looks like 0x80000, which is a signed int value -32768. When it gets saved in an unsigned long the compiler first makes it long (0xFFFF8000L so it still has the value -32767) and then unsigned so it becomes 4294934528UL.

Specifying 1U or 1UL would prevent the problem.

johnwasser:

Thanks for the explanation! Of course. When you put it that way it seems so obvious now :slight_smile:

Yup! That did the trick! Thanks a lot you two, that is something I never would have been able to figure out on my own. I can't say I've ever seen that before but it's an extremely important thing to know. Thanks again!

I've been gored by that one before. It's often when I forget that a "character" is, by default, SIGNED:

char Character = 0x80;

unsigned int Unsigned;

Unsigned = Character; // Now Unsigned = 0xFF80