Driving a TM1638 8-digit display module from uLisp


These TM1638 modules are available very cheaply from sites such as eBay, AliExpress, and Banggood, and are ideal for displaying a value from a sensor or calculation:

They use an SPI protocol, and you write to the displays by sending a sequence of bytes to the displays. However, they are slightly cumbersome to drive because each byte in the stream sets the state of a single segment in all the displays.

Connect the display using the appropriate SPI pins as follows:

Pin Connection Arduino Mega 2560
GND Ground GND
STB Enable 10
VCC +5V 5V

The third column shows the pins I used when testing the program on an Arduino Mega 2560. You can use any suitable pin as the Enable pin; specify it with this defvar:

(defvar en 10)

The following routine on turns on the display and sets the brightness; the parameter can be from 0 (dimmest) to 7 (brightest):

(defun on (bri)
  (with-spi (str en 1000 0 2)
    (write-byte (+ #x88 bri) str)))

The following list gives the segment definitions for the digits 0 to 9 and the hexadecimal digits A to F:

(defvar seg '(#x7e #x30 #x6d #x79 #x33 #x5b #x5f #x70
              #x7f #x7b #x77 #x1f #x4e #x3d #x4f #x47))

This routine clr clears the display:

(defun clr ()
  (with-spi (str en 1000 0 2)
    (write-byte #x40 str))
  (with-spi (str en 1000 0 2)
    (write-byte #xC0 str)
    (dotimes (n 16)
      (write-byte 0 str))))

Finally this routine put writes eight digits to the display:

(defun put (dp &rest dig)
  (with-spi (str en 1000 0 2)
    (write-byte #x40 str))
  (with-spi (str en 1000 0 2)
    (write-byte #xc0 str)
    (dotimes (n 7)
      (let ((byt 0))
        (dotimes (c 8)
          (setq byt
                    (nth (nth c dig) seg)
                    (- n 6))
                  (- 7 c))
        (write-byte byt str)
        (write-byte 0 str)))
    (write-byte (ash 1 (- 7 dp)) str)))

The first parameter specifies the decimal point position, from 0 to 7, or 8 for no decimal point.

The remaining eight parameters specify the number to show on each display. For example, to display “0123.4567” give the command:

(put 3 0 1 2 3 4 5 6 7)

Reading the keypad is left as an exercise for the reader.


A minor extension; define the segments as:

(defvar seg '(#x7e #x30 #x6d #x79 #x33 #x5b #x5f #x70 #x7f 
              #x7b #x77 #x1f #x4e #x3d #x4f #x47 #x00 #x01))

You can now display a space by giving the value 16 and a minus sign by giving the value 17. So, for example:

(put 8 15 10 12 14 17 0 15 15)

displays “FACE-OFF”.


Hmm… OK, this is absurd, but, consider this, considering certain similarities of the shapes and sounds:

0: O
1: I J Y
2: N X Z
3: E M
4: A H U
5: C S
6: G K Q
7: T L R
8: F W V
9: B D P

“Then you can write like this”:
7432 104 542 87173 7163 7415