diff options
Diffstat (limited to 'instructables/cube8/main.c')
-rw-r--r-- | instructables/cube8/main.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/instructables/cube8/main.c b/instructables/cube8/main.c new file mode 100644 index 0000000..be31861 --- /dev/null +++ b/instructables/cube8/main.c @@ -0,0 +1,285 @@ +/* + * Code to controll an 8x8x8 ledcube using avr + * http://www.instructables.com/id/Led-Cube-8x8x8/ + * See lisence.txt for lisence. + */ +#include "main.h" +#include "effect.h" +#include "launch_effect.h" +#include "draw.h" + +// Main loop +// the AVR enters this function at boot time +int main (void) +{ + + // This function initiates IO ports, timers, interrupts and + // serial communications + ioinit(); + + // This variable specifies which layer is currently being drawn by the + // cube interrupt routine. We assign a value to it to make sure it's not >7. + current_layer = 1; + + int i; + + // Boot wait + // This function serves 3 purposes + // 1) We delay starting up any interrupts, as drawing the cube causes a lot + // noise that can confuse the ISP programmer. + // 2) Listen for button press. One button means go into rs232 mode, + // The other means go into autonomous mode and start doing stuff. + // 3) Random seed. The bootwait function counts forever from 0 to 255. + // Whenever you press the button, this counter stops, and the number it + // stopped at is used as a random seed. This ensures true randomness at + // every boot. Without this (or some similar process) the cube would + // produce the same "random" sequence every time + i = bootwait(); + + // Enable interrupts + // This starts the routine that draws the cube content + sei(); + + // Result for bootwait() is 2: + // Go to rs232 mode. this function loops forever. + if (i == 2) + { + rs232(); + } + + // Result of bootwait() is something other than 2: + // Do awesome effects. Loop forever. + while (1) + { + // Show the effects in a predefined order + for (i=0; i<EFFECTS_TOTAL; i++) + launch_effect(i); + + // Show the effects in a random order. + // Comment the two lines above and uncomment this + // if you want the effects in a random order. + //launch_effect(rand()%EFFECTS_TOTAL); + } + +} + +/* + * Multiplexer/framebuffer routine + * This function is called by an interrupt generated by timer 2. + * Every time it runs, it does the following: + * 1) Disable the output for the multiplexer array. + * 2) Turn of all layers. + * 3) Load the current layer from the cube buffer onto the + * multiplexer array. + * 4) Enable output from the multiplexer array. + * 5) Turn on the current layer. + * 6) Increment the current_layer variable, so the next layer is + * drawn the next time this function runs. +*/ + +ISR(TIMER2_COMP_vect) +{ + int i; + + LAYER_SELECT = 0x00; // Turn all cathode layers off. (all transistors off) + OE_PORT |= OE_MASK; // Set OE high, disabling all outputs on latch array + + // Loop through all 8 bytes of data in the current layer + // and latch it onto the cube. + for (i = 0; i < 8; i++) + { + // Set the data on the data-bus of the latch array. + PORTA = cube[current_layer][i]; + // Increment the latch address chip, 74HC138, to create + // a rising edge (LOW to HIGH) on the current latch. + LATCH_ADDR = (LATCH_ADDR & LATCH_MASK_INV) | (LATCH_MASK & (i+1)); + } + + OE_PORT &= ~OE_MASK; // Set OE low, enabling outputs on the latch array + LAYER_SELECT = (0x01 << current_layer); // Transistor ON for current layer + + // Increment the curren_layer counter so that the next layer is + // drawn the next time this function runs. + current_layer++; + // We want to count from 0-7, so set it to 0 when it reaches 8. + if (current_layer == 8) + current_layer = 0; +} + + +void ioinit (void) +{ + DDRA = 0xff; // DATA bus output + DDRB = 0xef; // Button on B4 + DDRC = 0xff; // Layer select output + DDRD = 0xdf; // Button on D5 + + + PORTA = 0x00; // Set data bus off + PORTC = 0x00; // Set layer select off + PORTB = 0x10; // Enable pull up on button. + PORTD = 0x20; // Enable pull up on button. + + + // Timer 2 + // Frame buffer interrupt + // 14745600/128/11 = 10472.72 interrupts per second + // 10472.72/8 = 1309 frames per second + OCR2 = 10; // interrupt at counter = 10 + TCCR2 |= (1 << CS20) | (1 << CS22); // Prescaler = 128. + TCCR2 |= (1 << WGM21); // CTC mode. Reset counter when OCR2 is reached. + TCNT2 = 0x00; // initial counter value = 0; + TIMSK |= (1 << OCIE2); // Enable CTC interrupt + + + + // Initiate RS232 + // USART Baud rate is defined in MYUBRR + UBRRH = MYUBRR >> 8; + UBRRL = MYUBRR; + // UCSRC - USART control register + // bit 7-6 sync/ascyn 00 = async, 01 = sync + // bit 5-4 parity 00 = disabled + // bit 3 stop bits 0 = 1 bit 1 = 2 bits + // bit 2-1 frame length 11 = 8 + // bit 0 clock polarity = 0 + UCSRC = 0b10000110; + // Enable RS232, tx and rx + UCSRB = (1<<RXEN)|(1<<TXEN); + UDR = 0x00; // send an empty byte to indicate powerup. + + +} + +// Boot wait function +// This function does 3 things: +// 1) Delay startup of interrupt. I've had some problems with in circuit +// serial programming when the cube was running. I guess switching all +// those LEDs on and off generates some noise. +// 2) Set a random random seed based on the delay between boot time and +// the time you press a button. +// 3) Select mode of operation, autonomous or rs232 controlled. +unsigned int bootwait (void) +{ + // All the LED_PORT... code blinks the red and green status LEDs. + + unsigned int x = 0; + LED_PORT |= LED_GREEN; + while (1) + { + x++; // increment x by one. + srand(x); // use counter x as random seed + + delay_ms(1000); + LED_PORT &= ~LED_GREEN; // green off, red on + LED_PORT |= LED_RED; + + // Listen for button presses and return with the + // apropriate number. + if (!(PIND & RS232_BTN)) + return 2; + + if (!(PINB & MAIN_BTN)) + return 1; + + delay_ms(1000); + LED_PORT &= ~LED_RED; // red off, green on + LED_PORT |= LED_GREEN; + + // Same as above. I do it twise because there are two delays + // in this loop, used for the red and green led blinking.. + if (!(PIND & RS232_BTN)) + return 2; + + if (!(PINB & MAIN_BTN)) + return 1; + } +} + +// Take input from a computer and load it onto the cube buffer +void rs232(void) +{ + int tempval; + int x = 0; + int y = 0; + int escape = 0; + + while (1) + { + // Switch state on red LED for debugging + // Should switch state every time the code + // is waiting for a byte to be received. + LED_PORT ^= LED_RED; + + // Wait until a byte has been received + while ( !(UCSRA & (1<<RXC)) ); + + // Load the received byte from rs232 into a buffer. + tempval = UDR; + + // Uncommet this to echo data back to the computer + // for debugging purposes. + //UDR = tempval; + + // Every time the cube receives a 0xff byte, + // it goes into sync escape mode. + // if a 0x00 byte is then received, the x and y counters + // are reset to 0. This way the x and y counters are + // always the same on the computer and in the cube. + // To send an 0xff byte, you have to send it twice! + + // Go into sync escape mode + if (tempval == 0xff) + { + // Wait for the next byte + while ( !(UCSRA & (1<<RXC)) ); + // Get the next byte + tempval = UDR; + + // Sync signal is received. + // Reset x and y counters to 0. + if (tempval == 0x00) + { + x = 0; + y = 0; + escape = 1; + } + // if no 0x00 byte is received, proceed with + // the byte we just received. + } + + if (escape == 0) + { + // Load data into the current position in the buffer + fb[x][y] = tempval; + + // Check if we have reached the limits of the buffer array. + if (y == 7) + { + if (x == 7) + { + // All data is loaded. Reset both counters + y = 0; + x = 0; + // Copy the data onto the cube. + tmp2cube(); + } else + { + // A layer is loaded, reset y and increment x. + x++; + y = 0; + } + } else + { + // We are in the middle of loading a layer. increment y. + y++; + } + + } else + { + escape = 0; + } + } +} + + |