LED Cube/Custom Programs

Aus Wiki CCC Göttingen
Zur Navigation springen Zur Suche springen
LED Cube Series

Chapter 1: Build

Section 0: Preparation and Requirements

Section 1: PCB Soldering Manual

Section 2: Cube Soldering Manual

Section 3: Assembly and Testing

Chapter 2: Play

Using the Cube

Creating an Animation

Chapter 3: Learn

Understanding the Electronics

Custom Programs

This page introduces you to the software section of the LED Cube. It specifically shows you how you can write your own programs, for example if you do not want to use the pattern program or require additional functionality. This page assumes some basic knowledge about the C programming language and how to use a shell. It does not assume knowledge about the microcontroller architecture or the toolchain.

However, we are not taking care about how this works with Windows. If you want to use Windows anyway, you can figure it out by yourself (yes, it is possible) or get a decent operating system.

Requirements[Bearbeiten]

First of all, you need an AVR Toolchain. You can skip installing avrdude if you do not have a programmer and want to update the program using the bootloader, which is probably what you want. This provides you with

  • avr-gcc: The C compiler for AVR microcontrollers
  • avr-libc: The C standard library for AVR microcontrollers
  • avr-binutils: Various tools for manipulating and working with outputs from avr-gcc

Also, you need the bootloader utility. To get this, you should follow the section Building the Bootloader Application from Building From Source. This time you can skip this step if you are using an external programmer. But this is probably not what you want. After following that step, you have an application called bootloadHID. This is used to update the firmware of your cube.

Getting Started[Bearbeiten]

So, let's now create a first program. This will not do very much: It will simply light up the center LED of the cube - all the time. Create an empty folder, in this folder create a file led-cube.c and paste the following code into it:

// This includes the definitions for DDRC, DDRD, PC4, PD6, PORTC, PORTD:
#include <avr/io.h>

int main(void)
{
	DDRC |= (1 << PC4);	// {1}
	DDRD |= (1 << PD6);	// {2}

	PORTC |= (1 << PC4);	// {3}
	PORTD |= (1 << PD6);	// {4}

	while(1);		// {5}
}

First of all some explanation. The line numbers on the side are the same as the indices in the following enumeration:

  1. This first line of code is already quite interesting for someone who never used a microcontroller. If you read the LED Cube/Understanding The Electronics page, you know that the microcontroller contained the switches we talked about. The situation is actually much more complicated. The microcontroller is not only able to connect a pin to the positive supply voltage or nothing, it is also able to connect it to the negative pole. We will call the positive supply voltage Vcc and the negative GND (pronounced Ground).
    Now, DDRC is a "register". This means that it has a fixed address in the memory of the microcontroller and is somewhat different from other memory regions. Also, it one byte or 8 bits wide. The difference of this register in particular is, that its value controls how the pins of the microcontroller behave. When a bit is zero, its corresponding switch is open. When a bit is one, a switch controlled by this bit of this register is closed and connect the pin to either GND or Vcc.
    Which pin (or which switch that is connected to a specific pin) is controlled, is labeled by the bit number. Or, in the case of this command, 'PC4' is the pin that will have a closed switch afterwards.
  2. This is basically the same. DDRD is like DDRC, but as DDRC is only a byte wide, we have to have several of those DDRx-Registers to control all output pins of the microcontroller. In this case we are closing the switch for pin PD6.
  3. This line very much resembles line 1. But instead of DDRC, we see a PORTC. We already know that DDRC closes the switch to Vcc or GND, but we do not know to which of them: This decides this. If a bit in PORTx is 1, the corresponding switch is closed to Vcc. If it is 0, the switch is closed to GND. So, this command sets the PC4-st pin of PORTC to 1 and thus the pin PC4 is now connected to Vcc. This is what we wanted, as we have to connect the column to Vcc to light up an LED in the column.
  4. It remains to connect the plane to GND, to make the LED light up. However, in this line, the pin PD6 is, analogously to the previous line, also connected to Vcc. For this to make sense, remember that the switches which connect the planes to ground are separated from the microcontroller in the transistor array. So this pin merely signals the transistor array to switch a plane to ground, not directly connects the plane to ground. And the transistor array only switches to ground, if its input pins are high.
  5. Just running while (1); might be a little bit confusing, but it actually makes sense here. The program will be locked in this infinte loop and if you remember, this is what we want: We simply want to switch on one single LED and then wait forever.

So, enough explanation, let's get this program to run. First, we need to compile it:

avr-gcc -DF_CPU=16000000UL -mmcu=atmega8 led-cube.c -o led-cube.elf

This calls the AVR-GCC C compiler. There are two interesting parameters:

  • -DF_CPU=16000000UL defines a constant F_CPU, which is required by some functions of avr-libc and we will simply include it all the time. It defines the clock frequency at which the CPU runs. (You can not change this value! This is merely a way to tell the microcontroller what crystal oscillator is connected. If you change the crystal oscillator, however, you have to update this value, of course.)
  • -mmcu=atmega8 informs the compiler which specific microcontroller will be used. This is important as there are some subtle differences between them.

Now, hopefully, we are left with a binary led-cube.elf. This is nice, but still not really what we need. ELF files are specifically designed to be loaded dynamically, for example like a desktop computer loads programs. We actually need the program in a form so we do not have to do anything. This preparatory step is performed by avr-objcopy:

avr-objcopy -R .eeprom -O ihex -v led-cube.elf led-cube.hex

This command basically performs what the ELF loader does on linux. The result is a Intel HEX File, which is not much more than a copy of the memory after the program has been loaded. You can actually have a look at this file, it looks like this (Yours will probably be more messy, but if you add a bunch of additional parameters to the previous commands, we remove additional useless code, and the end result is quite small):

:1000000012C019C018C017C016C015C014C013C044
:1000100012C011C010C00FC00EC00DC00CC00BC06C
:100020000AC009C008C011241FBECFE5D4E0DEBF5E
:10003000CDBF02D006C0E4CFA49A8E9AAC9A969A0D
:06004000FFCFF894FFCF92
:00000001FF

And that's it! The whole program to light up one LED on your cube. But, this still is not what we intended. We finally want to see an LED light up, now! This is done with the bootloadHID command from last time. But first, remember to set the bootloader jumper correctly. If you look at the bootloader Jumper without having to look through the cube, it has to be on the left position. Then reconnect your cube to your PC and write the hex file to it:

/<path-to-bootloadhid>/bootloadHID -r led-cube.hex

And voilà, you should be able to see a single LED light up! Maybe the program will throw some errors, these can probably be fixed by adding a correct udev rule for the device or prepend sudo to the command:

sudo /<path-to-bootloadhid>/bootloadHID -r led-cube.hex

Displaying a Whole Column[Bearbeiten]