/************************************************************************************
Charliecube
Controls 3 x 3 x 3 LED cube that uses charlieplexing
It needs 6 (!) I/O ports, so the reset pin is disabled. ISP is therefor not working.
The internal clock oscillator is used at 4.8 Mhz
The fuses have to be set accordingly
************************************************************************************/
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
volatile uint8_t LED_control[27] = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0};
// array of LEDs, 0 means LED off
// always in RAM -> also visible in INT
volatile uint8_t current_LED_A; // current active charlieplexing anode (2^0-5)
volatile uint8_t current_LED_K; // current active charlieplexing cathode (2^0-5)
volatile uint8_t current_LED; // current active LED (0-26)
uint8_t current_delay = 2;
#ifndef F_CPU
#warning "F_CPU was undefined, now set to 4800000"
#define F_CPU 4800000UL // system clock in Hz
#endif
/************************************************************************************
timer OVF interrupt
timer has 8 bits and generates the multiplexing timing
************************************************************************************/
ISR (TIM0_COMPA_vect)
{
PORTB = 0; // Reset port contents, all ports 0, no pull-ups
DDRB = 0; // All pins Inputs
current_LED++;
if (current_LED > 26)
{
current_LED = 0;
current_LED_A = 1;
current_LED_K = 2;
}
else
{
current_LED_K = current_LED_K << 1;
if (current_LED_K == current_LED_A)
{
current_LED_K = current_LED_K << 1;
}
if ((current_LED_K > 32) || ((current_LED_K > 16) && (current_LED_A > 4)))
{
current_LED_K = 1;
current_LED_A = current_LED_A << 3;
}
if (current_LED_A > 32)
{
current_LED_A = current_LED_A >> 5;
}
}
// Set IO pins according to current anode / cathode activation counters
if (LED_control[current_LED] != 0)
{
DDRB = DDRB | current_LED_A;
DDRB = DDRB | current_LED_K;
PORTB = PORTB | current_LED_A;
}
}
/************************************************************************************
// device initialisation
************************************************************************************/
void ioinit (void)
{
/* timer 1 init; the controller runs at 8MHz
For the servos a PWM frequency of 50Hz/20ms is needed. The pulse width is between
1ms and 2ms. There shall be more than 100 steps between 1ms and 2ms, so a resolution of
10盜 must be implemented. This means the counter must run at 100kHz. This is
4MHz / 40. The closest prescaler value is 8, resulting in 500kHz or an 2盜
resolution.
*/
TCCR0A = (1 <<WGM01); // CTC mode, no OC pins are used
TCCR0B = (1 <<CS01) | (1 << CS00); // CTC mode, divide system clock by 64
// so the timer clock is about 75000 Hz
OCR0A = 25; // count to 25
// so the interrupt frequency is about 3000 Hz
// this gives us 1600 cycles per INT
// the matrix has 27 LEDs, so the frame rate
// is 1000 / 27 = 111 Hz
TIMSK = (1 <<OCIE0A); // enable OCIE0A interrupt
PORTB = 0; // Reset port contents, all ports 0, nu pull-ups
DDRB = 0; // All pins Inputs
sei();
}
/************************************************************************************
main routine
************************************************************************************/
void clear_all(void);
void set_level(uint8_t level);
void delay_100ms(uint8_t duration);
void delay_uniform(void);
void set_pattern(uint8_t start, uint8_t LED_on, uint8_t LED_off, uint8_t repeat);
void set_pattern_plus(uint8_t start, uint8_t LED_on, uint8_t LED_off, uint8_t repeat);
void set_cube(uint8_t position);
void set_column(uint8_t position);
int main (void)
{
uint8_t i;
uint8_t j;
current_LED_A = 33;
current_LED_K = 33;
current_LED = 33;
ioinit();
while (1) // loop forever
{
// Rolling plane
current_delay = 2;
for ( i=0; i<4; i++)
{
set_pattern_plus(0, 3, 9, 3);
set_pattern_plus(3, 3, 6, 3);
set_pattern_plus(6, 3, 3, 3);
set_pattern_plus(9, 9, 0, 1);
}
// 3D checker board & inverted
current_delay = 5;
for ( i=0; i<4; i++)
{
set_pattern_plus(0, 1, 1, 14);
set_pattern_plus(1, 1, 1, 14);
}
delay_uniform();
/*
// every single LED (good for HW tests)
clear_all();
for ( i = 0; i < 27; i++ )
{
LED_control[i] = 1;
delay_100ms(1);
LED_control[i] = 0;
}
*/
// cycle through planes (xy)
current_delay = 2;
for ( i=0; i<3; i++)
{
set_pattern_plus(0, 9, 0, 1);
set_pattern_plus(9, 9, 0, 1);
set_pattern_plus(18, 9, 0, 1);
// cycle through planes (xz)
set_pattern_plus(0, 3, 6, 3);
set_pattern_plus(3, 3, 6, 3);
set_pattern_plus(6, 3, 6, 3);
// cycle through planes (yz)
set_pattern_plus(0, 1, 2, 9);
set_pattern_plus(1, 1, 2, 9);
set_pattern_plus(2, 1, 2, 9);
}
// a circling column
current_delay = 2;
for ( i=0; i<3; i++)
{
// activate columns
set_column(0);
set_column(1);
set_column(2);
set_column(5);
set_column(8);
set_column(7);
set_column(6);
set_column(3);
}
// activate all LEDs
current_delay = 8;
clear_all();
LED_control[0] = 1;
delay_uniform();
set_cube(0);
set_pattern_plus(0,27,0,1);
set_cube(13);
clear_all();
LED_control[26] = 1;
delay_uniform();
// cube in cube
current_delay = 4;
for ( i=0; i<2; i++)
{
set_cube(0);
set_cube(9);
set_cube(10);
set_cube(1);
set_cube(4);
set_cube(13);
set_cube(12);
set_cube(3);
}
/*
// 3 middle plains
current_delay = 10;
clear_all();
set_pattern(9, 9, 0, 1);
delay_uniform();
set_pattern(3, 3, 6, 3);
delay_uniform();
set_pattern(1, 1, 2, 9);
delay_uniform();
*/
// propeller
current_delay = 1;
for ( j=0; j<3; j++)
{
uint8_t layer;
layer=j*9;
for ( i=0; i<5; i++)
{
set_pattern_plus(0+layer,1,3,3);
set_pattern_plus(1+layer,1,2,3);
set_pattern_plus(2+layer,1,1,3);
set_pattern_plus(3+layer,1,0,3);
}
}
};
return 0;
}
/************************************************************************************
support routines for LED manipulation
************************************************************************************/
// clear all LEDs
void clear_all(void)
{
uint8_t i = 0;
/* clear all elements of array 'LED_control' to 0 */
for ( i = 0; i < 27; i++ )
{
LED_control[i] = 0;
}
}
/************************************************************************************
universal LED manipulation rotine for 3D patterns
************************************************************************************/
void set_pattern_plus(uint8_t start, uint8_t LED_on, uint8_t LED_off, uint8_t repeat)
{
clear_all();
set_pattern(start, LED_on, LED_off, repeat);
delay_uniform();
}
void set_pattern(uint8_t start, uint8_t LED_on, uint8_t LED_off, uint8_t repeat)
{
uint8_t i = 0;
uint8_t j = 0;
uint8_t current_LED = start;
for ( i = repeat; i > 0; i-- ) // repeat several times
{
for ( j = LED_on; j > 0; j--) // set zero or more consecutive LEDs
{
if (current_LED < 27)
{
LED_control[current_LED++] = 1;
}
}
current_LED = current_LED + LED_off;
}
}
void set_column(uint8_t position)
{
set_pattern_plus(position,1,8,3);
}
void set_cube(uint8_t position)
{
clear_all();
set_pattern (position,2,1,2);
set_pattern (position+9,2,1,2);
delay_uniform();
}
// delay generator
void delay_100ms(uint8_t duration)
{
uint8_t i = 0;
for ( i = 0; i < duration; i++ )
{
_delay_ms(100);
}
}
void delay_uniform(void)
{
uint8_t i = 0;
for ( i = 0; i < current_delay; i++ )
{
_delay_ms(100);
}
}
// CharlieCube.c
// Displaying CharlieCube.c.