1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
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;
}
}
}
|