which got me thinking...
which got me soldering...
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
}