In the previous part of this online LED cube tutorial we’ve finished the entire hardware side of the cube. Now, while that’s looking awesome, it is still a dead object. In this final part I’ll guide you through the steps to get started in the Arduino environment, so you can create your own cool effects.

Programming the cube

If you are somewhat used to programming in the Arduino IDE you know that there are 2 main functions that you use for programming your input and output, namely setup and loop. You also use the digitalRead and digitalWrite methods to get and set values from and to certain input or output pins on your Arduino board. An example code is shown below that makes an LED blink at timed intervals.

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);     
}
 
void loop() {
  digitalWrite(13, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(1000);              // wait for a second
}

While this way of working is great for most projects, in our LED cube these methods are just way to slow to be used. Why? Well, since we are using a system of multiplexing, which means that not all LEDs are turned on at once (although they may seem to be because of the principle of persistence of vision), we will have to turn the LEDs on and off several dozens, if not hundreds, of times per second. The general functions in the Arduino IDE are just not created to do exactly that.

If we cannot use the standard functions provided by the SDK, then what do we need to use? We are actually going to address entire pin ranges at once. For example, the 8-bit data bus is going to be addressed as 1 single unit, as well as the 3 bits for the timer IC (74HC138). But to be able to do that, the setup function is going to look quite different.
The Arduino chip (ATMega 328) is divided into 4 ports (port A, port B, port C and port D) and each port consists of 8 pins, which correspond to 8 bits. That means that if we are going to put the value of 7 to port C, for example, 3 pins on the Arduino are going to be set at once. We need this system to be able to use the 74HC138′s all 8 output lines when providing only 3 input lines.

If you hooked up the cube as I’ve described in the previous sections, you Arduino chip’s port configuration should be:

  • port B, bits 0-2: address bus or timer IC (digital pins 8-10)
  • port B, bit 3: output enable (digital pin 11)
  • port B, bits 4-5 : layer selection (digital pins 12-13)
  • port C, bits 0-5: layer selection (analog pins 0-5)
  • port D: the data bus (digital pins 0-7)

The other pins on the Arduino chip are used for power (VCC and ground), the chrystal and some other features. These will not be used in our programming scheme.

With this in mind the setup function looks like this:

void setup() {
  int i;
 
  // Set all pins as output pins
  for(i=0; i<14; i++)
    pinMode(i, OUTPUT);
 
  // pinMode(A0, OUTPUT) as specified in the arduino reference didn't work. So I accessed the registers directly.
  DDRC = 0xff;
  PORTC = 0x00;
 
  // Reset any PWM configuration that the arduino may have set up automagically!
  TCCR2A = 0x00;
  TCCR2B = 0x00;
 
  TCCR2A |= (0x01 << WGM21); // CTC mode. clear counter on TCNT2 == OCR2A
  OCR2A = 10; // Interrupt every 25600th cpu cycle (256*100)
  TCNT2 = 0x00; // start counting at 0
  TCCR2B |= (0x01 << CS22) | (0x01 << CS21); // Start the clock with a 256 prescaler
 
  TIMSK2 |= (0x01 << OCIE2A);
}

As you can see in the comments in the code above, I’ve also set an interrupt so we can do some stuff there. Since the Arduino programming (or hardware programming in general) consists mainly of running an infinite loop, we need to be able to stop it, change some parameters etc at certain intervals, hence the interrupt routine. Now, in the Arduino documentation you can find that the ISR interrupt routine is called whenever the timer/counter reaches the OCR2A value.

ISR (TIMER2_COMPA_vect) {
  // All layer selects off
  PORTC = 0x00;
  PORTB &= 0x0f;
 
  PORTB |= 0x08; // output enable off.
 
  for (int i=0; i<8; i++) {
    // Put the data on port D
    PORTD = cube[current_layer][i];
    // Trigger the latch IC to move the input to the output pins
    // via the use of the 74HC138 timer IC
    PORTB = (PORTB & 0xF8) | (0x07 & (i+1));
  }
 
  PORTB &= 0b00110111; // Output enable on.
 
  // Remember the layers are controlled by analog pins 0-5 
  // and digital pins 12 and 13.
  if (current_layer < 6) {
    PORTC = (0x01 << current_layer);
  } 
  else if (current_layer == 6) {
    digitalWrite(12, HIGH);
  } 
  else {
    digitalWrite(13, HIGH);
  }
 
  // Enable the next layer
  current_layer++;
 
  // If we run out of layers, just start at the beginning again
  if (current_layer == 8)
    current_layer = 0;
}

With these methods in place you’re now all set for programming your special effects on the LED cube. The loop method just implements an infinite loop of your special effects. If you want to start with some example code I suggest you download it from this location. (I’m not taking any credit for this startup code, but I have used it to get cracking at the effects myself. Once you understand how the existing code works it is easy to create your own effects.)

This also concludes the blog post series on the Arduino LED cube. Should you use this to build one yourself and to create your own special effects, please leave a comment with a link to some photos or video material. I’m always curious to see what everyone else is up to. Have fun!