Code and designs for custom physical keyboards
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

166 lines
5.5 KiB

(add-build-config-label "Keypad")
(set-cakelisp-option cakelisp-src-dir "Dependencies/cakelisp/src")
(add-cakelisp-search-directory "Dependencies/gamelib/src")
(add-cakelisp-search-directory "Dependencies/cakelisp/runtime")
(add-cakelisp-search-directory "src")
(import "CHelpers.cake")
(c-import
;; sdk
"<stdio.h>"
"pico/stdlib.h"
"bsp/board.h"
"tusb.h"
;; Our stuff
"usb_descriptors.h"
"stdint.h")
(var row-pins (array (const int)) ;; Samples. Yellow wires.
(array 29 28 27 26)) ;; A3 A2 A1 A0
(var column-pins (array (const int)) ;; Sends power. White wires.
(array 2 3 4 5 6)) ;; 2 3 4 5 6
(var last-scan-matrix (array 4 (array 5 int)) (array 0))
(var matrix (array 4 (array 5 int)) (array 0))
(var matrix-to-keys (array 4 (array 5 char))
(array
(array 'n' '7' '4' '1' '0') ;; a3
(array '/' '8' '5' '2' '_') ;; a2
(array '*' '9' '6' '3' '.') ;; a1
(array '+' 'b' '_' '_' 'e'))) ;; a0
(var keycode-unused (const uint8_t) 0)
(var matrix-to-keycodes (array 4 (array 5 uint8_t))
(array
(array HID_KEY_NUM_LOCK HID_KEY_KEYPAD_7 HID_KEY_KEYPAD_4 HID_KEY_KEYPAD_1 HID_KEY_KEYPAD_0) ;; a3
(array HID_KEY_KEYPAD_DIVIDE HID_KEY_KEYPAD_8 HID_KEY_KEYPAD_5 HID_KEY_KEYPAD_2 keycode-unused) ;; a2
(array HID_KEY_KEYPAD_MULTIPLY HID_KEY_KEYPAD_9 HID_KEY_KEYPAD_6 HID_KEY_KEYPAD_3 HID_KEY_KEYPAD_DECIMAL) ;; a1
(array HID_KEY_KEYPAD_SUBTRACT HID_KEY_KEYPAD_ADD keycode-unused keycode-unused HID_KEY_KEYPAD_ENTER))) ;; a0
(defmacro print-line (line any)
;; (tokenize-push output (call-on println Serial (token-splice line)))
(return true))
(defun main (&return int)
(board_init)
(tusb_init)
(each-item-in-array row-pins row row-pin int
(gpio_init row-pin)
(gpio_set_dir row-pin GPIO_IN)
(gpio_pull_up row-pin))
(each-item-in-array column-pins column col-pin int
(gpio_init col-pin)
(gpio_set_dir col-pin GPIO_OUT))
(while true
(tud_task)
(hid-task))
;; Should never happen
(return 1))
(defun hid-task ()
(var poll-interval-ms uint32_t 1)
(var-static start-ms uint32_t 0)
(when (< (- (board_millis) start-ms) poll-interval-ms)
;; (todo feature) Should this actually delay to save power?
(return))
(set start-ms (+ start-ms poll-interval-ms))
(scan-matrix)
(var any-key-pressed bool false)
(each-in-range (array-size row-pins) row
(each-in-range (array-size column-pins) column
(when (and (at row column matrix)
(not (at row column last-scan-matrix)))
(set any-key-pressed true))))
;; Wake up host if we are in suspend mode
;; and REMOTE_WAKEUP feature is enabled by host
(cond
((and any-key-pressed (tud_suspended))
(tud_remote_wakeup))
(true ;; Host is awake and should receive our report
(send-keyboard-report))))
(defun send-keyboard-report ()
(unless (tud_hid_ready)
(return))
(var num-keys-reported int 0)
;; We are limited here in the number of keys we can send at once. It should be possible to roll
;; them over or use a different API to send more keys.
(var keycodes (array 6 uint8_t) (array 0))
(var key-just-pressed bool false)
(var key-just-released bool false)
(ignore ;; Sanity check (press boot button)
(var-static has-key bool false)
(var button-pressed uint32_t (board_button_read))
(if button-pressed
(scope
(set (at 0 keycodes) HID_KEY_A)
(set has-key true)
(set key-just-pressed true))
(scope
(set key-just-released has-key)
(set has-key false))))
(each-in-range (array-size row-pins) row
(each-in-range (array-size column-pins) column
(when (and (at row column matrix)
(not (at row column last-scan-matrix))
(< num-keys-reported (array-size keycodes)))
(var key-code uint8_t (at row column matrix-to-keycodes))
(unless (= key-code keycode-unused)
(set (at num-keys-reported keycodes) key-code)
(incr num-keys-reported)
(set key-just-pressed true)))
(when (and (at row column last-scan-matrix)
(not (at row column matrix)))
(set key-just-released true))
(set (at row column last-scan-matrix)
(at row column matrix))))
;; Avoid sending multiple empty key reports if nothing changed
(when (or key-just-pressed key-just-released)
(tud_hid_keyboard_report
REPORT_ID_KEYBOARD
0 ;; modifier
keycodes)))
(defun scan-matrix ()
(each-item-in-array column-pins column col-pin int
(gpio_set_dir col-pin GPIO_OUT)
(gpio_put col-pin 0)
(sleep_us 1) ;; Seems necessary to prevent extraneous key presses.
(each-item-in-array row-pins row row-pin int
(set (at row column matrix) (= 0 (gpio_get row-pin))))
(gpio_put col-pin 1)))
;; Invoked when received SET_REPORT control request or
;; received data on OUT endpoint ( Report ID = 0, Type = 0 )
;; In other words, the computer is telling US to do something (e.g. set caps lock LED)
(defun-nodecl tud_hid_set_report_cb
(instance uint8_t
report_id uint8_t
report_type hid_report_type_t
buffer (addr (const uint8_t))
bufsize uint16_t)
(ignore))
;; Invoked when received GET_REPORT control request
;; Application must fill buffer report's content and return its length.
;; Return zero will cause the stack to STALL request
(defun-nodecl tud_hid_get_report_cb
(instance uint8_t
report_id uint8_t
report_type hid_report_type_t
buffer (addr uint8_t)
reqlen uint16_t
&return uint16_t)
;; (todo robustness) I think I need to implement this...
(return 0))