cootiecage. a 4x4x4 charlieplexed led array.

MiniPOV4 and previous versions

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

cootiecage. a 4x4x4 charlieplexed led array.

Post by mtbf0 »

a while back a user on this forum was asking about using the minipov3 for a larger led cube than 3x3x3 version built by bre and mitch on the make: weekend project ever so long ago.

which got me thinking...

which got me soldering...

Image

Image

Image

which got me coding.

Code: Select all

/*
    cootiecage.c - Version 1.0
    
    Copyright 2009 Bobby Cossum.  All Rights reserved.
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
			
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or BANNED FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
					
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
			
    a cootie cage is a 4x4x4 matrix of leds, constructed from eight
    cooties.

    each cootie is a 2x4 matrix of leds connected with their cathodes
    wired in common.  looks like an eight legged bug, ergo, a cootie.

    the cootie cage is built from two stacks of 4 cooties each.  each
    of these stacks brings to mind a bunk bed, ergo, a cootie cage.

    these are the second and third reasons for my choice of a name for
    this project.  if i ever get the firmware to do what i want it to,
    i'll tell you the first.

    the cootie cage is wired as a charlieplexed array and is controlled
    using only nine i/o pins.  at any time the common cathode of one
    cootie will be brought low.  the anodes of the eight leds in the 
    cootie are connected to the other 8 pins.  those that are to be
    turned on will have their anodes brought high.  the pins connected
    to leds that are not to be lit are set up as inputs.

    for a controller i will be using a minipov3 board from adafruit
    industries.

    here is an attempt at a perspective drawing of the cootie cage.
    each pair of digits represents an led.  the first digit is the
    anode connection and the second is cathode connection.  in the
    diagram leds connected by slashes are in the same horizontal
    plane.  leds arranged vertically above and below each other
    are, in fact, above and below each other.  leds with the same
    cathode connection are part of the same cootie.  the two
    panels on the left represent one stack of four cooties and the
    two panels on the right represent another.

             53               03               47               07
             /                /                /                /
            /                /                /                /
          63 52            13 02            57 46            17 06
          /  /             /  /             /  /             /  /
         /  /             /  /             /  /             /  /
       73 62 51         23 12 01         67 56 45         27 16 05
       /  /  /          /  /  /          /  /  /          /  /  /
      /  /  /          /  /  /          /  /  /          /  /  /
    83 72 61 50      43 32 21 10      87 76 65 54      37 26 15 04
       /  /  /          /  /  /          /  /  /          /  /  /
      /  /  /          /  /  /          /  /  /          /  /  /
    82 71 60         42 31 20         86 75 64         36 25 14
       /  /             /  /             /  /             /  / 
      /  /             /  /             /  /             /  /
    81 70            41 30            85 74            35 24
       /                /                /                /
      /                /                /                /
    80               40               84               34

    connections numbered 0-7 are connected to pins b0-b7 and
    connection 8 is connected to d3, which is the sensor pad on
    the minipov3 board.  note that the led numbering and the
    resistor numbering on the minipov3 are reversed, so that b0
    is connected to r8 and b7 is connected to r1.

    here are the connections by vertical level of the cage.  

            50----10   54----04            51----01   45----05 
            /     /    /     /             /     /    /     / 
           /     /    /     /             /     /    /     /  
         60    20   64    14            61    21   65    15   
         /     /    /     /             /     /    /     /    
        /     /    /     /             /     /    /     /     
      70    30   74    24            71    31   75    25      
      /     /    /     /             /     /    /     /       
     /     /    /     /             /     /    /     /        
   80----40   84----34            81----41   85----35         
         level 1                        level 2

            52----02   46----06            53----03   47----07 
            /     /    /     /             /     /    /     / 
           /     /    /     /             /     /    /     /  
         62    12   56    16            63    13   57    17   
         /     /    /     /             /     /    /     /    
        /     /    /     /             /     /    /     /     
      72    32   76    26            73    23   67    27      
      /     /    /     /             /     /    /     /       
     /     /    /     /             /     /    /     /        
   82----42   86----36            83----43   87----37         
         level 3                        level 4

*/

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

#define LED0 PB0
#define LED1 PB1
#define LED2 PB2
#define LED3 PB3
#define LED4 PB4
#define LED5 PB5
#define LED6 PB6
#define LED7 PB7
#define LED8 PD2

#define END 0x00
#define LOOP 0x01
#define REPEAT 0x02
#define INVERT 0x03
#define MIN_DURATION 0x04

uint16_t foo PROGMEM = 666;

