diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..d50b0a3 --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,66 @@ +# Mini-LED-Cube 1.0 +# +# Copyright (C) 2009 Paul Wilhelm +# http://mosfetkiller.de/?s=miniledcube + +# Project specific settings +TARGET = miniledcube-1.0 +MCU = attiny2313 +SRC = main.c + +# You probably want to change this to your own programming device + +# AVR ISP mkII +PGMDEV = avrispmkII +PGMOPT = -P usb # Try -B 10 in case of programming errors + +# Pony-STK200 +#PGMDEV = pony-stk200 +#PGMOPT = -E noreset + +# AVR-GCC and AVRDUDE need to be installed +CC = avr-gcc +OBJCOPY = avr-objcopy +AVRDUDE = avrdude +REMOVE = rm -f + +# Some C flags +CSTANDARD = gnu99 +CFLAGS = -Wall -O2 + +help: + @echo + @echo "Availiable targets:" + @echo " help - Display this help" + @echo + @echo " compile - Compiles source code" + @echo " info - Outputs device memory information" + @echo " program - Programs the device" + @echo " clean - Deletes temporary files" + @echo " fuses - Writes fuse settings to device (necessary only once per device)" + @echo + @echo " all - Compile, info, program, clean" + @echo + @echo "IMPORTANT: Device programming may only be possible as super user" + @echo + @echo "See Makefile for contact information." + @echo + +all: compile info program clean + +compile: + @$(CC) -std=$(CSTANDARD) $(CFLAGS) -mmcu=$(MCU) $(SRC) -o $(TARGET).elf + @$(OBJCOPY) -O ihex -j .text -j .data $(TARGET).elf $(TARGET).hex + +info: + avr-size $(TARGET).elf + +program: + @$(AVRDUDE) -p $(MCU) -q -q -u -V -c $(PGMDEV) $(PGMOPT) -U flash:w:$(TARGET).hex:i + +fuses: + @$(AVRDUDE) -p $(MCU) -q -q -u -V -c $(PGMDEV) $(PGMOPT) -U lfuse:w:0xE4:m -U hfuse:w:0xDF:m + +clean: + @$(REMOVE) $(TARGET).elf + diff --git a/firmware/main.c b/firmware/main.c new file mode 100644 index 0000000..2d0cd39 --- /dev/null +++ b/firmware/main.c @@ -0,0 +1,280 @@ +// Mini-LED-Cube 1.0 +// +// Copyright (C) 2009 Paul Wilhelm +// http://mosfetkiller.de/?s=miniledcube + +// Interner RC-Oszillator, CKDIV8 Disabled +#define F_CPU 8000000UL + +// Includes +#include +#include +#include +#include + +// Bitpopelei +#define set_bit(var, bit) ((var) |= (1 << (bit))) +#define clear_bit(var, bit) ((var) &= (unsigned)~(1 << (bit))) + +// Bool +#define FALSE 0 +#define TRUE 1 + +// Definitionen +#define PIXEL_TON 30 +#define PIXEL_TOFF 10 + +// Cube-Array +unsigned char cube[3][3][3]; +unsigned char buffer[3][3][3]; // Framebuffer + +// Prototypen +void init(); + +// Programmzähler +unsigned int program_counter = 0; + +// Framezähler +volatile unsigned char frame_counter = 0, fps = 0; + + +// Pixelmakros +#define PSET(x,y,z) (0b01000000 | ((z * 3 + x) + y * 9)) +#define PCLEAR(x,y,z) (0b00000000 | ((z * 3 + x) + y * 9)) + +// Instructions +#define CLEAR 0b10000000 +#define SET 0b10010000 +#define FPS 0b10110000 +#define NEXT 0b11110000 + +// Variablen +#define VAR_FPS 0 + +// Für CLEAR und SET +#define CLEAR_ALL 0 +#define SET_ALL 0 + +// Für NEXT +#define JUMP_FORWARD 1 +#define JUMP_BACKWARD 2 + + +// Programmcode +const prog_char program_1[] = +{ + FPS, 10, + + CLEAR, SET + 12, NEXT, CLEAR, SET + 14, NEXT, // Vor- und zurück tanzen + CLEAR, SET + 12, NEXT, CLEAR, SET + 14, NEXT, + CLEAR, SET + 12, NEXT, CLEAR, SET + 15, NEXT, + CLEAR, SET + 12, NEXT, CLEAR, SET + 15, + + FPS, 5, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, // Umdrehung + FPS, 5, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + FPS, 6, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, + FPS, 6, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + FPS, 7, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, + FPS, 7, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + FPS, 8, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, + FPS, 8, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + FPS, 9, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, + FPS, 9, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + FPS, 10, CLEAR, SET + 8, NEXT, CLEAR, SET + 14, NEXT, + FPS, 10, CLEAR, SET + 2, NEXT, CLEAR, SET + 15, NEXT, + CLEAR, SET + 8, NEXT, CLEAR, SET + 12, NEXT, // Umfallen + CLEAR, SET + 4, NEXT, CLEAR, SET + 5, NEXT, CLEAR, SET + 6, NEXT, // Ebenen + CLEAR, SET + 1, NEXT, CLEAR, SET + 2, NEXT, CLEAR, SET + 3, NEXT, + CLEAR, SET + 7, NEXT, CLEAR, SET + 8, NEXT, CLEAR, SET + 9, NEXT, + + FPS, 10, // Außen langlaufen + + CLEAR, + PSET(0,0,0), PSET(1,0,0), PSET(0,1,0), PSET(1,1,0), PSET(0,2,0), PSET(1,2,0), NEXT, + PCLEAR(0,0,0), PSET(2,0,0), PCLEAR(0,1,0), PSET(2,1,0), PCLEAR(0,2,0), PSET(2,2,0), NEXT, + PCLEAR(1,0,0), PSET(2,0,1), PCLEAR(1,1,0), PSET(2,1,1), PCLEAR(1,2,0), PSET(2,2,1), NEXT, + PCLEAR(2,0,0), PSET(2,0,2), PCLEAR(2,1,0), PSET(2,1,2), PCLEAR(2,2,0), PSET(2,2,2), NEXT, + PCLEAR(2,0,1), PSET(1,0,2), PCLEAR(2,1,1), PSET(1,1,2), PCLEAR(2,2,1), PSET(1,2,2), NEXT, + PCLEAR(2,0,2), PSET(0,0,2), PCLEAR(2,1,2), PSET(0,1,2), PCLEAR(2,2,2), PSET(0,2,2), NEXT, + PCLEAR(1,0,2), PSET(0,0,1), PCLEAR(1,1,2), PSET(0,1,1), PCLEAR(1,2,2), PSET(0,2,1), NEXT, + PCLEAR(0,0,2), PSET(0,0,0), PCLEAR(0,1,2), PSET(0,1,0), PCLEAR(0,2,2), PSET(0,2,0), NEXT, + PCLEAR(0,0,1), PCLEAR(0,1,1), PCLEAR(0,2,1), + + SET + 7, NEXT, CLEAR, SET + 8, NEXT, CLEAR, SET + 9, NEXT, + CLEAR, SET + 8, NEXT, CLEAR, SET + 7, NEXT, +}; + +const prog_char *program_pointer = program_1; +unsigned int program_length = sizeof(program_1); + + +// Main +int main(void) +{ + // Initialisierung + init(); + + // Hauptschleife + while (1) + { + unsigned char instruction = pgm_read_byte(&(program_pointer[program_counter])); + + if ((instruction & 0b10000000) == 0) + // Pixel + { + unsigned char coord = instruction & 0b00111111; // Uns interessieren nur die 6 untersten Bits + + unsigned char y = coord / 9; + unsigned char x = (coord - y * 9) % 3; + unsigned char z = (coord - y * 9) / 3; + buffer[x][y][z] = (instruction & 0b01000000) / 0b01000000; + } + else + // Instruction + { + unsigned char operation = instruction & 0b11110000; // Uns interessieren nur die 4 obersten Bits + + if (operation == CLEAR || operation == SET) + { + unsigned char frame = instruction & 0b00001111; + + // Folgende Werte entsprechen SET_ALL + unsigned char x_min = 0, x_max = 3, y_min = 0, y_max = 3, z_min = 0, z_max = 3; + + // Folgendes kann noch optimiert werden. + + // Y-Z-Ebene (links, mitte, rechts) + if (frame == 1) { x_min = 0; x_max = 1; } + if (frame == 2) { x_min = 1; x_max = 2; } + if (frame == 3) { x_min = 2; x_max = 3; } + + // X-Z-Ebene (unten, mitte, oben) + if (frame == 4) { y_min = 0; y_max = 1; } + if (frame == 5) { y_min = 1; y_max = 2; } + if (frame == 6) { y_min = 2; y_max = 3; } + + // X-Y-Ebene (hinten, mitte, vorne) + if (frame == 7) { z_min = 0; z_max = 1; } + if (frame == 8) { z_min = 1; z_max = 2; } + if (frame == 9) { z_min = 2; z_max = 3; } + + if (frame < 10) + { + for (unsigned char z = z_min; z < z_max; z++) + for (unsigned char y = y_min; y < y_max; y++) + for (unsigned char x = x_min; x < x_max; x++) + if (operation == SET) buffer[x][y][z] = 1; else buffer[x][y][z] = 0; + } else + { + for (unsigned char a = 0; a < 3; a++) + for (unsigned char b = 0; b < 3; b++) + { + unsigned char x = 0, y = 0, z = 0; + + if (frame == 10) { x = a; y = b; z = b; } // Unten hinten nach oben vorne + if (frame == 11) { x = a; y = a; z = b; } // Unten links nach oben rechts + if (frame == 12) { x = a; y = b; z = 2 - b; } // Unten vorne nach oben hinten + if (frame == 13) { x = a; y = 2 - a; z = b; } // Oben links nach unten rechts + if (frame == 14) { x = b; y = a; z = b; } // Hinten links nach vorne rechts + if (frame == 15) { x = a; y = 2 - b; z = 2 - a; } // Vorne links nach hinten rechts + + if (operation == SET) buffer[x][y][z] = 1; else buffer[x][y][z] = 0; + } + } + } else + + if (operation == FPS) + { + if (program_counter + 1 < program_length) program_counter++; // else: Fehler + unsigned char byte = pgm_read_byte(&(program_pointer[program_counter])); + + fps = byte; + } else + + if (operation == NEXT) + { + // VAR_FPS = 0: Frame nicht zeichnen, keine Wartezeit + if (fps > 0) + { + // Temporäres Array ins "echte" Array kopieren + for (unsigned char z = 0; z < 3; z++) + for (unsigned char y = 0; y < 3; y++) + for (unsigned char x = 0; x < 3; x++) + cube[x][y][z] = buffer[x][y][z]; + + for (unsigned char i = 0; i < fps; i++) + { + _delay_ms(5); + } + } + } + + } + + // Programmzähler erhöhen, bzw. bei Erreichen des Programmendes wieder von vorn beginnen + if (program_counter + 1 < program_length) program_counter++; else program_counter = 0; + } +} + + +// Initialisierung +void init() +{ + // Ports vorbereiten + DDRB = 0b11111111; // PB0-PB7: LED 1-8 (Kathoden) + PORTB = 0b11111111; // HIGH + + DDRD = 0b1111000; // PD6: LED 9 (Kathode); PD5-PD3: A-C (Anoden) + PORTD = 0b1000000; + + // Timer-Interrupt "TIMER1" vorbereiten + cli(); + + set_bit(TIMSK, OCIE1A); + set_bit(TCCR1B, WGM12); + + // Animations-Geschwindigkeit + OCR1AH = 0x01; + OCR1AL = 0x00; + + clear_bit(TCCR1B, CS12); // Prescaler 64 + set_bit(TCCR1B, CS11); + set_bit(TCCR1B, CS10); + + sei(); +} + + +// Interruptvektor von TIMER1 +SIGNAL(SIG_OUTPUT_COMPARE1A) +{ + // Pixel multiplexen + for (unsigned char z = 0; z < 3; z++) + { + for (unsigned char y = 0; y < 3; y++) + { + for (unsigned char x = 0; x < 3; x++) + { + unsigned char n = z * 3 + x; + + // LED an + if (cube[x][y][z] == 1) + { + if (n < 8) clear_bit(PORTB, n); else clear_bit(PORTD, 6); + set_bit(PORTD, y + 3); + } + // ON-Time + for (unsigned long i = 0; i < PIXEL_TON; i++) { asm volatile("nop"::); } + + // LED aus + if (cube[x][y][z] == 1) + { + clear_bit(PORTD, y + 3); + if (n < 8) set_bit(PORTB, n); else set_bit(PORTD, 6); + } + // OFF-Time + for (unsigned long i = 0; i < PIXEL_TOFF; i++) { asm volatile("nop"::); } + } + } + } +}