Here is code with the matrix pins made virtual with PWM control 64 levels.
You can set any of the "pins" on each LED to a value from 0 to 255. The rest is handled by iprocess.
This is just a simple example with advanced stuff stripped out.
Hope it helps others.
/**************************************************************
* Name : Matrix LED Controller Via Shift Registers
* By : Joseph Francis
*
* Open Source
*
* Date : 03 Nov, 2009
* Version : 1.0
* Notes : Code for using a 74HC595 Shift Register
* : to run a matrix with PWM on 128 virtual ports using 3 SR's
****************************************************************/
#include <TimerOne.h>
#define TICKER_MAX 64
#define TICKER_STEP 4
int timerDelay = 200;
byte srCommon = B11111011;
//--- Pin connected to ST_CP of 74HC595
int latchPin = 10;
//--- Pin connected to SH_CP of 74HC595
int clockPin = 13;
//--- Pin connected to DS of 74HC595
int dataPin = 11;
//--- Used for faster latching
int latchPinPORTB = latchPin - 8;
//======================
int groundMax = 8; //number of multiplex
int ticker = 0;
int groundAt = 255;
byte srPins[128] = {
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,
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
};
void pwmWrite(int port, byte val){
srPins[port] = val;
}
void iProcess(){
ticker++;
if( ticker > TICKER_MAX )
ticker = 0;
int myPos = ticker * TICKER_STEP;
groundAt++;
if( groundAt > groundMax - 1 )
groundAt = 0;
//IMPORTANT: This but be swapped for common cathod
byte mySR1 = B11111111;
byte mySR2 = B11111111;
srCommon = 0;
/* for Common Cathode
byte mySR1 = 0;
byte mySR2 = 0;
srCommon = B11111111;
*/
for (int i = 0 ; i < 8; i++ ){
int iLED = i;
int myLev = 0;
myLev = 1;
if (srPins[i+(groundAt*16)] > myPos)
myLev = 0;
bitWrite(mySR1,i,myLev );
myLev = 1;
if (srPins[(i+8)+(groundAt*16)] > myPos)
myLev = 0;
bitWrite(mySR2,i,myLev );
}
bitWrite(srCommon,groundAt,1 );
/* for Common Cathode - use this instead
for (int i = 0 ; i < 8; i++ ){
int iLED = i;
int myLev = 0;
myLev = 1;
if (srPins[i+(groundAt*16)] > myPos)
myLev = 1;
bitWrite(mySR1,i,myLev );
myLev = 0;
if (srPins[(i+8)+(groundAt*16)] > myPos)
myLev = 1;
bitWrite(mySR2,i,myLev );
}
bitWrite(srCommon,groundAt,0);
*/
//SR's are daisy chained. The 3rd SR is the common (anode or cathode)
//Update order if needed so the common is correct for your arrangement
spi_transfer(srCommon);
spi_transfer(mySR2);
spi_transfer(mySR1);
latchOn();
latchOff();
}
int fadeStep = 5;
int fadeDelay = 10;
int currLED = 0;
void loop() {
srPins[currLED] = 0;
currLED++;
if (currLED > 127)
currLED = 0;
for( int i = 0 ; i < 255 ; i += fadeStep){
srPins[currLED] = i;
delay(fadeDelay);
}
}
void latchOn(){
bitSet(PORTB,latchPinPORTB);
}
void latchOff(){
bitClear(PORTB,latchPinPORTB);
}
void setupSPI(){
byte clr;
SPCR |= ( (1<<SPE) | (1<<MSTR) ); // enable SPI as master
//SPCR |= ( (1<<SPR1) | (1<<SPR0) ); // 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
delay(10);
}
byte spi_transfer(byte data)
{
SPDR = data; // Start the transmission
loop_until_bit_is_set(SPSR, SPIF);
return SPDR; // return the received byte, we don't need that
}
void setup() {
randomSeed(analogRead(0));
Serial.begin(9600);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
digitalWrite(latchPin,LOW);
digitalWrite(dataPin,LOW);
digitalWrite(clockPin,LOW);
setupSPI();
Timer1.initialize(timerDelay); // Timer for updating pwm pins
Timer1.attachInterrupt(iProcess);
}