hello-lkm: buttons
authorTero Marttila <terom@paivola.fi>
Sat, 05 Apr 2014 01:50:26 +0300
changeset 59 7090f61e5e17
parent 58 a445e08b63e0
child 60 b9648067e9d7
hello-lkm: buttons
src/hello-lkm.c
--- 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);
     }
 }