Arduino, 74HC595 and PWM; how?

Hi!

I've been struggling with this question for a few days now. I am rather new to the shift registers, but I feel I can handle them well. The problem lies within setting up an active PWM signal to the shift register. I'll elaborate.

I tried the simple tutorial on the arduino site: http://www.arduino.cc/en/Tutorial/ShiftOut This works fine (linked 2 74HC595's together etc) and tried to manually input stuff so that I was able to create lots of different flickering stuff.

The problem now is that I am able to make 10 leds with different intensity levels (for instance 10% 20% 30% etc...) But I am unable to create a code for 1 led that smoothly fades in and out.

As I looked at the stuff that is on youtube, this HAS to be possible (I found terms such as ISR and Port Manipulation: http://www.arduino.cc/en/Reference/PortManipulation to speed up the program. I have worked with this on a general PIC, but that was a long time ago ;D I am probably able to figure this out, but I need to get started somewhere.

So is there someone who as an easy answer as to how to fade in and out a single led through a 74HC595 that is hooked up to an arduino?

I just had a bright moment and tested the following code:

//**************************************************************//
//  Name    : shiftOutCode, Hello World                                 //
//  Author  : Carlyn Maw,Tom Igoe, David A. Mellis  //
//  Date    : 25 Oct, 2006                                      //
//  Version : 1.0                                               //
//  Notes   : Code for using a 74HC595 Shift Register           //
//          : to count from 0 to 255                            //
//****************************************************************

//Pin connected to ST_CP of 74HC595
int latchPin = 8;
//Pin connected to SH_CP of 74HC595
int clockPin = 12;
////Pin connected to DS of 74HC595
int dataPin = 11;
int h = 0;
int l = 256 - h;
int counter = 0;
int switcher = 0;

void setup() {
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  if (counter == 1){
    if (h == 256){
      switcher = 1;
    }
    if (h == 0) {
      switcher = 0;
    }
    if (switcher == 0) {
      h++;
    } else {
      h--;
    }
    counter = 0;
  }
  
  for(int high = 0; high < h; high++){
    digitalWrite(latchPin, LOW);
  digitalWrite(dataPin, HIGH);
  digitalWrite(clockPin,LOW);
  digitalWrite(clockPin,HIGH);
  digitalWrite(latchPin,HIGH);
  }
  
  for(int low = 0; low < l; low++){
  digitalWrite(latchPin, LOW);
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  digitalWrite(clockPin, HIGH);
  digitalWrite(latchPin, HIGH);
  }
  
  
  
  l = 256 - h;
  counter++;
  
}

This is very ugly but seems to work (all my leds connected to the 74HC595 are fading, but that’s okay).

Any comments are welcome, I see if I can expand this.

As far as I'm aware, the 595 doesn't support PWM. (most common way to fade an LED) You're just going to be able to do an ON/OFF.

If you're looking to fade more than one LED, you can take a look at the TLC5940, able to PWM up to 16 outputs. (I haven't experimented so not sure if it's single LED capable or not)

Do a google search for TLC5940 and take a look at the datasheet. Should be exactly what you're looking for.

I am aware that the 595 does not support output in terms of analog signals, but as you can see I am trying to fix this through switching between on and off very quickly (which is the definition of PWM as far as I know).

The problem that I am having is that I am able to switch on a specific number of leds (for instance I have 8 leds on 1 595, and I am able to light 0x00x00x or xx00x0xxx or whatever, and I am able to fade in/out one led or many leds with the same frequency. However I am unable to combine these two things! So I can not fade the first leds 1time a second and the second 1time per 5 seconds, the third one should be off at all times, the fourth one should fade in 1 time a second, but opposite to the first led, etc etc.

So how do I combine this?

kick

This may help you. I've got some demo code on there too. It used 4x 595 chips to do PWM for an RGB matrix.

Hey Madworm!

I already contacted you via Youtube I thought! :D

Tnx for your work and tips so far! I have only the biggest trouble of changing your code into something that will work for a series of leds without the matrix (so just 20+ leds in a single string, instead of a 8x8 matrix).

Still seem to get stuck in the translation from 8x8 to a single string.

if your problem is just the code, writing a wrapper function that translates number from 0…20 to the row/column numbers my current code expects is easy.

something like:

char number;
char row = number/8;
char led = number%8;

hey marworm!

I made some heavy cuts in your existing code and I have it semi-working at the moment. I only have a few problems left:

This is my code so far

/*
2009 - robert:aT:spitzenpfeil_d*t:org - V3_x board test
*/

#define __spi_clock 13   // SCK - hardware SPI gaat naar 11
#define __spi_latch 10    // gaat naar 12
#define __spi_data 11    // MOSI - hardware SPI 14
#define __spi_data_in 12 // MISO - hardware SPI (unused)
#define __display_enable 9
#define __rows 1
#define __max_row __rows-1
#define __leds_per_row 8
#define __max_led __leds_per_row-1
#define __brightness_levels 255 // 0...15 above 28 is bad for ISR ( move to timer1, lower irq freq ! )
#define __max_brightness __brightness_levels-1
#define __fade_delay 4

#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0130 // 32 levels --> 0x0130; 38 --> 0x0157 (flicker)



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

byte brightness_red[__leds_per_row][__rows];      /* memory for RED LEDs */


void setup(void) {
  randomSeed(555);
  pinMode(__spi_clock,OUTPUT);
  pinMode(__spi_latch,OUTPUT);
  pinMode(__spi_data,OUTPUT);
  pinMode(__spi_data_in,INPUT);
  pinMode(__display_enable,OUTPUT);
  digitalWrite(__spi_latch,LOW);
  digitalWrite(__spi_data,LOW);
  digitalWrite(__spi_clock,LOW);
  setup_hardware_spi();
  delay(10);
 // set_matrix_rgb(0,0,0);                  /* set the display to BLACK */
  setup_timer1_ovf();                        /* enable the framebuffer display */
  Serial.begin(9600);
}


void loop(void) {
        
//fader();
//test();
test2();
  
}

void test(void){
  byte row = 0;
  byte led0 = 0;
  byte led1 = 32;
  byte led2 = 64;
  byte led3 = 96;
  byte led4 = 128;
  byte led5 = 160;
  byte led6 = 192;
  byte led7 = 224;
  
  set_led_red(row,0,led0);
  set_led_red(row,1,led1);
  set_led_red(row,2,led2);
  set_led_red(row,3,led3);
  set_led_red(row,4,led4);
  set_led_red(row,5,led5);
  set_led_red(row,6,led6);
  set_led_red(row,7,led7);
}

void test2(void){
  byte row = 0;
  byte led0 = 255;
  byte led1 = 255;
  byte led2 = 0;
  byte led3 = 0;
  byte led4 = 255;
  byte led5 = 0;
  byte led6 = 255;
  byte led7 = 255;
  
  set_led_red(row,0,led0);
  set_led_red(row,1,led1);
  set_led_red(row,2,led2);
  set_led_red(row,3,led3);
  set_led_red(row,4,led4);
  set_led_red(row,5,led5);
  set_led_red(row,6,led6);
  set_led_red(row,7,led7);
}


void fader(void) {                                    /* fade the matrix form BLACK to WHITE and back */
  byte ctr1;
  byte led;
  byte row;
 
  for(ctr1 = 0; ctr1 < __max_brightness; ctr1++) {
    for(row = 0; row <= __max_row; row++) {
      for(led = 0; led <= __max_led; led++) {
         set_led_red(row,6,ctr1);
         set_led_red(row,7,ctr1);
     
     }
   }
    delay(__fade_delay);
  }
  
  for(ctr1 = __max_brightness; ctr1 > 0; ctr1--) {
    for(row = 0; row <= __max_row; row++) {
      for(led = 0; led <= __max_led; led++) {
         set_led_red(row,6,ctr1);
         set_led_red(row,7,ctr1);
    
      }
    }
    delay(__fade_delay);
  }


}


void set_led_red(byte row, byte led, byte red) {
  brightness_red[row][led] = red;
}


/*
Functions dealing with hardware specific jobs / settings
*/

void setup_hardware_spi(void) {
  byte clr;
  // spi prescaler: 
  // SPI2X SPR1 SPR0
  //   0     0     0    fosc/4
  //   0     0     1    fosc/16
  //   0     1     0    fosc/64
  //   0     1     1    fosc/128
  //   1     0     0    fosc/2
  //   1     0     1    fosc/8
  //   1     1     0    fosc/32
  //   1     1     1    fosc/64
  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) ); // set prescaler bits
  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  clr=SPSR; // clear SPI status reg
  clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits
  //SPSR &= ~(1<<SPI2X); // clear prescaler bits
}


