//4x4x4 LED Cube using ATtiny85 and MAX7219
//P.Beard
//Dec 2013
//Pins for serial communications with the MAX7219 IC
#define M7219DATA 0
#define M7219CLK 2
#define M7219LOAD 1
#define LDR 3
//MAX7219 Control Registers
#define INTENSITY 0x0A
#define ON_OFF 0x0C
#define DECODE 0x09
#define SCAN_DIGITS 0x0B
#define TEST_MODE 0x0F
word layer[4]; //Holds the current bit pattern for each layer of the cube
int animSpeed;
void setup() {
pinMode(M7219DATA, OUTPUT);
pinMode(M7219CLK, OUTPUT);
pinMode(M7219LOAD, OUTPUT);
pinMode(LDR, INPUT);
send7219(ON_OFF, 1);//Switch on LED Driver
send7219(DECODE, 0);//Set no decoding on any digits
send7219(SCAN_DIGITS, 7);//Scan all 8 digits
for (int l=0; l<4; l++) {
layer[l] = 0;
}
updateCube();
send7219(TEST_MODE, 1);//Display test mode on
delay (1000);
send7219(TEST_MODE, 0);//Display test mode off
delay (1000);
randomiseCube();
updateCube();
}
void loop() {
rotatingSquare();
rotation();
randomRiseAndFall();
rain();
bubbles();
}
void rotatingSquare() {
//Rotating square
// 07 03 15 11
// 02 04 10 12
// 00 05 08 13
// 01 06 09 14
layer[0] = B00000000 << 8 | B10000111;
animSpeed = 75;
for (int i = 0; i<100; i++) {
layer[0] = rotateClockwise(layer[0]);
layer[1] = layer[0];
layer[2] = layer[0];
layer[3] = layer[0];
updateCube();
}
layer[1] = rotateClockwise(layer[0]);
layer[2] = rotateClockwise(layer[1]);
layer[3] = rotateClockwise(layer[2]);
for (int i = 0; i<100; i++) {
for (int l = 0; l<4; l++) {
layer[l] = rotateClockwise(layer[l]);
}
updateCube();
}
}
void rotation() {
//Rotation
animSpeed = 151;
for (int l=0; l<4; l++) {
layer[l] = 1 << random(16) | 1 << random(16);
}
for (int i=0; i<150; i++) {
for (int l=0; l<4; l++) {
layer[l] = rotateClockwise(layer[l]);
}
animSpeed--;
updateCube();
}
for (int i=0; i<150; i++) {
for (int l=0; l<4; l++) {
layer[l] = rotateAnticlockwise(layer[l]);
}
animSpeed++;
updateCube();
}
animSpeed = 25;
for (int i=0; i<100; i++) {
layer[0] = rotateClockwise(layer[0]);
layer[1] = rotateAnticlockwise(layer[1]);
layer[2] = rotateClockwise(layer[2]);
layer[3] = rotateAnticlockwise(layer[3]);
updateCube();
}
}
void randomRiseAndFall() {
//Random rise & fall
layer[1] = 0;
layer[2] = 0;
layer[3] = layer[0] ^ 0xFFFF; // top layer opposite of bottom layer
word w;
int l = 0;
animSpeed = 100;
for (int i=0; i<33; i++) {
do {
//Pick an led on the bottom row that is lit
w = 1 << random(16);
}
while ((layer[0] & w) == 0);
//Move that led up to the top layer
do {
layer[l] = layer[l++] ^ w;
layer[l] = layer[l] ^ w;
updateCube();
}
while (l < 3);
do {
//Pick an led on the top row that is lit
w = 1 << random(16);
}
while ((layer[3] & w) == 0);
//Move that led down to the bottom layer
do {
layer[l] = layer[l--] ^ w;
layer[l] = layer[l] ^ w;
updateCube();
}
while (l > 0);
}
}
void bubbles() {
//Random Bubbles
animSpeed = 100;
for (int i=0; i<100; i++) {
for (int l=3; l>=0; l--) {
layer[l] = layer[l-1];
}
layer[0] = 1<<random(16) | 1<<random(16) | 1<<random(16);
updateCube();
}
}
void rain() {
//Random Rain
animSpeed = 50;
for (int i=0; i<100; i++) {
for (int l=0; l<3; l++) {
layer[l] = layer[l+1];
}
layer[3] = 1<<random(16) | 1<<random(16) | 1<<random(16);
updateCube();
}
}
void send7219(byte reg, byte val) { //Send 2 bytes to the MAX7219 Driver IC
shiftOut(M7219DATA, M7219CLK, MSBFIRST, reg); //Send the register we want to set
shiftOut(M7219DATA, M7219CLK, MSBFIRST, val); //Send the value
digitalWrite(M7219LOAD, HIGH); //Tell MAX7219 to load the data
digitalWrite(M7219LOAD, LOW);
}
void updateCube() { //Update the cube LEDs
static int LDRavg;
send7219(2, highByte(layer[0]));
send7219(6, lowByte(layer[0]));
send7219(8, highByte(layer[1]));
send7219(4, lowByte(layer[1]));
send7219(3, highByte(layer[2]));
send7219(7, lowByte(layer[2]));
send7219(5, highByte(layer[3]));
send7219(1, lowByte(layer[3]));
LDRavg = (analogRead(LDR) + LDRavg * 15) / 16;
send7219 (INTENSITY, (LDRavg >> 6)); //Set intensity
delay (animSpeed);
}
void randomiseCube() {//Set entire cube to random
for (int l=0; l<4; l++) {
layer[l] = random(65536);
}
}
word rotateClockwise(word w) { //Rotate w0 clockwise
// 07 03 15 11
// 02 04 10 12
// 00 05 08 13
// 01 06 09 14
byte bit1 = bitRead(w, 1);
bitWrite(w, 1, bitRead(w, 6));
bitWrite(w, 6, bitRead(w, 9));
bitWrite(w, 9, bitRead(w, 14));
bitWrite(w, 14, bitRead(w, 13));
bitWrite(w, 13, bitRead(w, 12));
bitWrite(w, 12, bitRead(w, 11));
bitWrite(w, 11, bitRead(w, 15));
bitWrite(w, 15, bitRead(w, 3));
bitWrite(w, 3, bitRead(w, 7));
bitWrite(w, 7, bitRead(w, 2));
bitWrite(w, 2, bitRead(w, 0));
bitWrite(w, 0, bit1);
byte bit5 = bitRead(w, 5);
bitWrite(w, 5, bitRead(w, 8));
bitWrite(w, 8, bitRead(w, 10));
bitWrite(w, 10, bitRead(w, 4));
bitWrite(w, 4, bit5);
return w;
}
word rotateAnticlockwise(word w) { //Rotate w0 anticlockwise
// 07 03 15 11
// 02 04 10 12
// 00 05 08 13
// 01 06 09 14
byte bit1 = bitRead(w, 1);
bitWrite(w, 1, bitRead(w, 0));
bitWrite(w, 0, bitRead(w, 2));
bitWrite(w, 2, bitRead(w, 7));
bitWrite(w, 7, bitRead(w, 3));
bitWrite(w, 3, bitRead(w, 15));
bitWrite(w, 15, bitRead(w, 11));
bitWrite(w, 11, bitRead(w, 12));
bitWrite(w, 12, bitRead(w, 13));
bitWrite(w, 13, bitRead(w, 14));
bitWrite(w, 14, bitRead(w, 9));
bitWrite(w, 9, bitRead(w, 6));
bitWrite(w, 6, bit1);
byte bit5 = bitRead(w, 5);
bitWrite(w, 5, bitRead(w, 4));
bitWrite(w, 04, bitRead(w, 10));
bitWrite(w, 10, bitRead(w, 8));
bitWrite(w, 8, bit5);
return w;
}