//
//  this table contains the display data.  each line of nine
//  bytes constitutes a frame.  the first byte of the frame
//  specifies its duration in 16.384ms units.  to display a
//  frame for approximately 1 second, specify a duration of
//  61 or 0x3d.  the remaining eight bytes in a frame specify
//  the display data - two bytes per level, one byte per
//  cootie, one nybble per row.
//
//                     +---------+---------+---------+---------+
//             cathode | 0    4  | 1    5  | 2    6  | 3    7  |
//                     +---------+---------+---------+---------+
//             cootie# | 0    1  | 2    3  | 4    5  | 6    7  |
//		       +---------+---------+---------+---------+
//		   row | 12   34 | 12   34 | 12   34 | 12   34 |
//		+------+---------+---------+---------+---------+
//		| dur. | level 1 | level 2 | level 3 | level 4 |
//		+------+---------+---------+---------+---------+
uint8_t disp[] PROGMEM = 
                {0x04, 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
                 0x04, 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
                 0x04, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
                 0x04, 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
                 0x04, 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
                 LOOP, 4,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0xff,
                 0x04, 0x00,0x00,0x00,0x0f,0x00,0x0f,0x00,0xff,
                 0x04, 0x00,0x0f,0x00,0x0f,0x00,0x0f,0x00,0x0f,
                 0x04, 0x00,0xff,0x00,0x0f,0x00,0x0f,0x00,0x00,
                 0x04, 0x0f,0xff,0x00,0x0f,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0x00,0xf0,0x00,0xf0,0x00,0x00,0x00,
                 0x04, 0xf0,0x00,0xf0,0x00,0xff,0x00,0x00,0x00,
                 0x04, 0x00,0x00,0xf0,0x00,0xff,0xf0,0x00,0x00,
                 0x04, 0x00,0x00,0x00,0xf0,0xff,0xf0,0x00,0x00,
                 0x04, 0x00,0x00,0x0f,0xf0,0x0f,0xf0,0x00,0x00,
                 0x04, 0x00,0x00,0xff,0xf0,0xf0,0x00,0x00,0x00,
                 0x04, 0x00,0x00,0xff,0x00,0xf0,0x00,0xf0,0x00,
                 0x04, 0x00,0x00,0xf0,0x00,0xf0,0x00,0xff,0x00,
                 0x04, 0x00,0x00,0x00,0x00,0xf0,0x00,0xff,0xf0,
                 REPEAT,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0xff,
                 0x04, 0x00,0x00,0x00,0x0f,0x00,0x0f,0x00,0xff,
                 0x04, 0x00,0x0f,0x00,0x0f,0x00,0x0f,0x00,0x0f,
                 0x04, 0x00,0xff,0x00,0x0f,0x00,0x0f,0x00,0x00,
                 0x04, 0x0f,0xff,0x00,0x0f,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xff,0x00,0xf0,0x00,0xf0,0x00,0x00,0x00,
                 0x04, 0xf0,0x00,0xf0,0x00,0xff,0x00,0x00,0x00,
                 0x04, 0x00,0x00,0xf0,0x00,0xff,0xf0,0x00,0x00,
                 0x04, 0x00,0x00,0x00,0xf0,0xff,0xf0,0x00,0x00,
                 0x10, 0x00,0x00,0x0f,0xf0,0x0f,0xf0,0x00,0x00,
                 0x04, 0x00,0x00,0x06,0x60,0x06,0x60,0x00,0x00,
                 0x08, 0x90,0x09,0x00,0x00,0x00,0x00,0x90,0x09,
                 LOOP, 4,
                 0x08, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x08, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x08, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 REPEAT,
                 LOOP, 4,
                 0x07, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x07, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x07, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 REPEAT,
                 LOOP, 4,
                 0x06, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x06, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x06, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 REPEAT,
                 LOOP, 4,
                 0x05, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x05, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x05, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 REPEAT,
                 LOOP, 4,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x04, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 REPEAT,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x04, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x04, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x04, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x41,0x82,0x41,0x82,0x41,0x82,0x41,0x82,
                 0x04, 0x28,0x14,0x28,0x14,0x28,0x14,0x28,0x14,
                 0x04, 0x90,0x09,0x90,0x09,0x90,0x09,0x90,0x09,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                 0x04, 0xf0,0x00,0xf0,0x00,0xf0,0x00,0xf0,0x00,
                 0x04, 0x0f,0x00,0x0f,0x00,0x0f,0x00,0x0f,0x00,
                 0x04, 0x00,0xf0,0x00,0xf0,0x00,0xf0,0x00,0xf0,
                 0x04, 0x00,0x0f,0x00,0x0f,0x00,0x0f,0x00,0x0f,
                 0x04, 0x00,0xf0,0x00,0xf0,0x00,0xf0,0x00,0xf0,
                 0x04, 0x0f,0x00,0x0f,0x00,0x0f,0x00,0x0f,0x00,
                 0x04, 0xf0,0x00,0xf0,0x00,0xf0,0x00,0xf0,0x00,
                 0x04, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                 END };

uint8_t *dispPtr = disp;
uint8_t *framPtr = 0;
uint8_t duration = 0;
uint8_t tick = 0;

uint8_t left, right;
uint8_t cootie, common;
uint8_t outp, data, mask;

uint8_t *loopPtr;
uint8_t loopCnt;