void setup_timer1_ovf(void) {
  // Arduino runs at 16 Mhz...
  // Timer1 (16bit) Settings:
  // prescaler (frequency divider) values:   CS12    CS11   CS10
  //                                           0       0      0    stopped
  //                                           0       0      1      /1  
  //                                           0       1      0      /8  
  //                                           0       1      1      /64
  //                                           1       0      0      /256 
  //                                           1       0      1      /1024
  //                                           1       1      0      external clock on T1 pin, falling edge
  //                                           1       1      1      external clock on T1 pin, rising edge
  //
  TCCR1B &= ~ ( (1<<CS11) );
  TCCR1B |= ( (1<<CS12) | (1<<CS10) );      
  //normal mode
  TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) );
  TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) );
  //Timer1 Overflow Interrupt Enable  
  TIMSK1 |= (1<<TOIE1);
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  // enable all interrupts
  sei(); 
}


ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  byte cycle;
  
  digitalWrite(__display_enable,LOW); // enable display inside ISR
  
  for(cycle = 0; cycle < __max_brightness; cycle++) {
    byte led;
    byte row = B00000000;      // row: current source. on when (1)
    byte red;                  // current sinker, on when (0)
            // current sinker, on when (0)

    for(row = 0; row <= __max_row; row++) {
      
      red = B11111111;            // off
      
      for(led = 0; led <= __max_led; led++) {
        if(cycle > brightness_red[row][led]) {
          red &= ~(1<<led);
        }
  
      }

      digitalWrite(__spi_latch,LOW);
      //spi_transfer(B00000001<<row);
      //spi_transfer(blue);
      //spi_transfer(green);
      spi_transfer(red);
      digitalWrite(__spi_latch,HIGH);
    }
  }
  digitalWrite(__display_enable,HIGH);    // disable display outside ISR
}


