Adafruit PyGamer and PyBadge
The Adafruit PyGamer, PyBadge, and PyBadge LC boards would be good platforms for experimenting with writing your own games in Lisp. They each incorporate a 1.8" 160x128 colour TFT display, and are based on the ATSAMD51. They are both supported by the ARM Version 3.2 of uLisp.
ARM Version 3.2 of uLisp includes graphics extensions, to allow you to plot points, lines, shapes, and text to the PyGamer, PyBadge, or PyBadge LC colour TFT display. It also includes an ARM assembler that allows you to generate machine-code functions, integrated with Lisp, written in ARM code. The assembler itself is written in Lisp to make it easy to extend it or add new instructions.
For reference information about the graphics extensons see: Graphics extensions.
For some demo graphics programs see: Graphics examples.
For an overview of the ARM assembler and details of how to install it see: ARM assembler overview.
For a summary of the instructions in the RISC-V assembler see: ARM assembler instructions.
For example programs see: ARM assembler examples.
Many of the features are common to all three boards.
The eight pushbuttons on the PyBadge are connected to an 8-bit 74165 shift register, and to read them you have to shift the bits into the shift register and then read the 8-bit value. They are indicated by a single 8-bit value:
The routine readbuttons returns an 8-bit number where each bit represents the state of one of the 8 keys on the keypad:
(defun readbuttons () (let ((buttons 0) (clock 48) (data 49) (latch 50)) (pinmode clock t) (digitalWrite clock 0) (pinmode latch t) (digitalWrite latch 1) (pinmode data nil) (digitalwrite latch 0) (digitalWrite latch 1) (dotimes (b 8 buttons) (setq buttons (logior (ash buttons 1) (if (digitalread data) 1 0))) (digitalwrite clock 1) (digitalwrite clock 0))))
The PyGamer incorporates an analogue joystick. You can read it with the following routine:
(defun joystick () (let ((x 0) (x0 536) (y 0) (y0 523)) (dotimes (s 3) (incf x (analogread 25)) (incf y (analogread 24))) (list (- (truncate x 3) x0) (- (truncate y 3) y0))))
It returns a number between -512 (left) and 511 (right) for the x direction, and -512 (up) to 512 (down) for the y direction. You can change the values of x0 and y0 to get close to zero for the centre position.
The four pushbuttons on the PyGamer are connected to an 8-bit 74165 shift register, and to read them you have to shift the bits into the shift register and then read the 8-bit value. They are indicated by a single 8-bit value:
The routine readkeys returns an 8-bit number where each bit represents the state of one of the 8 keys on the keypad. It sets the bottom four bits according to the position of the joystick, for compatibility with the PyBadge:
(defun readbuttons () (let ((buttons 0) (clock 48) (data 49) (latch 50)) (pinmode clock t) (digitalWrite clock 0) (pinmode latch t) (digitalWrite latch 1) (pinmode data nil) (digitalwrite latch 0) (digitalWrite latch 1) (dotimes (b 8 buttons) (setq buttons (logior (ash buttons 1) (if (digitalread data) 1 0))) (digitalwrite clock 1) (digitalwrite clock 0)) (let* ((j (joystick)) (x (first j)) (y (second j))) (logior buttons (cond ((< x -350) 1) ((< y -350) 2) ((> y 350) 4) ((> x 350) 8) (t 0)))))
You can read the battery voltage with the following routine:
(defun battery () (* (/ (analogread 20) 1023) 2.0 3.3))
It returns the voltage as a floating-point number.
There's a light sensor above the display which you can read with the following routine:
(defun light-sensor () (analogread 21))
It returns an integer between 0 (dark) and 1023 (bright).
You can change the display backlight with the following routine:
(defun backlight (n) (analogwrite 47 n))
It takes a number between 0 (dim) and 255 (normal).
The PyGamer and PyBadge (but not PyBadge LC) include an LIS3DH accelerometer.
By default the sensor is in low-power mode, so to take readings you need to set the rate. The following routine lis3dh-rate takes a parameter from 0 to 9:
(defun lis3dh-rate (x) (with-i2c (s #x19) (write-byte #x20 s) (write-byte (logior (ash x 4) 7) s)))
For example (rate 2) gives a 10Hz sample rate.
The following routine lis3dh-xyz then returns the acceleration data as a list of three signed integers.
(defun lis3dh-xyz () (with-i2c (s #x19) (write-byte (+ #x28 #x80) s) (restart-i2c s 6) (let (dat) (dotimes (i 3) (push (s16 s) dat)) (reverse dat))))
It uses this routine s16 to give a signed 16-bit integer from two bytes, LSB first:
(defun s16 (s) (let ((d (logior (read-byte s) (ash (read-byte s) 8)))) (- d (ash (logand d #x8000) 1))))
At the default full-scale sensitivity, 2g, a value of 32767 represents 2g and -32768 represents -2g.For example:
> (xyz) (300 412 -16768)
If the PyBadge or PyGamer is flat on a table the x and y figures will be close to zero, and the z value will be close to -16384, corresponding to -1g because the sensor is on the back of the board.
Here's a simple etch-a-sketch program for the PyGamer that lets you draw with the joystick:
(defun etchasketch () (let ((x0 80) (y0 64)) (loop (let* ((r (readbuttons)) (j (joystick)) (x (+ 80 (truncate (* (first j) 80) 512))) (y (+ 64 (truncate (* (second j) 64) 512)))) (if (> r 32) (fill-screen 0)) (draw-line x0 y0 x y) (setq x0 x y0 y) (delay 10)))))
Press A or B to clear the screen.