ISR (TIMER0_OVF_vect) {
//
//  adjust the timer
//
//  the way the timer is configured this routine will run
//  8000000 / 64 / 256, or about 488, times a second.  since
//  it takes 16 times through this routine to update the
//  entire display, this gives a refresh rate of about 30Hz
//  which may give some visible flicker, so we'll diddle the
//  timer counter register to double the interrupt frequency
//  like so...
//
  TCNT0 = 125;				// tada... 60.09615385Hz
//
//  when the music's over, turn out the lights...
//
  DDRD &= ~(1 << LED8);			// set pin 8 as input
  DDRB = 0;				// set pins 0-7 as inputs
  PORTD &= ~(1 << LED8);		// clear pin 8
  PORTB = 0x00;				// clear pins 0-7
//
//  turn me on, dead man
//
  if (data & 0x80) {			// if bit 8 is set
    DDRD |= (1 << LED8);		// set data direction bits
    PORTD |= (1 << LED8);		// and set port bit
  }
  DDRB = (uint8_t)(left + right + (1 << common));// set PORTB data direction
  PORTB = (uint8_t)(left + right);	// output bits 0-7
//
//  prepare data for next call
//
  if (tick ^= 1) {
    if (cootie == 0) {			// end of frame?
      if (duration--) {			// repeat frame?
        dispPtr = framPtr;
      }
      else {				// move to next frame
        while ((duration = pgm_read_byte (dispPtr++)) < MIN_DURATION) {
          if (duration == END) {	// end of table
            dispPtr = disp;
          }
  	  if (duration == LOOP) {	// loop instruction
	    loopCnt = pgm_read_byte (dispPtr++);  // get count
	    loopPtr = dispPtr;		// save loop address
	  }
	  if (duration == REPEAT) {	// repeat instruction
	    if (--loopCnt) dispPtr = loopPtr;
	  }
	}
        framPtr = dispPtr;
      }
    }
    outp = pgm_read_byte (dispPtr++);	// read data byte
    common = (cootie >> 1);		// that'll be cootie / 2
    if (cootie & 0x01) common += 4;	// map cootie to common
    mask = (1 << common) - 1;		// compute data mask
    cootie = (cootie + 1) & 0x07;	// next cootie
    data = outp & 0xf0;			// high order nybble first
  }
  else {
    data = outp & 0x0f;			// low order nybble next
  }
//
//  extract data to left and right of common pin
//
  left = data & mask;			// mask left data
  right = (data & ~mask) << 1;		// mask right data & shift past common
}

int main (void) {
//
//  set up timer0 to overflow 8000000 / 64 / 128
//
  TCCR0A = 0x00;			// mode zero, prescale = 64
  TCCR0B = (1 << CS01) | (1 << CS00);
  TIMSK = (1 << TOIE0);			// enable overflow interrupt
  sei ();				// enable interrupts globally
//
//  now get on with it
//

  while (1);				// 'til hell freezes over
}

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Re: cootiecage. a 4x4x4 charlieplexed led array.

Post by adafruit »

no only did you do a great job designing and building it but you are really good at documenting it too!!

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: cootiecage. a 4x4x4 charlieplexed led array.

Post by mtbf0 »

ladyada wrote:no only did you do a great job designing and building it but you are really good at documenting it too!!
yeah. had to. didn't know what i was doing.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: cootiecage. a 4x4x4 charlieplexed led array.

Post by mtbf0 »

a couple of things i forgot to mention...

the minipov3 cannot be programmed with the cootie cage connected to SCK, so it must be disconnected. i just bent little hooks at the ends of the three wires connected there and hang them on the proper wires of the cage when it's time to run.

i attached the led anodes through 200 ohm resistors to limit the current through each to about 10ma. (i'm using a 5v switching wall wart.) the firmware only turns on at most 4 leds at a time to keep from frying the pins sinking the current.

each of r1-r8, (pb0-pb7), has a cathode connection to the cube attached at the end closer to the mcu, and an anode connection at the end further away.

i soldered a ninth resistor into the sensor pad, (pd2), on the minipov3. attached a wire to the far end which is connected to anode 8 in the cube.

as you can see, i added a facility to loop parts of the animation. this saves a lot of memory if you have parts of your animation sequence that repeat. the loops cannot be nested. the way the loops are encoded also precludes the use of durations less than 4. this bit was an afterthought and is not well documented in the code.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: cootiecage. a 4x4x4 charlieplexed led array.

Post by mtbf0 »

modified the code to light as many as eight leds at once instead of just four and, so far, the chip is surviving. this doubles the duty cycle which doubles the brightness. yippee.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: cootiecage. a 4x4x4 charlieplexed led array.

Post by mtbf0 »

i recently had a pm from someone trying to figure out how to build one of these, so i made a diagram of the construction of half a cage. i know the perspective isn't very good, but the face with the bent leads is supposed to be the foreground. the other half of the cage is a mirror image of the first half and in a finished cage the faces with the bent leads will be facing each other in the middle of the cage.

Image

... and here's a diagram of the whole thing. the part of the circuit in the other diagram is in the background of this one...

Image

Locked
Please be positive and constructive with your questions and comments.

Return to “MiniPOV”