by on
reference


War on Christmas Lights from Donald Delmar Davis on Vimeo.

Last year I cast a series of led arrays for thing-a-day but hadnt wired them up. With this in mind I ordered some hc595s in the last group order but the snow canceled the meeting.

I have been thinking about using the avr to drive the arrays directly and control them using i2c. I hacked 4 dorkboards by cutting the traces for pins 5-12 on the top of the board and soldering 220 ohm resistors on the other side. This gives a one chip solution which does not require a constant refresh or other attention.

/*
 Simple 4 node array of leds (8x8).
 */
#include <Wire.h>

int node=0; /* CHANGE THIS BASED ON POSITION OF ARRAY */

volatile int twi_group_offset=128;

volatile int colpins[]={//12,11,10,9,8,7,6,5};
5,6,7,8,9,10,11,12};
volatile int rowpins[]={
17,16,15,14,13,4,3,2};
//2,3,4,13,14,15,16,17};

volatile unsigned long int myTics;

unsigned char pixels[]={0x3E, 0x51, 0x49, 0x45, 0x3E,0,0,node,
0x00, 0x42, 0x7F, 0x40, 0x00,0,1,1,
0x42, 0x61, 0x51, 0x49, 0x46,0,2,2,
0x21, 0x41, 0x45, 0x4B, 0x31,0,3,3
};

void setup()                    // run once, when the sketch starts
{

	int i;
	myTics=millis();
	for(i=0;i<8;i++){
		pinMode(rowpins[i],OUTPUT);
		digitalWrite(rowpins[i],LOW);
		pinMode(colpins[i],OUTPUT);
		digitalWrite(colpins[i],HIGH);
	}    // sets the digital pin as output
	if (node==0) {
		Wire.begin();
		Serial.begin(19200);
		updateSlaveNodes();
	} else {
		Wire.begin(node+twi_group_offset);
		Wire.onReceive(updatePixels); // register event
	}
}

void loop()                    //move most of this into a timer loop.
{
	int r,c;
	int lastcolpin=colpins[0];
	for (c=0;c<8;c++){
		lastcolpin=colpins[c];
		for(r=0;r<8;r++){
			if ((pixels[c]>>r)&0x01){
				//if ((!r)||((7-c)==r)||(c==r)){
				digitalWrite(rowpins[r], HIGH);
			} else {
				digitalWrite(rowpins[r], LOW);
			}
		}
		digitalWrite(colpins[c], LOW);
		if (node==0) {
			updateSlaveNodes();
		} else {
			//delayMicroseconds(1500); //
			delay(3);
		}
		digitalWrite(lastcolpin, HIGH);
	}
}

