hello-lkm: Control the JY-LKM1638 LED 7-segment display module
authorTero Marttila <terom@paivola.fi>
Sat, 05 Apr 2014 00:37:32 +0300
changeset 58 a445e08b63e0
parent 57 ee412c5be8b1
child 59 7090f61e5e17
hello-lkm: Control the JY-LKM1638 LED 7-segment display module
Makefile
src/hello-lkm.c
src/stdlib.h
--- a/Makefile	Fri Apr 04 13:14:04 2014 +0300
+++ b/Makefile	Sat Apr 05 00:37:32 2014 +0300
@@ -1,6 +1,4 @@
-PROG = hello
-# console
-# dmx
+PROG = hello-lkm
 
 ELF = build/src/$(PROG).elf
 HEX = build/src/$(PROG).hex
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hello-lkm.c	Sat Apr 05 00:37:32 2014 +0300
@@ -0,0 +1,241 @@
+/*
+ * Control the JY-LKM1638 LED display module.
+ */
+
+#include <avr/io.h>
+
+#include "stdlib.h"
+
+// XXX
+#include "timer.c"
+
+#define LKM_DDR     DDRC
+#define LKM_PORT    PORTC
+#define LKM_CLK     0
+#define LKM_DIO     1
+#define LKM_STB     2
+
+enum lkm_cmd {
+    LKM_CMD_DATA    = 0b01000000,
+    LKM_CMD_CONTROL = 0b10000000,
+    LKM_CMD_ADDRESS = 0b11000000,
+
+    LKM_DATA_WRITE          = 0b00000010,
+    LKM_DATA_READ           = 0b00000000,
+
+    LKM_DATA_ADDR_AUTO      = 0b00000000,
+    LKM_DATA_ADDR_FIXED     = 0b00000100,
+
+    LKM_DATA_TEST_NORMAL    = 0b00000000,
+    LKM_DATA_TEST_TEST      = 0b00001000,
+
+    LKM_ADDRESS             = 0b00001111,
+    
+    LKM_CONTROL_INTENSITY       = 0b00000111,
+    LKM_CONTROL_INTENSITY_MIN   = 0b00000000,
+    LKM_CONTROL_INTENSITY_MAX   = 0b00000111,
+
+    LKM_CONTROL_DISPLAY_OFF = 0b00000000,
+    LKM_CONTROL_DISPLAY_ON  = 0b00001000,
+};
+
+static const uint8_t LKM_DISPLAY_FONT[] = {
+    0b00111111,	// 0
+    0b00000110,	// 1
+    0b01011011,	// 2
+    0b01001111,	// 3
+    0b01100110,	// 4
+    0b01101101,	// 5
+    0b01111101,	// 6
+    0b00000111,	// 7
+    0b01111111,	// 8
+    0b01100111,	// 9
+    0b01110111,	// A
+    0b01111100,	// B
+    0b00111001,	// C
+    0b01011110,	// D
+    0b01111001,	// E
+    0b01110001,	// F
+};
+
+enum {
+    LKM_DISPLAY_DOT     = 0b10000000,
+};
+
+void lkm_init ()
+{
+    // strobe off: high output
+    // XXX: should use an external pull-up resistor?
+    sbi(&LKM_PORT, LKM_STB);
+    sbi(&LKM_DDR, LKM_STB);
+
+    // clock low
+    sbi(&LKM_DDR, LKM_CLK);
+    cbi(&LKM_PORT, LKM_CLK);
+
+    // data tri-state
+    cbi(&LKM_DDR, LKM_DIO);
+    cbi(&LKM_DDR, LKM_DIO);
+}
+
+/*
+ * Select the LKM for write.
+ */
+void lkm_start ()
+{
+    // clock low
+    cbi(&LKM_PORT, LKM_CLK);
+
+    // data out
+    sbi(&LKM_DDR, LKM_DIO);
+
+    // select on: low
+    cbi(&LKM_PORT, LKM_STB);
+}
+
+/*
+ * Write out one byte
+ */
+void lkm_write (byte b)
+{
+    char i;
+
+    for (i = 0; i < 8; i++) {
+        // low
+        cbi(&LKM_PORT, LKM_CLK);
+
+        // set output
+        if (b & 1)
+            sbi(&LKM_PORT, LKM_DIO);
+        else 
+            cbi(&LKM_PORT, LKM_DIO);
+        
+        // clock high
+        sbi(&LKM_PORT, LKM_CLK);
+        
+        // next bit
+        b >>= 1;
+    }
+}
+
+/*
+ * End command.
+ */
+void lkm_end ()
+{
+    // select off: high
+    sbi(&LKM_PORT, LKM_STB);
+
+    // tristate data
+    cbi(&LKM_DDR, LKM_DIO);
+}
+
+void lkm_cmd (byte cmd)
+{
+    lkm_start();
+    lkm_write(cmd);
+    lkm_end();
+}
+
+void lkm_cmd1 (byte cmd, byte arg)
+{
+    lkm_start();
+    lkm_write(cmd);
+    lkm_write(arg);
+    lkm_end();
+}
+
+static inline void lkm_control (byte display, byte intensity)
+{
+    lkm_cmd(LKM_CMD_CONTROL
+        |   (display ? LKM_CONTROL_DISPLAY_ON : LKM_CONTROL_DISPLAY_OFF)
+        |   (intensity & LKM_CONTROL_INTENSITY)
+    );
+}
+
+/*
+ * Blank display.
+ */
+static void lkm_display_blank ()
+{
+    char i;
+
+    lkm_cmd(LKM_CMD_DATA
+        |   LKM_DATA_WRITE
+        |   LKM_DATA_ADDR_AUTO
+        |   LKM_DATA_TEST_NORMAL
+    );
+    
+    // write out all 16 bytes of 0x00
+    lkm_start();
+    lkm_write(LKM_CMD_ADDRESS | (0x0) & LKM_ADDRESS);
+    for (i = 0; i < 16; i++) {
+        lkm_write(0x00);
+    }
+    lkm_end();
+
+}
+
+/*
+ * Set raw output mask for given display 0..7
+ */
+static inline void lkm_display (byte display, byte value)
+{
+    lkm_cmd(LKM_CMD_DATA
+        |   LKM_DATA_WRITE
+        |   LKM_DATA_ADDR_AUTO
+        |   LKM_DATA_TEST_NORMAL
+    );
+    lkm_cmd1(LKM_CMD_ADDRESS | ((display * 2 + 0) & LKM_ADDRESS),
+        value
+    );
+}
+
+/*
+ * Set 4-bit hexadecimal output for given display 0..7
+ */
+static inline void lkm_display_hex (byte display, byte hex)
+{
+    lkm_display(display, LKM_DISPLAY_FONT[hex]);
+}
+
+/*
+ * Set 16-bit hexadecimal output on displays 4..7
+ */
+void lkm_display_hex16 (short hex)
+{
+    lkm_display_hex(7, ((hex >> 0) & 0xF));
+    lkm_display_hex(6, ((hex >> 4) & 0xF));
+    lkm_display_hex(5, ((hex >> 8) & 0xF));
+    lkm_display_hex(4, ((hex >> 12) & 0xF));
+}
+
+// debug
+#define DEBUG_DDR   DDRB
+#define DEBUG_PORT  PORTB
+#define DEBUG_LED   0
+
+int main (void)
+{
+    // led_init();
+    sbi(&DEBUG_DDR, DEBUG_LED);
+
+    timer_init();
+    lkm_init();
+
+    sei();
+
+    // setup display
+    lkm_display_blank();
+    lkm_control(LKM_CONTROL_DISPLAY_ON, LKM_CONTROL_INTENSITY_MAX);
+
+    // start
+    unsigned short i = 0;
+    unsigned short timeout = 8000;
+        
+    while (true) {
+        lkm_display_hex16(i++);
+
+        timer_sleep(timeout);
+    }
+}
--- a/src/stdlib.h	Fri Apr 04 13:14:04 2014 +0300
+++ b/src/stdlib.h	Sat Apr 05 00:37:32 2014 +0300
@@ -1,6 +1,8 @@
 #define false       0
 #define true        1
 
+typedef uint8_t byte;
+
 typedef volatile uint8_t ioport_t;
 
 static inline ioport_t tbi(ioport_t *port, int bit)