byte spi_transfer(byte data) {
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte. (we don't need that here)
}

The problems I have are:

  1. my resolution is way to low, i have flickering leds when using PWM signals (i.e. leds that are lit at 200/256 or 125/256 etc) also leds that are at 0/256 are not really off but glow a little. I tried decreasing the number of brightness-tints (#define __brightness_levels 255) to 15 or something but this does not fix the flickering problem and the leds are not at their brightest (even those with 256/256 or 16/16).

  2. I tried connecting multiple 595’s together, but this doesn’t seem to work :frowning: I used the tutorial for connecting multiple 595’s and with the regular use of the software by arduino: http://www.arduino.cc/en/Tutorial/ShiftOut) it works (so it is probably not the circuit, but the code). I tried adding extra ‘leds’ to the leds_per_row, but this doesn’t work.

I need about 42 leds on this single row, and I am really getting fed up with this nagging trial and error (what am I doing anyway ;( )

I really hope you, or anyone else can help me!

The brightness issue may have a few reasons that come into my mind:

  • You try to pull too much current out of the 595s (they are pretty weak)
  • You seem to have inverted the duty cycle:
if(cycle > brightness_red[row][led]) {
  red &= ~(1<<led);
}

This will turn the led off, if you set the brightness to 255 (assuming the LED’s anodes are connected to +5V and the cathodes go into the 595s). This is the best way to go, as sinking current works better as sourcing current.

  • In your experiment you only use 1 row and 1 595 for “red”.

This means the code will finish 24x faster than usual. The value of __TIMER1_CNT has been tuned to work with 8 rows and 3 colors. It determines how often the ISR gets called. If the code finishes early, the unused time interval between the calls gets longer. To avoid other problems, the display is turned off in that period. So you need to adapt (reduce) __TIMER1_CNT in this case.

If you experiment with it, you will see that if it is way too large, the display will flicker or even blink. If it is too small, the code will effectively lock up.

#define __spi_clock 13   // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11    // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)
#define __display_enable 9
#define __rows 8         // each "row" will be a single 595 shift register (all daisy chained, led anodes go to +5V, cathodes into the 595s)
#define __max_row __rows-1
#define __leds_per_row 8 // don't change that. hardware SPI can only transfer blocks of 8 bits.
#define __max_led __leds_per_row-1
#define __brightness_levels 32 // __TIMER1_CNT MUST be tuned if you change this value !!
#define __max_brightness __brightness_levels-1

#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0130 // 32 levels --> 0x0130; increase, if the ISR() takes longer, decrease if the ISR() finishes early

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

byte brightness_red[__leds_per_row][__rows];      /* memory for RED LEDs */

void setup(void) {
  pinMode(__spi_clock,OUTPUT);
  pinMode(__spi_latch,OUTPUT);
  pinMode(__spi_data,OUTPUT);
  pinMode(__spi_data_in,INPUT);
  pinMode(__display_enable,OUTPUT);
  digitalWrite(__spi_latch,LOW);
  digitalWrite(__spi_data,LOW);
  digitalWrite(__spi_clock,LOW);
  setup_hardware_spi();
  delay(10);
  setup_timer1_ovf();                        /* enable the framebuffer display */
}


void loop(void) {

}


/*
basic functions to set the LEDs
*/

void set_led_red(byte led_number, byte value) { /* this translates numbers from e.g. 0...63 to the row/column scheme: led 0...7, row 0...7 */
  byte row = led_number/8; // "row0" = shift register 0 , "row1" = shift register 1 .... all daisy chained !
  byte led = led_number%8; // led position on a shift register
  brightness_red[row][led] = value;
}


/*
Functions dealing with hardware specific jobs / settings
*/

void setup_hardware_spi(void) {
  byte clr;
  // spi prescaler: 
  // SPI2X SPR1 SPR0
  //   0     0     0    fosc/4
  //   0     0     1    fosc/16
  //   0     1     0    fosc/64
  //   0     1     1    fosc/128
  //   1     0     0    fosc/2
  //   1     0     1    fosc/8
  //   1     1     0    fosc/32
  //   1     1     1    fosc/64
  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) ); // set prescaler bits
  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  clr=SPSR; // clear SPI status reg
  clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits
  //SPSR &= ~(1<<SPI2X); // clear prescaler bits
}


void setup_timer1_ovf(void) {
  // Arduino runs at 16 Mhz...
  // Timer1 (16bit) Settings:
  // prescaler (frequency divider) values:   CS12    CS11   CS10
  //                                           0       0      0    stopped
  //                                           0       0      1      /1  
  //                                           0       1      0      /8  
  //                                           0       1      1      /64
  //                                           1       0      0      /256 
  //                                           1       0      1      /1024
  //                                           1       1      0      external clock on T1 pin, falling edge
  //                                           1       1      1      external clock on T1 pin, rising edge
  //
  TCCR1B &= ~ ( (1<<CS11) );
  TCCR1B |= ( (1<<CS12) | (1<<CS10) );      
  //normal mode
  TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) );
  TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) );
  //Timer1 Overflow Interrupt Enable  
  TIMSK1 |= (1<<TOIE1);
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  // enable all interrupts
  sei(); 
}


ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  byte cycle;
  
  digitalWrite(__display_enable,LOW);  // enable display inside ISR
  
  for(cycle = 0; cycle < __max_brightness; cycle++) {
    byte led;
    byte row;
    byte red;  // current sinker, on when (0)

    digitalWrite(__spi_latch,LOW);  // prepare the daisy chained 595s to receive data bytes

    for(row = 0; row <= __max_row; row++) {
      red = B11111111;  // off
      for(led = 0; led <= __max_led; led++) {
        if(cycle < brightness_red[row][led]) {
          red &= ~(1<<led);
        }
      }
      spi_transfer(red); // send 1 byte of data
    }
    
    digitalWrite(__spi_latch,HIGH); // end data transfer for all bytes 
  }
  
  digitalWrite(__display_enable,HIGH);    // disable display outside ISR
}