void updateSlaveNodes() {
	int slavenode, row, pixel8;
	pixel8=8;
	for (slavenode=1;slavenode<4;slavenode++){
		Wire.beginTransmission(slavenode+twi_group_offset);
		for (row=0;row<8;row++) {
			Wire.send(pixels[pixel8++]);
		}
		Wire.endTransmission();
	};
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void updatePixels(int howMany)
{ int r=0;
	while(Wire.available()) //
	{
		pixels[r++] = Wire.receive(); // receive byte as a character
	}
}


The next step is to move the actual refresh into a timer routine so that the body of code for the main node can focus on content.

/*------------------------------------------------------------ xmas timer array
 *
 * This is the program for a 4x8x8 led display using a processor for each segment
 * To program the different nodes set the node variable below. Node 0 is the
 * master node which keeps the pixel memory for the remaining nodes.
 *
 * The master node takes its memory and sends updates to each of the other segments
 * via the i2c buss using the Wire library.
 *
 * Each node takes its 8byte array of data and strobes it onto the display using
 * the 8 bit timer2 overflow interrupt.
 *
 * The first 7 characters are loaded into the display memory using the
 * loadCharacters function. The array is then shifted to the left in the main loop
 * with the next character being loaded a collumn at a time.
 *
 * The character data and the bitmap for the 5x7 array are stored in program
 * memory.
 *
 * Since interrupts are used all of the globals are marked as volatile.
 *
 * CopyLeft 2008 Donald Delmar Davis, Tempus Dictum, Inc.
 * This is free software (GPL)
 */
#include <Wire.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#define FREEK 180

int node=0; /* CHANGE THIS BASED ON POSITION OF ARRAY */

unsigned char PROGMEM Font5x7[] = {
0x00, 0x00, 0x00, 0x00, 0x00,// (space)
0x00, 0x00, 0x5F, 0x00, 0x00,// !
0x00, 0x07, 0x00, 0x07, 0x00,// "
0x14, 0x7F, 0x14, 0x7F, 0x14,// #
0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
0x23, 0x13, 0x08, 0x64, 0x62,// %
0x36, 0x49, 0x55, 0x22, 0x50,// &
0x00, 0x05, 0x03, 0x00, 0x00,// '
0x00, 0x1C, 0x22, 0x41, 0x00,// (
0x00, 0x41, 0x22, 0x1C, 0x00,// )
0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
0x08, 0x08, 0x3E, 0x08, 0x08,// +
0x00, 0x50, 0x30, 0x00, 0x00,// ,
0x08, 0x08, 0x08, 0x08, 0x08,// -
0x00, 0x60, 0x60, 0x00, 0x00,// .
0x20, 0x10, 0x08, 0x04, 0x02,// /
0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
0x00, 0x42, 0x7F, 0x40, 0x00,// 1
0x42, 0x61, 0x51, 0x49, 0x46,// 2
0x21, 0x41, 0x45, 0x4B, 0x31,// 3
0x18, 0x14, 0x12, 0x7F, 0x10,// 4
0x27, 0x45, 0x45, 0x45, 0x39,// 5
0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
0x01, 0x71, 0x09, 0x05, 0x03,// 7
0x36, 0x49, 0x49, 0x49, 0x36,// 8
0x06, 0x49, 0x49, 0x29, 0x1E,// 9
0x00, 0x36, 0x36, 0x00, 0x00,// :
0x00, 0x56, 0x36, 0x00, 0x00,// ;
0x00, 0x08, 0x14, 0x22, 0x41,// <
0x14, 0x14, 0x14, 0x14, 0x14,// =
0x41, 0x22, 0x14, 0x08, 0x00,// >
0x02, 0x01, 0x51, 0x09, 0x06,//
0x32, 0x49, 0x79, 0x41, 0x3E,// @
0x7E, 0x11, 0x11, 0x11, 0x7E,// A
0x7F, 0x49, 0x49, 0x49, 0x36,// B
0x3E, 0x41, 0x41, 0x41, 0x22,// C
0x7F, 0x41, 0x41, 0x22, 0x1C,// D
0x7F, 0x49, 0x49, 0x49, 0x41,// E
0x7F, 0x09, 0x09, 0x01, 0x01,// F
0x3E, 0x41, 0x41, 0x51, 0x32,// G
0x7F, 0x08, 0x08, 0x08, 0x7F,// H
0x00, 0x41, 0x7F, 0x41, 0x00,// I
0x20, 0x40, 0x41, 0x3F, 0x01,// J
0x7F, 0x08, 0x14, 0x22, 0x41,// K
0x7F, 0x40, 0x40, 0x40, 0x40,// L
0x7F, 0x02, 0x04, 0x02, 0x7F,// M
0x7F, 0x04, 0x08, 0x10, 0x7F,// N
0x3E, 0x41, 0x41, 0x41, 0x3E,// O
0x7F, 0x09, 0x09, 0x09, 0x06,// P
0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
0x7F, 0x09, 0x19, 0x29, 0x46,// R
0x46, 0x49, 0x49, 0x49, 0x31,// S
0x01, 0x01, 0x7F, 0x01, 0x01,// T
0x3F, 0x40, 0x40, 0x40, 0x3F,// U
0x1F, 0x20, 0x40, 0x20, 0x1F,// V
0x7F, 0x20, 0x18, 0x20, 0x7F,// W
0x63, 0x14, 0x08, 0x14, 0x63,// X
0x03, 0x04, 0x78, 0x04, 0x03,// Y
0x61, 0x51, 0x49, 0x45, 0x43,// Z
0x00, 0x00, 0x7F, 0x41, 0x41,// [
0x02, 0x04, 0x08, 0x10, 0x20,// "\"
0x41, 0x41, 0x7F, 0x00, 0x00,// ]
0x04, 0x02, 0x01, 0x02, 0x04,// ^
0x40, 0x40, 0x40, 0x40, 0x40,// _
0x00, 0x01, 0x02, 0x04, 0x00,// `
0x20, 0x54, 0x54, 0x54, 0x78,// a
0x7F, 0x48, 0x44, 0x44, 0x38,// b
0x38, 0x44, 0x44, 0x44, 0x20,// c
0x38, 0x44, 0x44, 0x48, 0x7F,// d
0x38, 0x54, 0x54, 0x54, 0x18,// e
0x08, 0x7E, 0x09, 0x01, 0x02,// f
0x08, 0x14, 0x54, 0x54, 0x3C,// g
0x7F, 0x08, 0x04, 0x04, 0x78,// h
0x00, 0x44, 0x7D, 0x40, 0x00,// i
0x20, 0x40, 0x44, 0x3D, 0x00,// j
0x00, 0x7F, 0x10, 0x28, 0x44,// k
0x00, 0x41, 0x7F, 0x40, 0x00,// l
0x7C, 0x04, 0x18, 0x04, 0x78,// m
0x7C, 0x08, 0x04, 0x04, 0x78,// n
0x38, 0x44, 0x44, 0x44, 0x38,// o
0x7C, 0x14, 0x14, 0x14, 0x08,// p
0x08, 0x14, 0x14, 0x18, 0x7C,// q
0x7C, 0x08, 0x04, 0x04, 0x08,// r
0x48, 0x54, 0x54, 0x54, 0x20,// s
0x04, 0x3F, 0x44, 0x40, 0x20,// t
0x3C, 0x40, 0x40, 0x20, 0x7C,// u
0x1C, 0x20, 0x40, 0x20, 0x1C,// v
0x3C, 0x40, 0x30, 0x40, 0x3C,// w
0x44, 0x28, 0x10, 0x28, 0x44,// x
0x0C, 0x50, 0x50, 0x50, 0x3C,// y
0x44, 0x64, 0x54, 0x4C, 0x44,// z
0x00, 0x08, 0x36, 0x41, 0x00,// {
0x00, 0x00, 0x7F, 0x00, 0x00,// |
0x00, 0x41, 0x36, 0x08, 0x00,// }
0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
};

unsigned char PROGMEM banner[] = "Happy Holidays!!! Merry Christmas!!! May every man with \"Merry Christmas\" on his lips be boiled in his own blood pudding with a steak of holly driven through his heart! Christmas! BAH! HUMBUG!!!";

volatile int twi_group_offset=128;
volatile int colpins[]={5,6,7,8,9,10,11,12};
volatile int rowpins[]={17,16,15,14,13,4,3,2};

volatile unsigned long int myTics; //counter for timer interrupt.

volatile unsigned char pixels[]={
0x3E, 0x51, 0x49, 0x45, 0x3E,0,0,node,
0x00, 0x42, 0x7F, 0x40, 0x00,0,0,1,
0x42, 0x61, 0x51, 0x49, 0x46,0,0,2,
0x21, 0x41, 0x45, 0x4B, 0x31,0,0,3,
0x00, 0x00, 0x00, 0x00, 0,0,0,0}; //pixel buffer rounded out to another 8 bytes

volatile unsigned int cursorpos=0;
volatile unsigned int charcursor=0;
volatile unsigned int currentcol;
volatile unsigned int charmod;
volatile unsigned char currentchar;
volatile unsigned int currentcharoffset;

/*---------------------------------------------------------------------setup()
 * initialize timer2
 * setup the i/o pins (initialized with everything off);
 * Initialize the i2c buss;
 * if master node then load initial characters into display.
 */
void setup()                    // run once, when the sketch starts
{

    int i;
    currentcol=0;

    /*------------ setting up timer two. ----------------*/
    TCCR2A = 0;                           // normal mode
    TCCR2B = 1<<CS22 | 1<<CS21 | 0<<CS20; // clock selection
    TIMSK2 |= 1<<TOIE2;          // enable overflow interupt
    TCNT2=FREEK;                 // adjustment of period
    ASSR=0; // paranoid
    myTics=0;                    // counter for delays
    sei();                       // enable interrupts

   for(i=0;i<8;i++){
		pinMode(rowpins[i],OUTPUT);
		digitalWrite(rowpins[i],LOW);
		pinMode(colpins[i],OUTPUT);
		digitalWrite(colpins[i],HIGH);
    }

    if (node==0) {            // master node
		Wire.begin();
		Serial.begin(19200);
                loadCharacters();
		updateSlaveNodes();
    } else {
		Wire.begin(node+twi_group_offset);
		Wire.onReceive(updatePixels); // register event
    }
}

/*------------------------------------------------------------ISR(TIMER2_OVF_vect)
 * loads the next collumn into the led array
 *
 */
ISR(TIMER2_OVF_vect) {
int r;
  digitalWrite(colpins[currentcol], HIGH); //turn off the last column.

  if (++currentcol > 7) {
    currentcol=0;
  }
  for(r=0;r<8;r++){
	if ((pixels[currentcol]>>r)&0x01){
		digitalWrite(rowpins[r], HIGH);
	} else {
		digitalWrite(rowpins[r], LOW);
	}
  }
  digitalWrite(colpins[currentcol], LOW);
  myTics++;
  TCNT2 = FREEK;


};

/*--------------------------------------------------------------loadCharacters()
 * load the initial characters into the pixel buffer
 */
void loadCharacters(){
  int ch,i,j;
  cursorpos=0;
  for (charcursor=0;charcursor<7;charcursor++) {
    ch=pgm_read_byte_near(banner+charcursor);
    j= (ch-32) & 0x000000ff;
    j = j*5;
   for (charmod=0;charmod<5;charmod++){
      pixels[cursorpos++]=pgm_read_byte_near(Font5x7+j+charmod);
    }
  }

}

/*------------------------------------------------------------updateSlaveNodes()
 *  send the pixel data to the slave nodes.
 */

void updateSlaveNodes() {
  int slavenode, row, pixel8;

  if (node==0) {
	pixel8=8;
	for (slavenode=1;slavenode<4;slavenode++){
		Wire.beginTransmission(slavenode+twi_group_offset);
		for (row=0;row<8;row++) {
			Wire.send(pixels[pixel8++]);
		}
		Wire.endTransmission();
	};
  }
}
/*------------------------------------------------------------------udatePixels()
 * recieve the pixel data from the master node.
 * executes whenever data is received from master
 * this function is registered as an event, see setup()
 */
void updatePixels(int ignored)
{ int r=0;
	while(Wire.available()) //
	{
		pixels[r++] = Wire.receive(); // receive byte as a character
	}
}

/*-------------------------------------------------------------------------loop()
 *
 */
void loop()
{int c;

 //int head=pixels[0];
 updateSlaveNodes();

 for (c=1;c<35;c++) {
   pixels[c-1]=pixels[c];
 }


  if (charmod==5) {//get next char
    charcursor++;
    charmod=0;
    currentchar=pgm_read_byte_near(banner+charcursor);
    if (currentchar=='\0'){
       currentchar=pgm_read_byte_near(banner);
       charcursor=0;
    }
    currentcharoffset = (currentchar-32) & 0x000000ff;
    currentcharoffset = currentcharoffset*5;
  }

  pixels[34]=pgm_read_byte_near(Font5x7+currentcharoffset+charmod);
  charmod++;

 while (myTics<90) ; myTics=0L; //wait a few hundred millisecconds

}



http://www.dorkbotpdx.org/files/xmastimer-081225b.zip

Leave a Reply

  • (will not be published)