Lisp calculator with a 7-segment display


Here’s the starting point for a project I’ve been working on. It’s a calculator, written in uLisp, with an 8-digit 7-segment display. It lets you do something like:

(show (acos -1))

to display the result of the calculation, pi:

The display is a very low-cost 8-digit display with an SPI interface, available on sites such as AliExpress for under a dollar/pound; for example: MAX7219 8 Digit LED Display. The program will run on any uLisp platform, but to display floating-point calculations you need a 32-bit platform, such as the Adafruit ItsyBitsy M0.

Connecting the display

Connect the display using the appropriate SPI pins as follows:

Pin Connection
CS Enable

For details of which pins are used for the SPI interface on different processors see Language reference: with-spi.

You can use any suitable pin as the Enable pin; specify it as follows:

(defvar en 2)

Display command

The routine cmd writes a display command to the display, consisting of an address or command code followed by a data value:

(defun cmd (a d)
  (with-spi (str en)
    (write-byte a str)
    (write-byte d str)))

Initialising the display

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

(defun on (bri)
  (cmd #xF 0)    ; Test mode off
  (cmd #xB 7)    ; 8 digits
  (cmd #xC 1)    ; Enable display
  (cmd #xA bri))

Defining the segment definitions

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

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

Clearing the display

This routine clr clears the display:

(defun clr ()
  (dotimes (d 8) (cmd d 0)))

Showing a value

Finally this routine show writes a value to the display:

(defun show (val)
  (let ((str (princ-to-string val))
        (d 8) b)
    (dotimes (i (length str))
      (let ((c (char-code (char str i))))
        (setq b 
               ((= (char-code #\.) c) 
                (incf d)
                (+ b #x80))
                  ((<= (char-code #\0) c (char-code #\9))
                   (- c (char-code #\0)))
                  ((<= (char-code #\A) c (char-code #\F)) 
                   (+ (- c (char-code #\A)) 10))
                  ((<= (char-code #\a) c (char-code #\f)) 
                   (+ (- c (char-code #\a)) 10))
                  ((= (char-code #\-) c) 17)
                  (t 16))
      (cmd d b)
      (decf d))))

It works by converting the value to a string, using the uLisp function princ-to-string, converts each digit to the appropriate seven-segment pattern from the list seg, and then writes this to the appropriate display digit. If it encounters a decimal point in the value it sets the top bit of the previous digit, to display a decimal point after it.

With the addition of a small keypad this could form the basis of a custom scientific calculator, written in Lisp.