1 /* |
1 /* |
2 * Control the JY-LKM1638 LED display module. |
2 * Control the JY-LKM1638 LED display module. |
3 */ |
3 */ |
4 |
4 |
5 #include <avr/io.h> |
5 #include <avr/io.h> |
|
6 #include <util/delay.h> |
6 |
7 |
7 #include "stdlib.h" |
8 #include "stdlib.h" |
8 |
9 |
9 // XXX |
10 // XXX |
10 #include "timer.c" |
11 #include "timer.c" |
11 |
12 |
12 #define LKM_DDR DDRC |
13 #define LKM_DDR DDRC |
13 #define LKM_PORT PORTC |
14 #define LKM_PORT PORTC |
|
15 #define LKM_PIN PINC |
14 #define LKM_CLK 0 |
16 #define LKM_CLK 0 |
15 #define LKM_DIO 1 |
17 #define LKM_DIO 1 |
16 #define LKM_STB 2 |
18 #define LKM_STB 2 |
17 |
19 |
18 enum lkm_cmd { |
20 enum lkm_cmd { |
19 LKM_CMD_DATA = 0b01000000, |
21 LKM_CMD_DATA = 0b01000000, |
20 LKM_CMD_CONTROL = 0b10000000, |
22 LKM_CMD_CONTROL = 0b10000000, |
21 LKM_CMD_ADDRESS = 0b11000000, |
23 LKM_CMD_ADDRESS = 0b11000000, |
22 |
24 |
23 LKM_DATA_WRITE = 0b00000010, |
25 LKM_DATA_WRITE = 0b00000000, |
24 LKM_DATA_READ = 0b00000000, |
26 LKM_DATA_READ = 0b00000010, |
25 |
27 |
26 LKM_DATA_ADDR_AUTO = 0b00000000, |
28 LKM_DATA_ADDR_AUTO = 0b00000000, |
27 LKM_DATA_ADDR_FIXED = 0b00000100, |
29 LKM_DATA_ADDR_FIXED = 0b00000100, |
28 |
30 |
29 LKM_DATA_TEST_NORMAL = 0b00000000, |
31 LKM_DATA_TEST_NORMAL = 0b00000000, |
60 |
62 |
61 enum { |
63 enum { |
62 LKM_DISPLAY_DOT = 0b10000000, |
64 LKM_DISPLAY_DOT = 0b10000000, |
63 }; |
65 }; |
64 |
66 |
|
67 enum { |
|
68 LKM_LED_OFF = 0b00, |
|
69 LKM_LED_RED = 0b01, |
|
70 LKM_LED_GREEN = 0b10, |
|
71 LKM_LED_ORANGE = 0b11, |
|
72 }; |
|
73 |
|
74 /* |
|
75 * Button bitfields. |
|
76 * |
|
77 * The JY-LKM1638 uses K3 |
|
78 */ |
|
79 enum { |
|
80 LKM_BUTTON_K1L = 0b00000100, |
|
81 LKM_BUTTON_K2L = 0b00000010, |
|
82 LKM_BUTTON_K3L = 0b00000001, |
|
83 |
|
84 LKM_BUTTON_K1H = 0b01000000, |
|
85 LKM_BUTTON_K2H = 0b00100000, |
|
86 LKM_BUTTON_K3H = 0b00010000, |
|
87 }; |
|
88 |
65 void lkm_init () |
89 void lkm_init () |
66 { |
90 { |
67 // strobe off: high output |
91 // strobe off: high output |
68 // XXX: should use an external pull-up resistor? |
92 // XXX: should use an external pull-up resistor? |
69 sbi(&LKM_PORT, LKM_STB); |
93 sbi(&LKM_PORT, LKM_STB); |
92 // select on: low |
116 // select on: low |
93 cbi(&LKM_PORT, LKM_STB); |
117 cbi(&LKM_PORT, LKM_STB); |
94 } |
118 } |
95 |
119 |
96 /* |
120 /* |
|
121 * Select the LKM for read. |
|
122 */ |
|
123 void lkm_in () |
|
124 { |
|
125 // data in |
|
126 cbi(&LKM_DDR, LKM_DIO); |
|
127 cbi(&LKM_PORT, LKM_DIO); |
|
128 |
|
129 // select on: low |
|
130 cbi(&LKM_PORT, LKM_STB); |
|
131 |
|
132 // clock high |
|
133 sbi(&LKM_PORT, LKM_CLK); |
|
134 |
|
135 // pause |
|
136 _delay_us(1); |
|
137 } |
|
138 |
|
139 /* |
97 * Write out one byte |
140 * Write out one byte |
98 */ |
141 */ |
99 void lkm_write (byte b) |
142 void lkm_write (byte b) |
100 { |
143 { |
101 char i; |
144 char i; |
102 |
145 |
103 for (i = 0; i < 8; i++) { |
146 for (i = 0; i < 8; i++) { |
104 // low |
147 // clock read: low |
105 cbi(&LKM_PORT, LKM_CLK); |
148 cbi(&LKM_PORT, LKM_CLK); |
106 |
149 |
107 // set output |
150 // set output |
108 if (b & 1) |
151 if (b & 1) |
109 sbi(&LKM_PORT, LKM_DIO); |
152 sbi(&LKM_PORT, LKM_DIO); |
110 else |
153 else |
111 cbi(&LKM_PORT, LKM_DIO); |
154 cbi(&LKM_PORT, LKM_DIO); |
112 |
155 |
113 // clock high |
156 // clock write: high |
114 sbi(&LKM_PORT, LKM_CLK); |
157 sbi(&LKM_PORT, LKM_CLK); |
115 |
158 |
116 // next bit |
159 // next bit |
117 b >>= 1; |
160 b >>= 1; |
118 } |
161 } |
119 } |
162 } |
120 |
163 |
121 /* |
164 /* |
|
165 * Read in one byte. |
|
166 */ |
|
167 byte lkm_read () |
|
168 { |
|
169 byte b = 0; |
|
170 char i; |
|
171 |
|
172 // XXX: this loop is timing-critical; we must allow the signal to settle betwen clocks |
|
173 for (i = 0; i < 8; i++) { |
|
174 // next bit |
|
175 b >>= 1; |
|
176 |
|
177 // clock read: low |
|
178 cbi(&LKM_PORT, LKM_CLK); |
|
179 |
|
180 // pause |
|
181 _delay_us(1); |
|
182 |
|
183 // read input |
|
184 if (tbi(&LKM_PIN, LKM_DIO)) |
|
185 b |= 0x80; |
|
186 |
|
187 // pause |
|
188 _delay_us(1); |
|
189 |
|
190 // clock write: high |
|
191 sbi(&LKM_PORT, LKM_CLK); |
|
192 |
|
193 // pause |
|
194 _delay_us(1); |
|
195 } |
|
196 |
|
197 return b; |
|
198 } |
|
199 |
|
200 /* |
122 * End command. |
201 * End command. |
123 */ |
202 */ |
124 void lkm_end () |
203 void lkm_end () |
125 { |
204 { |
126 // select off: high |
205 // select off: high |
152 | (intensity & LKM_CONTROL_INTENSITY) |
231 | (intensity & LKM_CONTROL_INTENSITY) |
153 ); |
232 ); |
154 } |
233 } |
155 |
234 |
156 /* |
235 /* |
157 * Blank display. |
236 * Blank display/LEDs |
158 */ |
237 */ |
159 static void lkm_display_blank () |
238 static void lkm_clear () |
160 { |
239 { |
161 char i; |
240 char i; |
162 |
241 |
163 lkm_cmd(LKM_CMD_DATA |
242 lkm_cmd(LKM_CMD_DATA |
|
243 | LKM_DATA_TEST_NORMAL |
|
244 | LKM_DATA_ADDR_AUTO |
164 | LKM_DATA_WRITE |
245 | LKM_DATA_WRITE |
165 | LKM_DATA_ADDR_AUTO |
|
166 | LKM_DATA_TEST_NORMAL |
|
167 ); |
246 ); |
168 |
247 |
169 // write out all 16 bytes of 0x00 |
248 // write out all 16 bytes of 0x00 |
170 lkm_start(); |
249 lkm_out(); |
171 lkm_write(LKM_CMD_ADDRESS | (0x0) & LKM_ADDRESS); |
250 lkm_write(LKM_CMD_ADDRESS | (0x0) & LKM_ADDRESS); |
172 for (i = 0; i < 16; i++) { |
251 for (i = 0; i < 16; i++) { |
173 lkm_write(0x00); |
252 lkm_write(0x00); |
174 } |
253 } |
175 lkm_end(); |
254 lkm_end(); |
180 * Set raw output mask for given display 0..7 |
259 * Set raw output mask for given display 0..7 |
181 */ |
260 */ |
182 static inline void lkm_display (byte display, byte value) |
261 static inline void lkm_display (byte display, byte value) |
183 { |
262 { |
184 lkm_cmd(LKM_CMD_DATA |
263 lkm_cmd(LKM_CMD_DATA |
|
264 | LKM_DATA_TEST_NORMAL |
|
265 | LKM_DATA_ADDR_AUTO |
185 | LKM_DATA_WRITE |
266 | LKM_DATA_WRITE |
186 | LKM_DATA_ADDR_AUTO |
|
187 | LKM_DATA_TEST_NORMAL |
|
188 ); |
267 ); |
189 lkm_cmd1(LKM_CMD_ADDRESS | ((display * 2 + 0) & LKM_ADDRESS), |
268 lkm_cmd1(LKM_CMD_ADDRESS | ((display * 2 + 0) & LKM_ADDRESS), |
190 value |
269 value |
191 ); |
270 ); |
192 } |
271 } |
193 |
272 |
194 /* |
273 /* |
|
274 * Set raw output mask for given led 0..7 |
|
275 */ |
|
276 static inline void lkm_led (byte led, byte value) |
|
277 { |
|
278 lkm_cmd(LKM_CMD_DATA |
|
279 | LKM_DATA_TEST_NORMAL |
|
280 | LKM_DATA_ADDR_AUTO |
|
281 | LKM_DATA_WRITE |
|
282 ); |
|
283 lkm_cmd1(LKM_CMD_ADDRESS | ((led * 2 + 1) & LKM_ADDRESS), |
|
284 value |
|
285 ); |
|
286 } |
|
287 |
|
288 /* |
195 * Set 4-bit hexadecimal output for given display 0..7 |
289 * Set 4-bit hexadecimal output for given display 0..7 |
196 */ |
290 */ |
197 static inline void lkm_display_hex (byte display, byte hex) |
291 static inline void lkm_display_hex (byte display, byte hex) |
198 { |
292 { |
199 lkm_display(display, LKM_DISPLAY_FONT[hex]); |
293 lkm_display(display, LKM_DISPLAY_FONT[hex]); |
|
294 } |
|
295 |
|
296 /* |
|
297 * Read the 8-bit key states |
|
298 */ |
|
299 byte lkm_buttons () |
|
300 { |
|
301 byte k3 = 0; |
|
302 char i; |
|
303 |
|
304 lkm_out(); |
|
305 lkm_write(LKM_CMD_DATA |
|
306 | LKM_DATA_TEST_NORMAL |
|
307 | LKM_DATA_ADDR_AUTO |
|
308 | LKM_DATA_READ |
|
309 ); |
|
310 lkm_in(); |
|
311 for (i = 0; i < 4; i++) { |
|
312 /* |
|
313 * The ordering of keys used is weird; it seems to go 04 15 26 37 |
|
314 */ |
|
315 k3 |= lkm_read() << i; |
|
316 |
|
317 /* |
|
318 k3 >>= 1; |
|
319 |
|
320 byte b = lkm_read(); |
|
321 |
|
322 if (b & LKM_BUTTON_K3L) |
|
323 k3 |= 0b00001000; |
|
324 |
|
325 if (b & LKM_BUTTON_K3H) |
|
326 k3 |= 0b10000000; |
|
327 */ |
|
328 } |
|
329 lkm_end(); |
|
330 |
|
331 return k3; |
200 } |
332 } |
201 |
333 |
202 /* |
334 /* |
203 * Set 16-bit hexadecimal output on displays 4..7 |
335 * Set 16-bit hexadecimal output on displays 4..7 |
204 */ |
336 */ |
224 lkm_init(); |
356 lkm_init(); |
225 |
357 |
226 sei(); |
358 sei(); |
227 |
359 |
228 // setup display |
360 // setup display |
229 lkm_display_blank(); |
361 lkm_clear(); |
230 lkm_control(LKM_CONTROL_DISPLAY_ON, LKM_CONTROL_INTENSITY_MAX); |
362 lkm_control(LKM_CONTROL_DISPLAY_ON, LKM_CONTROL_INTENSITY_MAX); |
231 |
363 |
232 // start |
364 // start |
233 unsigned short i = 0; |
365 byte state[8] = { }; |
234 unsigned short timeout = 8000; |
366 char i; |
235 |
367 |
236 while (true) { |
368 while (true) { |
237 lkm_display_hex16(i++); |
369 // scan input |
238 |
370 byte buttons = lkm_buttons(); |
239 timer_sleep(timeout); |
371 |
240 } |
372 for (i = 0; i < 8; i++) { |
241 } |
373 if (!(buttons & (1 << i))) { |
|
374 continue; |
|
375 } else if (state[i] >= 0xF) { |
|
376 state[i] = 0; |
|
377 } else { |
|
378 state[i]++; |
|
379 } |
|
380 |
|
381 lkm_display_hex(i, state[i]); |
|
382 lkm_led(i, state[i]); |
|
383 |
|
384 xbi(&DEBUG_PORT, DEBUG_LED); |
|
385 } |
|
386 |
|
387 timer_sleep(16000); |
|
388 } |
|
389 } |