--- a/src/hello-lkm.c Sat Apr 05 00:37:32 2014 +0300
+++ b/src/hello-lkm.c Sat Apr 05 01:50:26 2014 +0300
@@ -3,6 +3,7 @@
*/
#include <avr/io.h>
+#include <util/delay.h>
#include "stdlib.h"
@@ -11,6 +12,7 @@
#define LKM_DDR DDRC
#define LKM_PORT PORTC
+#define LKM_PIN PINC
#define LKM_CLK 0
#define LKM_DIO 1
#define LKM_STB 2
@@ -20,8 +22,8 @@
LKM_CMD_CONTROL = 0b10000000,
LKM_CMD_ADDRESS = 0b11000000,
- LKM_DATA_WRITE = 0b00000010,
- LKM_DATA_READ = 0b00000000,
+ LKM_DATA_WRITE = 0b00000000,
+ LKM_DATA_READ = 0b00000010,
LKM_DATA_ADDR_AUTO = 0b00000000,
LKM_DATA_ADDR_FIXED = 0b00000100,
@@ -62,6 +64,28 @@
LKM_DISPLAY_DOT = 0b10000000,
};
+enum {
+ LKM_LED_OFF = 0b00,
+ LKM_LED_RED = 0b01,
+ LKM_LED_GREEN = 0b10,
+ LKM_LED_ORANGE = 0b11,
+};
+
+/*
+ * Button bitfields.
+ *
+ * The JY-LKM1638 uses K3
+ */
+enum {
+ LKM_BUTTON_K1L = 0b00000100,
+ LKM_BUTTON_K2L = 0b00000010,
+ LKM_BUTTON_K3L = 0b00000001,
+
+ LKM_BUTTON_K1H = 0b01000000,
+ LKM_BUTTON_K2H = 0b00100000,
+ LKM_BUTTON_K3H = 0b00010000,
+};
+
void lkm_init ()
{
// strobe off: high output
@@ -81,7 +105,7 @@
/*
* Select the LKM for write.
*/
-void lkm_start ()
+void lkm_out ()
{
// clock low
cbi(&LKM_PORT, LKM_CLK);
@@ -94,6 +118,25 @@
}
/*
+ * Select the LKM for read.
+ */
+void lkm_in ()
+{
+ // data in
+ cbi(&LKM_DDR, LKM_DIO);
+ cbi(&LKM_PORT, LKM_DIO);
+
+ // select on: low
+ cbi(&LKM_PORT, LKM_STB);
+
+ // clock high
+ sbi(&LKM_PORT, LKM_CLK);
+
+ // pause
+ _delay_us(1);
+}
+
+/*
* Write out one byte
*/
void lkm_write (byte b)
@@ -101,7 +144,7 @@
char i;
for (i = 0; i < 8; i++) {
- // low
+ // clock read: low
cbi(&LKM_PORT, LKM_CLK);
// set output
@@ -110,7 +153,7 @@
else
cbi(&LKM_PORT, LKM_DIO);
- // clock high
+ // clock write: high
sbi(&LKM_PORT, LKM_CLK);
// next bit
@@ -119,6 +162,42 @@
}
/*
+ * Read in one byte.
+ */
+byte lkm_read ()
+{
+ byte b = 0;
+ char i;
+
+ // XXX: this loop is timing-critical; we must allow the signal to settle betwen clocks
+ for (i = 0; i < 8; i++) {
+ // next bit
+ b >>= 1;
+
+ // clock read: low
+ cbi(&LKM_PORT, LKM_CLK);
+
+ // pause
+ _delay_us(1);
+
+ // read input
+ if (tbi(&LKM_PIN, LKM_DIO))
+ b |= 0x80;
+
+ // pause
+ _delay_us(1);
+
+ // clock write: high
+ sbi(&LKM_PORT, LKM_CLK);
+
+ // pause
+ _delay_us(1);
+ }
+
+ return b;
+}
+
+/*
* End command.
*/
void lkm_end ()
@@ -132,14 +211,14 @@
void lkm_cmd (byte cmd)
{
- lkm_start();
+ lkm_out();
lkm_write(cmd);
lkm_end();
}
void lkm_cmd1 (byte cmd, byte arg)
{
- lkm_start();
+ lkm_out();
lkm_write(cmd);
lkm_write(arg);
lkm_end();
@@ -154,20 +233,20 @@
}
/*
- * Blank display.
+ * Blank display/LEDs
*/
-static void lkm_display_blank ()
+static void lkm_clear ()
{
char i;
lkm_cmd(LKM_CMD_DATA
+ | LKM_DATA_TEST_NORMAL
+ | LKM_DATA_ADDR_AUTO
| LKM_DATA_WRITE
- | LKM_DATA_ADDR_AUTO
- | LKM_DATA_TEST_NORMAL
);
// write out all 16 bytes of 0x00
- lkm_start();
+ lkm_out();
lkm_write(LKM_CMD_ADDRESS | (0x0) & LKM_ADDRESS);
for (i = 0; i < 16; i++) {
lkm_write(0x00);
@@ -182,9 +261,9 @@
static inline void lkm_display (byte display, byte value)
{
lkm_cmd(LKM_CMD_DATA
+ | LKM_DATA_TEST_NORMAL
+ | LKM_DATA_ADDR_AUTO
| LKM_DATA_WRITE
- | LKM_DATA_ADDR_AUTO
- | LKM_DATA_TEST_NORMAL
);
lkm_cmd1(LKM_CMD_ADDRESS | ((display * 2 + 0) & LKM_ADDRESS),
value
@@ -192,6 +271,21 @@
}
/*
+ * Set raw output mask for given led 0..7
+ */
+static inline void lkm_led (byte led, byte value)
+{
+ lkm_cmd(LKM_CMD_DATA
+ | LKM_DATA_TEST_NORMAL
+ | LKM_DATA_ADDR_AUTO
+ | LKM_DATA_WRITE
+ );
+ lkm_cmd1(LKM_CMD_ADDRESS | ((led * 2 + 1) & LKM_ADDRESS),
+ value
+ );
+}
+
+/*
* Set 4-bit hexadecimal output for given display 0..7
*/
static inline void lkm_display_hex (byte display, byte hex)
@@ -200,6 +294,44 @@
}
/*
+ * Read the 8-bit key states
+ */
+byte lkm_buttons ()
+{
+ byte k3 = 0;
+ char i;
+
+ lkm_out();
+ lkm_write(LKM_CMD_DATA
+ | LKM_DATA_TEST_NORMAL
+ | LKM_DATA_ADDR_AUTO
+ | LKM_DATA_READ
+ );
+ lkm_in();
+ for (i = 0; i < 4; i++) {
+ /*
+ * The ordering of keys used is weird; it seems to go 04 15 26 37
+ */
+ k3 |= lkm_read() << i;
+
+ /*
+ k3 >>= 1;
+
+ byte b = lkm_read();
+
+ if (b & LKM_BUTTON_K3L)
+ k3 |= 0b00001000;
+
+ if (b & LKM_BUTTON_K3H)
+ k3 |= 0b10000000;
+ */
+ }
+ lkm_end();
+
+ return k3;
+}
+
+/*
* Set 16-bit hexadecimal output on displays 4..7
*/
void lkm_display_hex16 (short hex)
@@ -226,16 +358,32 @@
sei();
// setup display
- lkm_display_blank();
+ lkm_clear();
lkm_control(LKM_CONTROL_DISPLAY_ON, LKM_CONTROL_INTENSITY_MAX);
// start
- unsigned short i = 0;
- unsigned short timeout = 8000;
+ byte state[8] = { };
+ char i;
while (true) {
- lkm_display_hex16(i++);
+ // scan input
+ byte buttons = lkm_buttons();
+
+ for (i = 0; i < 8; i++) {
+ if (!(buttons & (1 << i))) {
+ continue;
+ } else if (state[i] >= 0xF) {
+ state[i] = 0;
+ } else {
+ state[i]++;
+ }
- timer_sleep(timeout);
+ lkm_display_hex(i, state[i]);
+ lkm_led(i, state[i]);
+
+ xbi(&DEBUG_PORT, DEBUG_LED);
+ }
+
+ timer_sleep(16000);
}
}