Add EVEN MORE comments.
The code:comment ratio in menelaus.scm is now 1:1.
This commit is contained in:
parent
382d557dda
commit
317dd9a83d
5 changed files with 126 additions and 30 deletions
10
Makefile
10
Makefile
|
@ -32,4 +32,12 @@ usb_keyboard.s: usb_keyboard.h usb_keyboard.c
|
||||||
avr-gcc -std=gnu99 -S -D F_CPU=$(F_CPU)UL -mmcu=$(MCU) -c \
|
avr-gcc -std=gnu99 -S -D F_CPU=$(F_CPU)UL -mmcu=$(MCU) -c \
|
||||||
-o usb_keyboard.s usb_keyboard.c
|
-o usb_keyboard.s usb_keyboard.c
|
||||||
|
|
||||||
.PHONY: build upload test clean count
|
udev: /etc/udev/rules.d/a-star.rules
|
||||||
|
|
||||||
|
/etc/udev/rules.d/a-star.rules:
|
||||||
|
echo "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1ffb\", \
|
||||||
|
ATTRS{idProduct}==\"0101\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"" > $@
|
||||||
|
echo "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1ffb\", \
|
||||||
|
ATTRS{idProduct}==\"2300\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"" >> $@
|
||||||
|
|
||||||
|
.PHONY: build upload test clean count udev
|
||||||
|
|
10
README.md
10
README.md
|
@ -36,7 +36,12 @@ can be invoked by a key combo and sometimes a hard reset is
|
||||||
necessary. On the A-star Micro used in the Atreus kits, this is done
|
necessary. On the A-star Micro used in the Atreus kits, this is done
|
||||||
by shorting GND and RST twice in under a second, which causes the
|
by shorting GND and RST twice in under a second, which causes the
|
||||||
onboard LED to pulse. The Keyboardio Atreus has a reset button you can
|
onboard LED to pulse. The Keyboardio Atreus has a reset button you can
|
||||||
press with a pin to the bottom of the board.
|
press with a pin to the bottom of the board. On linux-based systems
|
||||||
|
you can monitor for the bootloader activation using `sudo dmesg --follow`.
|
||||||
|
|
||||||
|
Some linux-based systems will need a udev rule to grant permissions to
|
||||||
|
the USB device for uploading firmware. If you get permission denied on
|
||||||
|
`/dev/ttyACM0` or whatever it is, try running `sudo make udev`.
|
||||||
|
|
||||||
## Known bugs
|
## Known bugs
|
||||||
|
|
||||||
|
@ -72,7 +77,8 @@ into Racket and simulates the GPIO functions with a test harness:
|
||||||
|
|
||||||
Copyright © 2014-2020 Phil Hagelberg and contributors
|
Copyright © 2014-2020 Phil Hagelberg and contributors
|
||||||
|
|
||||||
Released under the [GNU GPL version 3](https://www.gnu.org/licenses/gpl.html).
|
Released under the [GNU GPL version 3](https://www.gnu.org/licenses/gpl.html)
|
||||||
|
or any later version.
|
||||||
|
|
||||||
Uses [PJRC USB Keyboard library](http://www.pjrc.com/teensy/usb_keyboard.html)
|
Uses [PJRC USB Keyboard library](http://www.pjrc.com/teensy/usb_keyboard.html)
|
||||||
which is Copyright © 2009 PJRC.COM, LLC and released under the MIT/X11 license.
|
which is Copyright © 2009 PJRC.COM, LLC and released under the MIT/X11 license.
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
(define (combo modifier keycode) (list (car modifier) keycode))
|
(define (combo modifier keycode) (list (car modifier) keycode))
|
||||||
(define (uncombo keycode) (and (= 2 (length keycode)) (car (cdr keycode))))
|
(define (uncombo keycode) (and (= 2 (length keycode)) (car (cdr keycode))))
|
||||||
|
|
||||||
;; we're treating these a little differently; they are not literal USB values
|
;; We're treating these a little differently; they are not literal USB values.
|
||||||
(define mod-ctrl (modify 1))
|
(define mod-ctrl (modify 1))
|
||||||
(define mod-shift (modify 2))
|
(define mod-shift (modify 2))
|
||||||
(define mod-alt (modify 3))
|
(define mod-alt (modify 3))
|
||||||
|
|
10
layout.scm
10
layout.scm
|
@ -21,7 +21,7 @@
|
||||||
;; (sft key-5)
|
;; (sft key-5)
|
||||||
key-backtick)
|
key-backtick)
|
||||||
|
|
||||||
;;;; layers
|
;;;; Layers
|
||||||
|
|
||||||
;; NB: the middle keys (ctrl and alt on the 42-key, also ~ and \ on the 44-key
|
;; NB: the middle keys (ctrl and alt on the 42-key, also ~ and \ on the 44-key
|
||||||
;; variant) are physically in two separate columns, but electrically they are
|
;; variant) are physically in two separate columns, but electrically they are
|
||||||
|
@ -53,6 +53,7 @@
|
||||||
key-n key-m key-comma key-period key-slash
|
key-n key-m key-comma key-period key-slash
|
||||||
|
|
||||||
key-esc key-tab mod-super mod-shift key-backspace mod-alt
|
key-esc key-tab mod-super mod-shift key-backspace mod-alt
|
||||||
|
;; fn takes us to fn-layer below while it is held down
|
||||||
key-space fn key-quote key-left-bracket key-enter))
|
key-space fn key-quote key-left-bracket key-enter))
|
||||||
|
|
||||||
(define fn-layer
|
(define fn-layer
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
key-dash key-equal (sft key-3) (sft key-dash) (sft key-equal) mod-ctrl
|
key-dash key-equal (sft key-3) (sft key-dash) (sft key-equal) mod-ctrl
|
||||||
(sft key-8) key-1 key-2 key-3 (sft key-right-bracket)
|
(sft key-8) key-1 key-2 key-3 (sft key-right-bracket)
|
||||||
|
|
||||||
|
;; set-layer 2 takes us to l2-layer below; doesn't need to be held
|
||||||
(set-layer 2) key-insert mod-super mod-shift key-backspace mod-alt
|
(set-layer 2) key-insert mod-super mod-shift key-backspace mod-alt
|
||||||
key-space fn key-e key-0 key-right-bracket))
|
key-space fn key-e key-0 key-right-bracket))
|
||||||
|
|
||||||
|
@ -75,10 +77,13 @@
|
||||||
key-delete key-left key-down key-right key-page-down 0
|
key-delete key-left key-down key-right key-page-down 0
|
||||||
key-down key-f4 key-f5 key-f6 key-f11
|
key-down key-f4 key-f5 key-f6 key-f11
|
||||||
|
|
||||||
(set-layer 0) key-vol-up 0 0 reset mod-ctrl
|
;; the B key enters the bootloader
|
||||||
|
0 key-vol-up 0 0 reset mod-ctrl
|
||||||
|
;; the N key switches to hardware dvorak mode
|
||||||
(set-layer 4) key-f1 key-f2 key-f3 key-f12
|
(set-layer 4) key-f1 key-f2 key-f3 key-f12
|
||||||
|
|
||||||
0 key-vol-down mod-super mod-shift key-backspace mod-alt
|
0 key-vol-down mod-super mod-shift key-backspace mod-alt
|
||||||
|
;; tapping the fn key brings us back to the base layer
|
||||||
key-space (set-layer 0) key-printscreen key-scroll-lock key-pause))
|
key-space (set-layer 0) key-printscreen key-scroll-lock key-pause))
|
||||||
|
|
||||||
(define hard-dvorak-layer
|
(define hard-dvorak-layer
|
||||||
|
@ -109,3 +114,4 @@
|
||||||
|
|
||||||
(set! layers (vector base-layer fn-layer l2-layer
|
(set! layers (vector base-layer fn-layer l2-layer
|
||||||
hard-dvorak-layer hard-dvorak-fn-layer))
|
hard-dvorak-layer hard-dvorak-fn-layer))
|
||||||
|
(set! current-layer (vector-ref layers 0))
|
||||||
|
|
124
menelaus.scm
124
menelaus.scm
|
@ -1,4 +1,18 @@
|
||||||
;;; menelaus.scm - a USB keyboard firmware for the Atreus.
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; menelaus.scm
|
||||||
|
|
||||||
|
;; a USB keyboard firmware for the Atreus.
|
||||||
|
|
||||||
|
;; Copyright © 2014-2020 Phil Hagelberg
|
||||||
|
;; Released under the GNU General Public License version 3 or any later
|
||||||
|
;; version.
|
||||||
|
|
||||||
|
;; The point of a keyboard firmware is to translate physical key presses on
|
||||||
|
;; switches into USB keycodes that get sent to the host. This process takes
|
||||||
|
;; several phases:
|
||||||
|
|
||||||
|
;; Matrix scan -> Debounce -> Track press/release -> Layout lookup -> Send USB
|
||||||
|
|
||||||
|
;; Each phase is described in more detail below.
|
||||||
|
|
||||||
;; Note that there are a few unusual style choices made here because
|
;; Note that there are a few unusual style choices made here because
|
||||||
;; it is written in a shared subset of Microscheme and Racket so that it
|
;; it is written in a shared subset of Microscheme and Racket so that it
|
||||||
|
@ -7,9 +21,11 @@
|
||||||
;; For one example, we use `and' where `when' would be more idiomatic. We
|
;; For one example, we use `and' where `when' would be more idiomatic. We
|
||||||
;; are also missing the `cond' form.
|
;; are also missing the `cond' form.
|
||||||
|
|
||||||
;; In general when you see an -aux function, it is an internal function which
|
;; When you see an -aux function, it is an internal function which recursively
|
||||||
;; recursively steps thru a vector/list with the initial arguments calculated
|
;; steps thru a vector/list with the initial arguments calculated by its
|
||||||
;; by its non-aux equivalent.
|
;; non-aux equivalent. The -aux function is never called directly.
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(include "keycodes.scm")
|
(include "keycodes.scm")
|
||||||
(include "layout.scm")
|
(include "layout.scm")
|
||||||
|
@ -33,7 +49,7 @@
|
||||||
;; isn't yet part of Microscheme:
|
;; isn't yet part of Microscheme:
|
||||||
;; https://github.com/ryansuchocki/microscheme/issues/32
|
;; https://github.com/ryansuchocki/microscheme/issues/32
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; Utility
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Utility functions
|
||||||
|
|
||||||
(define (find-aux v x n max)
|
(define (find-aux v x n max)
|
||||||
(let ((y (vector-ref v n)))
|
(let ((y (vector-ref v n)))
|
||||||
|
@ -61,14 +77,19 @@
|
||||||
;; Return a copy of lst with all elements equal to v removed.
|
;; Return a copy of lst with all elements equal to v removed.
|
||||||
(define (remove-all v lst) (remove-aux v lst (list) #t))
|
(define (remove-all v lst) (remove-aux v lst (list) #t))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; The Matrix
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Matrix Scan
|
||||||
|
|
||||||
;; A scan is defined as a list containing the key positions which are currently
|
;; This phase is responsible for determining the current state of the key
|
||||||
|
;; matrix; that is, which keys are reading as down for a given instant.
|
||||||
|
|
||||||
|
;; It returns a scan list containing the key positions which are currently
|
||||||
;; pressed for a given pass thru the key matrix. We specifically do not attempt
|
;; pressed for a given pass thru the key matrix. We specifically do not attempt
|
||||||
;; to look up what the keys are mapped to yet; we have to do that later on after
|
;; to look up what the keys are mapped to yet; we have to do that later on after
|
||||||
;; identifying presses and releases, otherwise we run into layer-switching bugs.
|
;; identifying presses and releases, otherwise we run into layer-switching bugs.
|
||||||
;; Each element in the list is an integer representation of the key in question.
|
;; Each element in the list is an integer representation of the key in question.
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; Which key in a layout vector is represented by the given row and column?
|
;; Which key in a layout vector is represented by the given row and column?
|
||||||
(define (offset-for row col)
|
(define (offset-for row col)
|
||||||
(+ col (* row (length columns))))
|
(+ col (* row (length columns))))
|
||||||
|
@ -106,7 +127,10 @@
|
||||||
(scan-matrix (scan-column scan (car rows-left) columns)
|
(scan-matrix (scan-column scan (car rows-left) columns)
|
||||||
(cdr rows-left)))))
|
(cdr rows-left)))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; Debouncing
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Debounce
|
||||||
|
|
||||||
|
;; This phase is responsible for filtering out spurious keypresses detected
|
||||||
|
;; by the matrix scan due to physical properties of switching logic.
|
||||||
|
|
||||||
;; Electrical contacts do not switch cleanly from high to low voltage; there is
|
;; Electrical contacts do not switch cleanly from high to low voltage; there is
|
||||||
;; a short period of "bounce" while the signal settles into its new position.
|
;; a short period of "bounce" while the signal settles into its new position.
|
||||||
|
@ -114,6 +138,8 @@
|
||||||
;; only considering the data we get trustworthy if we get the same value three
|
;; only considering the data we get trustworthy if we get the same value three
|
||||||
;; times in a row.
|
;; times in a row.
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define debounce-passes 3)
|
(define debounce-passes 3)
|
||||||
|
|
||||||
(define (debounce-matrix-aux last-scan passes-left)
|
(define (debounce-matrix-aux last-scan passes-left)
|
||||||
|
@ -127,7 +153,11 @@
|
||||||
(define (debounce-matrix)
|
(define (debounce-matrix)
|
||||||
(debounce-matrix-aux (list) debounce-passes))
|
(debounce-matrix-aux (list) debounce-passes))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; Press and release tracking
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Track press/release
|
||||||
|
|
||||||
|
;; This phase is responsible for comparing the current state of the keys to
|
||||||
|
;; the previous pass and interpreting which keys are newly pressed and which are
|
||||||
|
;; newly released.
|
||||||
|
|
||||||
;; If we didn't have layers, we'd be done now. But since we have layers, we
|
;; If we didn't have layers, we'd be done now. But since we have layers, we
|
||||||
;; can't assume a 1:1 mapping between keys pressed and keycodes we should send.
|
;; can't assume a 1:1 mapping between keys pressed and keycodes we should send.
|
||||||
|
@ -145,6 +175,8 @@
|
||||||
;; Because of this, it's necessary to track press and release on the level of
|
;; Because of this, it's necessary to track press and release on the level of
|
||||||
;; physical keys and only map it to keycodes when a new press is detected.
|
;; physical keys and only map it to keycodes when a new press is detected.
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; Which physical keys were pressed during the last scan?
|
;; Which physical keys were pressed during the last scan?
|
||||||
(define last-keys-down (vector #f #f #f #f #f #f #f #f #f #f))
|
(define last-keys-down (vector #f #f #f #f #f #f #f #f #f #f))
|
||||||
|
|
||||||
|
@ -182,11 +214,45 @@
|
||||||
(for-each remove-last-down (cdr p/r))
|
(for-each remove-last-down (cdr p/r))
|
||||||
p/r))
|
p/r))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; Generating Keycodes
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Layout lookup
|
||||||
|
|
||||||
;; Given keys that have been pressed, turn those into keycodes for our USB
|
;; This phase is responsible for taking keys that have been pressed and turning
|
||||||
;; frame. Given keys that are released, update the press/release tracking
|
;; those into keycodes for our USB frame, and also for taking the keys that
|
||||||
;; data to reflect them.
|
;; have been released and removing from the USB frame and press/release
|
||||||
|
;; tracking data.
|
||||||
|
|
||||||
|
;; In order to release keys consistently across layer changes, it's necessary
|
||||||
|
;; to store which physical keys are responsible for which keycodes being sent.
|
||||||
|
;; A key being released means that we stop sending the keycode that was bound
|
||||||
|
;; to that key when it was pressed, not the keycode bound to that key in the
|
||||||
|
;; current layer!
|
||||||
|
|
||||||
|
;; Data is stored in two vectors: modifiers and keycodes-down. Modifiers is of
|
||||||
|
;; length 4 because there are only 4 modifiers; (we ignore that left-shift
|
||||||
|
;; and right-shift can be distinguished). The keycodes-down vector is of length
|
||||||
|
;; 6 because that is defined in the USB standard as the number of non-modifier
|
||||||
|
;; keycodes that a single USB frame can represent.
|
||||||
|
|
||||||
|
;; If you have more than ten fingers, I'm sorry; try a different firmware.
|
||||||
|
|
||||||
|
;; The layout is defined in layout.scm as a vector of layer vectors. Each layer
|
||||||
|
;; vector is simply a vector of elements, arranged one row after another
|
||||||
|
;; corresponding to the physical keys.
|
||||||
|
|
||||||
|
;; Most of these elements are integers; these correspond to normal USB keycodes
|
||||||
|
;; as defined in keycodes.scm.
|
||||||
|
|
||||||
|
;; Some elements are lists; these indicate modifier keys. A list of length 1 is
|
||||||
|
;; simply a single modifier key, while a list of length 2 is a modifier plus a
|
||||||
|
;; non-modifier simultaneously. This is how we can define a ! key despite there
|
||||||
|
;; being no USB keycode for the ! character; it is defined as a combo of shift
|
||||||
|
;; and 1.
|
||||||
|
|
||||||
|
;; Finally some elements are Scheme procedures (aka functions). These procedures
|
||||||
|
;; get called with #t when they are first pressed and with #f when released.
|
||||||
|
;; These are mostly used for layer switching but could be used for anything.
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; Vectors to store keycodes for the USB frame we are preparing to send.
|
;; Vectors to store keycodes for the USB frame we are preparing to send.
|
||||||
(define modifiers (vector 0 0 0 0))
|
(define modifiers (vector 0 0 0 0))
|
||||||
|
@ -254,18 +320,14 @@
|
||||||
(release-modifier modifier-slot key 0)
|
(release-modifier modifier-slot key 0)
|
||||||
#f)))))
|
#f)))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;; SHOWTIME
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Send USB
|
||||||
|
|
||||||
;; Prepare the GPIO pins and initialize the USB connection.
|
;; This phase is responsible for the initialization, the main loop, and
|
||||||
(define (init)
|
;; actually sending the USB frame to the host once it has been calculated.
|
||||||
(set! current-layer (vector-ref layers 0))
|
|
||||||
(for-each-vector output row-pins)
|
|
||||||
(for-each-vector high row-pins)
|
|
||||||
(for-each-vector input column-pins)
|
|
||||||
(for-each-vector high column-pins) ; activate pullup resistors
|
|
||||||
|
|
||||||
(call-c-func "usb_init")
|
;; Not much left to do here; just tying up loose ends bringing it all together.
|
||||||
(pause 200))
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; Take press/release data and set USB keycodes and modifiers.
|
;; Take press/release data and set USB keycodes and modifiers.
|
||||||
(define (set-usb-frame press/release)
|
(define (set-usb-frame press/release)
|
||||||
|
@ -283,10 +345,24 @@
|
||||||
|
|
||||||
;; Scan the matrix, determine the appropriate keycodes, and send them.
|
;; Scan the matrix, determine the appropriate keycodes, and send them.
|
||||||
(define (loop)
|
(define (loop)
|
||||||
|
;; Microscheme doesn't have garbage collection; it has you preallocate
|
||||||
|
;; everything you can, and then run your code that might allocate more memory
|
||||||
|
;; inside this `free!' macro. When you enter this macro, the heap pointer gets
|
||||||
|
;; saved, and when you leave, it gets set back to the point it was previously,
|
||||||
|
;; effectively garbage-collecting any allocations which happened inside the
|
||||||
|
;; macro in one fell swoop. Primitive, but effective.
|
||||||
(free! (let ((keys-scanned (debounce-matrix)))
|
(free! (let ((keys-scanned (debounce-matrix)))
|
||||||
(set-usb-frame (press/release-for keys-scanned))
|
(set-usb-frame (press/release-for keys-scanned))
|
||||||
(apply usb-send (cons modifiers (vector->list keycodes-down)))))
|
(apply usb-send (cons modifiers (vector->list keycodes-down)))))
|
||||||
(loop))
|
(loop))
|
||||||
|
|
||||||
(init)
|
;; Prepare the GPIO pins.
|
||||||
|
(for-each-vector output row-pins)
|
||||||
|
(for-each-vector high row-pins)
|
||||||
|
(for-each-vector input column-pins)
|
||||||
|
(for-each-vector high column-pins) ; activate pullup resistors
|
||||||
|
|
||||||
|
;; Initialize the USB connection and go!
|
||||||
|
(call-c-func "usb_init")
|
||||||
|
(pause 200)
|
||||||
(loop)
|
(loop)
|
||||||
|
|
Loading…
Reference in a new issue