|
LED matrix schematic |
|
Arduino Nano connections (jumpered, no soldering)
|
1: // http://orpex.blogspot.com
2: // ATmega168
3: // PIN DEFINITIONS:
4: // PB1-4 ROW DRIVERS (0-3)
5: // PC0-5,PD2-3: COLUMN DRIVERS (0-7)
6: // PD7 BUTTON
7: #define F_CPU 14745600
8: #define ROWS 4
9: #define COLS 16
10: #include <stdio.h>
11: #include <inttypes.h>
12: #include <avr/io.h>
13: #include <avr/interrupt.h>
14: #include <avr/pgmspace.h>
15: #include <util/delay.h>
16: volatile uint8_t la_row, real_row;
17: volatile uint8_t la_data[COLS];
18: inline uint8_t ledarray_get(uint8_t i, uint8_t j)
19: {
20: if(i < ROWS && j < COLS)
21: {
22: if((la_data[j] & (1<<i)) != 0)
23: {
24: return 1;
25: }
26: }
27: return 0;
28: }
29: inline void ledarray_set(uint8_t i, uint8_t j, uint8_t onoff)
30: {
31: if(i < ROWS && j < COLS)
32: {
33: if(onoff)
34: {
35: la_data[j] |= (1<<i);
36: }
37: else
38: {
39: la_data[j] &= ~(1<<i);
40: }
41: }
42: }
43: //sense variable indicates direction of LED: sense == 1 indicates COL wire must be
44: //high for LED to turn on. sense == 0, COL wire must be low to turn LED on
45: inline void ledarray_set_columndriver(uint8_t j, uint8_t onoff, uint8_t sense)
46: {
47: // cols 0-5: PC0-5
48: // cols 6-7: PD2-3
49: if(j < 6)
50: {
51: if(onoff)
52: { //led on
53: DDRC |= (1 << (PC0 + j));
54: if(sense)
55: {
56: PORTC |= (1 << (PC0 + j));
57: }
58: else
59: {
60: PORTC &= ~(1<< (PC0 + j));
61: }
62: }
63: else
64: { // led off, pins to high impedance
65: DDRC &= ~(1 << (PC0 + j));
66: PORTC &= ~(1 << (PC0 + j));
67: }
68: }
69: else
70: {
71: if(onoff)
72: { //led on
73: DDRD |= (1 << (PD2 + (j-6)));
74: if(sense)
75: {
76: PORTD |= (1 << (PD2 + (j-6)));
77: }
78: else
79: {
80: PORTD &= ~(1 << (PD2 + (j-6)));
81: }
82: }
83: else
84: { // led off, pins to high impedance
85: DDRD &= ~(1 << (PD2 + j));
86: PORTD &= ~(1 << (PD2 + j));
87: }
88: }
89: }
90: inline void ledarray_all_off()
91: {
92: // turn off all row drivers
93: DDRB &= ~( (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) );
94: PORTB &= ~( (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) );
95: // turn off all column drivers
96: DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
97: PORTC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
98: DDRD &= ~( (1<<PD2) | (1<<PD3) );
99: PORTD &= ~( (1<<PD2) | (1<<PD3) );
100: }
101: SIGNAL(SIG_OVERFLOW0)
102: {
103: // turn off old row driver
104: DDRB &= ~(1 << (PB1 + real_row));
105: PORTB &= ~(1 << (PB1 + real_row));
106: ledarray_all_off();
107: // increment row number
108: if (++la_row == 2*ROWS) la_row = 0;
109: // set column drivers appropriately
110: uint8_t j;
111: if (la_row%2 == 0)
112: {
113: // even la_row number: fill even columns
114: real_row = la_row / 2;
115: for(j=0; j<COLS/2; j++)
116: {
117: ledarray_set_columndriver(j, ledarray_get(real_row, 2*j), 1);
118: }
119: // activate row driver SINK
120: PORTB &= ~(1 << (PB1 + real_row));
121: DDRB |= (1 << (PB1 + real_row));
122: }
123: else
124: {
125: // odd la_row number: fill odd columns
126: real_row = (la_row-1)/2;
127: for(j=0; j<COLS/2; j++)
128: {
129: ledarray_set_columndriver(j, ledarray_get(real_row, 2*j + 1), 0);
130: }
131: // activate row driver SOURCE
132: PORTB |= (1 << (PB1 + real_row));
133: DDRB |= (1 << (PB1 + real_row));
134: }
135: }
136: void ledarray_init()
137: {
138: // Timer0 CK/64 (900Hz)
139: TCCR0B = (1<<CS01) | (1<<CS00);
140: TIMSK0 = (1<<TOIE0);
141: // outputs (set row drivers high for off)
142: DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
143: DDRD &= ~( (1<<PD2) | (1<<PD3) );
144: DDRB &= ~( (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) );
145: }
146: void powerup_sequence()
147: {
148: uint8_t j;
149: for(j=0; j<COLS; j++)
150: {
151: ledarray_set(0, j, 1);
152: _delay_ms(262);
153: }
154: }
155: void back_inner_c()
156: {
157: uint8_t j;
158: for(j=0; j<COLS; j++)
159: {
160: ledarray_set(1, j, 1);
161: }
162: }
163: void ledarray_testpattern()
164: {
165: uint8_t i, j;
166: for(i=0;i<ROWS;i++)
167: {
168: for(j=0;j<COLS;j++)
169: {
170: ledarray_set(i,j, 0);
171: }
172: }
173: while (1)
174: {
175: for(i=0;i<ROWS;i++)
176: {
177: for(j=0;j<COLS;j++)
178: {
179: ledarray_set(i,j, 1 - ledarray_get(i,j));
180: _delay_ms(60);
181: }
182: }
183: }
184: }
185: int main()
186: {
187: ledarray_init();
188: // activate interrupts
189: sei();
190: DDRD &= ~(1<<PD7); // set PD7 as input
191: PORTD |= (1<<PD7); // turn on internal pull up resistor for PD7
192: // if we don't incur a delay here we read the last state of PD7
193: _delay_ms(150);
194: if (PIND & (1<<PD7))
195: {
196: powerup_sequence();
197: _delay_ms(1049);
198: back_inner_c();
199: }
200: else
201: {
202: ledarray_testpattern();
203: }
204: while (1)
205: {
206: while (PIND & (1<<PD7))
207: {
208: // loop waiting for button press
209: }
210: _delay_ms(150);
211: while (!(PIND & (1<<PD7)))
212: {
213: // loop waiting for button release
214: }
215: // blade activation
216: uint16_t j, r = 4900,cyc=0;
217: for(j=0; j<COLS; j++)
218: {
219: // all on
220: ledarray_set(2, j, 1);
221: ledarray_set(3, j, 1);
222: }
223: _delay_ms(300);
224: while ((PIND & (1<<PD7)))
225: {
226: // loop waiting for button press
227: for(j=0; j<COLS; j++)
228: {
229: ledarray_set(2, j, (j%3!=cyc));
230: ledarray_set(3, j, (j%3!=cyc));
231: }
232: _delay_us(r);
233: if (++r > 5300) r = 5300;
234: if (++cyc==3) cyc=0;
235: }
236: // blade deactivation
237: for(j=0; j<COLS; j++)
238: {
239: // all off
240: ledarray_set(2, j, 0);
241: ledarray_set(3, j, 0);
242: }
243: _delay_ms(300);
244: while (!(PIND & (1<<PD7)))
245: {
246: // loop waiting for button release
247: }
248: }
249: return 0;
250: }
Nice work. I'm going to start prepping my disc next week, cutting the plastic out and what not. Excited to give this a shot.
ReplyDeleteI'm assuming that row 3 and 4 are for each of the inner rings?
I actually just posted the code - in that version I was using row 1 (index 0) for everything since that is all I had wired up at that point. Row 1 is the front inner C, row 2 is the back inner C (if installed), row 3 is the right half of the blade starting at the index hole, row 4 is the left half of the blade starting where row 3 left off.
ReplyDeleteSo I used a Dremel with a cut-off disc to remove the excess plastic. On the back half of the disc I left the screw posts (and about a eighth of an inch of the "tripod" plastic around it for support) that attaches the front half and the blade index hole as a guide. I left the top of the disc mostly unmodified (speaker holder and the button mount). The battery compartment was fun...
ReplyDelete