byte spi_transfer(byte data) {
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte. (we don't need that here)
}

I’ve made some modifications and added comments. You will still have to play with __TIMER1_CNT and __brightness_levels to optimize how often the display refresh ISR() routine gets called, so you don’t get any funny behaviour or flickering. The code compiles on 017, but I haven’t checked it on real hardware.

wow madworm u are the man!

I will try this tomorrow asap and keep you posted!

Tnx for everything man! Much appreciated!

Man-O-Man,

It is getting more and more troubling :frowning:

I am sorry to bother you all so much with this but here is the update:

  • I changed the circuit into the sinking-current method you advised (i.e. switching the leds so that they draw current from the source (+5V) and ground into the 595’s.

  • Used your code (copy/paste) and added the following lines in the void loop(void){}

set_led_red(0,0);
   set_led_red(1,2);
   set_led_red(2,12);
   set_led_red(3,32);
   set_led_red(4,32);
   set_led_red(5,32);
   set_led_red(6,32); 
   set_led_red(7,32);
   set_led_red(8,32);

But it doesn’t IT DOES work (for the first 595, the second doesn’t work)

It all seems so random? If I change the number of rows to for instance 6 or 2 or 1 or whatever, it sometimes ‘works’ (i.e. I get some feedback in the form of lights). But most of the time it just does nothing :’(

There was a bug in your code (I REALLY HOPE SO, otherwise I am back to the start ;( ) :

your code

ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  byte cycle;
  
  digitalWrite(__display_enable,LOW);  // enable display inside ISR
  
  for(cycle = 0; cycle < __max_brightness; cycle++) {
    byte led;
    byte row;
    byte red;  // current sinker, on when (0)

    digitalWrite(__spi_latch,LOW);  // prepare the daisy chained 595s to receive data bytes

    for(row = 0; row <= __max_row; row++) {
      red = B11111111;  // off
      for(led = 0; led <= __max_led; led++) {
        if(cycle < brightness_red[row][led]) {
          red &= ~(1<<led);
        }
      }
      spi_transfer(red); // send 1 byte of data
    } // THIS TIDLE IS WRONG
    
    digitalWrite(__spi_latch,HIGH); // end data transfer for all bytes
  }
  
  digitalWrite(__display_enable,HIGH);    // disable display outside ISR
}

which hopefully should be

ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  byte cycle;
  
  digitalWrite(__display_enable,LOW);  // enable display inside ISR
  
  for(cycle = 0; cycle < __max_brightness; cycle++) {
    byte led;
    byte row;
    byte red;  // current sinker, on when (0)

    digitalWrite(__spi_latch,LOW);  // prepare the daisy chained 595s to receive data bytes

    for(row = 0; row <= __max_row; row++) {
      red = B11111111;  // off
      for(led = 0; led <= __max_led; led++) {
        if(cycle < brightness_red[row][led]) {
          red &= ~(1<<led);
        }
      }
      spi_transfer(red); // send 1 byte of data
    
    
    digitalWrite(__spi_latch,HIGH); // end data transfer for all bytes
  }
} // THIS TIDLE IS RIGHT!
  
  digitalWrite(__display_enable,HIGH);    // disable display outside ISR
}

Some questions that come to mind:

  • Do you still use the 0-255 for the intensity when you have 32 different color ‘levels’? Or do you use 0-31?

(probably the 0-32 right?)

* How come I still keep getting dimmed leds even if I try the 255/255 setting when I use your code from above. I tried changing the timer (once I set it to 0x005 or so and I got good bright leds, only the PWM did not work anymore (obviously :slight_smile: ). semi fixed

I only have trouble getting the 2nd ‘row’ (i.e. 595) to work, it worked before, but now it doesn’t :smiley: I daisy-chained them together (all the pins from my arduino to the first 595 are also connected to the second, except for pin 14 @ the first 595). I connected the pin 9 of the first 595 tot the 14 of the second. Also all the correct plus and minusses from the second plus and minus from the first 595 are chained to those of the second 595.

I’ll try to see if I can fix this problem somehow. Tnx for all your help so far!

A bump for this one.

Tried all weekend to get the intensity of the outputs higher, but everything seems useless (except for the increasing of the number brightness levels (but this is probably not the way to go right?).

So my final questions are:

  • How can I increase my intensity? (the flickering is gone by the way). Current levels are:

define __brightness_levels 32

define __TIMER1_CNT 0x0090

  • I can't seem to get the second 595 to work with your existing code :'( I tried everything (rewire-ring, mess with the code). Any last idea's on this one?

Anyways really thanks for everything so far! You have been a great help!

Found 'the' problem:

The second 595 starts at led 56, instead of 8.

Which means that the correct way to address the first and the second 595 is:

//first 595
 brightness_red[0][0] = 32;
   brightness_red[0][1] = 0;
   brightness_red[0][2] = 32;
   brightness_red[0][3] = 0;
   brightness_red[0][4] = 32;
   brightness_red[0][5] = 0;
   brightness_red[0][6] = 32;
   brightness_red[0][7] = 0
//second 595
 brightness_red[7][0] = 32;
   brightness_red[7][1] = 0;
   brightness_red[7][2] = 32;
   brightness_red[7][3] = 0;
   brightness_red[7][4] = 32;
   brightness_red[7][5] = 0;
   brightness_red[7][6] = 32;
   brightness_red[7][7] = 0

Any idea why it skips row 1-6 ?

Okay I hooked up about 5 595’s now, and supposedly the program starts at row 0 (595 nr 1) then it goes to row 7 (595 nr 8) then back to row 6, row 5 etc etc until it reaches row 1 (595 nr 2).

For my purpose I can work around it by addressing the leds in the correct order, but I find it strange that it works like this. Anyway, I guess I am as good as ready (except for the glitch). madworm thank you very very much for everything. Your work is much appreciated!

If you can give any insights on the glitch u are welcome to, since I am interested in how it is possible that this glitch is still there.

#define __spi_clock 13   // SCK - hardware SPI
#define __spi_latch 10
#define __spi_data 11    // MOSI - hardware SPI
#define __spi_data_in 12 // MISO - hardware SPI (unused)
#define __display_enable 9
#define __rows 8         // each "row" will be a single 595 shift register (all daisy chained, led anodes go to +5V, cathodes into the 595s)
#define __max_row __rows-1
#define __leds_per_row 8 // don't change that. hardware SPI can only transfer blocks of 8 bits.
#define __max_led __leds_per_row-1
#define __brightness_levels 32 // __TIMER1_CNT MUST be tuned if you change this value !!
#define __max_brightness __brightness_levels-1

#define __TIMER1_MAX 0xFFFF // 16 bit CTR
#define __TIMER1_CNT 0x0090 // 32 levels --> 0x0130; increase, if the ISR() takes longer, decrease if the ISR() finishes early

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

byte brightness_red[__leds_per_row][__rows];      /* memory for RED LEDs */

void setup(void) {
  pinMode(__spi_clock,OUTPUT);
  pinMode(__spi_latch,OUTPUT);
  pinMode(__spi_data,OUTPUT);
  pinMode(__spi_data_in,INPUT);
  pinMode(__display_enable,OUTPUT);
  digitalWrite(__spi_latch,LOW);
  digitalWrite(__spi_data,LOW);
  digitalWrite(__spi_clock,LOW);
  setup_hardware_spi();
  delay(10);
  setup_timer1_ovf();                        /* enable the framebuffer display */
}


void loop(void) {
   brightness_red[0][0] = 32;   // 595 nr 1
   brightness_red[7][0] = 32;   // 595 nr 8
   brightness_red[6][0] = 32;   // 595 nr 7
   brightness_red[5][0] = 32;   // 595 nr 6 etc etc

  // fader();
}

void fader(void) {                                    /* fade the matrix form BLACK to WHITE and back */
  byte ctr1;
  byte led;
  byte row;
 
  for(ctr1 = 0; ctr1 < __max_brightness; ctr1++) {
    for(row = 0; row <= __max_row; row++) {
      for(led = 0; led <= __max_led; led++) {
         set_led_red(56,ctr1);
         set_led_red(0,ctr1);
     
     }
   }
    delay(100);
  }
  
  for(ctr1 = __max_brightness; ctr1 > 0; ctr1--) {
    for(row = 0; row <= __max_row; row++) {
      for(led = 0; led <= __max_led; led++) {
         set_led_red(56,ctr1);
         set_led_red(0,ctr1);
    
      }
    }
    delay(100);
  }


}


/*
basic functions to set the LEDs
*/

void set_led_red(byte led_number, byte value) { /* this translates numbers from e.g. 0...63 to the row/column scheme: led 0...7, row 0...7 */
  byte row = led_number/8; // "row0" = shift register 0 , "row1" = shift register 1 .... all daisy chained !
  byte led = led_number%8; // led position on a shift register
  brightness_red[row][led] = value;
}


/*
Functions dealing with hardware specific jobs / settings
*/

void setup_hardware_spi(void) {
  byte clr;
  // spi prescaler:
  // SPI2X SPR1 SPR0
  //   0     0     0    fosc/4
  //   0     0     1    fosc/16
  //   0     1     0    fosc/64
  //   0     1     1    fosc/128
  //   1     0     0    fosc/2
  //   1     0     1    fosc/8
  //   1     1     0    fosc/32
  //   1     1     1    fosc/64
  SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
  //SPCR |= ( (1<<SPR1) ); // set prescaler bits
  SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear prescaler bits
  clr=SPSR; // clear SPI status reg
  clr=SPDR; // clear SPI data reg
  SPSR |= (1<<SPI2X); // set prescaler bits
  //SPSR &= ~(1<<SPI2X); // clear prescaler bits
}


void setup_timer1_ovf(void) {
  // Arduino runs at 16 Mhz...
  // Timer1 (16bit) Settings:
  // prescaler (frequency divider) values:   CS12    CS11   CS10
  //                                           0       0      0    stopped
  //                                           0       0      1      /1  
  //                                           0       1      0      /8  
  //                                           0       1      1      /64
  //                                           1       0      0      /256
  //                                           1       0      1      /1024
  //                                           1       1      0      external clock on T1 pin, falling edge
  //                                           1       1      1      external clock on T1 pin, rising edge
  //
  TCCR1B &= ~ ( (1<<CS11) );
  TCCR1B |= ( (1<<CS12) | (1<<CS10) );      
  //normal mode
  TCCR1B &= ~ ( (1<<WGM13) | (1<<WGM12) );
  TCCR1A &= ~ ( (1<<WGM11) | (1<<WGM10) );
  //Timer1 Overflow Interrupt Enable  
  TIMSK1 |= (1<<TOIE1);
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  // enable all interrupts
  sei();
}


ISR(TIMER1_OVF_vect) { /* Framebuffer interrupt routine */
  TCNT1 = __TIMER1_MAX - __TIMER1_CNT;
  byte cycle;
  
  digitalWrite(__display_enable,LOW);  // enable display inside ISR
  
  for(cycle = 0; cycle < __max_brightness; cycle++) {
    byte led;
    byte row;
    byte red;  // current sinker, on when (0)

    digitalWrite(__spi_latch,LOW);  // prepare the daisy chained 595s to receive data bytes

    for(row = 0; row <= __max_row; row++) {
      red = B11111111;  // off
      for(led = 0; led <= __max_led; led++) {
        if(cycle < brightness_red[row][led]) {
          red &= ~(1<<led);
        }
      }
      spi_transfer(red); // send 1 byte of data
    
    
    digitalWrite(__spi_latch,HIGH); // end data transfer for all bytes
    }
    
  }
  digitalWrite(__display_enable,HIGH);    // disable display outside ISR
}


byte spi_transfer(byte data) {
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte. (we don't need that here)
}

Well if the order of the shift registers is wrong, something must be screwed up in the order of the data that goes out.

Currently I don’t understand how you could see anything lighting up properly on shift registers other than #1. As they are all daisy chained, the complete set of data must be shifted into and through the chain, then the latch pin must go HIGH again.

Right now the latch pin is set HIGH inside the for loop, after one byte of data has been sent. All the other data bytes are shifted into the chain, but not “activated”. Activation needs a LOW/HIGH transition of the latch pin. When the cycle loop runs for the next time some of the old data that is still in there may get activated. This is flawed.

The correct procedure is this:

  • latch pin: LOW
  • send ALL data bytes, fill the chain 100%
  • latch pin: HIGH

Hey! first of all, I want to tell you that this is my first post in this forum! and also, that I'm from Argentina so, sorry if my english is not very good!

I was reading like 20 different posts about this thing of RGB LEDs, matrix, and how to control them, and I can't believe I just found someone that is (or was) exactly in the same situation than me.

I'm just started with arduino and shift registers, and I'm working in a small array of RGB LED's. It consists in 8 RGB LEDs that I want to control them independent and then in the future, extend it to an array of 100 LEDs more or less... The point why I want to do this is because in my room I have.. eem. well, I don't know how it calls so here there is a picture of the design of my room:

http://img46.imageshack.us/img46/2707/theproject.jpg

There you can see the drawn LEDs on the top. Imagine a lot of them there, making flashes, waves, changing the colors, etc. I think that could be really cool!

I posted this here because I think is the same concept than you (SuperFruitig) and well, then, maybe I could start a new topic about this.

Now I'm going to read your blog madworm. I think it's awesome, really! So I hope that help me to start with the project.

Thank you guys!