Compare commits
226 Commits
master
...
partial-re
Author | SHA1 | Date |
---|---|---|
Ben Blazak | ebb353b3f2 | |
Ben Blazak | 73341cd9a1 | |
Ben Blazak | 737428aec8 | |
Ben Blazak | 84fa10fb5f | |
Ben Blazak | d384925813 | |
Ben Blazak | cff9b31a76 | |
Ben Blazak | a67d215524 | |
Ben Blazak | 8342b0be46 | |
Ben Blazak | 620221bd20 | |
Ben Blazak | 203cb66b26 | |
Ben Blazak | f3635dc579 | |
Ben Blazak | c32f396e31 | |
Ben Blazak | 5e931a7481 | |
Ben Blazak | f980028d82 | |
Ben Blazak | 74c685a8b4 | |
Ben Blazak | c5afdb9659 | |
Ben Blazak | 24f4aca7c8 | |
Ben Blazak | e8ad0d91c0 | |
Ben Blazak | b099061e6a | |
Ben Blazak | 0fd92b5840 | |
Ben Blazak | 49e8f32a60 | |
Ben Blazak | 5647b58120 | |
Ben Blazak | 5ee88643fd | |
Ben Blazak | 5d35f2ed44 | |
Ben Blazak | d2d0e1ffab | |
Ben Blazak | 79fec2cff0 | |
Ben Blazak | 47038151ce | |
Ben Blazak | a353e8a5d1 | |
Ben Blazak | c50b98922c | |
Ben Blazak | 881bdf6f94 | |
Ben Blazak | 3219738831 | |
Ben Blazak | 1ed4e02e5d | |
Ben Blazak | faf96737a0 | |
Ben Blazak | 3aec8246ea | |
Ben Blazak | 57edc24c49 | |
Ben Blazak | b5101045be | |
Ben Blazak | b58439380a | |
Ben Blazak | db52b78324 | |
Ben Blazak | e83f2a3325 | |
Ben Blazak | fdc5d836c3 | |
Ben Blazak | 0c0efafd7b | |
Ben Blazak | 4a5014d3a3 | |
Ben Blazak | 90f598ebe5 | |
Ben Blazak | bcdbc2e37e | |
Ben Blazak | 24884d470a | |
Ben Blazak | d607d7de3e | |
Ben Blazak | 9df12828d0 | |
Ben Blazak | eec0923dca | |
Ben Blazak | b41a716631 | |
Ben Blazak | f0fcfca4ef | |
Ben Blazak | 55e96d5413 | |
Ben Blazak | 6b9d6916b2 | |
Ben Blazak | 6990d46c60 | |
Ben Blazak | 9622b1af1d | |
Ben Blazak | 90f6b2c867 | |
Ben Blazak | d5ae5e003b | |
Ben Blazak | bae1ae882a | |
Ben Blazak | a90b6103a7 | |
Ben Blazak | 14cdab0868 | |
Ben Blazak | 9553dca085 | |
Ben Blazak | f8c5d7eb3c | |
Ben Blazak | ab74c80758 | |
Ben Blazak | c7350e1435 | |
Ben Blazak | 7c44123178 | |
Ben Blazak | 5fb869cf60 | |
Ben Blazak | f630e3c6f5 | |
Ben Blazak | d73b58e23d | |
Ben Blazak | 3cd6d819df | |
Ben Blazak | ec59f2ecd0 | |
Ben Blazak | d8c3572ed1 | |
Ben Blazak | f9e473b929 | |
Ben Blazak | 43b304c66d | |
Ben Blazak | 725e08e8c7 | |
Ben Blazak | ef054fa0c7 | |
Ben Blazak | 242d04c9f1 | |
Ben Blazak | fbbd4570d4 | |
Ben Blazak | 6a13099a71 | |
Ben Blazak | e4d12764a8 | |
Ben Blazak | 9ba3061fcb | |
Ben Blazak | 6a85102a3b | |
Ben Blazak | be0c70cf15 | |
Ben Blazak | 2f1eec1040 | |
Ben Blazak | 6a4d062f7d | |
Ben Blazak | f90d02f9c3 | |
Ben Blazak | 0bf0181ffa | |
Ben Blazak | a32a9b59d6 | |
Ben Blazak | 2f628df002 | |
Ben Blazak | c07f7c56e9 | |
Ben Blazak | a72e6ba4bc | |
Ben Blazak | 57c43089ec | |
Ben Blazak | c6944a095e | |
Ben Blazak | 9796e78efb | |
Ben Blazak | a05c0e42bd | |
Ben Blazak | b5ffe83a38 | |
Ben Blazak | 6727ed89b9 | |
Ben Blazak | 69252d682e | |
Ben Blazak | 9ace7653c1 | |
Ben Blazak | 82c6cbf4f0 | |
Ben Blazak | 585503ac14 | |
Ben Blazak | 68649988cc | |
Ben Blazak | 952c2792ed | |
Ben Blazak | a89e6528f2 | |
Ben Blazak | 36edee2eb7 | |
Ben Blazak | 559fe97d7e | |
Ben Blazak | aec1bac3bb | |
Ben Blazak | 4d50761b0d | |
Ben Blazak | c24adb542c | |
Ben Blazak | 0a9f4ec61d | |
Ben Blazak | b8156b4329 | |
Ben Blazak | f6252243cc | |
Ben Blazak | 30da4f3d31 | |
Ben Blazak | be8c826e90 | |
Ben Blazak | d280078435 | |
Ben Blazak | 0f72931b12 | |
Ben Blazak | 950bddd579 | |
Ben Blazak | 9ce41f8bc5 | |
Ben Blazak | f96b75a4f8 | |
Ben Blazak | b9ff36161e | |
Ben Blazak | 1f4dd59ea1 | |
Ben Blazak | a49110b2fc | |
Ben Blazak | 3959e83f38 | |
Ben Blazak | e5c3490dd6 | |
Ben Blazak | 58dd5ab239 | |
Ben Blazak | 3c99158e55 | |
Ben Blazak | b7e03df76b | |
Ben Blazak | 0ab9483b94 | |
Ben Blazak | 56cbc8cb1b | |
Ben Blazak | 0c23c45627 | |
Ben Blazak | 8867e1b9d2 | |
Ben Blazak | 8b5daa9d92 | |
Ben Blazak | 3a2af3a9dd | |
Ben Blazak | 3582dfbc65 | |
Ben Blazak | e82c23b21c | |
Ben Blazak | 681d51ffb1 | |
Ben Blazak | 10959ee7b9 | |
Ben Blazak | 29446dfb24 | |
Ben Blazak | 115ef0ff4f | |
Ben Blazak | d8e62debf7 | |
Ben Blazak | ad6a934f3a | |
Ben Blazak | 1905f05263 | |
Ben Blazak | 79cda0e3c4 | |
Ben Blazak | db74d1b26a | |
Ben Blazak | 76eaa117ce | |
Ben Blazak | 4e1bd12990 | |
Ben Blazak | 71d541be2f | |
Ben Blazak | 58255dc29b | |
Ben Blazak | f2acad70a4 | |
Ben Blazak | e560e476f7 | |
Ben Blazak | 1197f4d682 | |
Ben Blazak | d3e9291d49 | |
Ben Blazak | 4628061b53 | |
Ben Blazak | 193f443604 | |
Ben Blazak | 0b6103d6c3 | |
Ben Blazak | 7371362576 | |
Ben Blazak | 758e95b17e | |
Ben Blazak | b5b5db12a7 | |
Ben Blazak | 203aaf415b | |
Ben Blazak | 1deed9d9ac | |
Ben Blazak | a3445a539b | |
Ben Blazak | f74293d630 | |
Ben Blazak | 36ed9d1974 | |
Ben Blazak | a03b9ec2a1 | |
Ben Blazak | ff2e90997d | |
Ben Blazak | 49f3d4bdd5 | |
Ben Blazak | 65ecf576d3 | |
Ben Blazak | 414158768b | |
Ben Blazak | f031c99c1d | |
Ben Blazak | 9724cf9331 | |
Ben Blazak | a7d11e0af2 | |
Ben Blazak | 5e1877cb82 | |
Ben Blazak | 75e0336f6b | |
Ben Blazak | 467615a4b6 | |
Ben Blazak | ca56ea4647 | |
Ben Blazak | 20f9737b49 | |
Ben Blazak | 580c5e75a9 | |
Ben Blazak | 1130549da0 | |
Ben Blazak | eab3d541bf | |
Ben Blazak | f8f8bef3c1 | |
Ben Blazak | f9dfb539d1 | |
Ben Blazak | 058981cb83 | |
Ben Blazak | a210660b24 | |
Ben Blazak | 81e7e32dbe | |
Ben Blazak | a6ec78cd34 | |
Ben Blazak | 316f54596e | |
Ben Blazak | 0774d0dd09 | |
Ben Blazak | f47ab1315e | |
Ben Blazak | a12a83b555 | |
Ben Blazak | a5e2c20a84 | |
Ben Blazak | e4b1717657 | |
Ben Blazak | bf13abc274 | |
Ben Blazak | 98970c0698 | |
Ben Blazak | e3df599b85 | |
Ben Blazak | 30ad5663d2 | |
Ben Blazak | 9ac5701e7e | |
Ben Blazak | e8bec2c0a2 | |
Ben Blazak | 43d849606a | |
Ben Blazak | 75a987d5a8 | |
Ben Blazak | 03c07e0a99 | |
Ben Blazak | f7ba0b6497 | |
Ben Blazak | 76c4bb6c1f | |
Ben Blazak | c6d9875d5d | |
Ben Blazak | b1b04419e1 | |
Ben Blazak | fb55fcfb7d | |
Ben Blazak | 2964e68840 | |
Ben Blazak | e036276dd0 | |
Ben Blazak | 02fd5471ae | |
Ben Blazak | 7eec5fb237 | |
Ben Blazak | b37f69824c | |
Ben Blazak | b63e4ef0f0 | |
Ben Blazak | 38e3ff49f8 | |
Ben Blazak | a4696d8641 | |
Ben Blazak | ad0ff14ca6 | |
Ben Blazak | 1db6d4dd37 | |
Ben Blazak | 94e18d611d | |
Ben Blazak | 35bfca3662 | |
Ben Blazak | 0129fcae47 | |
Ben Blazak | be26a411f2 | |
Ben Blazak | f70568c1c0 | |
Ben Blazak | 9bb6198878 | |
Ben Blazak | c85dfd2aa6 | |
Ben Blazak | b409cf1b1e | |
Ben Blazak | b5e3c41751 | |
Ben Blazak | f6aa18498a | |
Ben Blazak | 88111197c3 | |
Ben Blazak | 5ed574cd85 | |
Ben Blazak | 778e400868 |
|
@ -0,0 +1,2 @@
|
|||
/local - *
|
||||
|
|
@ -1,423 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (MIT) (see "license.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Generate a depiction of the layout (in html + svg)
|
||||
|
||||
Depends on:
|
||||
- the UI info file (in JSON)
|
||||
"""
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
class Namespace():
|
||||
pass
|
||||
|
||||
template = Namespace()
|
||||
doc = Namespace()
|
||||
info = Namespace()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description = "Generate a picture of the firmware's "
|
||||
+ "keyboard layout" )
|
||||
|
||||
arg_parser.add_argument(
|
||||
'--ui-info-file',
|
||||
required = True )
|
||||
|
||||
args = arg_parser.parse_args(sys.argv[1:])
|
||||
|
||||
# constant file paths
|
||||
args.template_svg_file = './build-scripts/gen_layout/template.svg'
|
||||
args.template_js_file = './build-scripts/gen_layout/template.js'
|
||||
|
||||
# normalize paths
|
||||
args.ui_info_file = os.path.abspath(args.ui_info_file)
|
||||
args.template_svg_file = os.path.abspath(args.template_svg_file)
|
||||
args.template_js_file = os.path.abspath(args.template_js_file)
|
||||
|
||||
# set vars
|
||||
doc.main = '' # to store the html document we're generating
|
||||
template.svg = open(args.template_svg_file).read()
|
||||
template.js = open(args.template_js_file).read()
|
||||
info.all = json.loads(open(args.ui_info_file).read())
|
||||
|
||||
info.matrix_positions = info.all['mappings']['matrix-positions']
|
||||
info.matrix_layout = info.all['mappings']['matrix-layout']
|
||||
|
||||
# prefix
|
||||
doc.prefix = ("""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script>
|
||||
"""
|
||||
+ template.js +
|
||||
""" </script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Firmware Layout</h1>
|
||||
|
||||
<ul>
|
||||
<li>git commit date:
|
||||
<code>""" + info.all['miscellaneous']['git-commit-date'] + """</code></li>
|
||||
<li>git commit id:
|
||||
<code>""" + info.all['miscellaneous']['git-commit-id'] + """</code></li>
|
||||
</ul>
|
||||
|
||||
<h2>Notes</h2>
|
||||
|
||||
<ul>
|
||||
<li>Layer keys are labeled e.g. <code>la 2 +- 2</code>
|
||||
<ul>
|
||||
<li><code>la</code> is for "layer"</li>
|
||||
<li><code>2</code> indicates that the second pair of push|pop functions is
|
||||
being used (differently numbered pairs won't interfere with each
|
||||
other)</li>
|
||||
<li><code>+</code> indicates that the layer is being "pushed" onto the
|
||||
stack at some point, either when the key is pressed or when it is
|
||||
released</li>
|
||||
<li><code>-</code> indicates that the layer is being "popped" off of the
|
||||
stack at some point</li>
|
||||
<li>the last <code>2</code> indicates the layer-number that will be
|
||||
activated on "push".</li>
|
||||
</ul>
|
||||
See the project 'readme.md' file on <a
|
||||
href='https://github.com/benblazak/ergodox-firmware'>the github page</a> as a
|
||||
starting point for learning more about how this firmware implements
|
||||
layers.</li>
|
||||
<br>
|
||||
<li>Shifted keys are labeled with an <code>sh</code> at the beginning. This
|
||||
indicates that a 'Shift' is generated with that keypress, the same as if you
|
||||
had held down 'Shift', and pressed that key.</li>
|
||||
<br>
|
||||
<li><code>(null)</code> indicates that no keypress or keyrelease will be
|
||||
generated for that key, on that layer.</li>
|
||||
<br>
|
||||
<li>Blank keys indicate "transparency": if you press that key on that layer,
|
||||
the key will act as if it was on whatever layer is active below the current
|
||||
one.</li>
|
||||
<br>
|
||||
<li>Some keys may be labled with special functions (like
|
||||
<code>[btldr]</code>, which tells the Teensy to wait for the host to send it
|
||||
a new firmware).</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
|
||||
""")[1:-1]
|
||||
|
||||
# suffix
|
||||
doc.suffix = ("""
|
||||
</body>
|
||||
</html>
|
||||
|
||||
""")[1:-1]
|
||||
|
||||
# substitute into template
|
||||
# -------
|
||||
# note: this is not general enough to handle any possible layout well, at
|
||||
# the moment. but it should handle more standard ones well. (hopefully
|
||||
# minor) modifications may be necessary on a case by case basis
|
||||
# -------
|
||||
layer_number = -1
|
||||
for (layout, layer) in zip( info.matrix_layout,
|
||||
range(len(info.matrix_layout))):
|
||||
layer_number += 1
|
||||
svg = template.svg
|
||||
for (name, (code, press, release)) \
|
||||
in zip(info.matrix_positions, layout):
|
||||
replace = ''
|
||||
if press == 'kbfun_transparent':
|
||||
replace = ''
|
||||
elif press == 'kbfun_shift_press_release':
|
||||
replace = 'sh ' + keycode_to_string.get(code, '[n/a]')
|
||||
elif press == 'kbfun_jump_to_bootloader':
|
||||
replace = '[btldr]'
|
||||
elif press == 'NULL' and release == 'NULL':
|
||||
replace = '(null)'
|
||||
elif re.search(r'numpad', press+release):
|
||||
replace = '[num]'
|
||||
elif re.search(r'layer', press+release):
|
||||
replace = 'la ' + re.findall(r'\d+', press+release)[0] + ' '
|
||||
if re.search(r'push', press+release):
|
||||
replace += '+'
|
||||
if re.search(r'pop', press+release):
|
||||
replace += '-'
|
||||
replace += ' ' + str(code)
|
||||
else:
|
||||
replace = keycode_to_string.get(code, '[n/a]')
|
||||
|
||||
svg = re.sub(
|
||||
'>'+name+'<', '>'+replace+'<', svg )
|
||||
svg = re.sub(
|
||||
r"\('(" + name + r".*)'\)",
|
||||
r"('\1', " + str(layer) + r")",
|
||||
svg )
|
||||
|
||||
doc.main += '<h2>Layer ' + str(layer_number) + '</h2>\n' + svg
|
||||
|
||||
# change the font size
|
||||
doc.main = re.sub(r'22.5px', '15px', doc.main)
|
||||
|
||||
print(doc.prefix + doc.main + doc.suffix)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
keycode_to_string = {
|
||||
0x01: "Error", # ErrorRollOver
|
||||
0x02: "POSTFail",
|
||||
0x03: "Error", # ErrorUndefined
|
||||
0x04: "a A",
|
||||
0x05: "b B",
|
||||
0x06: "c C",
|
||||
0x07: "d D",
|
||||
0x08: "e E",
|
||||
0x09: "f F",
|
||||
0x0A: "g G",
|
||||
0x0B: "h H",
|
||||
0x0C: "i I",
|
||||
0x0D: "j J",
|
||||
0x0E: "k K",
|
||||
0x0F: "l L",
|
||||
0x10: "m M",
|
||||
0x11: "n N",
|
||||
0x12: "o O",
|
||||
0x13: "p P",
|
||||
0x14: "q Q",
|
||||
0x15: "r R",
|
||||
0x16: "s S",
|
||||
0x17: "t T",
|
||||
0x18: "u U",
|
||||
0x19: "v V",
|
||||
0x1A: "w W",
|
||||
0x1B: "x X",
|
||||
0x1C: "y Y",
|
||||
0x1D: "z Z",
|
||||
0x1E: "1 !",
|
||||
0x1F: "2 @",
|
||||
0x20: "3 #",
|
||||
0x21: "4 $",
|
||||
0x22: "5 %",
|
||||
0x23: "6 ^",
|
||||
0x24: "7 &",
|
||||
0x25: "8 *",
|
||||
0x26: "9 (",
|
||||
0x27: "0 )",
|
||||
0x28: "Return",
|
||||
0x29: "Esc",
|
||||
0x2A: "Backspace",
|
||||
0x2B: "Tab",
|
||||
0x2C: "Space",
|
||||
0x2D: "- _",
|
||||
0x2E: "= +",
|
||||
0x2F: "[ {",
|
||||
0x30: "] }",
|
||||
0x31: "\ |",
|
||||
0x32: "# ~",
|
||||
0x33: "; :",
|
||||
0x34: "\' \"",
|
||||
0x35: "` ~",
|
||||
0x36: ", <",
|
||||
0x37: ". >",
|
||||
0x38: "/ ?",
|
||||
0x39: "Caps",
|
||||
0x3A: "F1",
|
||||
0x3B: "F2",
|
||||
0x3C: "F3",
|
||||
0x3D: "F4",
|
||||
0x3E: "F5",
|
||||
0x3F: "F6",
|
||||
0x40: "F7",
|
||||
0x41: "F8",
|
||||
0x42: "F9",
|
||||
0x43: "F10",
|
||||
0x44: "F11",
|
||||
0x45: "F12",
|
||||
0x46: "PrintScreen",
|
||||
0x47: "ScrollLock",
|
||||
0x48: "Pause",
|
||||
0x49: "Ins", # Insert
|
||||
0x4A: "Hm", # Home
|
||||
0x4B: "Pg\u2191", # up arrow
|
||||
0x4C: "Delete",
|
||||
0x4D: "End",
|
||||
0x4E: "Pg\u2193", # down arrow
|
||||
0x4F: "\u2192", # right arrow
|
||||
0x50: "\u2190", # left arrow
|
||||
0x51: "\u2193", # down arrow
|
||||
0x52: "\u2191", # up arrow
|
||||
|
||||
0x53: "Num",
|
||||
0x54: "/",
|
||||
0x55: "*",
|
||||
0x56: "-",
|
||||
0x57: "+",
|
||||
0x58: "Enter",
|
||||
0x59: "1 End",
|
||||
0x5A: "2 \u2193", # down arrow
|
||||
0x5B: "3 Pg\u2193", # down arrow
|
||||
0x5C: "4 \u2190", # left arrow
|
||||
0x5D: "5",
|
||||
0x5E: "6 \u2192", # right arrow
|
||||
0x5F: "7 Hm", # Home
|
||||
0x60: "8 \u2191", # up arrow
|
||||
0x61: "9 Pg\u2191", # up arrow
|
||||
0x62: "0 Ins", # Insert
|
||||
0x63: ". Del",
|
||||
|
||||
0x64: "\ |",
|
||||
0x65: "App",
|
||||
0x66: "Power",
|
||||
|
||||
0x67: "=",
|
||||
|
||||
0x68: "F13",
|
||||
0x69: "F14",
|
||||
0x6A: "F15",
|
||||
0x6B: "F16",
|
||||
0x6C: "F17",
|
||||
0x6D: "F18",
|
||||
0x6E: "F19",
|
||||
0x6F: "F20",
|
||||
0x70: "F21",
|
||||
0x71: "F22",
|
||||
0x72: "F23",
|
||||
0x73: "F24",
|
||||
0x74: "Exec",
|
||||
0x75: "Help",
|
||||
0x76: "Menu",
|
||||
0x77: "Select",
|
||||
0x78: "Stop",
|
||||
0x79: "Again",
|
||||
0x7A: "Undo",
|
||||
0x7B: "Cut",
|
||||
0x7C: "Copy",
|
||||
0x7D: "Paste",
|
||||
0x7E: "Find",
|
||||
0x7F: "Mute",
|
||||
0x80: "VolUp",
|
||||
0x81: "VolDown",
|
||||
0x82: "LockingCapsLock",
|
||||
0x83: "LockingNumLock",
|
||||
0x84: "LockingScrollLock",
|
||||
|
||||
0x85: ",",
|
||||
0x86: "=",
|
||||
|
||||
0x87: "Int1",
|
||||
0x88: "Int2",
|
||||
0x89: "Int3",
|
||||
0x8A: "Int4",
|
||||
0x8B: "Int5",
|
||||
0x8C: "Int6",
|
||||
0x8D: "Int7",
|
||||
0x8E: "Int8",
|
||||
0x8F: "Int9",
|
||||
0x90: "LANG1",
|
||||
0x91: "LANG2",
|
||||
0x92: "LANG3",
|
||||
0x93: "LANG4",
|
||||
0x94: "LANG5",
|
||||
0x95: "LANG6",
|
||||
0x96: "LANG7",
|
||||
0x97: "LANG8",
|
||||
0x98: "LANG9",
|
||||
0x99: "AlternateErase",
|
||||
0x9A: "SysReq_Attention",
|
||||
0x9B: "Cancel",
|
||||
0x9C: "Clear",
|
||||
0x9D: "Prior",
|
||||
0x9E: "Return",
|
||||
0x9F: "Separator",
|
||||
0xA0: "Out",
|
||||
0xA1: "Oper",
|
||||
0xA2: "Clear_Again",
|
||||
0xA3: "CrSel_Props",
|
||||
0xA4: "ExSel",
|
||||
|
||||
0xB0: "00",
|
||||
0xB1: "000",
|
||||
|
||||
0xB2: "Thousands_Sep",
|
||||
0xB3: "Decimal_Sep",
|
||||
0xB4: "$",
|
||||
0xB5: "Currency_Subunit",
|
||||
|
||||
0xB6: "(",
|
||||
0xB7: ")",
|
||||
0xB8: "{",
|
||||
0xB9: "}",
|
||||
|
||||
0xBA: "Tab",
|
||||
0xBB: "Backspace",
|
||||
0xBC: "A",
|
||||
0xBD: "B",
|
||||
0xBE: "C",
|
||||
0xBF: "D",
|
||||
0xC0: "E",
|
||||
0xC1: "F",
|
||||
0xC2: "XOR",
|
||||
0xC3: "^",
|
||||
0xC4: "%",
|
||||
0xC5: "<",
|
||||
0xC6: ">",
|
||||
0xC7: "&",
|
||||
0xC8: "&&",
|
||||
0xC9: "|",
|
||||
0xCA: "||",
|
||||
0xCB: ":",
|
||||
0xCC: "#",
|
||||
0xCD: "Space",
|
||||
0xCE: "@",
|
||||
0xCF: "!",
|
||||
0xD0: "Mem_Store",
|
||||
0xD1: "Mem_Recall",
|
||||
0xD2: "Mem_Clear",
|
||||
0xD3: "Mem_+",
|
||||
0xD4: "Mem_-",
|
||||
0xD5: "Mem_*",
|
||||
0xD6: "Mem_/",
|
||||
0xD7: "+-",
|
||||
0xD8: "Clear",
|
||||
0xD9: "ClearEntry",
|
||||
0xDA: "Binary",
|
||||
0xDB: "Octal",
|
||||
0xDC: ".",
|
||||
0xDD: "Hexadecimal",
|
||||
|
||||
0xE0: "L-Ctrl",
|
||||
0xE1: "L-Shift",
|
||||
0xE2: "L-Alt",
|
||||
0xE3: "L-GUI",
|
||||
0xE4: "R-Ctrl",
|
||||
0xE5: "R-Shift",
|
||||
0xE6: "R-Alt",
|
||||
0xE7: "R-GUI",
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,475 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (MIT) (see "license.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Generate UI info file (in JSON)
|
||||
|
||||
Depends on:
|
||||
- the project source code
|
||||
- the project '.map' file (generated by the compiler)
|
||||
"""
|
||||
|
||||
_FORMAT_DESCRIPTION = ("""
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Version 0
|
||||
* ----------------------------------------------------------------------------
|
||||
* Hopefully the add-hoc conventions are clear enough... I didn't feel like
|
||||
* investing the time in making it a real JSON Schema when there aren't many
|
||||
* validators, and the most current completed draft at the moment (draft 3) is
|
||||
* expired...
|
||||
* ----------------------------------------------------------------------------
|
||||
* Please note that in general, fields may be added without changing the
|
||||
* version number, and that programs using this format are not required to fill
|
||||
* (or read) any of the given fields.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
var ui_info = {
|
||||
".meta-data": { // for the JSON file
|
||||
"version": "<number>",
|
||||
"date-generated": "<string>", // format: RFC 3339
|
||||
"description": "<string>",
|
||||
},
|
||||
"keyboard-functions": {
|
||||
"<(function name)>": {
|
||||
"position": "<number>", // as given by the .map file
|
||||
"length": "<number>", // as given by the .map file
|
||||
"comments": {
|
||||
"name": "<string>", // more user friendly name
|
||||
"description": "<string>",
|
||||
"notes": [
|
||||
"<string>",
|
||||
"..."
|
||||
],
|
||||
"..."
|
||||
}
|
||||
},
|
||||
"..."
|
||||
},
|
||||
"layout-matrices": {
|
||||
"<(matrix name)>": {
|
||||
"position": "<number>", // as given by the .map file
|
||||
"length": "<number>" // as given by the .map file
|
||||
},
|
||||
"..."
|
||||
},
|
||||
"mappings": {
|
||||
/*
|
||||
* The mappings prefixed with 'matrix' have their elements in the same
|
||||
* order as the .hex file (whatever order that is). The mappings
|
||||
* prefixed with 'physical' will have their elements in an order
|
||||
* corresponding to thier physical position on the keyboard. You can
|
||||
* convert between the two using the relative positions of the key-ids
|
||||
* in 'physical-positions' and 'matrix-positions'.
|
||||
*
|
||||
* The current order of 'physical' mappings is:
|
||||
* --------------------------------------------
|
||||
* // left hand, spatial positions
|
||||
* 00, 01, 02, 03, 04, 05, 06,
|
||||
* 07, 08, 09, 10, 11, 12, 13,
|
||||
* 14, 15, 16, 17, 18, 19,
|
||||
* 20, 21, 22, 23, 24, 25, 26,
|
||||
* 27, 28, 29, 30, 31,
|
||||
* 32, 33,
|
||||
* 34, 35, 36,
|
||||
* 37, 38, 39,
|
||||
|
||||
* // right hand, spatial positions
|
||||
* 40, 41, 42, 43, 44, 45, 46,
|
||||
* 47, 48, 49, 50, 51, 52, 53,
|
||||
* 54, 55, 56, 57, 58, 59,
|
||||
* 60, 61, 62, 63, 64, 65, 66,
|
||||
* 67, 68, 69, 70, 71,
|
||||
* 72, 73,
|
||||
* 74, 75, 76,
|
||||
* 77, 78, 79,
|
||||
* --------------------------------------------
|
||||
*/
|
||||
|
||||
"physical-positions": [ // list of key-ids
|
||||
"<string>", "..."
|
||||
],
|
||||
"matrix-positions": [ // list of key-ids
|
||||
"<string>", "..."
|
||||
],
|
||||
"matrix-layout": [
|
||||
[ // begin layer
|
||||
[ // begin key
|
||||
"<number>", // keycode
|
||||
"<string>", // press function name (ex: 'kbfun_...')
|
||||
"<string>" // release function name (ex: 'NULL')
|
||||
],
|
||||
"..." // more keys
|
||||
],
|
||||
"..." // more layers
|
||||
]
|
||||
},
|
||||
"miscellaneous": {
|
||||
"git-commit-date": "<string>", // format: RFC 3339
|
||||
"git-commit-id": "<string>",
|
||||
"number-of-layers": "<number>"
|
||||
}
|
||||
}
|
||||
""")[1:-1]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def gen_static(current_date=None, git_commit_date=None, git_commit_id=None):
|
||||
"""Generate static information"""
|
||||
|
||||
return {
|
||||
'.meta-data': {
|
||||
'version': 0, # the format version number
|
||||
'date-generated': current_date,
|
||||
'description': _FORMAT_DESCRIPTION,
|
||||
},
|
||||
'miscellaneous': {
|
||||
'git-commit-date': git_commit_date, # should be passed by makefile
|
||||
'git-commit-id': git_commit_id, # should be passed by makefile
|
||||
},
|
||||
}
|
||||
|
||||
def gen_derived(data):
|
||||
"""
|
||||
Generate derived information
|
||||
Should be called last
|
||||
"""
|
||||
return {
|
||||
'miscellaneous': {
|
||||
'number-of-layers':
|
||||
int( data['layout-matrices']['_kb_layout']['length']/(6*14) ),
|
||||
# because 6*14 is the number of bytes/layer for '_kb_layout'
|
||||
# (which is a uint8_t matrix)
|
||||
},
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def parse_mapfile(map_file_path):
|
||||
"""Parse the '.map' file"""
|
||||
|
||||
def parse_keyboard_function(f, line):
|
||||
"""Parse keyboard-functions in the '.map' file"""
|
||||
|
||||
search = re.search(r'(0x\S+)\s+(0x\S+)', next(f))
|
||||
position = int( search.group(1), 16 )
|
||||
length = int( search.group(2), 16 )
|
||||
|
||||
search = re.search(r'0x\S+\s+(\S+)', next(f))
|
||||
name = search.group(1)
|
||||
|
||||
return {
|
||||
'keyboard-functions': {
|
||||
name: {
|
||||
'position': position,
|
||||
'length': length,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def parse_layout_matrices(f, line):
|
||||
"""Parse layout matrix information in the '.map' file"""
|
||||
|
||||
name = re.search(r'.progmem.data.(_kb_layout\S*)', line).group(1)
|
||||
|
||||
search = re.search(r'(0x\S+)\s+(0x\S+)', next(f))
|
||||
position = int( search.group(1), 16 )
|
||||
length = int( search.group(2), 16 )
|
||||
|
||||
return {
|
||||
'layout-matrices': {
|
||||
name: {
|
||||
'position': position,
|
||||
'length': length,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# --- parse_mapfile() ---
|
||||
|
||||
# normalize paths
|
||||
map_file_path = os.path.abspath(map_file_path)
|
||||
# check paths
|
||||
if not os.path.exists(map_file_path):
|
||||
raise ValueError("invalid 'map_file_path' given")
|
||||
|
||||
output = {}
|
||||
|
||||
f = open(map_file_path)
|
||||
|
||||
for line in f:
|
||||
if re.search(r'^\s*\.text\.kbfun_', line):
|
||||
dict_merge(output, parse_keyboard_function(f, line))
|
||||
elif re.search(r'^\s*\.progmem\.data.*layout', line):
|
||||
dict_merge(output, parse_layout_matrices(f, line))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def find_keyboard_functions(source_code_path):
|
||||
"""Parse all files in the source directory"""
|
||||
|
||||
def read_comments(f, line):
|
||||
"""
|
||||
Read in properly formatted multi-line comments
|
||||
- Comments must start with '/*' and end with '*/', each on their own
|
||||
line
|
||||
"""
|
||||
comments = ''
|
||||
while(line.strip() != r'*/'):
|
||||
comments += line[2:].strip()+'\n'
|
||||
line = next(f)
|
||||
return comments
|
||||
|
||||
def parse_comments(comments):
|
||||
"""
|
||||
Parse an INI style comment string
|
||||
- Fields begin with '[field-name]', and continue until the next field,
|
||||
or the end of the comment
|
||||
- Fields '[name]', '[description]', and '[note]' are treated specially
|
||||
"""
|
||||
|
||||
def add_field(output, field, value):
|
||||
"""Put a field+value pair in 'output', the way we want it, if the
|
||||
pair is valid"""
|
||||
|
||||
value = value.strip()
|
||||
|
||||
if field is not None:
|
||||
if field in ('name', 'description'):
|
||||
if field not in output:
|
||||
output[field] = value
|
||||
else:
|
||||
if field == 'note':
|
||||
field = 'notes'
|
||||
|
||||
if field not in output:
|
||||
output[field] = []
|
||||
|
||||
output[field] += [value]
|
||||
|
||||
# --- parse_comments() ---
|
||||
|
||||
output = {}
|
||||
|
||||
field = None
|
||||
value = None
|
||||
for line in comments.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
if re.search(r'^\[.*\]$', line):
|
||||
add_field(output, field, value)
|
||||
field = line[1:-1]
|
||||
value = None
|
||||
|
||||
else:
|
||||
if value is None:
|
||||
value = ''
|
||||
if len(value) > 0 and value[-1] == '.':
|
||||
line = ' '+line
|
||||
value += ' '+line
|
||||
|
||||
add_field(output, field, value)
|
||||
|
||||
return output
|
||||
|
||||
def parse_keyboard_function(f, line, comments):
|
||||
"""Parse keyboard-functions in the source code"""
|
||||
|
||||
search = re.search(r'void\s+(kbfun_\S+)\s*\(void\)', line)
|
||||
name = search.group(1)
|
||||
|
||||
return {
|
||||
'keyboard-functions': {
|
||||
name: {
|
||||
'comments': parse_comments(comments),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# --- find_keyboard_functions() ---
|
||||
|
||||
# normalize paths
|
||||
source_code_path = os.path.abspath(source_code_path)
|
||||
# check paths
|
||||
if not os.path.exists(source_code_path):
|
||||
raise ValueError("invalid 'source_code_path' given")
|
||||
|
||||
output = {}
|
||||
|
||||
for tup in os.walk(source_code_path):
|
||||
for file_name in tup[2]:
|
||||
# normalize paths
|
||||
file_name = os.path.abspath( os.path.join( tup[0], file_name ) )
|
||||
|
||||
# ignore non '.c' files
|
||||
if file_name[-2:] != '.c':
|
||||
continue
|
||||
|
||||
f = open(file_name)
|
||||
|
||||
comments = ''
|
||||
for line in f:
|
||||
if line.strip() == r'/*':
|
||||
comments = read_comments(f, line)
|
||||
elif re.search(r'void\s+kbfun_\S+\s*\(void\)', line):
|
||||
dict_merge(
|
||||
output,
|
||||
parse_keyboard_function(f, line, comments) )
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def gen_mappings(matrix_file_path, layout_file_path):
|
||||
# normalize paths
|
||||
matrix_file_path = os.path.abspath(matrix_file_path)
|
||||
layout_file_path = os.path.abspath(layout_file_path)
|
||||
|
||||
def parse_matrix_file(matrix_file_path):
|
||||
match = re.search( # find the whole 'KB_MATRIX_LAYER' macro
|
||||
r'#define\s+KB_MATRIX_LAYER\s*\(([^)]+)\)[^{]*\{\{([^#]+)\}\}',
|
||||
open(matrix_file_path).read() )
|
||||
|
||||
return {
|
||||
"mappings": {
|
||||
"physical-positions": re.findall(r'k..', match.group(1)),
|
||||
"matrix-positions": re.findall(r'k..|na', match.group(2)),
|
||||
},
|
||||
}
|
||||
|
||||
def parse_layout_file(layout_file_path):
|
||||
match = re.findall( # find each whole '_kb_layout*' matrix definition
|
||||
r'(_kb_layout\w*)[^=]*=((?:[^{}]*\{){3}[^=]*(?:[^{}]*\}){3})',
|
||||
subprocess.getoutput("gcc -E '"+layout_file_path+"'") )
|
||||
|
||||
layout = {}
|
||||
# collect all the values
|
||||
for (name, matrix) in match:
|
||||
layout[name] = [
|
||||
re.findall( # find all numbers and function pointers
|
||||
r'[x0-9A-F]+|&\w+|NULL',
|
||||
re.sub( # replace '((void *) 0)' with 'NULL'
|
||||
r'\(\s*\(\s*void\s*\*\s*\)\s*0\s*\)',
|
||||
'NULL',
|
||||
el ) )
|
||||
for el in
|
||||
re.findall( # find each whole layer
|
||||
r'(?:[^{}]*\{){2}((?:[^}]|\}\s*,)+)(?:[^{}]*\}){2}',
|
||||
matrix ) ]
|
||||
|
||||
# make the numbers into actual numbers
|
||||
layout['_kb_layout'] = \
|
||||
[[eval(el) for el in layer] for layer in layout['_kb_layout']]
|
||||
# remove the preceeding '&' from function pointers
|
||||
for matrix in ('_kb_layout_press', '_kb_layout_release'):
|
||||
layout[matrix] = \
|
||||
[ [re.sub(r'&', '', el) for el in layer]
|
||||
for layer in layout[matrix] ]
|
||||
|
||||
return {
|
||||
"mappings": {
|
||||
"matrix-layout":
|
||||
# group them all properly
|
||||
[ [[c, p, r] for (c, p, r) in zip(code, press, release)]
|
||||
for (code, press, release) in
|
||||
zip( layout['_kb_layout'],
|
||||
layout['_kb_layout_press'],
|
||||
layout['_kb_layout_release'] ) ]
|
||||
},
|
||||
}
|
||||
|
||||
return dict_merge(
|
||||
parse_matrix_file(matrix_file_path),
|
||||
parse_layout_file(layout_file_path) )
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def dict_merge(a, b):
|
||||
"""
|
||||
Recursively merge two dictionaries
|
||||
- I was looking around for an easy way to do this, and found something
|
||||
[here]
|
||||
(http://www.xormedia.com/recursively-merge-dictionaries-in-python.html).
|
||||
This is pretty close, but i didn't copy it exactly.
|
||||
"""
|
||||
|
||||
if not isinstance(a, dict) or not isinstance(b, dict):
|
||||
return b
|
||||
|
||||
for (key, value) in b.items():
|
||||
if key in a:
|
||||
a[key] = dict_merge(a[key], value)
|
||||
else:
|
||||
a[key] = value
|
||||
|
||||
return a
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description = 'Generate project data for use with the UI' )
|
||||
|
||||
arg_parser.add_argument(
|
||||
'--current-date',
|
||||
help = ( "should be in the format rfc-3339 "
|
||||
+ "(e.g. 2006-08-07 12:34:56-06:00)" ),
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--git-commit-date',
|
||||
help = ( "should be in the format rfc-3339 "
|
||||
+ "(e.g. 2006-08-07 12:34:56-06:00)" ),
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--git-commit-id',
|
||||
help = "the git commit ID",
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--map-file-path',
|
||||
help = "the path to the '.map' file",
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--source-code-path',
|
||||
help = "the path to the source code directory",
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--matrix-file-path',
|
||||
help = "the path to the matrix file we're using",
|
||||
required = True )
|
||||
arg_parser.add_argument(
|
||||
'--layout-file-path',
|
||||
help = "the path to the layout file we're using",
|
||||
required = True )
|
||||
|
||||
args = arg_parser.parse_args(sys.argv[1:])
|
||||
|
||||
output = {}
|
||||
dict_merge( output, gen_static( args.current_date,
|
||||
args.git_commit_date,
|
||||
args.git_commit_id ) )
|
||||
dict_merge(output, parse_mapfile(args.map_file_path))
|
||||
dict_merge(output, find_keyboard_functions(args.source_code_path))
|
||||
dict_merge(output, gen_mappings( args.matrix_file_path,
|
||||
args.layout_file_path ))
|
||||
dict_merge(output, gen_derived(output))
|
||||
|
||||
print(json.dumps(output, sort_keys=True, indent=4))
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
function keyclick(position, layer) {
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 94 KiB |
|
@ -1,2 +0,0 @@
|
|||
This directory is for projects closely related to the firmware.
|
||||
|
|
@ -1,14 +1,3 @@
|
|||
# The MIT License (MIT)
|
||||
Retrieved from <http://www.opensource.org/licenses/MIT> on 2012-03-10
|
||||
|
||||
This copyright and licence apply to all files in this project, except where
|
||||
otherwise noted. If you feel that this infringes on any existing intellectual
|
||||
property, please email me at the address below.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
|
@ -27,3 +16,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Retrieved from <http://www.opensource.org/licenses/MIT> on 2012-03-10
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
*.o
|
||||
*.dep
|
||||
|
||||
*.eep
|
||||
*.elf
|
||||
*.hex
|
||||
*.map
|
||||
*.o
|
||||
*.o.dep
|
||||
|
|
@ -0,0 +1,307 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* The keyboard interface
|
||||
*
|
||||
* Keyboard implementations must implement all prototyped functions.
|
||||
*
|
||||
* Prefix: `kb__`
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef OPT__KB__ROWS
|
||||
#error "OPT__KB__ROWS not defined"
|
||||
#endif
|
||||
#ifndef OPT__KB__COLUMNS
|
||||
#error "OPT__KB__COLUMNS not defined"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// controller
|
||||
uint8_t kb__init (void);
|
||||
uint8_t kb__update_matrix (bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]);
|
||||
|
||||
// LED
|
||||
void kb__led__on (uint8_t led);
|
||||
void kb__led__off (uint8_t led);
|
||||
void kb__led__set (uint8_t led, float n);
|
||||
// -------
|
||||
bool kb__led__read (uint8_t led);
|
||||
// -------
|
||||
void kb__led__all_on (void);
|
||||
void kb__led__all_off (void);
|
||||
void kb__led__all_set (float percent);
|
||||
// -------
|
||||
void kb__led__state__power_on (void);
|
||||
void kb__led__state__ready (void);
|
||||
void kb__led__delay__error (void);
|
||||
void kb__led__delay__usb_init (void);
|
||||
|
||||
// layout
|
||||
void kb__led__logical_on (char led);
|
||||
void kb__led__logical_off (char led);
|
||||
// -------
|
||||
void kb__layout__exec_key ( bool pressed,
|
||||
uint8_t row,
|
||||
uint8_t column );
|
||||
void kb__layout__exec_key_layer ( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column );
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === OPT__KB__ROWS ===
|
||||
/** macros/OPT__KB__ROWS/description
|
||||
* The number of rows in a given keyboard's matrix
|
||||
*/
|
||||
|
||||
// === OPT__KB__COLUMNS ===
|
||||
/** macros/OPT__KB__COLUMNS/description
|
||||
* The number of columns in a given keyboard's matrix
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// controller -----------------------------------------------------------------
|
||||
|
||||
// === kb__init() ===
|
||||
/** functions/kb__init/description
|
||||
* Initialize the keyboard.
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Should be called exactly once by `main()` before entering the run loop.
|
||||
*/
|
||||
|
||||
// === kb__update_matrix() ===
|
||||
/** functions/kb__update_matrix/description
|
||||
* Update the given matrix to the current state of the keyboard.
|
||||
*
|
||||
* Arguments:
|
||||
* - `matrix`: The keyboard matrix to update.
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LED ------------------------------------------------------------------------
|
||||
|
||||
// === kb__led__on() ===
|
||||
/** functions/kb__led__on/description
|
||||
* Set the given LED 'on'.
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`: The number of the LED to set. Should be an integer between 1 and 5
|
||||
* inclusive; behavior is undefined otherwise.
|
||||
*
|
||||
* Notes:
|
||||
* - For US keyboards, likely only LEDs 1 through 3 will be physically present;
|
||||
* but the function should handle 4 through 5 gracefully anyway.
|
||||
*/
|
||||
|
||||
// === kb__led__off() ===
|
||||
/** functions/kb__led__off/description
|
||||
* Set the given LED 'off'.
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`: The number of the LED to set. Should be an integer between 1 and 5
|
||||
* inclusive; behavior is undefined otherwise.
|
||||
*
|
||||
* Notes:
|
||||
* - For US keyboards, likely only LEDs 1 through 3 will be physically present;
|
||||
* but the function should handle 4 through 5 gracefully anyway.
|
||||
*/
|
||||
|
||||
// === kb__led__set() ===
|
||||
/** functions/kb__led__set/description
|
||||
* Set the given LED's brightness (by percent).
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`: The number of the LED to set. Should be an integer between 1 and 5
|
||||
* inclusive; behavior is undefined otherwise.
|
||||
* - `percent`: The percent of the highest possible brightness at which to set
|
||||
* the LED. Should be a float between 0 and 1 inclusive; behavior is
|
||||
* undefined otherwise.
|
||||
*
|
||||
* Notes:
|
||||
* - For US keyboards, likely only LEDs 1 through 3 will be physically present;
|
||||
* but the function should handle 4 through 5 gracefully anyway.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === kb__led__read() ===
|
||||
/** functions/kb__led__read/description
|
||||
* Return whether the given LED is on (`true`) or off (`false`)
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`: The number of the LED to read. Should be an integer between 1 and
|
||||
* 5 inclusive; behavior is undefined otherwise.
|
||||
*
|
||||
* Returns:
|
||||
* - success: `true` if the given LED is on, and `false` otherwise; LEDs that
|
||||
* don't exist (and can't be set) should always be `false`
|
||||
*
|
||||
* Notes:
|
||||
* - For US keyboards, likely only LEDs 1 through 3 will be physically present;
|
||||
* but the function should handle 4 through 5 gracefully anyway.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === kb__led__all_on() ===
|
||||
/** functions/kb__led__all_on/description
|
||||
* Set all the LEDs 'on'.
|
||||
*/
|
||||
|
||||
// === kb__led__all_off() ===
|
||||
/** functions/kb__led__all_off/description
|
||||
* Set all the LEDs 'off'.
|
||||
*/
|
||||
|
||||
// === kb__led__all_set() ===
|
||||
/** functions/kb__led__all_set/description
|
||||
* Set all the LEDs' brightness (by percent).
|
||||
*
|
||||
* Arguments:
|
||||
* - `percent`: The percent of the highest possible brightness at which to set
|
||||
* the LED. Should be a float between 0 and 1 inclusive; behavior is
|
||||
* undefined otherwise.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === kb__led__state__power_on() ===
|
||||
/** functions/kb__led__state__power_on/description
|
||||
* Set the LEDs to the state that indicates the keyboard just powered on.
|
||||
*/
|
||||
|
||||
// === kb__led__state__ready() ===
|
||||
/** functions/kb__led__state__ready/description
|
||||
* Set the LEDs to the state that indicates the keyboard is ready to receive
|
||||
* keystrokes.
|
||||
*/
|
||||
|
||||
|
||||
// === kb__led__delay__error() ===
|
||||
/** functions/kb__led__delay__error/description
|
||||
* Signal a generic error
|
||||
*/
|
||||
|
||||
// === kb__led__delay__usb_init() ===
|
||||
/** functions/kb__led__delay__usb_init/description
|
||||
* Delay for a total of ~1 second, to allow the host to load drivers and such.
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// layout ---------------------------------------------------------------------
|
||||
|
||||
// === kb__led__logical_on ===
|
||||
/** functions/kb__led__logical_on/description
|
||||
* Set LEDs 'on' based ontheir meaning (e.g. 'numlock', 'capslock').
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`:
|
||||
* - `N`: numlock
|
||||
* - `C`: capslock
|
||||
* - `S`: scroll lock
|
||||
* - `O`: compose
|
||||
* - `K`: kana
|
||||
*/
|
||||
|
||||
// === kb__led__logical_off ===
|
||||
/** functions/kb__led__logical_off/description
|
||||
* Set LEDs 'off' based ontheir meaning (e.g. 'numlock', 'capslock').
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`:
|
||||
* - `N`: numlock
|
||||
* - `C`: capslock
|
||||
* - `S`: scroll lock
|
||||
* - `O`: compose
|
||||
* - `K`: kana
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === kb__layout__exec_key ===
|
||||
/** functions/kb__layout__exec_key/description
|
||||
* Perform the appropriate actions for a "press" or "release" of the key at the
|
||||
* given position.
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`:
|
||||
* - `true`: Indicates that the key to be "executed" has been pressed
|
||||
* - `false`: Indicates that the key to be "executed" has been released
|
||||
* - `row`: The row of the key to be "executed"
|
||||
* - `column`: The column of the key to be "executed"
|
||||
*
|
||||
* Notes:
|
||||
* - This function is responsible for almost all of a keyboard's apparent
|
||||
* functionality. It needs to be implemented at this level in order to hide
|
||||
* the actual representation of layers, keys, how and where they are stored,
|
||||
* etc. from `main()`.
|
||||
*/
|
||||
|
||||
// === kb__layout__exec_key_layer ===
|
||||
/** functions/kb__layout__exec_key_layer/description
|
||||
* Perform the appropriate actions for a "press" or "release" of the key at the
|
||||
* given position, on the given layer.
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`:
|
||||
* - `true`: Indicates that the key to be "executed" has been pressed
|
||||
* - `false`: Indicates that the key to be "executed" has been released
|
||||
* - `layer`: The layer of the key to be "executed"
|
||||
* - `row`: The row of the key to be "executed"
|
||||
* - `column`: The column of the key to be "executed"
|
||||
*
|
||||
* Notes:
|
||||
* - If the implementation does not support layers, the `layer` argument should
|
||||
* be ignored, and this function will be equivalent to
|
||||
* `kb__layout__exec_key()`.
|
||||
*/
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "controller" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "./controller/mcp23018.h"
|
||||
#include "./controller/teensy-2-0.h"
|
||||
#include "../../../firmware/lib/layout/eeprom-macro.h"
|
||||
#include "../../../firmware/keyboard.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t kb__init(void) {
|
||||
if (teensy__init()) // must be first (to initialize twi, and such)
|
||||
return 1;
|
||||
if (mcp23018__init()) // must be second
|
||||
return 2;
|
||||
|
||||
if (eeprom_macro__init())
|
||||
return 3;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
uint8_t kb__update_matrix(bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]) {
|
||||
if (teensy__update_matrix(matrix))
|
||||
return 1;
|
||||
if (mcp23018__update_matrix(matrix))
|
||||
return 2;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
## Circuit Diagram
|
||||
|
||||
![circuit diagram](controller/circuit-diagram.svg)
|
||||
|
||||
* Please also see documentation (especially the notes) in the '*.md' files.
|
||||
* Row and column assignments are to matrix positions, not physical positions.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
inkscape:version="0.48.2 r9819"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
height="416.48083"
|
||||
height="367.11203"
|
||||
width="950.8382"
|
||||
sodipodi:docname="circuit-diagram.svg"
|
||||
inkscape:export-filename="/home/ben/Desktop/programs/20120227--ergodox-firmware--for-the-ergodox-keyboard/src/test - circuit diagram/inkscape/_circuit-diagram.png"
|
||||
|
@ -27,7 +27,7 @@
|
|||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="455.81585"
|
||||
inkscape:cy="210.83562"
|
||||
inkscape:cy="161.46684"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer7"
|
||||
showgrid="true"
|
||||
|
@ -37,8 +37,8 @@
|
|||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="852"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1032"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
|
@ -1012,7 +1012,7 @@
|
|||
sodipodi:cy="185.44926"
|
||||
sodipodi:rx="80.96373"
|
||||
sodipodi:ry="82.024384"
|
||||
d="m 49.497478,185.44926 c 0,45.30082 -36.248696,82.02439 -80.963729,82.02439 -44.715034,0 -80.963729,-36.72357 -80.963729,-82.02439 0,0 0,0 0,0 l 80.963729,0 z"
|
||||
d="m 49.497478,185.44926 a 80.96373,82.024384 0 1 1 -161.927458,0 l 80.963729,0 z"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="3.1415927"
|
||||
transform="matrix(0,-0.11858905,0.11219624,0,469.59545,178.62704)"
|
||||
|
@ -1044,7 +1044,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1057,7 +1057,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1070,7 +1070,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1083,7 +1083,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1096,7 +1096,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1109,7 +1109,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1122,7 +1122,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1135,7 +1135,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1148,7 +1148,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1161,7 +1161,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1174,7 +1174,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1187,7 +1187,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1200,7 +1200,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1213,7 +1213,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1226,7 +1226,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1239,7 +1239,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1252,7 +1252,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1265,7 +1265,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1278,7 +1278,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1291,7 +1291,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1304,7 +1304,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1317,7 +1317,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1330,7 +1330,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1343,7 +1343,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1356,7 +1356,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1369,7 +1369,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1382,7 +1382,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1395,7 +1395,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1408,7 +1408,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1421,7 +1421,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1434,7 +1434,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1447,7 +1447,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1460,7 +1460,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1473,7 +1473,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1486,7 +1486,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1499,7 +1499,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1512,7 +1512,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1525,7 +1525,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1538,7 +1538,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1551,7 +1551,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1564,7 +1564,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1577,7 +1577,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1590,7 +1590,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1603,7 +1603,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1616,7 +1616,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1629,7 +1629,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1642,7 +1642,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1655,7 +1655,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1668,7 +1668,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1681,7 +1681,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1694,7 +1694,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1707,7 +1707,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1720,7 +1720,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1733,7 +1733,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1746,7 +1746,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1759,7 +1759,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1772,7 +1772,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1785,7 +1785,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -1798,7 +1798,7 @@
|
|||
<path
|
||||
inkscape:transform-center-y="-54.545454"
|
||||
inkscape:transform-center-x="22.727269"
|
||||
d="m 53,237.36218 c 0,2.76143 -2.238576,5 -5,5 -2.761424,0 -5,-2.23857 -5,-5 0,-2.76142 2.238576,-5 5,-5 2.761424,0 5,2.23858 5,5 z"
|
||||
d="m 53,237.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
|
||||
sodipodi:ry="5"
|
||||
sodipodi:rx="5"
|
||||
sodipodi:cy="237.36218"
|
||||
|
@ -2753,21 +2753,6 @@
|
|||
y="428.62506"
|
||||
id="tspan9632-9-43"
|
||||
style="font-style:normal;-inkscape-font-specification:Sans">LED 3</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:14px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="37.24189"
|
||||
y="388.43091"
|
||||
id="text3443"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3445"
|
||||
x="37.24189"
|
||||
y="388.43091">- Please also see documentation (especially the notes) in the *.md files</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="37.24189"
|
||||
y="405.93091"
|
||||
id="tspan3450">- Row and column assignments are to matrix positions, not physical positions</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#aaaa00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker11070);display:inline"
|
||||
d="m 534.99275,137.36829 0,-20.5"
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 152 KiB |
|
@ -0,0 +1,238 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* MCP23018 (driven by Teensy 2.0 over TWI) specific code, helping to implement
|
||||
* the "controller" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <util/twi.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
#include "../../../../firmware/lib/twi.h"
|
||||
#include "./mcp23018.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if OPT__KB__ROWS != 6 || OPT__KB__COLUMNS != 14
|
||||
#error "Expecting different keyboard dimensions"
|
||||
#endif
|
||||
|
||||
/** macros/(group) pin drive direction/description
|
||||
* Select which set of pins (rows or columns) will drive (alternate between
|
||||
* hi-Z and drive low), and which will be inputs (hi-Z)
|
||||
*
|
||||
* Members:
|
||||
* - `OPT__MCP23018__DRIVE_ROWS`
|
||||
* - `OPT__MCP23018__DRIVE_COLUMNS`
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - You must set exactly one of these variables to `1`, and the other to `0`
|
||||
*
|
||||
* - If you are using internal diodes (inside the key switches), set
|
||||
* `OPT__MCP23018__DRIVE_ROWS` to `1`
|
||||
*
|
||||
* - If the diode cathode is towards the square solder pad, set
|
||||
* `OPT__MCP23018__DRIVE_COLUMNS` to `1`
|
||||
*
|
||||
* - If the diode cathode is towards the circular solder pad, set
|
||||
* `OPT__MCP23018__DRIVE_ROWS` to `1`
|
||||
*/
|
||||
#if ( OPT__MCP23018__DRIVE_ROWS && OPT__MCP23018__DRIVE_COLUMNS ) \
|
||||
|| !( OPT__MCP23018__DRIVE_ROWS || OPT__MCP23018__DRIVE_COLUMNS )
|
||||
#error "MCP23018 pin drive direction incorrectly set"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// register addresses (see "mcp23018.md")
|
||||
#define IODIRA 0x00 // i/o direction register
|
||||
#define IODIRB 0x01
|
||||
#define GPPUA 0x0C // GPIO pull-up resistor register
|
||||
#define GPPUB 0x0D
|
||||
#define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT)
|
||||
#define GPIOB 0x13
|
||||
#define OLATA 0x14 // output latch register
|
||||
#define OLATB 0x15
|
||||
|
||||
// TWI aliases
|
||||
#define TWI_ADDR 0b0100000
|
||||
#define TWI_ADDR_WRITE ( (TWI_ADDR<<1) | TW_WRITE )
|
||||
#define TWI_ADDR_READ ( (TWI_ADDR<<1) | TW_READ )
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/mcp23018__init/description
|
||||
* Initialize the MCP23018
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: twi status code
|
||||
*
|
||||
* Notes:
|
||||
* - `twi__stop()` must be called *exactly once* for each twi block, the way
|
||||
* things are currently set up. this may change in the future.
|
||||
*/
|
||||
uint8_t mcp23018__init(void) {
|
||||
uint8_t ret;
|
||||
|
||||
// set pin direction
|
||||
// - unused : input : 1
|
||||
// - input : input : 1
|
||||
// - driving : output : 0
|
||||
twi__start();
|
||||
ret = twi__send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi__send(IODIRA);
|
||||
#if OPT__MCP23018__DRIVE_ROWS
|
||||
twi__send(0b11111111); // IODIRA
|
||||
twi__send(0b11000000); // IODIRB
|
||||
#elif OPT__MCP23018__DRIVE_COLUMNS
|
||||
twi__send(0b10000000); // IODIRA
|
||||
twi__send(0b11111111); // IODIRB
|
||||
#endif
|
||||
twi__stop();
|
||||
|
||||
// set pull-up
|
||||
// - unused : on : 1
|
||||
// - input : on : 1
|
||||
// - driving : off : 0
|
||||
twi__start();
|
||||
ret = twi__send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi__send(GPPUA);
|
||||
#if OPT__MCP23018__DRIVE_ROWS
|
||||
twi__send(0b11111111); // GPPUA
|
||||
twi__send(0b11000000); // GPPUB
|
||||
#elif OPT__MCP23018__DRIVE_COLUMNS
|
||||
twi__send(0b10000000); // GPPUA
|
||||
twi__send(0b11111111); // GPPUB
|
||||
#endif
|
||||
twi__stop();
|
||||
|
||||
// set logical value (doesn't matter on inputs)
|
||||
// - unused : hi-Z : 1
|
||||
// - input : hi-Z : 1
|
||||
// - driving : hi-Z : 1
|
||||
twi__start();
|
||||
ret = twi__send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi__send(OLATA);
|
||||
twi__send(0b11111111); //OLATA
|
||||
twi__send(0b11111111); //OLATB
|
||||
|
||||
out:
|
||||
twi__stop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** functions/mcp23018__update_matrix/description
|
||||
* Update the MCP23018 (left hand) half of the given matrix
|
||||
*
|
||||
* Arguments:
|
||||
* - `matrix`: A matrix of booleans, indicating whether the key at the given
|
||||
* matrix location is pressed or released
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: twi status code
|
||||
*/
|
||||
uint8_t mcp23018__update_matrix(bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]) {
|
||||
uint8_t ret, data;
|
||||
|
||||
// initialize things, just to make sure
|
||||
// - it's not appreciably faster to skip this, and it takes care of the
|
||||
// case when the i/o expander isn't plugged in during the first
|
||||
// init()
|
||||
ret = mcp23018__init();
|
||||
|
||||
// if there was an error
|
||||
if (ret) {
|
||||
// clear our part of the matrix
|
||||
for (uint8_t row=0; row<=5; row++)
|
||||
for (uint8_t col=0; col<=6; col++)
|
||||
matrix[row][col] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// update our part of the matrix ..........................................
|
||||
|
||||
#if OPT__MCP23018__DRIVE_ROWS
|
||||
for (uint8_t row=0; row<=5; row++) {
|
||||
// set active row low : 0
|
||||
// set other rows hi-Z : 1
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOB);
|
||||
twi__send( 0xFF & ~(1<<(5-row)) );
|
||||
twi__stop();
|
||||
|
||||
// read column data
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOA);
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_READ);
|
||||
twi__read(&data);
|
||||
twi__stop();
|
||||
|
||||
// update matrix
|
||||
for (uint8_t col=0; col<=6; col++) {
|
||||
matrix[row][col] = !( data & (1<<col) );
|
||||
}
|
||||
}
|
||||
|
||||
// set all rows hi-Z : 1
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOB);
|
||||
twi__send(0xFF);
|
||||
twi__stop();
|
||||
|
||||
#elif OPT__MCP23018__DRIVE_COLUMNS
|
||||
for (uint8_t col=0; col<=6; col++) {
|
||||
// set active column low : 0
|
||||
// set other columns hi-Z : 1
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOA);
|
||||
twi__send( 0xFF & ~(1<<col) );
|
||||
twi__stop();
|
||||
|
||||
// read row data
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOB);
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_READ);
|
||||
twi__read(&data);
|
||||
twi__stop();
|
||||
|
||||
// update matrix
|
||||
for (uint8_t row=0; row<=5; row++) {
|
||||
matrix[row][col] = !( data & (1<<(5-row)) );
|
||||
}
|
||||
}
|
||||
|
||||
// set all columns hi-Z : 1
|
||||
twi__start();
|
||||
twi__send(TWI_ADDR_WRITE);
|
||||
twi__send(GPIOA);
|
||||
twi__send(0xFF);
|
||||
twi__stop();
|
||||
|
||||
#endif
|
||||
|
||||
// /update our part of the matrix .........................................
|
||||
|
||||
return ret; // success
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* For inclusion by '../controller.c'
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__MCP23018__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__MCP23018__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t mcp23018__init (void);
|
||||
uint8_t mcp23018__update_matrix (bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__MCP23018__H
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
# Documentation : MCP23018
|
||||
|
||||
## Pinout and Pin assignments
|
||||
|
||||
* `+` indicates connected pin
|
||||
|
@ -23,7 +21,7 @@
|
|||
SDA +13 16+ RESET
|
||||
NC +14-------15+ ADDR
|
||||
|
||||
### MCP32018 Pin Assignments
|
||||
### MCP23018 Pin Assignments
|
||||
|
||||
power_negative Vss(GND) +01---.---28o NC
|
||||
NC o02 27o GPA7
|
||||
|
@ -40,17 +38,20 @@
|
|||
I2C SDA +13 16+ RESET = Vdd(Vcc) (see note)
|
||||
NC o14-------15+ ADDR = Vss(GND) (see note)
|
||||
|
||||
* notes:
|
||||
* Notes:
|
||||
|
||||
* Row and column assignments are to matrix positions, which may or may
|
||||
or may not correspond to the physical position of the key: e.g. the key
|
||||
where `row_4` and `column_2` cross will be scanned into the matrix at
|
||||
`[4][2]`, wherever it happens to be located on the keyboard. Mapping
|
||||
from one to the other (which only matters for defining layouts) is
|
||||
handled elsewhere.
|
||||
|
||||
* ADDR (pin15): Set slave address to `0b0100000` by connecting to Vss(GND).
|
||||
* The user-defined bits are the three least significant
|
||||
* I2C addresses are 7 bits long (the last bit in the byte is used for
|
||||
indicating read/write)
|
||||
|
||||
* RESET (pin16) must be externally biased. Since we're not going to
|
||||
trigger it ourselves, we can tie it high.
|
||||
* This is not noted in the I2C Pinout Description section of the
|
||||
|
@ -60,6 +61,7 @@
|
|||
* <http://davidn.org/wp/?p=89>
|
||||
* <http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1293498979>
|
||||
|
||||
|
||||
## Notes about Registers
|
||||
|
||||
register address function (for all bits)
|
||||
|
@ -75,28 +77,35 @@
|
|||
pins configured as output
|
||||
|
||||
* IOCON register (see datasheet section 1.6, table 1-5, register 1-8)
|
||||
|
||||
* BANK: bit 7; read/write; default = 0
|
||||
* 1: The registers associated with each port are separated into
|
||||
different banks
|
||||
* 0: The registers are in the same bank (addresses are sequential)
|
||||
|
||||
* SEQOP: bit 5; read/write; default = 0
|
||||
* 1: Sequential operation disabled, address pointer does not increment
|
||||
* 0: Sequential operation enabled, address pointer increments
|
||||
|
||||
* notes:
|
||||
* Notes:
|
||||
|
||||
* All addresses given for IOCON.BANK = 0, since that's the default value of
|
||||
the bit, and that's what we'll be using.
|
||||
* Initially, we want either columns or rows (see <../options.h>) set as
|
||||
hi-Z without pull-ups, and the other set of pins set as input with
|
||||
pull-ups. During the update function, we'll cycle through setting the
|
||||
first set low and checking each pin in the second set.
|
||||
|
||||
* abbreviations:
|
||||
* IODIR = I/O Direction Register
|
||||
* IOCON = I/O Control Register
|
||||
* GPPU = GPIO Pull-Up Resistor Register
|
||||
* GPIO = General Purpose I/O Port Register
|
||||
* OLAT = Output Latch Register
|
||||
* Initially, we want either columns or rows (see [the options file]
|
||||
(../../../options.mk)) set as hi-Z without pull-ups, and the other set of
|
||||
pins set as input with pull-ups. During the update function, we'll cycle
|
||||
through setting the first set low and checking each pin in the second
|
||||
set.
|
||||
|
||||
|
||||
* Abbreviations:
|
||||
* `GPIO`: General Purpose I/O Port Register
|
||||
* `GPPU`: GPIO Pull-Up Resistor Register
|
||||
* `IOCON`: I/O Control Register
|
||||
* `IODIR`: I/O Direction Register
|
||||
* `OLAT`: Output Latch Register
|
||||
|
||||
|
||||
## I²C Device Protocol (see datasheet section 1.3, figure 1-1)
|
||||
|
||||
|
@ -135,13 +144,16 @@
|
|||
Byte : S OP W ADDR --> SR OP R Dout --> P
|
||||
Sequential : S OP W ADDR --> SR OP R Dout ... Dout --> P
|
||||
|
||||
* notes:
|
||||
|
||||
* Notes:
|
||||
|
||||
* We'll be using sequential mode (ICON.SEQOP = 0; default) (see datasheet
|
||||
section 1.3.1).
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (MIT) (see "license.md")
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Teensy 2.0 specific code, helping to implement the "controller" section of
|
||||
* '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
#include "../../../../firmware/lib/twi.h"
|
||||
#include "./teensy-2-0.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if F_CPU != 16000000
|
||||
#error "Expecting different CPU frequency"
|
||||
#endif
|
||||
|
||||
#if OPT__KB__ROWS != 6 || OPT__KB__COLUMNS != 14
|
||||
#error "Expecting different keyboard dimensions"
|
||||
#endif
|
||||
|
||||
/** macros/(group) pin drive direction/description
|
||||
* Select which set of pins (rows or columns) will drive (alternate between
|
||||
* hi-Z and drive low), and which will be inputs (hi-Z)
|
||||
*
|
||||
* Members:
|
||||
* - `OPT__TEENSY__DRIVE_ROWS`
|
||||
* - `OPT__TEENSY__DRIVE_COLUMNS`
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - You must set exactly one of these variables to `1`, and the other to `0`
|
||||
*
|
||||
* - If you are using internal diodes (inside the key switches), set
|
||||
* `OPT__TEENSY__DRIVE_COLUMNS` to `1`
|
||||
*
|
||||
* - If the diode cathode is towards the square solder pad, set
|
||||
* `OPT__TEENSY__DRIVE_COLUMNS` to `1`
|
||||
*
|
||||
* - If the diode cathode is towards the circular solder pad, set
|
||||
* `OPT__TEENSY__DRIVE_ROWS` to `1`
|
||||
*/
|
||||
#if ( OPT__TEENSY__DRIVE_ROWS && OPT__TEENSY__DRIVE_COLUMNS ) \
|
||||
|| !( OPT__TEENSY__DRIVE_ROWS || OPT__TEENSY__DRIVE_COLUMNS )
|
||||
#error "Teensy pin drive direction incorrectly set"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// processor frequency (from <http://www.pjrc.com/teensy/prescaler.html>)
|
||||
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
|
||||
#define CPU_16MHz 0x00
|
||||
#define CPU_8MHz 0x01
|
||||
#define CPU_4MHz 0x02
|
||||
#define CPU_2MHz 0x03
|
||||
#define CPU_1MHz 0x04
|
||||
#define CPU_500kHz 0x05
|
||||
#define CPU_250kHz 0x06
|
||||
#define CPU_125kHz 0x07
|
||||
#define CPU_62kHz 0x08
|
||||
|
||||
|
||||
/*
|
||||
* pin macros
|
||||
* - note: you can move the `UNUSED`, `ROW`, and `COLUMN` pins around, but be
|
||||
* sure to keep the set of all the pins listed constant. other pins are not
|
||||
* movable, and either are referenced explicitly or have macros defined for
|
||||
* them elsewhere.
|
||||
* - note: if you change pin assignments, please be sure to update
|
||||
* "teensy-2-0.md" and 'circuit-diagram.svg'.
|
||||
*/
|
||||
|
||||
// --- unused
|
||||
#define UNUSED_0 C, 7
|
||||
#define UNUSED_1 D, 7
|
||||
#define UNUSED_2 D, 4 // hard to use with breadboard (on the end)
|
||||
#define UNUSED_3 D, 5 // hard to use with breadboard (on the end)
|
||||
#define UNUSED_4 E, 6 // hard to use with breadboard (internal)
|
||||
|
||||
// --- rows
|
||||
#define ROW_0 F, 7
|
||||
#define ROW_1 F, 6
|
||||
#define ROW_2 F, 5
|
||||
#define ROW_3 F, 4
|
||||
#define ROW_4 F, 1
|
||||
#define ROW_5 F, 0
|
||||
|
||||
// --- columns
|
||||
#define COLUMN_7 B, 0
|
||||
#define COLUMN_8 B, 1
|
||||
#define COLUMN_9 B, 2
|
||||
#define COLUMN_A B, 3
|
||||
#define COLUMN_B D, 2
|
||||
#define COLUMN_C D, 3
|
||||
#define COLUMN_D C, 6
|
||||
|
||||
// --- helpers
|
||||
#define SET |=
|
||||
#define CLEAR &=~
|
||||
|
||||
#define _teensypin_write(register, operation, pin_letter, pin_number) \
|
||||
do { \
|
||||
((register##pin_letter) operation (1<<(pin_number))); \
|
||||
_delay_us(1); /* allow pins time to stabilize */ \
|
||||
} while(0)
|
||||
|
||||
#define teensypin_write(register, operation, pin) \
|
||||
_teensypin_write(register, operation, pin)
|
||||
|
||||
#define _teensypin_read(pin_letter, pin_number) \
|
||||
((PIN##pin_letter) & (1<<(pin_number)))
|
||||
|
||||
#define teensypin_read(pin) \
|
||||
_teensypin_read(pin)
|
||||
|
||||
#define teensypin_write_all_unused(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, UNUSED_0); \
|
||||
teensypin_write(register, operation, UNUSED_1); \
|
||||
teensypin_write(register, operation, UNUSED_2); \
|
||||
teensypin_write(register, operation, UNUSED_3); \
|
||||
teensypin_write(register, operation, UNUSED_4); } \
|
||||
while(0)
|
||||
|
||||
#define teensypin_write_all_row(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, ROW_0); \
|
||||
teensypin_write(register, operation, ROW_1); \
|
||||
teensypin_write(register, operation, ROW_2); \
|
||||
teensypin_write(register, operation, ROW_3); \
|
||||
teensypin_write(register, operation, ROW_4); \
|
||||
teensypin_write(register, operation, ROW_5); } \
|
||||
while(0)
|
||||
|
||||
#define teensypin_write_all_column(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, COLUMN_7); \
|
||||
teensypin_write(register, operation, COLUMN_8); \
|
||||
teensypin_write(register, operation, COLUMN_9); \
|
||||
teensypin_write(register, operation, COLUMN_A); \
|
||||
teensypin_write(register, operation, COLUMN_B); \
|
||||
teensypin_write(register, operation, COLUMN_C); \
|
||||
teensypin_write(register, operation, COLUMN_D); } \
|
||||
while(0)
|
||||
|
||||
|
||||
/*
|
||||
* update macros
|
||||
*/
|
||||
#define update_rows_for_column(matrix, column) \
|
||||
do { \
|
||||
/* set column low (set as output) */ \
|
||||
teensypin_write(DDR, SET, COLUMN_##column); \
|
||||
/* read rows 0..5 and update matrix */ \
|
||||
matrix[0x0][0x##column] = ! teensypin_read(ROW_0); \
|
||||
matrix[0x1][0x##column] = ! teensypin_read(ROW_1); \
|
||||
matrix[0x2][0x##column] = ! teensypin_read(ROW_2); \
|
||||
matrix[0x3][0x##column] = ! teensypin_read(ROW_3); \
|
||||
matrix[0x4][0x##column] = ! teensypin_read(ROW_4); \
|
||||
matrix[0x5][0x##column] = ! teensypin_read(ROW_5); \
|
||||
/* set column hi-Z (set as input) */ \
|
||||
teensypin_write(DDR, CLEAR, COLUMN_##column); \
|
||||
} while(0)
|
||||
|
||||
#define update_columns_for_row(matrix, row) \
|
||||
do { \
|
||||
/* set row low (set as output) */ \
|
||||
teensypin_write(DDR, SET, ROW_##row); \
|
||||
/* read columns 7..D and update matrix */ \
|
||||
matrix[0x##row][0x7] = ! teensypin_read(COLUMN_7); \
|
||||
matrix[0x##row][0x8] = ! teensypin_read(COLUMN_8); \
|
||||
matrix[0x##row][0x9] = ! teensypin_read(COLUMN_9); \
|
||||
matrix[0x##row][0xA] = ! teensypin_read(COLUMN_A); \
|
||||
matrix[0x##row][0xB] = ! teensypin_read(COLUMN_B); \
|
||||
matrix[0x##row][0xC] = ! teensypin_read(COLUMN_C); \
|
||||
matrix[0x##row][0xD] = ! teensypin_read(COLUMN_D); \
|
||||
/* set row hi-Z (set as input) */ \
|
||||
teensypin_write(DDR, CLEAR, ROW_##row); \
|
||||
} while(0)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/teensy__init/description
|
||||
* Initialize the Teensy
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
*/
|
||||
uint8_t teensy__init(void) {
|
||||
// CPU speed : should match F_CPU in makefile
|
||||
CPU_PRESCALE(CPU_16MHz);
|
||||
|
||||
// onboard LED
|
||||
// (tied to GND for hardware convenience)
|
||||
DDRD &= ~(1<<6); // set D(6) as input
|
||||
PORTD &= ~(1<<6); // set D(6) internal pull-up disabled
|
||||
|
||||
// (tied to Vcc for hardware convenience)
|
||||
DDRB &= ~(1<<4); // set B(4) as input
|
||||
PORTB &= ~(1<<4); // set B(4) internal pull-up disabled
|
||||
|
||||
// keyboard LEDs (see "PWM on ports OC1(A|B|C)" in "teensy-2-0.md")
|
||||
kb__led__all_off(); // (just to put the pins in a known state)
|
||||
TCCR1A = 0b10101001; // set and configure fast PWM
|
||||
TCCR1B = 0b00001001; // set and configure fast PWM
|
||||
|
||||
// I2C (TWI)
|
||||
twi__init(); // on pins D(1,0)
|
||||
|
||||
// unused pins
|
||||
teensypin_write_all_unused(DDR, CLEAR); // set as input
|
||||
teensypin_write_all_unused(PORT, SET); // set internal pull-up enabled
|
||||
|
||||
// rows and columns
|
||||
teensypin_write_all_row(DDR, CLEAR); // set as input (hi-Z)
|
||||
teensypin_write_all_column(DDR, CLEAR); // set as input (hi-Z)
|
||||
#if OPT__TEENSY__DRIVE_ROWS
|
||||
teensypin_write_all_row(PORT, CLEAR); // pull-up disabled
|
||||
teensypin_write_all_column(PORT, SET); // pull-up enabled
|
||||
#elif OPT__TEENSY__DRIVE_COLUMNS
|
||||
teensypin_write_all_row(PORT, SET); // pull-up enabled
|
||||
teensypin_write_all_column(PORT, CLEAR); // pull-up disabled
|
||||
#endif
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** functions/teensy__update_matrix/description
|
||||
* Update the Teensy (right hand) half of the given matrix
|
||||
*
|
||||
* Arguments:
|
||||
* - `matrix`: A matrix of booleans, indicating whether the key at the given
|
||||
* matrix location is pressed or released
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
*/
|
||||
uint8_t teensy__update_matrix(bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]) {
|
||||
#if OPT__TEENSY__DRIVE_ROWS
|
||||
update_columns_for_row(matrix, 0);
|
||||
update_columns_for_row(matrix, 1);
|
||||
update_columns_for_row(matrix, 2);
|
||||
update_columns_for_row(matrix, 3);
|
||||
update_columns_for_row(matrix, 4);
|
||||
update_columns_for_row(matrix, 5);
|
||||
#elif OPT__TEENSY__DRIVE_COLUMNS
|
||||
update_rows_for_column(matrix, 7);
|
||||
update_rows_for_column(matrix, 8);
|
||||
update_rows_for_column(matrix, 9);
|
||||
update_rows_for_column(matrix, A);
|
||||
update_rows_for_column(matrix, B);
|
||||
update_rows_for_column(matrix, C);
|
||||
update_rows_for_column(matrix, D);
|
||||
#endif
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* For inclusion by '../controller.c'
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t teensy__init (void);
|
||||
uint8_t teensy__update_matrix (bool matrix[OPT__KB__ROWS][OPT__KB__COLUMNS]);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__H
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
# Documentation : Teensy 2.0
|
||||
|
||||
## Pinout and Pin assignments
|
||||
|
||||
* `+` indicates connected pin
|
||||
|
@ -44,19 +42,22 @@
|
|||
Vcc ----/ | \---- RST
|
||||
GND-------/
|
||||
|
||||
* notes:
|
||||
* Notes:
|
||||
|
||||
* Row and column assignments are to matrix positions, which may or may
|
||||
or may not correspond to the physical position of the key: e.g. the key
|
||||
where `row_4` and `column_2` cross will be scanned into the matrix at
|
||||
`[4][2]`, wherever it happens to be located on the keyboard. Mapping
|
||||
from one to the other (which only matters for defining layouts) is
|
||||
handled elsewhere.
|
||||
|
||||
* LEDs are labeled using numbers (starting with '1') instead of letters
|
||||
(starting with 'A') as on the PCB.
|
||||
* SCL and SDA: Need external pull-up resistors. Sometimes the Teensy
|
||||
internal pull-ups are enough (see datasheet section 20.5.1), but i think
|
||||
for this project we'll want external ones. The general recommendation
|
||||
for 400kHz I²C seems to be 2.2kΩ.
|
||||
(starting with 'A') as on the the prototype PCB by Fredrik (late 2012).
|
||||
|
||||
* SCL and SDA (pins D(0) and D(1)) need external pull-up resistors.
|
||||
Sometimes the Teensy internal pull-ups are enough (see datasheet section
|
||||
20.5.1), but i think for this project we'll want external ones. The
|
||||
general recommendation for 400kHz I²C seems to be 2.2kΩ.
|
||||
|
||||
|
||||
## Notes about Registers
|
||||
|
@ -73,22 +74,27 @@
|
|||
write 1 toggles the value of PORTxn
|
||||
read returns the logical value (1|0) of the pin
|
||||
|
||||
* notes:
|
||||
* Notes:
|
||||
|
||||
* Unused pins should be set as input, with internal pullup enabled in order
|
||||
to give them a defined level (see datasheet section 10.2.6).
|
||||
|
||||
* PD6 (the onboard LED) already has a defined level (low), so there's no
|
||||
reason to set internal pull-up enabled on it. If we do, it will source
|
||||
current to the LED, which is fine, but unnecessary.
|
||||
* Initially, we want either columns or rows (see <../options.h>) set as
|
||||
hi-Z without pull-ups, and the other set of pins set as input with
|
||||
pull-ups. During the update function, we'll cycle through setting the
|
||||
first set low and checking each pin in the second set.
|
||||
|
||||
* Initially, we want either columns or rows (see [the options file]
|
||||
(../../../options.mk)) set as hi-Z without pull-ups, and the other set of
|
||||
pins set as input with pull-ups. During the update function, we'll cycle
|
||||
through setting the first set low and checking each pin in the second
|
||||
set.
|
||||
* To set a pin hi-Z on this board, set it as input with pull-up
|
||||
disabled.
|
||||
* Switching the driving pins (the first set of pins) between hi-Z and
|
||||
drive low (treating them as if they were open drain) seems just as
|
||||
good as, and a little safer than, driving them high when they're not
|
||||
active.
|
||||
|
||||
* We need to delay for at least 1 μs between changing the column pins and
|
||||
reading the row pins. I would assume this is to allow the pins time to
|
||||
stabalize.
|
||||
|
@ -104,44 +110,63 @@
|
|||
(http://geekhack.org/showthread.php?22780-Interest-Check-Custom-split-ergo-keyboard&p=606865&viewfull=1#post606865).
|
||||
Before adding a delay we were having [strange problems with ghosting]
|
||||
(http://geekhack.org/showthread.php?22780-Interest-Check-Custom-split-ergo-keyboard&p=605857&viewfull=1#post605857).
|
||||
|
||||
|
||||
|
||||
### PWM on ports OC1(A|B|C) (see datasheet section 14.10)
|
||||
|
||||
* notes: settings:
|
||||
* PWM pins should be set as outputs.
|
||||
* we want Waveform Generation Mode 5
|
||||
(fast PWM, 8-bit)
|
||||
(see table 14-5)
|
||||
* set `TCCRB[4,3],TCCRA[1,0]` to `0,1,0,1`
|
||||
* we want "Compare Output Mode, Fast PWM" to be `0b10`
|
||||
"Clear OCnA/OCnB/OCnC on compare match, set OCnA/OCnB/OCnC at TOP"
|
||||
(see table 14-3)
|
||||
this way higher values of `OCR1(A|B|C)` will mean longer 'on' times for
|
||||
the LEDs (provided they're hooked up to GND; other way around if they're
|
||||
hooked up to Vcc)
|
||||
* when in a fast PWM mode, set `TCCR1A[7,6,5,4,3,2]` to `1,0,1,0,1,0`
|
||||
* we want "Clock Select Bit Description" to be `0b001`
|
||||
"clkI/O/1 (No prescaling)"
|
||||
(see table 14-6)
|
||||
* set `TCCR1B[2,1,0]` to `0,0,1`
|
||||
* LEDs will be at minimum brightness until OCR1(A|B|C) are changed
|
||||
(since the default value of all the bits in those registers is 0)
|
||||
TCCR1A : Timer/Counter 1 Control Register A
|
||||
.---------------------------------------------------------------------.
|
||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|---------------------------------------------------------------------|
|
||||
| COM1A1 | COM1A0 | COM1B1 | COM1B0 | COM1C1 | COM1C0 | WGM11 | WGM10 |
|
||||
'---------------------------------------------------------------------'
|
||||
|
||||
TCCR1B : Timer/Counter 1 Control Register B
|
||||
.------------------------------------------------------------------.
|
||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|------------------------------------------------------------------|
|
||||
| ICNC1 | ICES1 | Reserved | WGM13 | WGM12 | CS12 | CS11 | CS10 |
|
||||
'------------------------------------------------------------------'
|
||||
|
||||
* We want:
|
||||
* `WGM` = `0b0101` : Fast PWM mode, 8-bit (see section 14.8.3)
|
||||
* `COM1(A|B|C)` = `0b10` : Clear OC1(A|B|C) on compare match, set
|
||||
OC1(A|B|C) at TOP
|
||||
* That is, we want `TCCR1A |= 0b10101000`
|
||||
* This way higher values of `OCR1(A|B|C)` will mean longer 'on'
|
||||
times for the LEDs (provided they're hooked up to GND; other way
|
||||
around if they're hooked up to Vcc)
|
||||
* `CS` = `0b001` : clk_i/o / 1 (no prescaling)
|
||||
* LEDs will be at minimum brightness until OCR1(A|B|C) are changed
|
||||
(since the default value of all the bits in those registers is
|
||||
`0`)
|
||||
|
||||
|
||||
* Notes:
|
||||
|
||||
* PWM pins should be set as outputs.
|
||||
|
||||
* notes: behavior:
|
||||
* The pins source current when on, and sink current when off. They aren't
|
||||
set to high impediance for either.
|
||||
* In Fast PWM mode setting `OCR1(A|B|C)` to `0` does not make the output on
|
||||
`OC1(A|B|C)` constant low; just close. Per the datasheet, this isn't
|
||||
|
||||
* In Fast PWM mode, setting `OCR1(A|B|C)` to `0` does not make the output
|
||||
on `OC1(A|B|C)` constant low; just close. Per the datasheet, this isn't
|
||||
true for every PWM mode.
|
||||
|
||||
* abbreviations:
|
||||
* OCR = Output Compare Register
|
||||
* TCCR = Timer/Counter Control Register
|
||||
|
||||
* Abbreviations:
|
||||
* `COM`: Compare
|
||||
* `CS`: Clock Select
|
||||
* `ICES`: Input Capture Edge Select
|
||||
* `ICNC`: Input Capture Noise Canceler
|
||||
* `OCR`: Output Compare Register
|
||||
* `TCCR: Timer/Counter Control Register
|
||||
* `WGM`: Waveform Generation Module
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (MIT) (see "license.md")
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A layout modeled after the [Arensito layout]
|
||||
* (http://www.pvv.org/~hakonhal/main.cgi/keyboard)
|
||||
* by Håkon Hallingstad
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*
|
||||
* TODO:
|
||||
* - separate most of this into a template (so i can have different versions of
|
||||
* the "ben" layout, just like there are different "kinesis-mod" layouts)?
|
||||
* lol
|
||||
*/
|
||||
|
||||
|
||||
#include "./fragments/includes.part.h"
|
||||
#include "./fragments/macros.part.h"
|
||||
#include "./fragments/types.part.h"
|
||||
#include "./fragments/variables.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// keys
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/keys.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LED control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/led-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// matrix control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/matrix-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// layout
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// TODO: make this Arensito!
|
||||
// - add layers with other layouts? arensito, colemak, dvorak, qwerty
|
||||
static layout_t layout PROGMEM = {
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 0 : default
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
nop, 1, 2, 3, 4, 5, nop,
|
||||
nop, q, l, comma, p, nop, nop,
|
||||
nop, a, r, e, n, b,
|
||||
nop, z, w, period, h, j, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
ctrlL, altL,
|
||||
nop, nop, home,
|
||||
bs, del, end,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
nop, 6, 7, 8, 9, 0, nop,
|
||||
nop, nop, f, u, d, k, nop,
|
||||
g, s, i, t, o, nop,
|
||||
nop, v, c, y, m, x, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
altR, ctrlR,
|
||||
pageU, nop, nop,
|
||||
pageD, enter, space ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer TODO : symbols and function keys
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
transp, F1, F2, F3, F4, F5, F11,
|
||||
transp, braceL, braceR, brktL, brktR, nop, lpo2l2,
|
||||
transp, semicol, slash, dash, 0, colon,
|
||||
transp, 6, 7, 8, 9, plus, lpupo3l3,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
F12, F6, F7, F8, F9, F10, power,
|
||||
lpo2l2, nop, undersc, lessThan, grtrThan, dollar, volumeU,
|
||||
bkslash, 1, parenL, parenR, equal, volumeD,
|
||||
lpupo3l3, asterisk, 2, 3, 4, 5, mute,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer TODO : keyboard functions
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
btldr, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
nop, nop, nop, nop, nop, nop, dmp_sram,
|
||||
nop, nop, nop, nop, nop, nop, dmp_prog,
|
||||
nop, nop, nop, nop, nop, dmp_eepr,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop ),
|
||||
|
||||
// ............................................................................
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A Colemak layout using the "kinesis-mod" template
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
// left hand letter key block
|
||||
#define T_q q
|
||||
#define T_w w
|
||||
#define T_e f
|
||||
#define T_r p
|
||||
#define T_t g
|
||||
#define T_a a
|
||||
#define T_s r
|
||||
#define T_d s
|
||||
#define T_f t
|
||||
#define T_g d
|
||||
#define T_z z
|
||||
#define T_x x
|
||||
#define T_c c
|
||||
#define T_v v
|
||||
#define T_b b
|
||||
|
||||
// right hand letter key block
|
||||
#define T_y j
|
||||
#define T_u l
|
||||
#define T_i u
|
||||
#define T_o y
|
||||
#define T_p semicol
|
||||
#define T_h h
|
||||
#define T_j n
|
||||
#define T_k e
|
||||
#define T_l i
|
||||
#define T_semicol o
|
||||
#define T_n k
|
||||
#define T_m m
|
||||
#define T_comma comma
|
||||
#define T_period period
|
||||
#define T_slash slash
|
||||
|
||||
// right hand outer key block
|
||||
#define T_bkslash bkslash
|
||||
#define T_quote quote
|
||||
|
||||
|
||||
#include "./templates/kinesis-mod.c.h"
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A Dvorak layout using the "kinesis-mod" template
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
// left hand letter key block
|
||||
#define T_q quote
|
||||
#define T_w comma
|
||||
#define T_e period
|
||||
#define T_r p
|
||||
#define T_t y
|
||||
#define T_a a
|
||||
#define T_s o
|
||||
#define T_d e
|
||||
#define T_f u
|
||||
#define T_g i
|
||||
#define T_z semicol
|
||||
#define T_x q
|
||||
#define T_c j
|
||||
#define T_v k
|
||||
#define T_b x
|
||||
|
||||
// right hand letter key block
|
||||
#define T_y f
|
||||
#define T_u g
|
||||
#define T_i c
|
||||
#define T_o r
|
||||
#define T_p l
|
||||
#define T_h d
|
||||
#define T_j h
|
||||
#define T_k t
|
||||
#define T_l n
|
||||
#define T_semicol s
|
||||
#define T_n b
|
||||
#define T_m m
|
||||
#define T_comma w
|
||||
#define T_period v
|
||||
#define T_slash z
|
||||
|
||||
// right hand outer key block
|
||||
#define T_bkslash slash
|
||||
#define T_quote bkslash
|
||||
|
||||
|
||||
#include "./templates/kinesis-mod.c.h"
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A central place for all `#include`s relevant to the fragments of layout
|
||||
* code in this directory
|
||||
*
|
||||
* These includes, or at least most of them, will very likely be needed if any
|
||||
* of the other files in this directory are used.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "../../../../../firmware/lib/timer.h"
|
||||
#include "../../../../../firmware/lib/usb.h"
|
||||
#include "../../../../../firmware/lib/usb/usage-page/keyboard.h"
|
||||
#include "../../../../../firmware/lib/layout/eeprom-macro.h"
|
||||
#include "../../../../../firmware/lib/layout/key-functions.h"
|
||||
#include "../../../../../firmware/lib/layout/layer-stack.h"
|
||||
#include "../../../../../firmware/keyboard.h"
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* This code fragment implements and extends the definitions in
|
||||
* ".../lib/layout/keys.h" and extends the definitions in
|
||||
* ".../lib/layout/key-functions.h"
|
||||
*/
|
||||
|
||||
|
||||
/** macros/KEYS__DEFAULT/description
|
||||
* Define the functions for a default key (i.e. a normal key that presses and
|
||||
* releases a keycode as you'd expect)
|
||||
*
|
||||
* Needed by ".../lib/layout/keys.h"
|
||||
*/
|
||||
#define KEYS__DEFAULT(name, value) \
|
||||
void P(name) (void) { KF(press)(value); } \
|
||||
void R(name) (void) { KF(release)(value); }
|
||||
|
||||
/** macros/KEYS__SHIFTED/description
|
||||
* Define the functions for a "shifted" key (i.e. a key that sends a "shift"
|
||||
* along with the keycode)
|
||||
*
|
||||
* Needed by ".../lib/layout/keys.h"
|
||||
*/
|
||||
#define KEYS__SHIFTED(name, value) \
|
||||
void P(name) (void) { KF(press)(KEYBOARD__LeftShift); \
|
||||
KF(press)(value); } \
|
||||
void R(name) (void) { KF(release)(value); \
|
||||
KF(release)(KEYBOARD__LeftShift); }
|
||||
|
||||
/** macros/KEYS__LAYER__PUSH_POP/description
|
||||
* Define the functions for a layer push-pop key (i.e. a layer shift key).
|
||||
*
|
||||
* Naming Convention:
|
||||
* - Example: In the name `lpupo1l1`, we have the following:
|
||||
* - `l` : this is a layer key
|
||||
* - `pu` : it pushes the layer element onto the stack on "press"
|
||||
* - `po` : it pops the layer element out of the stack on "release"
|
||||
* - `1` : it pushes and pops the layer element with `layer_id` = 1
|
||||
* - `l` : (separate the `layer_id` from the `layer_number`)
|
||||
* - `1` : it pushes a layer element with `layer_number` = 1
|
||||
*
|
||||
* - The first and second number do not have to be the same (that is, the
|
||||
* `layer_id` and `layer_number` can be different; there may be situations
|
||||
* where you want or need this).
|
||||
*
|
||||
* - Only one of `pu` or `po` is necessary. A key with only `pu` should *only*
|
||||
* push the layer onto the stack, not pop anything out of it. A key with
|
||||
* only `po` should *only* pop the layer out of the stack.
|
||||
*
|
||||
* - If the function *only* pops the layer-element, the `layer_number` is not
|
||||
* important: layers are popped based only on their `layer_id`.
|
||||
*
|
||||
* Notes:
|
||||
* - To save space, if you define a push-pop function, the push (only) and pop
|
||||
* (only) functions may be defined as follows (using the example `lpupo1l1`
|
||||
* from above):
|
||||
*
|
||||
* #define keys__press__lpu1l1 P(lpupo1l1)
|
||||
* #define keys__release__lpu1l1 KF(nop)
|
||||
* #define keys__press__lpo1l1 R(lpupo1l1)
|
||||
* #define keys__release__lpo1l1 KF(nop)
|
||||
*/
|
||||
#define KEYS__LAYER__PUSH_POP(ID, LAYER) \
|
||||
void P(lpupo##ID##l##LAYER) (void) { layer_stack__push(0, ID, LAYER); } \
|
||||
void R(lpupo##ID##l##LAYER) (void) { layer_stack__pop_id(ID); }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/KF(2_keys_capslock)/description
|
||||
* Press the given keycode, and also press "capslock" if this is the second
|
||||
* consecutive time this function has been called with `pressed == true`.
|
||||
*
|
||||
* Meant to be used with the left and right "shift" keys.
|
||||
*/
|
||||
void KF(2_keys_capslock)(bool pressed, uint8_t keycode) {
|
||||
static uint8_t counter = 0;
|
||||
if (pressed) {
|
||||
counter++;
|
||||
KF(press)(keycode);
|
||||
}
|
||||
if (counter == 2 && pressed) {
|
||||
KF(toggle_capslock)();
|
||||
}
|
||||
if (!pressed) {
|
||||
counter--;
|
||||
KF(release)(keycode);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// --- default key definitions ------------------------------------------------
|
||||
|
||||
#include "../../../../../firmware/lib/layout/keys.h"
|
||||
|
||||
|
||||
// --- special meaning --------------------------------------------------------
|
||||
|
||||
/** keys/transp/description
|
||||
* transparent
|
||||
*
|
||||
* This key signals to the firmware (specifically the
|
||||
* `kb__layout__exec_key_location()` function) that it should look for what key
|
||||
* to "press" or "release" by going down the layer-stack until it finds a
|
||||
* non-transparent key at the same position.
|
||||
*
|
||||
* Notes:
|
||||
* - With this scheme, keys may be half transparent; that is, the "press" part
|
||||
* of a key may be transparent while the "release" part isn't, or vice versa.
|
||||
* I expect this to be fairly uncommon though.
|
||||
*/
|
||||
void KF(transp) (void) {}
|
||||
#define keys__press__transp KF(transp)
|
||||
#define keys__release__transp KF(transp)
|
||||
|
||||
/** keys/nop/description
|
||||
* no operation
|
||||
*
|
||||
* This key does nothing (and is not transparent).
|
||||
*/
|
||||
void KF(nop) (void) {}
|
||||
#define keys__press__nop KF(nop)
|
||||
#define keys__release__nop KF(nop)
|
||||
|
||||
|
||||
// --- special keycode --------------------------------------------------------
|
||||
|
||||
KEYS__DEFAULT( power, KEYBOARD__Power );
|
||||
KEYS__DEFAULT( volumeU, KEYBOARD__VolumeUp );
|
||||
KEYS__DEFAULT( volumeD, KEYBOARD__VolumeDown );
|
||||
KEYS__DEFAULT( mute, KEYBOARD__Mute );
|
||||
|
||||
|
||||
// --- special function -------------------------------------------------------
|
||||
|
||||
/** keys/shL2kcap/description
|
||||
* left shift + toggle capslock (if both shifts are pressed)
|
||||
*
|
||||
* This key always generates a left shift. If the `shR2kcap` is pressed at
|
||||
* the same time, "capslock" will be toggled.
|
||||
*/
|
||||
void P(shL2kcap) (void) { KF(2_keys_capslock)(true, KEYBOARD__LeftShift); }
|
||||
void R(shL2kcap) (void) { KF(2_keys_capslock)(false, KEYBOARD__LeftShift); }
|
||||
|
||||
/** keys/shR2kcap/description
|
||||
* right shift + toggle capslock (if both shifts are pressed)
|
||||
*
|
||||
* This key always generates a right shift. If the `shL2kcaps` is pressed at
|
||||
* the same time, "capslock" will be toggled.
|
||||
*/
|
||||
void P(shR2kcap) (void) { KF(2_keys_capslock)(true, KEYBOARD__RightShift); }
|
||||
void R(shR2kcap) (void) { KF(2_keys_capslock)(false, KEYBOARD__RightShift); }
|
||||
|
||||
/** keys/btldr/description
|
||||
* jump to the bootloader
|
||||
*
|
||||
* This prepares the Teensy to load a new firmware. If you press this without
|
||||
* meaning to, you must turn your keyboard off then on again (usually by
|
||||
* unplugging it, then plugging it back in).
|
||||
*/
|
||||
void P(btldr) (void) { KF(jump_to_bootloader)(); }
|
||||
void R(btldr) (void) {}
|
||||
|
||||
/** keys/dmp_sram/description
|
||||
* type the contents of SRAM, in ihex format
|
||||
*
|
||||
* This may take a while. To cancel, you must turn your keyboard off then on
|
||||
* again (usually by unplugging it, then plugging it back in).
|
||||
*/
|
||||
void P(dmp_sram) (void) { KF(dump_sram_ihex)( (void *)0, (void *)-1 ); }
|
||||
void R(dmp_sram) (void) {}
|
||||
|
||||
/** keys/dmp_prog/description
|
||||
* type the contents of PROGMEM, in ihex format
|
||||
*
|
||||
* This may take a while. To cancel, you must turn your keyboard off then on
|
||||
* again (usually by unplugging it, then plugging it back in).
|
||||
*/
|
||||
void P(dmp_prog) (void) { KF(dump_progmem_ihex)( (void *)0, (void *)-1 ); }
|
||||
void R(dmp_prog) (void) {}
|
||||
|
||||
/** keys/dmp_eepr/description
|
||||
* type the contents of the EEPROM, in ihex format
|
||||
*
|
||||
* This may take a while. To cancel, you must turn your keyboard off then on
|
||||
* again (usually by unplugging it, then plugging it back in).
|
||||
*/
|
||||
void P(dmp_eepr) (void) { KF(dump_eeprom_ihex)( (void *)0, (void *)-1 ); }
|
||||
void R(dmp_eepr) (void) {}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// --- layer ------------------------------------------------------------------
|
||||
|
||||
// - these are just some default layer key definitions; no need to stick to
|
||||
// them if they're inconvenient
|
||||
// - the functions for layers 1 and 2 are special here in that they turn on and
|
||||
// off the corresponding LED (the third LED is reserved for capslock)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(0, 0);
|
||||
#define keys__press__lpu0l0 P(lpupo0l0)
|
||||
#define keys__release__lpu0l0 KF(nop)
|
||||
#define keys__press__lpo0l0 R(lpupo0l0)
|
||||
#define keys__release__lpo0l0 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(1,1);
|
||||
void P(lpu1l1) (void) { layer_stack__push(0, 1, 1); kb__led__on(1); }
|
||||
#define keys__release__lpu1l1 KF(nop)
|
||||
void P(lpo1l1) (void) { layer_stack__pop_id(1); kb__led__off(1); }
|
||||
#define keys__release__lpo1l1 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(2,2);
|
||||
void P(lpu2l2) (void) { layer_stack__push(0, 2, 2); kb__led__on(2); }
|
||||
#define keys__release__lpu2l2 KF(nop)
|
||||
void P(lpo2l2) (void) { layer_stack__pop_id(2); kb__led__off(2); }
|
||||
#define keys__release__lpo2l2 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(3, 3);
|
||||
#define keys__press__lpu3l3 P(lpupo3l3)
|
||||
#define keys__release__lpu3l3 KF(nop)
|
||||
#define keys__press__lpo3l3 R(lpupo3l3)
|
||||
#define keys__release__lpo3l3 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(4, 4);
|
||||
#define keys__press__lpu4l4 P(lpupo4l4)
|
||||
#define keys__release__lpu4l4 KF(nop)
|
||||
#define keys__press__lpo4l4 R(lpupo4l4)
|
||||
#define keys__release__lpo4l4 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(5, 5);
|
||||
#define keys__press__lpu5l5 P(lpupo5l5)
|
||||
#define keys__release__lpu5l5 KF(nop)
|
||||
#define keys__press__lpo5l5 R(lpupo5l5)
|
||||
#define keys__release__lpo5l5 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(6, 6);
|
||||
#define keys__press__lpu6l6 P(lpupo6l6)
|
||||
#define keys__release__lpu6l6 KF(nop)
|
||||
#define keys__press__lpo6l6 R(lpupo6l6)
|
||||
#define keys__release__lpo6l6 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(7, 7);
|
||||
#define keys__press__lpu7l7 P(lpupo7l7)
|
||||
#define keys__release__lpu7l7 KF(nop)
|
||||
#define keys__press__lpo7l7 R(lpupo7l7)
|
||||
#define keys__release__lpo7l7 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(8, 8);
|
||||
#define keys__press__lpu8l8 P(lpupo8l8)
|
||||
#define keys__release__lpu8l8 KF(nop)
|
||||
#define keys__press__lpo8l8 R(lpupo8l8)
|
||||
#define keys__release__lpo8l8 KF(nop)
|
||||
|
||||
KEYS__LAYER__PUSH_POP(9, 9);
|
||||
#define keys__press__lpu9l9 P(lpupo9l9)
|
||||
#define keys__release__lpu9l9 KF(nop)
|
||||
#define keys__press__lpo9l9 R(lpupo9l9)
|
||||
#define keys__release__lpo9l9 KF(nop)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Default actions for changes in logical (host controlled) LED states
|
||||
*
|
||||
* Notes:
|
||||
* - LEDs 1 and 2 are controlled by the corresponding layer keys.
|
||||
*/
|
||||
|
||||
|
||||
void kb__led__logical_on(char led) {
|
||||
switch(led) {
|
||||
case 'N': break; // numlock
|
||||
case 'C': kb__led__on(3); break; // capslock
|
||||
case 'S': break; // scroll lock
|
||||
case 'O': break; // compose
|
||||
case 'K': break; // kana
|
||||
};
|
||||
}
|
||||
|
||||
void kb__led__logical_off(char led) {
|
||||
switch(led) {
|
||||
case 'N': break; // numlock
|
||||
case 'C': kb__led__off(3); break; // capslock
|
||||
case 'S': break; // scroll lock
|
||||
case 'O': break; // compose
|
||||
case 'K': break; // kana
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Default macro definitions
|
||||
*/
|
||||
|
||||
|
||||
/** macros/P/description
|
||||
* Expand `name` into the corresponding "press" function name
|
||||
*/
|
||||
#define P(name) keys__press__##name
|
||||
|
||||
/** macros/R/description
|
||||
* Expand `name` into the corresponding "release" function name
|
||||
*/
|
||||
#define R(name) keys__release__##name
|
||||
|
||||
/** macros/K/description
|
||||
* Expand into a "key" suitable for putting into the layout matrix
|
||||
*/
|
||||
#define K(name) { &keys__press__##name, &keys__release__##name }
|
||||
|
||||
/** macros/KF/description
|
||||
* Expand `name` into the corresponding "key_functions" function name
|
||||
*/
|
||||
#define KF(name) key_functions__##name
|
||||
|
||||
|
||||
/** macros/MATRIX_LAYER/description
|
||||
* Mapping from spatial position to matrix position.
|
||||
*
|
||||
* - Spatial position: Where the key is spatially, relative to other keys, both
|
||||
* on the keyboard and in the layout.
|
||||
* - Matrix position: The coordinate in the matrix to which a key is scanned by
|
||||
* the update functions.
|
||||
*
|
||||
* - Location numbers are in the format `row##column`, where both 'row' and
|
||||
* 'column' are single digit hex numbers corresponding to the matrix position
|
||||
* (which also corresponds to the row and column pin labels used in the
|
||||
* Teensy and MCP23018 files).
|
||||
*
|
||||
* - Coordinates:
|
||||
* - optional keys
|
||||
* - k15, k16 (left hand thumb group)
|
||||
* - k17, k18 (right hand thumb group)
|
||||
* - unused keys
|
||||
* - k36, k00 (left hand)
|
||||
* - k37, k0D (right hand)
|
||||
*
|
||||
* - Other Info:
|
||||
* ----------------------------------------------------
|
||||
* rows x columns = positions; used, unused
|
||||
* per hand: 6 x 7 = 42; 40, 2
|
||||
* total: 6 x 14 = 84; 80, 4
|
||||
*
|
||||
* left hand : rows 0..5, cols 0..6
|
||||
* right hand : rows 0..5, cols 7..D
|
||||
* ----------------------------------------------------
|
||||
*/
|
||||
#define MATRIX_LAYER( \
|
||||
/* the name of a macro to call on all arguments */ \
|
||||
M, \
|
||||
\
|
||||
/* for unused positions */ \
|
||||
na, \
|
||||
\
|
||||
/* left hand, spatial positions */ \
|
||||
k50,k51,k52,k53,k54,k55,k56, \
|
||||
k40,k41,k42,k43,k44,k45,k46, \
|
||||
k30,k31,k32,k33,k34,k35, \
|
||||
k20,k21,k22,k23,k24,k25,k26, \
|
||||
k10,k11,k12,k13,k14, \
|
||||
k05,k06, \
|
||||
k15,k16,k04, \
|
||||
k03,k02,k01, \
|
||||
\
|
||||
/* right hand, spatial positions */ \
|
||||
k57,k58,k59,k5A,k5B,k5C,k5D, \
|
||||
k47,k48,k49,k4A,k4B,k4C,k4D, \
|
||||
k38,k39,k3A,k3B,k3C,k3D, \
|
||||
k27,k28,k29,k2A,k2B,k2C,k2D, \
|
||||
k19,k1A,k1B,k1C,k1D, \
|
||||
k07,k08, \
|
||||
k09,k17,k18, \
|
||||
k0C,k0B,k0A ) \
|
||||
\
|
||||
/* matrix positions */ \
|
||||
{{ M( na),M(k01),M(k02),M(k03),M(k04),M(k05),M(k06), M(k07),M(k08),M(k09),M(k0A),M(k0B),M(k0C),M( na) }, \
|
||||
{ M(k10),M(k11),M(k12),M(k13),M(k14),M(k15),M(k16), M(k17),M(k18),M(k19),M(k1A),M(k1B),M(k1C),M(k1D) }, \
|
||||
{ M(k20),M(k21),M(k22),M(k23),M(k24),M(k25),M(k26), M(k27),M(k28),M(k29),M(k2A),M(k2B),M(k2C),M(k2D) }, \
|
||||
{ M(k30),M(k31),M(k32),M(k33),M(k34),M(k35),M( na), M( na),M(k38),M(k39),M(k3A),M(k3B),M(k3C),M(k3D) }, \
|
||||
{ M(k40),M(k41),M(k42),M(k43),M(k44),M(k45),M(k46), M(k47),M(k48),M(k49),M(k4A),M(k4B),M(k4C),M(k4D) }, \
|
||||
{ M(k50),M(k51),M(k52),M(k53),M(k54),M(k55),M(k56), M(k57),M(k58),M(k59),M(k5A),M(k5B),M(k5C),M(k5D) }}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A default way to execute keys
|
||||
*
|
||||
* Usage notes:
|
||||
* - Depends on (must be included after) "variables.part.h" and "keys.part.h"
|
||||
*/
|
||||
|
||||
|
||||
/** functions/kb__layout__exec_key/description
|
||||
* Assumptions:
|
||||
* - All arguments are valid.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - The default layer is layer 0.
|
||||
* - This function is only responsible for layer resolution (which includes the
|
||||
* handling of transparent keys). Everything else, it passes to
|
||||
* `kb__layout__exec_key_layer()`.
|
||||
*/
|
||||
void kb__layout__exec_key(bool pressed, uint8_t row, uint8_t column) {
|
||||
|
||||
// - to keep track of the layer a key was pressed on, so we can release on
|
||||
// the same layer
|
||||
static uint8_t pressed_layer[OPT__KB__ROWS][OPT__KB__COLUMNS];
|
||||
|
||||
uint8_t layer;
|
||||
void (*function)(void);
|
||||
|
||||
// handle the case that a key is released, and the layer it was pressed on
|
||||
// has a non-transparent release function in the given location
|
||||
|
||||
if (! pressed) {
|
||||
layer = pressed_layer[row][column];
|
||||
function = (void (*)(void))
|
||||
pgm_read_word( &( layout[ layer ]
|
||||
[ row ]
|
||||
[ column ]
|
||||
[ !pressed ] ) );
|
||||
|
||||
if (function != &KF(transp)) {
|
||||
kb__layout__exec_key_layer( pressed, layer, row, column );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, search through the layer stack for a layer with a
|
||||
// non-transparent key-function in the given location
|
||||
|
||||
// - altogether, unless we find a non-transparent key-function earlier, we
|
||||
// want to peek at offsets `0` through `layer_stack__size()`. this will
|
||||
// cause us to peek out of bounds on the last iteration, so that layer 0
|
||||
// will be the default (see the documentation for
|
||||
// ".../lib/layout/layer-stack")
|
||||
for (uint8_t i=0; i <= layer_stack__size(); i++) {
|
||||
layer = layer_stack__peek(i);
|
||||
function = (void (*)(void))
|
||||
pgm_read_word( &( layout[ layer ]
|
||||
[ row ]
|
||||
[ column ]
|
||||
[ !pressed ] ) );
|
||||
|
||||
if (function != &KF(transp)) {
|
||||
if (pressed)
|
||||
pressed_layer[row][column] = layer;
|
||||
|
||||
kb__layout__exec_key_layer( pressed, layer, row, column );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we get here, there was a transparent key in layer 0; do nothing
|
||||
}
|
||||
|
||||
/** functions/kb__layout__exec_key_layer/description
|
||||
* Assumptions:
|
||||
* - All arguments are valid.
|
||||
*
|
||||
* TODO:
|
||||
* - take care of the recording and such of macros :)
|
||||
* - need to ignore layer-shift keys when recording
|
||||
*/
|
||||
void kb__layout__exec_key_layer( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
void (*function)(void) = (void (*)(void))
|
||||
pgm_read_word( &( layout[ layer ]
|
||||
[ row ]
|
||||
[ column ]
|
||||
[ !pressed ] ) );
|
||||
if (! function) return;
|
||||
|
||||
// set default values
|
||||
// - the key-function will not be able to see the values set previously
|
||||
// - any function scheduled to run will be able to see the values set
|
||||
// by the immediately preceding function; but that may change in the
|
||||
// future, so it shouldn't be relied on. if functions need to
|
||||
// communicate with each other, they should share a file-local or global
|
||||
// variable.
|
||||
flags[0].key_type.sticky = false;
|
||||
flags[0].key_type.layer_shift = false;
|
||||
flags[0].key_type.layer_lock = false;
|
||||
|
||||
(*function)();
|
||||
|
||||
if (pressed)
|
||||
timer___tick_keypresses();
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Pieces of code meant to be (optionally) included in different layouts.
|
||||
|
||||
Please think of these files as code fragments, rather than actual C or header
|
||||
files.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Types used throughout the fragments of layout code in this directory
|
||||
*/
|
||||
|
||||
|
||||
/** types/_key_t/description
|
||||
* The type we will use for our "key"s
|
||||
*
|
||||
* Notes:
|
||||
* - Keys will be of the form
|
||||
* `_key_t key = { &press_function, &release_function };`
|
||||
*
|
||||
* - The fact that keys are of this type (composed of two
|
||||
* `void (*function)(void)` pointers) is assumed throughout most of these
|
||||
* layout files
|
||||
*/
|
||||
typedef void (*key_t[2])(void);
|
||||
|
||||
/** types/_layout_t/description
|
||||
* The type we will use for our layout matrix
|
||||
*
|
||||
* Notes:
|
||||
* - The first dimension of the matrix (left blank in the typedef since it
|
||||
* varies between layouts) is "layers"
|
||||
*/
|
||||
typedef const key_t layout_t[][OPT__KB__ROWS][OPT__KB__COLUMNS];
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Variables global to the fragments of layout code in this directory
|
||||
*/
|
||||
|
||||
|
||||
/** variables/layout/description
|
||||
* The variable containing our layout matrix
|
||||
*
|
||||
* Included here so that the matrix control code can come before the actual
|
||||
* definition of the layout.
|
||||
*/
|
||||
static layout_t layout PROGMEM;
|
||||
|
||||
/** variables/flags/description
|
||||
* A collection of flags pertaining to the operation of `...exec_key()`
|
||||
*
|
||||
* Notes:
|
||||
* - These should be set within key-functions, but only read inside
|
||||
* `...exec_key()`. The ability to read them outside that function should
|
||||
* not be counted on.
|
||||
*
|
||||
* Struct members:
|
||||
* - `key_type`: To indicate the type of key most recently pressed
|
||||
* - More than one type flag may be set (e.g. a key may be both a
|
||||
* layer-shift key and a sticky key).
|
||||
* - `key_type.sticky`
|
||||
* - `key_type.layer_shift`
|
||||
* - `key_type.layer_lock`
|
||||
*
|
||||
* Terms:
|
||||
* - A "sticky key" is one which, once pressed, remains pressed in software
|
||||
* (whether or not the user holds it down) ... TODO
|
||||
*
|
||||
* TODO:
|
||||
* - finish terms
|
||||
* - change other code (in "./matrix-control.part.h") to actually use the fact
|
||||
* that we have 2 of these now (so that there is an "old" version, and a
|
||||
* version to update)
|
||||
*/
|
||||
static struct {
|
||||
struct {
|
||||
bool sticky : 1;
|
||||
bool layer_shift : 1;
|
||||
bool layer_lock : 1;
|
||||
} key_type;
|
||||
} flags[2];
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* My QWERTY layout, at the moment. I imagine this will evolve over time.
|
||||
* Once I'm done with the Arensito layout, it may disappear altogether.
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
#include "./fragments/includes.part.h"
|
||||
#include "./fragments/macros.part.h"
|
||||
#include "./fragments/types.part.h"
|
||||
#include "./fragments/variables.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// keys
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/keys.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LED control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/led-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// matrix control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/matrix-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// layout
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static layout_t layout PROGMEM = {
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 0 : default
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
equal, 1, 2, 3, 4, 5, esc,
|
||||
tab, q, w, e, r, t, lpu2l2,
|
||||
bkslash, a, s, d, f, g,
|
||||
shL2kcap, z, x, c, v, b, lpupo2l2,
|
||||
guiL, grave, bkslash, arrowL, arrowR,
|
||||
ctrlL, altL,
|
||||
nop, nop, home,
|
||||
bs, del, end,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
lpu1l1, 6, 7, 8, 9, 0, dash,
|
||||
brktL, y, u, i, o, p, brktR,
|
||||
h, j, k, l, semicol, quote,
|
||||
lpupo2l2, n, m, comma, period, slash, shR2kcap,
|
||||
arrowL, arrowD, arrowU, arrowR, guiR,
|
||||
altR, ctrlR,
|
||||
pageU, nop, nop,
|
||||
pageD, enter, space ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 1 : number pad
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
transp, transp, transp, transp, transp, transp,
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
transp, ins, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
lpo1l1, transp, lpo1l1, equal, slash, asterisk, transp,
|
||||
transp, transp, 7, 8, 9, dash, transp,
|
||||
transp, 4, 5, 6, plus, transp,
|
||||
transp, transp, 1, 2, 3, enter, transp,
|
||||
transp, transp, period, enter, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, 0 ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 2 : symbols and function keys
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
transp, F1, F2, F3, F4, F5, F11,
|
||||
transp, braceL, braceR, brktL, brktR, nop, lpo2l2,
|
||||
transp, semicol, slash, dash, 0, colon,
|
||||
transp, 6, 7, 8, 9, plus, lpupo3l3,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
F12, F6, F7, F8, F9, F10, power,
|
||||
lpo2l2, caret, undersc, lessThan, grtrThan, dollar, volumeU,
|
||||
bkslash, 1, parenL, parenR, equal, volumeD,
|
||||
lpupo3l3, asterisk, 2, 3, 4, 5, mute,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 3 : keyboard functions
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
btldr, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
nop, nop, nop, nop, nop, nop, dmp_sram,
|
||||
nop, nop, nop, nop, nop, nop, dmp_prog,
|
||||
nop, nop, nop, nop, nop, dmp_eepr,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop ),
|
||||
|
||||
// ............................................................................
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A QWERTY layout using the "kinesis-mod" template
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
// left hand letter key block
|
||||
#define T_q q
|
||||
#define T_w w
|
||||
#define T_e e
|
||||
#define T_r r
|
||||
#define T_t t
|
||||
#define T_a a
|
||||
#define T_s s
|
||||
#define T_d d
|
||||
#define T_f f
|
||||
#define T_g g
|
||||
#define T_z z
|
||||
#define T_x x
|
||||
#define T_c c
|
||||
#define T_v v
|
||||
#define T_b b
|
||||
|
||||
// right hand letter key block
|
||||
#define T_y y
|
||||
#define T_u u
|
||||
#define T_i i
|
||||
#define T_o o
|
||||
#define T_p p
|
||||
#define T_h h
|
||||
#define T_j j
|
||||
#define T_k k
|
||||
#define T_l l
|
||||
#define T_semicol semicol
|
||||
#define T_n n
|
||||
#define T_m m
|
||||
#define T_comma comma
|
||||
#define T_period period
|
||||
#define T_slash slash
|
||||
|
||||
// right hand outer key block
|
||||
#define T_bkslash bkslash
|
||||
#define T_quote quote
|
||||
|
||||
|
||||
#include "./templates/kinesis-mod.c.h"
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
## TODO
|
||||
|
||||
- fix and expand the ability of keys to do different things depending on what
|
||||
kind of key was pressed before
|
||||
- fix the issue with shifted keys sometimes causing subsequent keys to be
|
||||
shifted, when typing quickly (shift should probably be released when the next
|
||||
key is pressed, in this case, if the original shifted key hasn't been
|
||||
released)
|
||||
- maybe implement a separate per-key debounce, so that keys are scanned
|
||||
quickly, but can only be pressed 1 time every 15 (or something) milliseconds.
|
||||
|
||||
### Tutorials
|
||||
- basic key functions
|
||||
- mention where people should look for more information; probably, the usb, key_functions, and keys headers; and others?
|
||||
- sticky keys
|
||||
- maybe write a chordmak (or asetniop) layaout, on top of a standard
|
||||
colemak layout, using chained sticky keys for the modifiers
|
||||
- macros
|
||||
- chorded keys
|
||||
- timed keys
|
||||
- automatic repetition of utf-8 sequence keys
|
||||
- layers
|
||||
- making layouts
|
||||
- changing the meaning of the LEDs
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A layout with the home layer adapted from the default Kinesis layout
|
||||
* (staying as close the original as possible). The position of the symbol
|
||||
* keys on the function layer was (roughly) taken from the Arensito layout.
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*
|
||||
* The template key prefix is `T_`, with the rest of the name indicating the
|
||||
* key's position in the QWERTY layout.
|
||||
*/
|
||||
|
||||
|
||||
#include "../fragments/includes.part.h"
|
||||
#include "../fragments/macros.part.h"
|
||||
#include "../fragments/types.part.h"
|
||||
#include "../fragments/variables.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// keys
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "../fragments/keys.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LED control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "../fragments/led-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// matrix control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "../fragments/matrix-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// layout
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static layout_t layout PROGMEM = {
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 0 : default
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
equal, 1, 2, 3, 4, 5, esc,
|
||||
tab, T_q, T_w, T_e, T_r, T_t, lpu2l2,
|
||||
caps, T_a, T_s, T_d, T_f, T_g,
|
||||
shL2kcap, T_z, T_x, T_c, T_v, T_b, lpupo2l2,
|
||||
guiL, grave, bkslash, arrowL, arrowR,
|
||||
ctrlL, altL,
|
||||
nop, nop, home,
|
||||
bs, del, end,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
lpu1l1, 6, 7, 8, 9, 0, dash,
|
||||
lpu2l2, T_y, T_u, T_i, T_o, T_p,T_bkslash,
|
||||
T_h, T_j, T_k, T_l,T_semicol, T_quote,
|
||||
lpupo2l2, T_n, T_m, T_comma, T_period, T_slash, shR2kcap,
|
||||
arrowU, arrowD, brktL, brktR, guiR,
|
||||
altR, ctrlR,
|
||||
pageU, nop, nop,
|
||||
pageD, enter, space ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 1 : number pad
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
nop, transp, transp, transp, transp, transp,
|
||||
transp, transp, transp, transp, transp, transp, transp,
|
||||
transp, ins, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
lpo1l1, transp, lpo1l1, equal, slash, asterisk, transp,
|
||||
transp, transp, 7, 8, 9, dash, transp,
|
||||
transp, 4, 5, 6, plus, transp,
|
||||
transp, transp, 1, 2, 3, enter, transp,
|
||||
transp, transp, period, enter, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, 0 ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 2 : symbols and function keys
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
transp, F1, F2, F3, F4, F5, F11,
|
||||
transp, braceL, braceR, brktL, brktR, nop, lpo2l2,
|
||||
transp, semicol, slash, dash, 0, colon,
|
||||
transp, 6, 7, 8, 9, plus, lpupo3l3,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
F12, F6, F7, F8, F9, F10, power,
|
||||
lpo2l2, nop, undersc, lessThan, grtrThan, dollar, volumeU,
|
||||
bkslash, 1, parenL, parenR, equal, volumeD,
|
||||
lpupo3l3, asterisk, 2, 3, 4, 5, mute,
|
||||
transp, transp, transp, transp, transp,
|
||||
transp, transp,
|
||||
transp, transp, transp,
|
||||
transp, transp, transp ),
|
||||
|
||||
// ............................................................................
|
||||
|
||||
MATRIX_LAYER( // layer 3 : keyboard functions
|
||||
// macro, unused,
|
||||
K, nop,
|
||||
// left hand ...... ......... ......... ......... ......... ......... .........
|
||||
btldr, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop,
|
||||
// right hand ..... ......... ......... ......... ......... ......... .........
|
||||
nop, nop, nop, nop, nop, nop, dmp_sram,
|
||||
nop, nop, nop, nop, nop, nop, dmp_prog,
|
||||
nop, nop, nop, nop, nop, dmp_eepr,
|
||||
nop, nop, nop, nop, nop, nop, nop,
|
||||
nop, nop, nop, nop, nop,
|
||||
nop, nop,
|
||||
nop, nop, nop,
|
||||
nop, nop, nop ),
|
||||
|
||||
// ............................................................................
|
||||
};
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A layout for testing newly build boards
|
||||
*
|
||||
* Implements the "layout" section of '.../firmware/keyboard.h'
|
||||
*/
|
||||
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
#include "../../../../firmware/lib/layout/key-functions.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LED control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "./fragments/led-control.part.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// matrix control
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/kb__layout__exec_key/description
|
||||
* Implementation notes:
|
||||
* - TODO
|
||||
*/
|
||||
void kb__layout__exec_key(bool pressed, uint8_t row, uint8_t column) {
|
||||
key_functions__type_string ( PSTR( "test: " ) );
|
||||
|
||||
key_functions__type_string ( PSTR( " pressed: " ) );
|
||||
key_functions__type_byte_hex ( pressed );
|
||||
|
||||
key_functions__type_string ( PSTR( " row: " ) );
|
||||
key_functions__type_byte_hex ( row );
|
||||
|
||||
key_functions__type_string ( PSTR( " column: " ) );
|
||||
key_functions__type_byte_hex ( column );
|
||||
|
||||
key_functions__type_string ( PSTR( "\n" ) );
|
||||
|
||||
// handle jumping to the bootloader if the three keys that cause this in
|
||||
// the templates/kinesis-mod layout are pressed (in any order)
|
||||
|
||||
static uint8_t count; // set to 0 by the compiler
|
||||
|
||||
if ( ( row == 0x2 && column == 0x6 ) ||
|
||||
( row == 0x2 && column == 0x7 ) ||
|
||||
( row == 0x5 && column == 0x0 ) ) {
|
||||
if (pressed) {
|
||||
count++;
|
||||
} else {
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (count >=3) {
|
||||
key_functions__jump_to_bootloader();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "LED" section of '.../firmware/keyboard.h'
|
||||
*
|
||||
* Code is specific to Teensy 2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
#include "../../../firmware/keyboard.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** macros/OPT__LED_BRIGHTNESS/description
|
||||
* A percentage of maximum brightness, with '1' being greatest and '0' being
|
||||
* not quite off
|
||||
*/
|
||||
#ifndef OPT__LED_BRIGHTNESS
|
||||
#error "OPT__LED_BRIGHTNESS not defined"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** types/struct led_state/description
|
||||
* For holding the state of the LEDs (if we ever need to save/restore them)
|
||||
*/
|
||||
struct led_state {
|
||||
bool led_1 : 1;
|
||||
bool led_2 : 1;
|
||||
bool led_3 : 1;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void kb__led__on(uint8_t led) {
|
||||
switch(led) {
|
||||
case 1: (DDRB |= (1<<5)); break; // topmost
|
||||
case 2: (DDRB |= (1<<6)); break; // middle
|
||||
case 3: (DDRB |= (1<<7)); break; // bottommost
|
||||
case 4: break;
|
||||
case 5: break;
|
||||
};
|
||||
}
|
||||
|
||||
void kb__led__off(uint8_t led) {
|
||||
switch(led) {
|
||||
case 1: (DDRB &= ~(1<<5)); break; // topmost
|
||||
case 2: (DDRB &= ~(1<<6)); break; // middle
|
||||
case 3: (DDRB &= ~(1<<7)); break; // bottommost
|
||||
case 4: break;
|
||||
case 5: break;
|
||||
};
|
||||
}
|
||||
|
||||
void kb__led__set(uint8_t led, float n) {
|
||||
switch(led) {
|
||||
case 1: (OCR1A = (uint8_t)((n) * 0xFF)); break; // topmost
|
||||
case 2: (OCR1B = (uint8_t)((n) * 0xFF)); break; // middle
|
||||
case 3: (OCR1C = (uint8_t)((n) * 0xFF)); break; // bottommost
|
||||
case 4: break;
|
||||
case 5: break;
|
||||
};
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
bool kb__led__read(uint8_t led) {
|
||||
switch(led) {
|
||||
case 1: return (PINB & (1<<5)); // topmost
|
||||
case 2: return (PINB & (1<<6)); // middle
|
||||
case 3: return (PINB & (1<<7)); // bottommost
|
||||
case 4: ;
|
||||
case 5: ;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
void kb__led__all_on(void) {
|
||||
for (uint8_t i=1; i<=3; i++)
|
||||
kb__led__on(i);
|
||||
}
|
||||
|
||||
void kb__led__all_off(void) {
|
||||
for (uint8_t i=1; i<=3; i++)
|
||||
kb__led__off(i);
|
||||
}
|
||||
|
||||
void kb__led__all_set(float n) {
|
||||
for (uint8_t i=1; i<=3; i++)
|
||||
kb__led__set(i, n);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
void kb__led__state__power_on(void) {
|
||||
kb__led__all_set( OPT__LED_BRIGHTNESS / 10 );
|
||||
kb__led__all_on();
|
||||
}
|
||||
|
||||
void kb__led__state__ready(void) {
|
||||
kb__led__all_off();
|
||||
kb__led__all_set( OPT__LED_BRIGHTNESS );
|
||||
}
|
||||
|
||||
void kb__led__delay__error(void) {
|
||||
// make sure LED states have stabilized
|
||||
// - i'm not quite sure how long this takes; i would think it'd be nearly
|
||||
// instant, but a bit of testing seemed to show that even 5ms isn't quite
|
||||
// long enough; 10ms seems to work; at least we can afford the time here
|
||||
_delay_ms(10);
|
||||
|
||||
struct led_state state = {
|
||||
.led_1 = kb__led__read(1),
|
||||
.led_2 = kb__led__read(2),
|
||||
.led_3 = kb__led__read(3),
|
||||
};
|
||||
|
||||
kb__led__all_off();
|
||||
_delay_ms(167);
|
||||
kb__led__all_on();
|
||||
_delay_ms(167);
|
||||
kb__led__all_off();
|
||||
_delay_ms(167);
|
||||
kb__led__all_on();
|
||||
_delay_ms(167);
|
||||
kb__led__all_off();
|
||||
_delay_ms(167);
|
||||
kb__led__all_on();
|
||||
_delay_ms(167);
|
||||
kb__led__all_off();
|
||||
_delay_ms(167);
|
||||
|
||||
if (state.led_1) kb__led__on(1);
|
||||
if (state.led_2) kb__led__on(2);
|
||||
if (state.led_3) kb__led__on(3);
|
||||
}
|
||||
|
||||
void kb__led__delay__usb_init(void) {
|
||||
// need to delay for a total of ~1 second
|
||||
kb__led__set( 1, OPT__LED_BRIGHTNESS );
|
||||
_delay_ms(333);
|
||||
kb__led__set( 2, OPT__LED_BRIGHTNESS );
|
||||
_delay_ms(333);
|
||||
kb__led__set( 3, OPT__LED_BRIGHTNESS );
|
||||
_delay_ms(333);
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* A central place for all ErgoDox source-level options
|
||||
*
|
||||
* Prefix: `OPT__`
|
||||
*
|
||||
* This file is meant to be globally included on the command line.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__OPTIONS__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__OPTIONS__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// firmware/main
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__DEBOUNCE_TIME 5
|
||||
// in milliseconds
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// firmware/keyboard/controller
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// --- if the diode cathode is towards the square solder pad
|
||||
#define OPT__TEENSY__DRIVE_ROWS 0
|
||||
#define OPT__TEENSY__DRIVE_COLUMNS 1
|
||||
#define OPT__MCP23018__DRIVE_ROWS 0
|
||||
#define OPT__MCP23018__DRIVE_COLUMNS 1
|
||||
// ............................................................................
|
||||
// --- if the diode cathode is towards the circular solder pad
|
||||
// #define OPT__TEENSY__DRIVE_ROWS 1
|
||||
// #define OPT__TEENSY__DRIVE_COLUMNS 0
|
||||
// #define OPT__MCP23018__DRIVE_ROWS 1
|
||||
// #define OPT__MCP23018__DRIVE_COLUMNS 0
|
||||
// ............................................................................
|
||||
// --- if you are using internal diodes (inside the key switches)
|
||||
// #define OPT__TEENSY__DRIVE_ROWS 0
|
||||
// #define OPT__TEENSY__DRIVE_COLUMNS 1
|
||||
// #define OPT__MCP23018__DRIVE_ROWS 1
|
||||
// #define OPT__MCP23018__DRIVE_COLUMNS 0
|
||||
// ............................................................................
|
||||
// ............................................................................
|
||||
// pin drive direction
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// firmware/keyboard/led
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__LED_BRIGHTNESS 0.5
|
||||
// a multiplier, with '1' being max
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// firmware/keyboard
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__KB__ROWS 6
|
||||
#define OPT__KB__COLUMNS 14
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// firmware/lib/...
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__TWI__FREQUENCY 400000
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__USB__STR_MANUFACTURER L"DIY"
|
||||
#define OPT__USB__STR_PRODUCT L"ErgoDox Ergonomic Keyboard"
|
||||
#define OPT__USB__VENDOR_ID 0x1d50 // Openmoko, Inc.
|
||||
#define OPT__USB__PRODUCT_ID 0x6028 // ErgoDox Ergonomic Keyboard
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define OPT__EEPROM__EEPROM_MACRO__START 0
|
||||
#define OPT__EEPROM__EEPROM_MACRO__END 1023
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__KEYBOARD__ERGODOX__OPTIONS__H
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# A central place for all ErgoDox-specific Makefile-level things
|
||||
#
|
||||
# This file is meant to be included by '.../firmware/makefile'
|
||||
#
|
||||
|
||||
|
||||
BINARY_FORMAT := ihex
|
||||
# the binary format to generate
|
||||
|
||||
MCU := atmega32u4
|
||||
# processor type (for the teensy 2.0)
|
||||
|
||||
F_CPU := 16000000
|
||||
# processor speed, in Hz; max value is 16000000 (16MHz); must match
|
||||
# initialization in source
|
||||
|
||||
KEYBOARD_LAYOUT := qwerty--ben
|
||||
# default layout for this keyboard
|
||||
|
||||
KEYBOARD_LAYOUTS := \
|
||||
test \
|
||||
arensito--ben \
|
||||
qwerty--ben \
|
||||
colemak--kinesis-mod \
|
||||
dvorak--kinesis-mod \
|
||||
qwerty--kinesis-mod
|
||||
# a list of all available layouts for this keyboard
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
$(call include_options_once,lib/eeprom)
|
||||
$(call include_options_once,lib/twi)
|
||||
$(call include_options_once,lib/layout/eeprom-macro)
|
||||
$(call include_options_once,lib/layout/key-functions)
|
||||
$(call include_options_once,lib/layout/layer-stack)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/*.c)
|
||||
SRC += $(wildcard $(CURDIR)/controller/*.c)
|
||||
SRC += $(wildcard $(CURDIR)/layout/$(KEYBOARD_LAYOUT)*.c)
|
||||
|
||||
CFLAGS += -include $(wildcard $(CURDIR)/options.h)
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* EEPROM interface
|
||||
*
|
||||
* Prefix: `eeprom__`
|
||||
*
|
||||
* Conventions:
|
||||
* - Because the EEPROM must be shared between different parts of the entire
|
||||
* program, there needs to be a way to define which blocks are to be used for
|
||||
* which purposes. To that end, all blocks of the EEPROM that are being used
|
||||
* must have their start and end addresses `#define`ed to two globally
|
||||
* visible macros, of the form `OPT__EEPROM__[prefix]__START` and
|
||||
* `OPT__EEPROM__[prefix]__END`, where "[prefix]" is the prefix usually given
|
||||
* to public functions, etc., in that section of the code.
|
||||
*
|
||||
* Notes:
|
||||
* - This is meant to be a replacement for the read, write, and update
|
||||
* functions provided by `<avr/eeprom.h>`, and should be preferred for those
|
||||
* operations. There are other things provided by that header that may be
|
||||
* useful however, and it's likely that both will be needed.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - Writes generated by calls to `eeprom__write()` and `eeprom__copy()` should
|
||||
* collectively execute in the order in which the calls were performed (i.e.
|
||||
* all writes should be sequential, in the expected order, regardless of the
|
||||
* function which generated them).
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__LIB__EEPROM__H
|
||||
#define ERGODOX_FIRMWARE__LIB__EEPROM__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t eeprom__read (void * from);
|
||||
uint8_t eeprom__write (void * to, uint8_t data);
|
||||
uint8_t eeprom__fill (void * to, uint8_t data, uint8_t length);
|
||||
uint8_t eeprom__copy (void * to, void * from, uint8_t length);
|
||||
|
||||
uint8_t eeprom__block_read (void * to, void * from, uint8_t length);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__LIB__EEPROM__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === eeprom__read() ===
|
||||
/** functions/eeprom__read/description
|
||||
* Read and return the data at `from` in the EEPROM memory space
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: The address of (i.e. a pointer to) the location from which to read
|
||||
*
|
||||
* Returns:
|
||||
* - success: The data stored at `from` in the EEPROM memory space
|
||||
* - failure: `0`
|
||||
*/
|
||||
|
||||
// === eeprom__write() ===
|
||||
/** functions/eeprom__write/description
|
||||
* Schedule a regular 1 byte write to the EEPROM memory space
|
||||
*
|
||||
* Arguments:
|
||||
* - `to`: The address of (i.e. a pointer to) the location to write to
|
||||
* - `data`: The data to write
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Writes are scheduled (i.e. buffered) because writing to EEPROMs takes an
|
||||
* enormous (relative to a microprocessor clock cycle) amount of time.
|
||||
* - Due to the technology used, EEPROM bytes, when cleared, have a logical
|
||||
* value of `1`. Interesting stuff, but I didn't read about it thoroughly
|
||||
* enough to give my own explanation here.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - Undefined behavior will result if
|
||||
* - `to` is not a valid address
|
||||
* - This function should only modify when necessary; that is, when the data to
|
||||
* be written is different than the data that's already there. This requires
|
||||
* more processor time (to read the current value and compare), but it's
|
||||
* better for the EEPROM (which has a limited write life), and will allow the
|
||||
* operation to complete *much* more quickly in the event that the data has
|
||||
* not changed.
|
||||
* - Writing `0xFF` should clear the memory (without writing anything), and
|
||||
* writing to a location currently set to `0xFF` should write without
|
||||
* clearing first.
|
||||
*/
|
||||
|
||||
// === eeprom__fill() ===
|
||||
/** functions/eeprom__fill/description
|
||||
* Fill a portion of the EEPROM with the given value
|
||||
*
|
||||
* Arguments:
|
||||
* - `to`: The address of (i.e. a pointer to) the location to start writing to
|
||||
* - `data`: The data to write
|
||||
* - `length`: The number of times to sequentially write `data`, incrementing
|
||||
* `to` each time
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
|
||||
// === eeprom__copy() ===
|
||||
/** functions/eeprom__copy/description
|
||||
* Copy data from one location in the EEPROM memory space to another
|
||||
*
|
||||
* Arguments:
|
||||
* - `to: The address of (i.e. a pointer to) the location to start writing to
|
||||
* - `from`: The address of (i.e. a pointer to) the location to start copying
|
||||
* from
|
||||
* - `length`: The number of bytes to sequentially copy
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* - If `to == from`, nothing should be done
|
||||
* - If `to < from`, copying should start with the given addresses, and
|
||||
* increment for `length - 1` bytes
|
||||
* - If `to > from`, copying should start with the given addresses, and
|
||||
* decrement for `length - 1` bytes
|
||||
*
|
||||
* - Undefined behavior will result if any address in the block of EEMEM you're
|
||||
* copying from or the block of EEMEM you're copying to is invalid.
|
||||
*/
|
||||
|
||||
// === eeprom__block_read() ===
|
||||
/** functions/eeprom__block_read/description
|
||||
* Read a block of data from the EEPROM into SRAM
|
||||
*
|
||||
* Arguments:
|
||||
* - `to`: The location (in SRAM) to start writing the data to
|
||||
* - `from`: The location (in EEMEM) to start reading the data from
|
||||
* - `length`: The number number of bytes to read, incrementing `to` and `from`
|
||||
* for each byte
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - As one would expect, this read is performed sequentially, and all data is
|
||||
* read in before returning. Delays due to the speed of the EEPROM are
|
||||
* introduced *for every byte* whether one uses this function or reads each
|
||||
* byte manually.
|
||||
* - Because doing a "block_write" would be either unbearably slow or require
|
||||
* us to work around the fact that the function would return before the data
|
||||
* was actually written (so where would the data to be written be stored?
|
||||
* we'd either have to copy it, or make an agreement with the calling
|
||||
* functions to `malloc()` the data and let us `free()` it, or some such), we
|
||||
* do not have any "block" functions that write to the EEPROM (except perhaps
|
||||
* `eeprom__fill()`, which is a special case). Better to be careful with
|
||||
* writes, and schedule them one at a time, using `eeprom__write()`.
|
||||
*/
|
||||
|
|
@ -0,0 +1,556 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the EEPROM interface defined in "../eeprom.h" for the ATMega32U4
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <util/atomic.h>
|
||||
#include <avr/io.h>
|
||||
#include "../../../firmware/lib/timer.h"
|
||||
#include "../eeprom.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
/** macros/MIN_UNUSED/description
|
||||
* The minimum number of elements to have unused after a resize (in any queue)
|
||||
*/
|
||||
#define MIN_UNUSED 0
|
||||
|
||||
/** macros/MAX_UNUSED/description
|
||||
* The maximum number of elements to have unused after a resize (in any queue)
|
||||
*/
|
||||
#define MAX_UNUSED 4
|
||||
|
||||
/** macros/(enum) action/description
|
||||
* Valid values for `write_t.action`, determining the type of action to perform
|
||||
* for an entry in `to_write`
|
||||
*
|
||||
* Members:
|
||||
* - `ACTION_WRITE`
|
||||
* - `ACTION_COPY`
|
||||
* - `ACTION_FILL`
|
||||
*/
|
||||
enum action {
|
||||
ACTION_WRITE,
|
||||
ACTION_COPY,
|
||||
ACTION_FILL,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
|
||||
/** types/write_t/description
|
||||
* To hold values for a "write" to the EEPROM
|
||||
*
|
||||
* Struct members:
|
||||
* - `action`: A field indicating what type of action to perform. Valid values
|
||||
* defined by `enum action`
|
||||
* - `to`: The address in EEPROM memory space to write to
|
||||
* - union:
|
||||
* - `data`: The data to write
|
||||
* - `ACTION_WRITE`
|
||||
* - `ACTION_FILL`
|
||||
* - `length`: The number of bytes left to copy
|
||||
* - `ACTION_COPY`
|
||||
*
|
||||
* Implementation notes:
|
||||
* - Since the ATMega32U4 only has 1024 bytes of EEPROM (addressed from
|
||||
* `0` to `1024-1` = 2^10-1), it's safe to restrict `to` to 10 bits.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t action : 6;
|
||||
uint16_t to : 10;
|
||||
union {
|
||||
uint8_t data;
|
||||
uint8_t length;
|
||||
};
|
||||
} write_t;
|
||||
|
||||
/** types/extra_t/description
|
||||
* To hold the extra values needed for some of the actions
|
||||
*
|
||||
* Struct members:
|
||||
* - union:
|
||||
* - `from`: The address in the EEPROM memory space to copy from
|
||||
* - `ACTION_COPY`
|
||||
* - `length`: The number of bytes left to fill
|
||||
* - `ACTION_FILL`
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
uint16_t from;
|
||||
uint8_t length;
|
||||
};
|
||||
} extra_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables ------------------------------------------------------------------
|
||||
|
||||
/** variables/status/description
|
||||
* Flags for keeping track of the status of these functions (since write
|
||||
* operations will be buffered)
|
||||
*
|
||||
* Members:
|
||||
* - `writing`: Indicates that writes are queued and/or being performed
|
||||
* - If this is `true`, `write_queued()` is either running or scheduled to
|
||||
* run, and should not be rescheduled by any function other than itself.
|
||||
* - If this is `false`, no writes are queued or being performed, and
|
||||
* `write_queued()` must be scheduled to run externally before writes
|
||||
* will commence.
|
||||
*/
|
||||
struct {
|
||||
bool writing : 1;
|
||||
} status;
|
||||
|
||||
/** variables/(group) queues/description
|
||||
* Members:
|
||||
* - `to_write`: To hold the write queue, and related metadata
|
||||
* - `to_extra`: To hold the extra data needed for copies and fills,
|
||||
* along with related metadata
|
||||
* - `to_extra` is an awkward name, but it does have the advantage that it
|
||||
* is the same length as `to_write`. And `to_write_extra` felt a bit too
|
||||
* long in some places...
|
||||
*
|
||||
* Struct members:
|
||||
* - `allocated`: The number of positions allocated
|
||||
* - `unused_front`: The number of unused positions at the beginning of the
|
||||
* queue
|
||||
* - `unused_back`: The number of unused positions at the end of the queue
|
||||
* - `to_write.data`: A queue of writes (and copies) to perform
|
||||
* - `to_extra.data`: A queue of extra information for each
|
||||
* `action == ACTION_COPY` or `action == ACTION_FILL` element in `to_write`
|
||||
*
|
||||
* Implementation notes:
|
||||
* - `unused_front` and `unused_back` each have a range of between -8 and 7,
|
||||
* inclusive. This means that, with the current scheme, `MAX_UNUSED` must
|
||||
* not be greater than 7. Note that negative values for either only make
|
||||
* sense temporarily, when adding an element to the queue.
|
||||
*/
|
||||
static struct {
|
||||
uint8_t allocated;
|
||||
int8_t unused_front : 4;
|
||||
int8_t unused_back : 4;
|
||||
write_t * data;
|
||||
} to_write;
|
||||
static struct {
|
||||
uint8_t allocated;
|
||||
int8_t unused_front : 4;
|
||||
int8_t unused_back : 4;
|
||||
extra_t * data;
|
||||
} to_extra;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variable manipulator functions ---------------------------------------------
|
||||
|
||||
/** functions/(group) resize/description
|
||||
* Resize the appropriate queue, so that the number of unused elements is
|
||||
* between `MIN_UNUSED` and `MAX_UNUSED`, inclusive
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - These functions are separate (though identical in source, before macro
|
||||
* expansion) partly because this allows us to deal with elements of the
|
||||
* queue as discrete blocks of memory. It might not be hard to make the
|
||||
* function generic, but then we would loose the ability to depend on type
|
||||
* information, and have to operate on each `queue.data` solely as an array
|
||||
* of bytes. It's a close call, but I feel like the conceptual clarity we
|
||||
* gain with this method outweighs the clarity we would gain by having only a
|
||||
* single function, with no copy+pasted code.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We use a signed type for `unused` in case `unused_front + unused_back`
|
||||
* turns out to be negative, as may happen when adding a new element to the
|
||||
* queue.
|
||||
* - The only time `realloc()` should fail is if we are trying to grow the
|
||||
* queue. See [the documentation]
|
||||
* (http://www.nongnu.org/avr-libc/user-manual/malloc.html)
|
||||
* for `malloc()` in avr-libc.
|
||||
*/
|
||||
static uint8_t resize_to_write(void) {
|
||||
#define queue to_write
|
||||
#define queue_type_size (sizeof(write_t))
|
||||
|
||||
int8_t unused = queue.unused_front + queue.unused_back;
|
||||
|
||||
if (MIN_UNUSED <= queue.unused_back && unused <= MAX_UNUSED)
|
||||
return 0; // no need to grow, shrink, or shift the queue
|
||||
|
||||
// shift down (if necessary), and update metadata
|
||||
// - start with the first used element, and copy it down to index 0
|
||||
// - copy the second used element down to index 1, and so on until we copy
|
||||
// the last used element
|
||||
// - if there are no empty elements at the front of the queue, this will do
|
||||
// nothing
|
||||
if (queue.unused_front != 0) {
|
||||
for (uint8_t i = queue.unused_front; i < queue.allocated - unused; i++)
|
||||
queue.data[i-queue.unused_front] = queue.data[i];
|
||||
|
||||
queue.unused_front = 0;
|
||||
queue.unused_back = unused;
|
||||
|
||||
if (MIN_UNUSED <= unused && unused <= MAX_UNUSED)
|
||||
return 0; // no need to grow or shrink the queue
|
||||
}
|
||||
|
||||
uint8_t new_allocated;
|
||||
if (UINT8_MAX >= queue.allocated - unused + (MIN_UNUSED+MAX_UNUSED)/2)
|
||||
new_allocated = queue.allocated - unused + (MIN_UNUSED+MAX_UNUSED)/2;
|
||||
else if (UINT8_MAX >= queue.allocated - unused + MIN_UNUSED)
|
||||
new_allocated = UINT8_MAX;
|
||||
else
|
||||
return 1; // unable to count the required number of elements
|
||||
|
||||
void * new_data = realloc( queue.data, queue_type_size * new_allocated );
|
||||
if (!new_data)
|
||||
return 1; // error: `realloc()` failed (unable to grow queue)
|
||||
|
||||
queue.unused_back += new_allocated - queue.allocated;
|
||||
queue.allocated = new_allocated;
|
||||
queue.data = new_data;
|
||||
|
||||
return 0; // success: queue reallocated
|
||||
|
||||
#undef queue
|
||||
#undef queue_type_size
|
||||
}
|
||||
static uint8_t resize_to_extra(void) {
|
||||
#define queue to_extra
|
||||
#define queue_type_size (sizeof(extra_t))
|
||||
|
||||
int8_t unused = queue.unused_front + queue.unused_back;
|
||||
|
||||
if (MIN_UNUSED <= queue.unused_back && unused <= MAX_UNUSED)
|
||||
return 0; // no need to grow, shrink, or shift the queue
|
||||
|
||||
// shift down (if necessary), and update metadata
|
||||
// - start with the first used element, and copy it down to index 0
|
||||
// - copy the second used element down to index 1, and so on until we copy
|
||||
// the last used element
|
||||
// - if there are no empty elements at the front of the queue, this will do
|
||||
// nothing
|
||||
if (queue.unused_front != 0) {
|
||||
for (uint8_t i = queue.unused_front; i < queue.allocated - unused; i++)
|
||||
queue.data[i-queue.unused_front] = queue.data[i];
|
||||
|
||||
queue.unused_front = 0;
|
||||
queue.unused_back = unused;
|
||||
|
||||
if (MIN_UNUSED <= unused && unused <= MAX_UNUSED)
|
||||
return 0; // no need to grow or shrink the queue
|
||||
}
|
||||
|
||||
uint8_t new_allocated;
|
||||
if (UINT8_MAX >= queue.allocated - unused + (MIN_UNUSED+MAX_UNUSED)/2)
|
||||
new_allocated = queue.allocated - unused + (MIN_UNUSED+MAX_UNUSED)/2;
|
||||
else if (UINT8_MAX >= queue.allocated - unused + MIN_UNUSED)
|
||||
new_allocated = UINT8_MAX;
|
||||
else
|
||||
return 1; // unable to count the required number of elements
|
||||
|
||||
void * new_data = realloc( queue.data, queue_type_size * new_allocated );
|
||||
if (!new_data)
|
||||
return 1; // error: `realloc()` failed (unable to grow queue)
|
||||
|
||||
queue.unused_back += new_allocated - queue.allocated;
|
||||
queue.allocated = new_allocated;
|
||||
queue.data = new_data;
|
||||
|
||||
return 0; // success: queue reallocated
|
||||
|
||||
#undef queue
|
||||
#undef queue_type_size
|
||||
}
|
||||
|
||||
/** functions/(group) pop/description
|
||||
* Remove the first element from the appropriate queue
|
||||
*
|
||||
* Members:
|
||||
* - `pop_to_write`: operates on the `to_write` queue
|
||||
* - `pop_to_extra`: operates on the `to_extra` queue
|
||||
*/
|
||||
static void pop_to_write(void) {
|
||||
to_write.unused_front++;
|
||||
resize_to_write();
|
||||
}
|
||||
static void pop_to_extra(void) {
|
||||
to_extra.unused_front++;
|
||||
resize_to_extra();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// back end functions ---------------------------------------------------------
|
||||
|
||||
/** functions/write/description
|
||||
* Write `data` to `address` in EEPROM memory space as soon as possible
|
||||
*
|
||||
* Arguments:
|
||||
* - `to: The address of the location to write to
|
||||
* - `data`: The data to write
|
||||
*
|
||||
* Notes:
|
||||
* - This function is static (and `eeprom__write()`, the public function, is
|
||||
* written below) because writes are really slow, and we're going to buffer
|
||||
* them.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - This function (and most of the comments) were taken more or less straight
|
||||
* from the data sheet, section 5.3
|
||||
* - If another write is in progress when this function is called, this
|
||||
* function will busy wait until the first write has been completed. This
|
||||
* may take quite some time (up to 3.4 ms if a write has just been started),
|
||||
* so we must be careful about when this function is called.
|
||||
* - This function starts the write to the EEPROM, but returns long before it
|
||||
* has been completed.
|
||||
*
|
||||
* Assumptions:
|
||||
* - The address passed as `to` is valid.
|
||||
* - Voltage will never fall below the specified minimum for the clock
|
||||
* frequency being used.
|
||||
* - A write to flash memory (PROGMEM) will never be in progress when this
|
||||
* function is called.
|
||||
*/
|
||||
static void write(uint16_t to, uint8_t data) {
|
||||
|
||||
// - if a write is in progress, this will also wait until it's finished
|
||||
uint8_t old_data = eeprom__read( (void *) to );
|
||||
|
||||
if (data == old_data) {
|
||||
// do nothing
|
||||
return;
|
||||
} else if (data == 0xFF) {
|
||||
// erase only (1.8 ms)
|
||||
EECR &= ~(1<<EEPM1); // clear
|
||||
EECR |= (1<<EEPM0); // set
|
||||
} else if (old_data == 0xFF) {
|
||||
// write only (1.8 ms)
|
||||
EECR |= (1<<EEPM1); // set
|
||||
EECR &= ~(1<<EEPM0); // clear
|
||||
} else {
|
||||
// erase and write in one (atomic) operation (3.4 ms)
|
||||
EECR &= ~(1<<EEPM1); // clear
|
||||
EECR &= ~(1<<EEPM0); // clear
|
||||
}
|
||||
|
||||
EEAR = to; // set up address register
|
||||
EEDR = data; // set up data register
|
||||
|
||||
// - interrupts must be disabled between these two operations, or else
|
||||
// "EEPROM Master Programming Enable" may time out (since it's cleared by
|
||||
// hardware 4 clock cycles after being written to `1` by software)
|
||||
ATOMIC_BLOCK(ATOMIC_FORCEON) {
|
||||
EECR |= (1<<EEMPE); // set "EEPROM Master Programming Enable" to `1`
|
||||
EECR |= (1<<EEPE); // start EEPROM write (then halt, 2 clock cycles)
|
||||
}
|
||||
}
|
||||
|
||||
/** functions/write_queued/description
|
||||
* Write (or copy, or fill) the next byte of data as dictated by our queue(s),
|
||||
* and schedule the write of the next byte if necessary
|
||||
*
|
||||
* Assumptions:
|
||||
* - The length of `to_extra` is always correct: i.e. there is exactly 1 entry
|
||||
* in `to_extra` for every `action == ACTION_...` element in `to_write` that
|
||||
* is supposed to have one.
|
||||
*/
|
||||
static void write_queued(void) {
|
||||
#define next_write ( to_write.data[to_write.unused_front] )
|
||||
#define next_extra ( to_extra.data[to_extra.unused_front] )
|
||||
#define length(queue) ( queue.allocated \
|
||||
- queue.unused_front \
|
||||
- queue.unused_back )
|
||||
|
||||
// if there's nothing to write
|
||||
// - checking for this here (instead of after writing) will cause this
|
||||
// function to be called an extra time before it stops rescheduling
|
||||
// itself; but it also allows us not to make assumptions about the state
|
||||
// of `to_write` when this function is called
|
||||
if ( length(to_write) == 0 ) {
|
||||
status.writing = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_write.action == ACTION_WRITE) {
|
||||
|
||||
// write 1 byte
|
||||
write( next_write.to, next_write.data );
|
||||
// prepare for the next
|
||||
pop_to_write();
|
||||
|
||||
} else if ( next_write.action == ACTION_COPY ) {
|
||||
|
||||
// if we're done with the current copy
|
||||
// - checking for this here requires an extra iteration between a copy
|
||||
// being finished and the next operation being started; but it also
|
||||
// allows us not to make assumptions about the state of `to_extra`
|
||||
// when this function is called
|
||||
if (next_write.length == 0) {
|
||||
pop_to_write();
|
||||
pop_to_extra();
|
||||
}
|
||||
|
||||
// copy 1 byte
|
||||
write( next_write.to, eeprom__read( (void *) next_extra.from ) );
|
||||
// prepare for the next
|
||||
if (next_write.to < next_extra.from) {
|
||||
++(next_write.to);
|
||||
++(next_extra.from);
|
||||
} else {
|
||||
--(next_write.to);
|
||||
--(next_extra.from);
|
||||
}
|
||||
--(next_write.length);
|
||||
|
||||
} else if ( next_write.action == ACTION_FILL ) {
|
||||
|
||||
// if we're done with the current fill
|
||||
// - checking for this here requires an extra iteration between a fill
|
||||
// being finished and the next operation being started; but it also
|
||||
// allows us not to make assumptions about the state of `to_extra`
|
||||
// when this function is called
|
||||
if (next_extra.length == 0) {
|
||||
pop_to_write();
|
||||
pop_to_extra();
|
||||
}
|
||||
|
||||
// fill 1 byte
|
||||
write( next_write.to, next_write.data );
|
||||
// prepare for the next
|
||||
--(next_extra.length);
|
||||
|
||||
} else {
|
||||
// if we get here, there was an invalid node: remove it
|
||||
pop_to_write();
|
||||
}
|
||||
|
||||
// - `(3.5/OPT__DEBOUNCE_TIME)+1` gives us the number of cycles we need to
|
||||
// wait until the next write, where `3.5` is the maximum number of
|
||||
// milliseconds an EEPROM write can take, and `OPT__DEBOUNCE_TIME` is the
|
||||
// minimum number of milliseconds a scan can take. Note that if the
|
||||
// division produces a nonzero integer (other than by truncation), the
|
||||
// `+1` is not strictly necessary; but in that case, being so close to
|
||||
// needing another cycle, we may as well wait anyway.
|
||||
timer__schedule_cycles( (3.5/OPT__DEBOUNCE_TIME)+1, &write_queued );
|
||||
|
||||
#undef next_write
|
||||
#undef next_extra
|
||||
#undef length
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// front end functions --------------------------------------------------------
|
||||
|
||||
/** functions/eeprom__read/description
|
||||
* Implementation notes:
|
||||
* - This function (and most of the comments) were taken more or less straight
|
||||
* from the data sheet, section 5.3
|
||||
* - If a write is in progress when this function is called, this function will
|
||||
* busy wait until the write has been completed. This may take quite some
|
||||
* time (up to 3.4 ms if a write has just been started), so you should be
|
||||
* careful about when this function is called.
|
||||
*
|
||||
* Assumptions:
|
||||
* - The address passed as `address` is valid.
|
||||
*/
|
||||
uint8_t eeprom__read(void * from) {
|
||||
while (EECR & (1<<EEPE)); // wait for previous write to complete
|
||||
EEAR = (uint16_t) from; // set up address register
|
||||
EECR |= (1<<EERE); // start EEPROM read (then halt, 4 clock cycles)
|
||||
return EEDR; // return the value in the data register
|
||||
}
|
||||
|
||||
uint8_t eeprom__write(void * address, uint8_t data) {
|
||||
to_write.unused_back--;
|
||||
if (resize_to_write()) {
|
||||
to_write.unused_back++;
|
||||
return 1; // resize failed
|
||||
}
|
||||
|
||||
uint8_t index = to_write.allocated - to_write.unused_back - 1;
|
||||
to_write.data[index].action = ACTION_WRITE;
|
||||
to_write.data[index].to = (uint16_t) address;
|
||||
to_write.data[index].data = data;
|
||||
|
||||
if (!status.writing) {
|
||||
timer__schedule_cycles( 0, &write_queued );
|
||||
status.writing = true;
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
uint8_t eeprom__fill(void * to, uint8_t data, uint8_t length) {
|
||||
to_write.unused_back--;
|
||||
to_extra.unused_back--;
|
||||
if (resize_to_write() || resize_to_extra()) {
|
||||
to_write.unused_back++; resize_to_write();
|
||||
to_extra.unused_back++; resize_to_extra();
|
||||
return 1; // resize failed
|
||||
}
|
||||
|
||||
uint8_t index;
|
||||
|
||||
index = to_write.allocated - to_write.unused_back - 1;
|
||||
to_write.data[index].action = ACTION_FILL;
|
||||
to_write.data[index].to = (uint16_t) to;
|
||||
to_write.data[index].data = data;
|
||||
|
||||
index = to_extra.allocated - to_extra.unused_back - 1;
|
||||
to_extra.data[index].length = length;
|
||||
|
||||
if (!status.writing) {
|
||||
timer__schedule_cycles( 0, &write_queued );
|
||||
status.writing = true;
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
uint8_t eeprom__copy(void * to, void * from, uint8_t length) {
|
||||
if (to == from)
|
||||
return 0; // nothing to do
|
||||
|
||||
to_write.unused_back--;
|
||||
to_extra.unused_back--;
|
||||
if (resize_to_write() || resize_to_extra()) {
|
||||
to_write.unused_back++; resize_to_write();
|
||||
to_extra.unused_back++; resize_to_extra();
|
||||
return 1; // resize failed
|
||||
}
|
||||
|
||||
uint8_t index;
|
||||
|
||||
index = to_write.allocated - to_write.unused_back - 1;
|
||||
to_write.data[index].action = ACTION_COPY;
|
||||
to_write.data[index].to = (uint16_t) to;
|
||||
to_write.data[index].length = length;
|
||||
|
||||
index = to_extra.allocated - to_extra.unused_back - 1;
|
||||
to_extra.data[index].from = (uint16_t) from;
|
||||
|
||||
if (!status.writing) {
|
||||
timer__schedule_cycles( 0, &write_queued );
|
||||
status.writing = true;
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
uint8_t eeprom__block_read(void * to, void * from, uint8_t length) {
|
||||
for (; length; length--)
|
||||
*(uint8_t *)(to+length-1) = eeprom__read( from+length-1 );
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# eeprom options
|
||||
#
|
||||
# This file is meant to be included by the using '.../options.mk'
|
||||
#
|
||||
|
||||
|
||||
# TODO: add `call`s like this to other files that depend on things implicitly
|
||||
$(call include_options_once,lib/timer)
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU).c)
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* EEPROM macro interface
|
||||
*
|
||||
* Prefix: `eeprom_macro__`
|
||||
*
|
||||
* This file is meant to be included and used by the keyboard layout
|
||||
* implementation.
|
||||
*
|
||||
* Warnings:
|
||||
* - This library is meant to be used for recording and playing back
|
||||
* *temporary* macros. Permanent macros should be assigned to a key in the
|
||||
* source of the layout directly. Macros created using this library may be
|
||||
* very difficult to retrieve and back up. They may also be erased without
|
||||
* warning in the event that they become corrupted by power loss or
|
||||
* invalidated by future changes in the format of the persistent data used to
|
||||
* store them.
|
||||
*
|
||||
* Terms:
|
||||
* - A "key" is a pair of actions, one for when the key is pressed and another
|
||||
* for when it is released. We specify keys by their `layer`, `row`, and
|
||||
* `column`.
|
||||
* - A "key-action" is a single action. We specify key-actions by their
|
||||
* `pressed` value (whether the action corresponds to a press (`true`) or
|
||||
* release (`false`)) and the key they belong to.
|
||||
* - A "macro" is a collection of data that lives in persistent memory, and
|
||||
* specifies possibly many actions to perform in the place of a single,
|
||||
* usually different, action. For the purposes of this library, macros live
|
||||
* in the EEPROM, and contain a key-action who's original behavior we wish
|
||||
* to mask, and a list of key-actions that should be sequentially performed
|
||||
* instead.
|
||||
* - A "keystroke" is a full press then release of a key. Keystrokes may
|
||||
* overlap each other.
|
||||
* - To "remap" a key-action is to assign a macro to it (masking, not
|
||||
* replacing, what the key-action originally did).
|
||||
* - The "EEPROM" is an "Electronically Erasable Programmable Read Only
|
||||
* Memory". It is where this library stores persistent data.
|
||||
* - "EEMEM" is "EEprom MEMory" (i.e. another way of referring to the memory of
|
||||
* the EEPROM, instead of the EEPROM itself) following the convention of
|
||||
* avr-gcc.
|
||||
*
|
||||
* Usage notes:
|
||||
* - When macros are recorded, the key they are assigned to does not loose its
|
||||
* functionality, but rather has that functionality masked by this new
|
||||
* definition.
|
||||
* - Timing information may not be recorded, due to the space restrictions of
|
||||
* the EEPROM. If it is not, keys which change meaning depending on the
|
||||
* timing with which they are pressed (and not just the sequence in which
|
||||
* they are pressed) may not be played back as expected.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - When macros are being recorded or played back, keys should operate with
|
||||
* their original meanings (i.e. macro lookup should be disabled). This may
|
||||
* be inconvenient at times, e.g. if the user wishes to have a single macro
|
||||
* execute several others which are already recorded, or if they wish to have
|
||||
* an especially long macro which cannot (because of implementation
|
||||
* limitations) be recorded in one piece. It has many benefits, however. It
|
||||
* makes it impossible to define recursive macros (which would never
|
||||
* terminate). It makes it impossible for new macros to accidentally
|
||||
* redefine old ones. Perhaps most importantly, it makes it possible to
|
||||
* create macros with actions that may have been masked by another macro, as
|
||||
* one might wish to do when, for example, quickly swapping the positions of
|
||||
* two letter keys.
|
||||
* - With sufficient trickiness, we could probably do away with having `layer`
|
||||
* in the key-actions that make up the body of macros (most of this
|
||||
* trickiness being in the logic for how users record macros and assign them
|
||||
* to key-actions). I could imagine there being situations where this turned
|
||||
* out to be useful... but I feel like much more often it would just be a bit
|
||||
* confusing. It would also be inconsistent a little, having two different
|
||||
* representations of a key-action. And it wouldn't actually save us that
|
||||
* much EEPROM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__EEPROM_MACRO__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__EEPROM_MACRO__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if OPT__EEPROM__EEPROM_MACRO__START > OPT__EEPROM__EEPROM_MACRO__END
|
||||
#error "OPT__EEPROM__EEPROM_MACRO__START must be smaller than ...END"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define ARGS bool pressed, uint8_t layer, uint8_t row, uint8_t column
|
||||
uint8_t eeprom_macro__init (void);
|
||||
uint8_t eeprom_macro__record_init ( ARGS );
|
||||
uint8_t eeprom_macro__record_action ( ARGS );
|
||||
uint8_t eeprom_macro__record_finalize (void);
|
||||
uint8_t eeprom_macro__record_cancel (void);
|
||||
uint8_t eeprom_macro__play ( ARGS );
|
||||
bool eeprom_macro__exists ( ARGS );
|
||||
uint8_t eeprom_macro__clear ( ARGS );
|
||||
uint8_t eeprom_macro__clear_all (void);
|
||||
#undef ARGS
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__EEPROM_MACRO__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === OPT__EEPROM__EEPROM_MACRO__START ===
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__START/description
|
||||
* The first EEMEM address in the block assigned to this section of the code
|
||||
*/
|
||||
|
||||
// === OPT__EEPROM__EEPROM_MACRO__END ===
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__END/description
|
||||
* The last EEMEM address in the block assigned to this section of the code
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === eeprom_macro__init() ===
|
||||
/** functions/eeprom_macro__init/description
|
||||
* Perform any necessary initializations
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Meant to be called exactly once by `kb__init()`
|
||||
*
|
||||
* Notes:
|
||||
* - This function should initialize our portion of the EEPROM to the current
|
||||
* format if we don't know how to read it; i.e. if the version of the data
|
||||
* stored is different than what we expect, or if the data has been
|
||||
* corrupted.
|
||||
*/
|
||||
|
||||
// === eeprom_macro__record_init() ===
|
||||
/** functions/eeprom_macro__record_init/description
|
||||
* Prepare to record a new macro
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) The key-action to remap
|
||||
* - `pressed`: Whether the key-action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key-action
|
||||
* - `row`: The row of the key-action
|
||||
* - `column`: The column of the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Only one macro may be recorded at a time. If another macro is being
|
||||
* recorded when this function is called (i.e. this function has been called
|
||||
* once already, and `...finalize()` has not been called yet), the old macro
|
||||
* should be thrown away, and this new one prepared for.
|
||||
* - If a macro remapping the given key-action already exists, it should be
|
||||
* deleted.
|
||||
*/
|
||||
|
||||
// === eeprom_macro__record_action() ===
|
||||
/** functions/eeprom_macro__record_action/description
|
||||
* Record the next key-action of the current macro
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) The key-action to record
|
||||
* - `pressed`: Whether the key-action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key-action
|
||||
* - `row`: The row of the key-action
|
||||
* - `column`: The column of the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - If this function fails, the macro currently being written may be canceled
|
||||
* (thrown away).
|
||||
*/
|
||||
|
||||
// === eeprom_macro__record_finalize() ===
|
||||
/** functions/eeprom_macro__record_finalize/description
|
||||
* Finalize the recording of the current macro
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Before this function is called, the macro (even though parts of it may be
|
||||
* written) should not be readable, or referenced anywhere in the EEPROM.
|
||||
*
|
||||
* Notes:
|
||||
* - If this function fails, the macro currently being written may be canceled
|
||||
* (thrown away).
|
||||
*/
|
||||
|
||||
// === eeprom_macro__record_cancel() ===
|
||||
/** functions/eeprom_macro__record_cancel/description
|
||||
* Cancel the recording of the current macro
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Depending on the implementation, this function may not be necessary due to
|
||||
* the behavior of `eeprom_macro__record_init()` and
|
||||
* `eeprom_macro__record_finalize()`. In that case this function should
|
||||
* simply do nothing.
|
||||
*/
|
||||
|
||||
// === eeprom_macro__play() ===
|
||||
/** functions/eeprom_macro__play/description
|
||||
* Play back recorded key-actions for the macro assigned to the specified key
|
||||
* action
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) The key-action to search for
|
||||
* - `pressed`: Whether the key-action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key-action
|
||||
* - `row`: The row of the key-action
|
||||
* - `column`: The column of the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0` (macro successfully played)
|
||||
* - failure: [other] (macro does not exist)
|
||||
*/
|
||||
|
||||
// === eeprom_macro__exists() ===
|
||||
/** functions/eeprom_macro__exists/description
|
||||
* Predicate indicating whether the specified key-action has been remapped
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) The key-action to search for
|
||||
* - `pressed`: Whether the key-action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key-action
|
||||
* - `row`: The row of the key-action
|
||||
* - `column`: The column of the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - `true`: if a macro remapping the given key-action exists
|
||||
* - `false`: if a macro remapping the given key-action does not exist
|
||||
*/
|
||||
|
||||
// === eeprom_macro__clear() ===
|
||||
/** functions/eeprom_macro__clear/description
|
||||
* Clear (delete) the macro assigned to the given key-action
|
||||
*
|
||||
* Arguments:
|
||||
* - (group) The key-action to un-remap
|
||||
* - `pressed`: Whether the key-action is a press (`true`) or a release
|
||||
* (`false`)
|
||||
* - `layer`: The layer of the key-action
|
||||
* - `row`: The row of the key-action
|
||||
* - `column`: The column of the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
|
||||
// === eeprom_macro__clear_all() ===
|
||||
/** functions/eeprom_macro__clear_all/description
|
||||
* Clear (delete) all macros in the EEPROM
|
||||
*
|
||||
* Notes:
|
||||
* - For the purposes of this function, "clearing" the EEPROM means to put it
|
||||
* in such a state that none of the functions declared here will be able to
|
||||
* find a macro for any key-action. This does not necessarily imply that the
|
||||
* EEPROM is in a fully known state.
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
|
|
@ -0,0 +1,931 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the eeprom-macro functionality defined in "../eeprom-macro.h" for
|
||||
* the ATMega32U4
|
||||
*
|
||||
*
|
||||
* Warnings:
|
||||
*
|
||||
* - A worst case `compress()` operation might take too much memory. Not sure
|
||||
* what (if anything) to do about this right now.
|
||||
* - Max EEPROM space for macros: 1024-5 = 1019 bytes
|
||||
* - Min space for a macro: 5 bytes
|
||||
* - Approximate space for a copy object in ".../lib/eeprom": 5 bytes
|
||||
* - Worst case would be EEMEM filled with the smallest possible macros,
|
||||
* alternating between valid and deleted. This would give us 1019/5/2 ~=
|
||||
* 100 noncontiguous deleted macros, which would be about as many copy
|
||||
* objects (plus a few write objects) in ".../lib/eeprom", so about 500
|
||||
* bytes. SRAM is 2560 bytes (per the PJRC website). Because of the way
|
||||
* ".../lib/eeprom" is written, much of this data would have to be
|
||||
* contiguous.
|
||||
* - At some point, I should probably consider changing how
|
||||
* ".../lib/eeprom" (and the layer-stack code, and everything else that
|
||||
* needs a variable amount of memory) manages its memory. Again, not
|
||||
* quite sure how, at the moment. For common cases, the current solution
|
||||
* might be sufficient.
|
||||
* - If this turns out to be a problem, the easiest solution (at the
|
||||
* expense of extra EEPROM wear in lower memory locations) would probably
|
||||
* be to simply call `compress()` more often.
|
||||
*
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* - The default state (the "erased" state) of this EEPROM is all `1`s, which
|
||||
* makes setting a byte to `0xFF` easier and faster in hardware than zeroing
|
||||
* it (and also causes less wear on the memory over time, I think). This is
|
||||
* reflected in some of the choices for default values, and such.
|
||||
*
|
||||
* - GCC and AVR processors (and Intel processors, for that matter) are
|
||||
* primarily little endian: in avr-gcc, multi-byte data types are allocated
|
||||
* with the least significant byte occupying the lowest address. Protocols,
|
||||
* data formats (including UTF-8), and such are primarily big endian. I like
|
||||
* little endianness better -- it just feels nicer to me -- but after writing
|
||||
* a bit of code, it seems that big endian serializations are easier to work
|
||||
* with, at least in C. For that reason, this code organizes bytes in a big
|
||||
* endian manner whenever it has a choice between the two.
|
||||
*
|
||||
* - For a long time, I was going to try to make this library robust in the
|
||||
* event of power loss, but in the end I decided not to. This feature is
|
||||
* meant to be used for *temporary* macros -- so, with the risk of power loss
|
||||
* during a critical time being fairly low, and the consequence of (detected)
|
||||
* data corruption hopefully more of an annoyance than anything else, I
|
||||
* decided the effort (and extra EEMEM usage) wasn't worth it.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../../../../firmware/keyboard.h"
|
||||
#include "../../../../firmware/lib/eeprom.h"
|
||||
#include "../eeprom-macro.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// checks ---------------------------------------------------------------------
|
||||
|
||||
/** macros/OPT__EEPROM__EEPROM_MACRO__END/description
|
||||
* Implementation notes:
|
||||
* - The ATMega32U4 only has 1024 bytes of EEPROM (beginning with byte 0)
|
||||
*/
|
||||
#if OPT__EEPROM__EEPROM_MACRO__END > 1023
|
||||
#error "OPT__EEPROM__EEPROM_MACRO__END must not be greater than 1023"
|
||||
#endif
|
||||
|
||||
#if OPT__EEPROM__EEPROM_MACRO__END - OPT__EEPROM__EEPROM_MACRO__START < 300
|
||||
#warn "Only a small space has been allocated for macros"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
/** macros/VERSION/description
|
||||
* The version number of the EEMEM layout
|
||||
*
|
||||
* Assignments:
|
||||
* - 0x00: Reserved: EEPROM not yet initialized, or in inconsistent state
|
||||
* - 0x01: First version
|
||||
* - ... : (not yet assigned)
|
||||
* - 0xFF: Reserved: EEPROM not yet initialized, or in inconsistent state
|
||||
*/
|
||||
#define VERSION 0x01
|
||||
|
||||
/** macros/(group) EEMEM layout/description
|
||||
* To define the layout of our section of the EEPROM
|
||||
*
|
||||
* Members:
|
||||
* - `EEMEM_START`: The address of the first byte of our block of EEMEM
|
||||
* - `EEMEM_START_ADDRESS_START`
|
||||
* - `EEMEM_START_ADDRESS_END`
|
||||
* - `EEMEM_END_ADDRESS_START`
|
||||
* - `EEMEM_END_ADDRESS_END`
|
||||
* - `EEMEM_VERSION_START`
|
||||
* - `EEMEM_VERSION_END`
|
||||
* - `EEMEM_MACROS_START`
|
||||
* - `EEMEM_MACROS_END`
|
||||
* - `EEMEM_END`: The address of the last byte of our block of EEMEM
|
||||
*
|
||||
* Terms:
|
||||
* - The "address" of a macro is the EEMEM address of the first byte of that
|
||||
* macro.
|
||||
* - The "header" of a macro is the part of the macro containing the macro's
|
||||
* type and length.
|
||||
* - The "data" of a macro is everything following the macro's header.
|
||||
*
|
||||
* Notes:
|
||||
* - `START_ADDRESS` and `END_ADDRESS` are written as part of our effort to
|
||||
* make sure that the assumptions in place when writing the data don't shift
|
||||
* (undetected) by the time it gets read. Either of these values could
|
||||
* change, legitimately, without `VERSION` being incremented, but it's
|
||||
* important that any two builds of the firmware that deal with this section
|
||||
* of the EEPROM have the same values for each.
|
||||
*
|
||||
*
|
||||
* EEMEM sections:
|
||||
*
|
||||
* - START_ADDRESS:
|
||||
* - byte 0: MSB of `EEMEM_START`
|
||||
* - byte 1: LSB of `EEMEM_START`
|
||||
*
|
||||
* - Upon initialization, if this block does not have the expected value,
|
||||
* our portion of the EEPROM should be reinitialized.
|
||||
*
|
||||
* - END_ADDRESS:
|
||||
* - byte 0: MSB of `EEMEM_END`
|
||||
* - byte 1: LSB of `EEMEM_END`
|
||||
*
|
||||
* - Upon initialization, if this block does not have the expected value,
|
||||
* our portion of the EEPROM should be reinitialized.
|
||||
*
|
||||
* - VERSION:
|
||||
* - byte 0:
|
||||
* - This byte will be set to `VERSION` as the last step of
|
||||
* initializing our portion of the EEPROM.
|
||||
* - Upon initialization, if this value is not equal to the current
|
||||
* `VERSION`, our portion of the EEPROM should be reinitialized.
|
||||
*
|
||||
* - MACROS: byte 0..`(EEMEM_END - EEMEM_VERSION_END - 1)`:
|
||||
* - This section will contain a series of zero or more macros, each with
|
||||
* the following format:
|
||||
* - byte 0: `type == TYPE_DELETED`
|
||||
* - byte 1: `length`: the total number of bytes used by this
|
||||
* macro, including the bytes for `type` and `length`
|
||||
* - byte 2...: (optional) undefined
|
||||
* - byte 0: `type == TYPE_VALID_MACRO`
|
||||
* - byte 1: `length`: the total number of bytes used by this
|
||||
* macro, including the bytes for `type` and `length`
|
||||
* - byte 2...: (variable length, as described below)
|
||||
* - `key-action` 0: the key-action which this macro remaps
|
||||
* - byte ...: (optional) (variable length, as described below)
|
||||
* - `key-action` 1...: the key-actions to which `key-action` 0
|
||||
* is remapped
|
||||
* - byte 0: `type == TYPE_END`
|
||||
* - byte 1...: (optional) undefined
|
||||
*
|
||||
* - The last macro in this series will have `type == TYPE_END`.
|
||||
*
|
||||
* - A key-action is a variable length encoding of the information in a
|
||||
* `key_action_t`, with the following format:
|
||||
*
|
||||
* byte 0
|
||||
* .----------------------------------------------.
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* |----------------------------------------------|
|
||||
* | continued | pressed | layer | row | column |
|
||||
* '----------------------------------------------'
|
||||
*
|
||||
* byte 1..3 (optional)
|
||||
* .----------------------------------------------.
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* |----------------------------------------------|
|
||||
* | continued | 1 | layer | row | column |
|
||||
* '----------------------------------------------'
|
||||
*
|
||||
* - `continued`:
|
||||
* - `1`: The next byte is part of this key-action
|
||||
* - `0`: The next byte is not part of this key-action (i.e. this
|
||||
* is the last byte in this key-action)
|
||||
*
|
||||
* - `pressed`:
|
||||
* - This value is stored *only* in the first byte. In all
|
||||
* subsequent bytes the bit should be set to `1`.
|
||||
*
|
||||
* - `layer`, `row`, `column`:
|
||||
* - In the first byte of this key-action, these fields contain the
|
||||
* two most significant bits of their respective values such that
|
||||
* these bits are nonzero in *any* of `layer`, `row`, or
|
||||
* `column`. In subsequent bytes of this key-action, these
|
||||
* fields contain the pair of bits to the right of the pair of
|
||||
* bits in the previous key-action byte (the next less
|
||||
* significant pair of bits). If `layer`, `row`, and `column`
|
||||
* all equal `0`, then these three fields will all equal `0`, and
|
||||
* there will only be 1 byte written for this key-action.
|
||||
*
|
||||
* - Example of an encoded key-action:
|
||||
*
|
||||
* --- as a key_action_t ---
|
||||
* pressed = false
|
||||
* layer = 0 b 00 00 01 00
|
||||
* row = 0 b 00 01 10 01
|
||||
* column = 0 b 00 10 00 11
|
||||
* | '- least significant pair of bits
|
||||
* '- most significant pair of bits
|
||||
*
|
||||
* --- in EEMEM ---
|
||||
* byte 0 = 0 b 1 0 00 01 10
|
||||
* byte 1 = 0 b 1 1 01 10 00
|
||||
* byte 2 = 0 b 0 1 00 01 11
|
||||
* | | | | '- column bit pair
|
||||
* | | | '- row bit pair
|
||||
* | | '- layer bit pair
|
||||
* | '- pressed / 1
|
||||
* '- continued
|
||||
*/
|
||||
#define EEMEM_START ((void *)OPT__EEPROM__EEPROM_MACRO__START)
|
||||
#define EEMEM_START_ADDRESS_START (EEMEM_START + 0)
|
||||
#define EEMEM_START_ADDRESS_END (EEMEM_START_ADDRESS_START + 1)
|
||||
#define EEMEM_END_ADDRESS_START (EEMEM_START_ADDRESS_END + 1)
|
||||
#define EEMEM_END_ADDRESS_END (EEMEM_END_ADDRESS_START + 1)
|
||||
#define EEMEM_VERSION_START (EEMEM_END_ADDRESS_END + 1)
|
||||
#define EEMEM_VERSION_END (EEMEM_VERSION_START + 0)
|
||||
#define EEMEM_MACROS_START (EEMEM_VERSION_END + 1)
|
||||
#define EEMEM_MACROS_END (EEMEM_END - 0)
|
||||
#define EEMEM_END ((void *)OPT__EEPROM__EEPROM_MACRO__END)
|
||||
|
||||
/** macros/(group) type/description
|
||||
* Aliases for valid values of the "type" field in `MACROS`
|
||||
*
|
||||
* Members:
|
||||
* - `TYPE_DELETED`
|
||||
* - `TYPE_VALID_MACRO`
|
||||
* - `TYPE_END`
|
||||
*/
|
||||
#define TYPE_DELETED 0x00
|
||||
#define TYPE_VALID_MACRO 0x01
|
||||
#define TYPE_END 0xFF
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
|
||||
/** types/key_action_t/description
|
||||
* To hold everything needed to represent a single key-action (the press or
|
||||
* release of a specific key on a specific layer of the layout matrix).
|
||||
*
|
||||
* Struct members:
|
||||
* - `pressed`: Whether the key is pressed (`true`) or not (`false`)
|
||||
* - `layer`: The layer of the key, in the layout matrix
|
||||
* - `row`: The row of the key, in the layout matrix
|
||||
* - `column`: The column of the key, in the layout matrix
|
||||
*
|
||||
* Notes:
|
||||
* - Since these fields together can reference any key (on any layer)
|
||||
* unambiguously, a `key_action_t` may also serve as a UID for a key.
|
||||
*/
|
||||
typedef struct {
|
||||
bool pressed;
|
||||
uint8_t layer;
|
||||
uint8_t row;
|
||||
uint8_t column;
|
||||
} key_action_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables ------------------------------------------------------------------
|
||||
|
||||
/** variables/end_macro/description
|
||||
* The EEMEM address of the macro with `type == TYPE_END`
|
||||
*/
|
||||
void * end_macro;
|
||||
|
||||
/** variables/new_end_macro/description
|
||||
* The EEMEM address of where to write the next byte of a macro in progress (or
|
||||
* `0` if no macro is in progress)
|
||||
*
|
||||
* Mnemonic:
|
||||
* - This macro will become the new `end_macro` when the macro currently being
|
||||
* written is finalized.
|
||||
*
|
||||
* Note:
|
||||
* - This variable should be the primary indicator of whether a macro is in
|
||||
* progress or not.
|
||||
*/
|
||||
void * new_end_macro;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// local functions ------------------------------------------------------------
|
||||
|
||||
/** functions/read_key_action/description
|
||||
* Read the key-action beginning at `from` in the EEPROM
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: A pointer to the location in EEPROM from which to begin reading
|
||||
* - `k`: A pointer to the variable in which to store the key-action
|
||||
*
|
||||
* Returns:
|
||||
* - success: The number of bytes read
|
||||
*
|
||||
* Notes:
|
||||
* - See the documentation for "(group) EEMEM layout" above for a description
|
||||
* of the layout of key-actions in EEMEM.
|
||||
*/
|
||||
static uint8_t read_key_action(void * from, key_action_t * k) {
|
||||
uint8_t byte;
|
||||
|
||||
// handle the first byte
|
||||
// - since this byte (and no others) stores the value of `k->pressed`
|
||||
// - also, this allows us to avoid `|=` in favor of `=` for this byte
|
||||
|
||||
byte = eeprom__read(from++);
|
||||
uint8_t read = 1;
|
||||
|
||||
k->pressed = byte >> 6 & 0b01;
|
||||
k->layer = byte >> 4 & 0b11;
|
||||
k->row = byte >> 2 & 0b11;
|
||||
k->column = byte >> 0 & 0b11;
|
||||
|
||||
// handle all subsequent bytes
|
||||
// - we assume the stream is valid. especially, we do not check to make
|
||||
// sure that the key-action is no more than 4 bytes long.
|
||||
|
||||
while (byte >> 7) {
|
||||
byte = eeprom__read(from++);
|
||||
read++;
|
||||
|
||||
// shift up (make more significant) the bits we have so far, to make
|
||||
// room for the bits we just read
|
||||
k->layer <<= 2;
|
||||
k->row <<= 2;
|
||||
k->column <<= 2;
|
||||
|
||||
// logical or the bits we just read into the lowest (least significant)
|
||||
// positions
|
||||
k->layer |= byte >> 4 & 0b11;
|
||||
k->row |= byte >> 2 & 0b11;
|
||||
k->column |= byte >> 0 & 0b11;
|
||||
}
|
||||
|
||||
return read; // success
|
||||
}
|
||||
|
||||
/** functions/write_key_action/description
|
||||
* Write the given information to a key-action beginning at `to` in the
|
||||
* EEPROM, and return the number of bytes written.
|
||||
*
|
||||
* Arguments:
|
||||
* - `to`: A pointer to the location in EEPROM at which to begin writing
|
||||
* - `k`: A pointer to the key-action to write
|
||||
* - `limit`: A pointer to the last address to which we are allowed to write
|
||||
*
|
||||
* Returns:
|
||||
* - success: The number of bytes written
|
||||
* - failure: `0`
|
||||
*
|
||||
* Warnings:
|
||||
* - Writes are not atomic: if there are 4 bytes to be written, and the first
|
||||
* three writes succeed, the 4th may still fail.
|
||||
*
|
||||
* Notes:
|
||||
* - See the documentation for "(group) EEMEM layout" above for a description
|
||||
* of the layout of key-actions in EEMEM.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We handle the `layer`, `row`, and `column` variables (inside `k`) as being
|
||||
* made up of 4 pairs of bits.
|
||||
* - We deal with these bits beginning with the high (most significant) pair,
|
||||
* and shifting left (towards the most significant end of the byte) to
|
||||
* discard bit pairs we're done with.
|
||||
* - This method seemed faster (i.e. generated less assembly code) when I
|
||||
* was testing than leaving the `layer`, `row`, and `column` bytes as
|
||||
* they were and using a variable mask (as in `k.layer & 0b11 << i*2`).
|
||||
* It's probably worthwhile to note that I was looking at the assembly
|
||||
* (though not closely) and function size with optimizations turned on.
|
||||
*/
|
||||
static uint8_t write_key_action(void * to, key_action_t * k, void * limit) {
|
||||
// ignore the bits we don't need to write
|
||||
// - if the leading two bits of all three variables are `0b00`, we don't
|
||||
// need to write a key-action byte containing that pair of bits
|
||||
// - the maximum number of pairs of bits we can ignore is 3; the last pair
|
||||
// (the least significant) must be written to the EEPROM regardless of
|
||||
// its value
|
||||
// - we set `i` here (and make it global to the function) because we need
|
||||
// to make sure to *consider writing* exactly 4 pairs of bits. some may
|
||||
// be skipped, some or all may be written, but the total of both must be
|
||||
// 4.
|
||||
|
||||
uint8_t i = 0;
|
||||
|
||||
for (; i<3 && !((k->layer|k->row|k->column) & 0xC0); i++) {
|
||||
k->layer <<= 2;
|
||||
k->row <<= 2;
|
||||
k->column <<= 2;
|
||||
}
|
||||
|
||||
uint8_t written = 4-i;
|
||||
|
||||
// write key-action bytes for all bit pairs that weren't ignored
|
||||
// - the first byte contains the value of `k->pressed`; the same position
|
||||
// is set to `1` in all subsequent bytes
|
||||
// - all bytes except the last one written (containing the least
|
||||
// significant bits) have their first bit set to `1`
|
||||
|
||||
uint8_t byte = k->pressed << 6;
|
||||
|
||||
for (; i<4; i++) {
|
||||
byte = byte | ( i<3 ) << 7
|
||||
| ( k->layer & 0xC0 ) >> 2
|
||||
| ( k->row & 0xC0 ) >> 4
|
||||
| ( k->column & 0xC0 ) >> 6 ;
|
||||
|
||||
if ( to > limit ) return 0; // out of bounds
|
||||
if ( eeprom__write(to++, byte) ) return 0; // write failed
|
||||
|
||||
byte = 1 << 6;
|
||||
|
||||
k->layer <<= 2;
|
||||
k->row <<= 2;
|
||||
k->column <<= 2;
|
||||
}
|
||||
|
||||
return written; // success
|
||||
}
|
||||
|
||||
/** functions/find_key_action/description
|
||||
* Find the macro remapping the given key-action (if it exists).
|
||||
*
|
||||
* Arguments:
|
||||
* - `k`: A pointer to the key-action to search for
|
||||
*
|
||||
* Returns:
|
||||
* - success: The EEMEM address of the desired macro
|
||||
* - failure: `0`
|
||||
*
|
||||
* Notes:
|
||||
* - The address `0` (or really `NULL`, which is `#define`ed to `((void *)0)`)
|
||||
* is a valid address in the EEPROM; but because macros are not placed first
|
||||
* in the EEPROM, we can still use it to signal nonexistence or failure.
|
||||
* - See the documentation for "(group) EEMEM layout" above for a description
|
||||
* of the layout of macros in EEMEM.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - It would be more efficient to convert the given key-action into the same
|
||||
* binary representation as used in the EEPROM, once, and then compare that
|
||||
* directly with the encoded key-action bytes read; but I don't think it'll
|
||||
* have enough of an impact on performance to justify rewriting the
|
||||
* ...key_action() functions, and it seems like this solution is a little bit
|
||||
* cleaner (since it results in slightly fewer functions and keeps the
|
||||
* representation of a key-function in SRAM consistent).
|
||||
*/
|
||||
static void * find_key_action(key_action_t * k) {
|
||||
void * current = EEMEM_MACROS_START;
|
||||
|
||||
for ( uint8_t type = eeprom__read(current);
|
||||
type != TYPE_END;
|
||||
current += eeprom__read(current+1), type = eeprom__read(current) ) {
|
||||
|
||||
if (type == TYPE_VALID_MACRO) {
|
||||
|
||||
key_action_t k_current;
|
||||
read_key_action(current+2, &k_current);
|
||||
|
||||
if ( k->pressed == k_current.pressed
|
||||
&& k->layer == k_current.layer
|
||||
&& k->row == k_current.row
|
||||
&& k->column == k_current.column ) {
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // key-action not found
|
||||
}
|
||||
|
||||
/** functions/find_next_deleted/description
|
||||
* Find the first deleted macro at or after the given macro.
|
||||
*
|
||||
* Arguments:
|
||||
* - `start`: The EEMEM address of the macro at which to begin searching
|
||||
*
|
||||
* Returns:
|
||||
* - success: The EEMEM address of the first deleted macro at or after `start`
|
||||
* - failure: `0` (no deleted macros were found at or after `start`)
|
||||
*/
|
||||
static void * find_next_deleted(void * start) {
|
||||
for ( uint8_t type = eeprom__read(start);
|
||||
type != TYPE_END;
|
||||
start += eeprom__read(start+1), type = eeprom__read(start) ) {
|
||||
|
||||
if (type == TYPE_DELETED)
|
||||
return start;
|
||||
}
|
||||
|
||||
return 0; // no deleted macro found
|
||||
}
|
||||
|
||||
/** functions/find_next_nondeleted/description
|
||||
* Find the first macro at or after the given macro that is not marked as
|
||||
* deleted.
|
||||
*
|
||||
* Arguments:
|
||||
* - `start`: The EEMEM address of the macro at which to begin searching
|
||||
*
|
||||
* Returns:
|
||||
* - success: The EEMEM address of the first non-deleted macro at or after
|
||||
* `start`
|
||||
*
|
||||
* Notes:
|
||||
* - Since the sequence of macros must end with a `TYPE_END` macro (which is,
|
||||
* of course, not a deleted macro), this function will always find a
|
||||
* non-deleted macro at or after the one passed.
|
||||
*/
|
||||
static void * find_next_nondeleted(void * start) {
|
||||
for ( uint8_t type = eeprom__read(start);
|
||||
type == TYPE_DELETED;
|
||||
start += eeprom__read(start+1), type = eeprom__read(start) );
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
/** functions/compress/description
|
||||
* Remove any gaps in the EEPROM caused by deleted macros
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure:
|
||||
* - `1`: write failed; data unchanged
|
||||
* - `2`: write failed; data lost; any macro currently being written is
|
||||
* cancelled
|
||||
*
|
||||
* Notes:
|
||||
* - As a rough idea of the time it might take for a compress to be fully
|
||||
* committed to EEMEM: 1024 bytes * 5 ms/byte = 5120 ms ~= 5 seconds.
|
||||
* - It's important to keep in mind that nothing will be written to the EEPROM
|
||||
* until after this function returns (since writes are done 1 byte per
|
||||
* keyboard scan cycle, at the end of each scan cycle). But the code should
|
||||
* not depend on that.
|
||||
* - It's also important to remember that this function will not be interrupted
|
||||
* by the recording of any new key-actions for an in-progress macro (though,
|
||||
* key-actions may be queued for writing before all `compress()` writes have
|
||||
* been completed).
|
||||
*
|
||||
* Implementation notes:
|
||||
* - Before performing any copy operation, we invalidate the portion of the
|
||||
* EEPROM we are going to modify by setting the first byte of it (which is,
|
||||
* and will be, the beginning of a macro) to `TYPE_END`. This way, as long
|
||||
* as writes to the EEPROM are atomic (or, as long as we don't lose power
|
||||
* while writing one of these crucial `type` bytes) the EEPROM will always be
|
||||
* in a consistent state.
|
||||
* - If power is lost before all writes have been committed, the portion of
|
||||
* the EEPROM that has not yet been compressed will remain invalidated
|
||||
* (so data will be lost, but the list of macros will not be corrupted).
|
||||
* - If the user tries to execute a macro before all writes have been
|
||||
* committed, and the macro is in the portion of the EEPROM that has
|
||||
* already been compressed, it will be found as normal. If the macro is
|
||||
* in the portion of the EEPROM that is still being modified, it will
|
||||
* temporarily appear not to exist.
|
||||
* - In any case, no extra checks need to be performed, the possibility of
|
||||
* data loss is kept very low, and the possibility of data corruption
|
||||
* (which would, in this scheme, be undetected...) is (I think, for our
|
||||
* purposes) vanishingly small.
|
||||
*/
|
||||
static uint8_t compress(void) {
|
||||
uint8_t ret; // for function return codes (to test for errors)
|
||||
|
||||
void * to_overwrite; // the first byte with a value we don't need to keep
|
||||
void * to_compress; // the first byte of the data we do need to keep
|
||||
void * next; // the next macro after the data to keep (usually)
|
||||
|
||||
uint8_t type; // the type of the first macro in `to_compress`
|
||||
void * type_location; // the final location of this `type` byte in EEMEM
|
||||
|
||||
to_overwrite = find_next_deleted(EEMEM_MACROS_START);
|
||||
if (! to_overwrite) return 0; // success: nothing to compress
|
||||
|
||||
// - here `next` is the next macro to consider keeping
|
||||
// - we could set `next = to_overwrite`, but then this would depend on
|
||||
// writes being delayed
|
||||
next = to_overwrite + eeprom__read(to_overwrite+1);
|
||||
|
||||
// invalidate the portion of the EEPROM we'll be working on
|
||||
ret = eeprom__write(to_overwrite, TYPE_END);
|
||||
if (ret) return 1; // write failed; data unchanged
|
||||
|
||||
while (next <= end_macro) {
|
||||
to_compress = find_next_nondeleted(next);
|
||||
|
||||
// `next` will always be 1 byte beyond the data we wish to copy
|
||||
// - since the EEPROM is only 2^10 bytes, and pointers are 16 bits, we
|
||||
// don't have to worry about overflow
|
||||
next = find_next_deleted(to_compress);
|
||||
if (! next) next = new_end_macro;
|
||||
if (! next) next = end_macro+1;
|
||||
|
||||
// save the `type` so we can write it last
|
||||
type = eeprom__read(to_compress);
|
||||
type_location = to_overwrite;
|
||||
to_overwrite++;
|
||||
to_compress++;
|
||||
|
||||
// copy the data in at most `UINT8_MAX` size chunks
|
||||
// - because the `length` argument of `eeprom__write()` is a `uint8_t`
|
||||
// - even though macros (individually) will be at most `UINT8_MAX`
|
||||
// bytes long, the block of macros we need to save may be longer
|
||||
for ( uint16_t length = next-to_compress;
|
||||
length;
|
||||
length = next-to_compress ) {
|
||||
|
||||
if (length > UINT8_MAX)
|
||||
length = UINT8_MAX;
|
||||
|
||||
ret = eeprom__copy(to_overwrite, to_compress, length);
|
||||
if (ret) goto out; // write failed; data lost
|
||||
to_overwrite += length;
|
||||
to_compress += length;
|
||||
}
|
||||
|
||||
// invalidate the portion of the EEPROM we'll be working on next
|
||||
// - no need to do this if there's nothing more to compress
|
||||
if (next <= end_macro) {
|
||||
ret = eeprom__write(to_overwrite, TYPE_END);
|
||||
if (ret) goto out; // write failed; data lost
|
||||
}
|
||||
|
||||
// lastly, write the `type` we saved earlier
|
||||
// (revalidate the portion of the EEPROM we're done with)
|
||||
ret = eeprom__write(type_location, type);
|
||||
if (ret) goto out; // write failed; data lost
|
||||
}
|
||||
|
||||
// update state variables
|
||||
if (new_end_macro) {
|
||||
end_macro -= (new_end_macro-to_overwrite);
|
||||
new_end_macro = to_overwrite;
|
||||
} else {
|
||||
end_macro = to_overwrite-1;
|
||||
}
|
||||
|
||||
return 0; // success: compression finished
|
||||
|
||||
out:
|
||||
end_macro = type_location;
|
||||
new_end_macro = 0;
|
||||
return 2; // write failed; data lost
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// helper functions -----------------------------------------------------------
|
||||
|
||||
/** functions/write_key_action_for_new_macro/description
|
||||
* Write the given key-action to the next empty space in the macros block of
|
||||
* the EEPROM
|
||||
*
|
||||
* Arguments:
|
||||
* - `k`: A pointer to the key-action to write
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Assumptions:
|
||||
* - A macro is currently in progress (i.e. `new_end_macro` is not `0`).
|
||||
*
|
||||
* Notes:
|
||||
* - We make sure to leave 1 empty byte for the end macro.
|
||||
* - We update the value of `new_end_macro` (either to indicate the bytes that
|
||||
* were written, or to cancel the new macro if writing failed).
|
||||
*/
|
||||
static inline uint8_t write_key_action_for_new_macro(key_action_t * k) {
|
||||
uint8_t ret; // for function return values
|
||||
|
||||
ret = write_key_action(new_end_macro, k, EEMEM_MACROS_END-1);
|
||||
if (! ret) {
|
||||
if ( compress() ) goto out; // compress failed (macro cancelled)
|
||||
|
||||
ret = write_key_action(new_end_macro, k, EEMEM_MACROS_END-1);
|
||||
if (! ret) goto out; // write failed again, or not enough room
|
||||
}
|
||||
|
||||
new_end_macro += ret;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
new_end_macro = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** functions/delete_macro_if_exists/description
|
||||
* Deletes the macro remapping the given key-action, if it exists
|
||||
*
|
||||
* Arguments:
|
||||
* - `k`: A pointer to the key-action to delete
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
static inline uint8_t delete_macro_if_exists(key_action_t * k) {
|
||||
void * k_location = find_key_action(k);
|
||||
|
||||
if (k_location)
|
||||
if ( eeprom__write(k_location, TYPE_DELETED) )
|
||||
return 1; // write failed
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// public functions -----------------------------------------------------------
|
||||
|
||||
/** functions/eeprom_macro__init/description
|
||||
* Implementation notes:
|
||||
* - The initialization of static EEPROM values that this function is supposed
|
||||
* to do when the EEPROM is not in a valid state (for this build of the
|
||||
* firmware) is done in `eeprom_macro__clear_all()`.
|
||||
*/
|
||||
uint8_t eeprom_macro__init(void) {
|
||||
#define TEST(address, offset, expected) \
|
||||
if ( eeprom__read((address)+(offset)) != (expected) ) \
|
||||
return eeprom_macro__clear_all()
|
||||
|
||||
TEST( EEMEM_START_ADDRESS_START, 0, (uint16_t)EEMEM_START >> 8 );
|
||||
TEST( EEMEM_START_ADDRESS_START, 1, (uint16_t)EEMEM_START & 0xFF );
|
||||
|
||||
TEST( EEMEM_END_ADDRESS_START, 0, (uint16_t)EEMEM_END >> 8 );
|
||||
TEST( EEMEM_END_ADDRESS_START, 1, (uint16_t)EEMEM_END & 0xFF );
|
||||
|
||||
TEST( EEMEM_VERSION_START, 0, VERSION );
|
||||
|
||||
#undef TEST
|
||||
|
||||
// find the end macro
|
||||
void * current = EEMEM_MACROS_START;
|
||||
for ( uint8_t type = eeprom__read(current);
|
||||
type != TYPE_END;
|
||||
current += eeprom__read(current+1), type = eeprom__read(current) );
|
||||
|
||||
end_macro = current;
|
||||
new_end_macro = 0;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** functions/eeprom_macro__record_init/description
|
||||
* Implementation notes:
|
||||
* - At minimum, for a normal macro, we will need a `type` byte, a `length`
|
||||
* byte, and 3 key-actions (the key-action to remap, 1 press, and 1 release).
|
||||
* Key-actions take a minimum of 1 byte, so our minimum macro will be 5
|
||||
* bytes.
|
||||
*/
|
||||
uint8_t eeprom_macro__record_init( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
if (new_end_macro)
|
||||
eeprom_macro__record_cancel();
|
||||
|
||||
if ( end_macro + 5 > EEMEM_MACROS_END )
|
||||
return 1; // not enough room
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = pressed,
|
||||
.layer = layer,
|
||||
.row = row,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
if ( delete_macro_if_exists(&k) ) return 1; // failure
|
||||
|
||||
new_end_macro = end_macro + 2;
|
||||
|
||||
return write_key_action_for_new_macro(&k);
|
||||
}
|
||||
|
||||
/** functions/eeprom_macro__record_action/description
|
||||
* Implementation notes:
|
||||
* - Macros can only be `UINT8_MAX` bytes long in total. If we don't have at
|
||||
* least 4 bytes left before exceeding that limit (since 4 bytes is the
|
||||
* maximum length of a key-action), we simply stop recording actions. This
|
||||
* is certainly not optimal behavior... but I think it'll end up being the
|
||||
* least surprising, where our other options are to either finalize the
|
||||
* macro, or return an error code.
|
||||
* - If longer macros are desired, there are several ways one might modify
|
||||
* the implementation to allow them. The simplest method would be to
|
||||
* make `length` a 2 byte variable. That would reduce the number of
|
||||
* small macros one could have, however. Alternately, one could steal 2
|
||||
* bits from the `type` byte, which would save space, but make things
|
||||
* more difficult to read. Another method would be to introduce a
|
||||
* `TYPE_CONTINUED`, or something similar, where the data section of a
|
||||
* macro of this type would continue the data section of the previous
|
||||
* macro. That would make the logic of recording macros (and playing
|
||||
* them back) a little more complicated though.
|
||||
*/
|
||||
uint8_t eeprom_macro__record_action( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
if (! new_end_macro)
|
||||
return 1; // no macro in progress
|
||||
|
||||
if ( new_end_macro - end_macro > UINT8_MAX - 4 )
|
||||
return 0; // macro too long, ignoring further actions
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = pressed,
|
||||
.layer = layer,
|
||||
.row = row,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
return write_key_action_for_new_macro(&k);
|
||||
}
|
||||
|
||||
uint8_t eeprom_macro__record_finalize(void) {
|
||||
if ( eeprom__write( new_end_macro, TYPE_END ) ) goto out;
|
||||
if ( eeprom__write( end_macro+1, new_end_macro - end_macro ) ) goto out;
|
||||
if ( eeprom__write( end_macro, TYPE_VALID_MACRO ) ) goto out;
|
||||
|
||||
end_macro = new_end_macro;
|
||||
new_end_macro = 0;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
new_end_macro = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t eeprom_macro__record_cancel(void) {
|
||||
new_end_macro = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t eeprom_macro__play( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = pressed,
|
||||
.layer = layer,
|
||||
.row = row,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
void * k_location = find_key_action(&k);
|
||||
if (! k_location) return 1; // macro does not exist
|
||||
|
||||
uint8_t length = eeprom__read(k_location+1);
|
||||
length -= 2;
|
||||
k_location += 2;
|
||||
while (length) {
|
||||
uint8_t read = read_key_action(k_location, &k);
|
||||
kb__layout__exec_key_layer( k.pressed, k.layer, k.row, k.column );
|
||||
length -= read;
|
||||
k_location += read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool eeprom_macro__exists( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = pressed,
|
||||
.layer = layer,
|
||||
.row = row,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
return find_key_action(&k);
|
||||
}
|
||||
|
||||
uint8_t eeprom_macro__clear( bool pressed,
|
||||
uint8_t layer,
|
||||
uint8_t row,
|
||||
uint8_t column ) {
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = pressed,
|
||||
.layer = layer,
|
||||
.row = row,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
return delete_macro_if_exists(&k);
|
||||
}
|
||||
|
||||
/** functions/eeprom_macro__clear_all/description
|
||||
* Implementation notes:
|
||||
* - Since the `eeprom__...` functions only modify data when necessary, we
|
||||
* don't need to worry here about excessive EEPROM wear when writing; so it's
|
||||
* easier to initialize all static EEPROM values every time this function
|
||||
* runs than to do most of these initializations as a special case in
|
||||
* `eeprom_macro__init()`.
|
||||
*/
|
||||
uint8_t eeprom_macro__clear_all(void) {
|
||||
#define WRITE(address, offset, value) \
|
||||
if (eeprom__write( (address)+(offset), (value) )) return 1
|
||||
|
||||
WRITE( EEMEM_START_ADDRESS_START, 0, (uint16_t)EEMEM_START >> 8 );
|
||||
WRITE( EEMEM_START_ADDRESS_START, 1, (uint16_t)EEMEM_START & 0xFF );
|
||||
|
||||
WRITE( EEMEM_END_ADDRESS_START, 0, (uint16_t)EEMEM_END >> 8 );
|
||||
WRITE( EEMEM_END_ADDRESS_START, 1, (uint16_t)EEMEM_END & 0xFF );
|
||||
|
||||
WRITE( EEMEM_VERSION_START, 0, VERSION );
|
||||
|
||||
WRITE( EEMEM_MACROS_START, 0, TYPE_END );
|
||||
|
||||
#undef WRITE
|
||||
|
||||
end_macro = EEMEM_MACROS_START;
|
||||
new_end_macro = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# eeprom-macro options
|
||||
#
|
||||
# This file is meant to be included by the using '.../options.mk'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU).c)
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Some generally useful functions to aid in creating keys
|
||||
*
|
||||
* Prefix: `key_functions__`
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEY_FUNCTIONS__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEY_FUNCTIONS__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// basic
|
||||
void key_functions__press (uint8_t keycode);
|
||||
void key_functions__release (uint8_t keycode);
|
||||
void key_functions__toggle (uint8_t keycode);
|
||||
|
||||
// device
|
||||
void key_functions__jump_to_bootloader (void);
|
||||
void key_functions__dump_sram_ihex (void * from, void * last);
|
||||
void key_functions__dump_progmem_ihex (void * from, void * last);
|
||||
void key_functions__dump_eeprom_ihex (void * from, void * last);
|
||||
|
||||
// special
|
||||
void key_functions__toggle_capslock (void);
|
||||
void key_functions__type_byte_hex (uint8_t byte);
|
||||
void key_functions__type_string (const char * string);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEY_FUNCTIONS__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// basic ----------------------------------------------------------------------
|
||||
|
||||
// === key_functions__press() ===
|
||||
/** functions/key_functions__press/description
|
||||
* Generate a normal keypress (and send the USB report).
|
||||
*
|
||||
* Arguments:
|
||||
* - `keycode`: The keycode to "press"
|
||||
*/
|
||||
|
||||
// === key_functions__release() ===
|
||||
/** functions/key_functions__release/description
|
||||
* Generate a normal keyrelease (and send the USB report).
|
||||
*
|
||||
* Arguments:
|
||||
* - `keycode`: The keycode to "release"
|
||||
*/
|
||||
|
||||
// === key_functions__toggle() ===
|
||||
/** functions/key_functions__toggle/description
|
||||
* Toggle the key pressed or unpressed.
|
||||
*
|
||||
* Arguments:
|
||||
* - `keycode`: The keycode to "toggle"
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// device ---------------------------------------------------------------------
|
||||
|
||||
// === key_functions__jump_to_bootloader() ===
|
||||
/** functions/key_functions__jump_to_bootloader/description
|
||||
* For reflashing the controller.
|
||||
*/
|
||||
|
||||
// === (group) dump...ihex() ===
|
||||
/** functions/(group) dump...ihex/description
|
||||
* Type, in ihex format, the specified data from the appropriate memory space.
|
||||
*
|
||||
* Members:
|
||||
* - `key_functions__dump_sram_ihex`: Types data from the SRAM
|
||||
* - `key_functions__dump_progmem_ihex`: Types data from the PROGMEM
|
||||
* - `key_functions__dump_eeprom_ihex`: Types data from the EEPROM
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: A pointer to the location from which to start reading
|
||||
* - `last`: A pointer to the last location from which to read
|
||||
*
|
||||
* Usage notes:
|
||||
* - To print the entire contents of, e.g., SRAM, write
|
||||
*
|
||||
* key_functions__dump_sram_ihex( (void *)0, (void *)-1 );
|
||||
*
|
||||
* See "Implementation notes" below.
|
||||
*
|
||||
* Warnings:
|
||||
* - These functions may take a long time to complete, and there is no way to
|
||||
* stop them aside from resetting the controller.
|
||||
* - It may occur to you that one could read out the contents of PROGMEM using
|
||||
* the appropriate function, and then use the resulting ihex file to reflash
|
||||
* a Teensy. I would *not* recommend doing this however, unless you really
|
||||
* know what you're doing, or you're prepared for the chance that you might
|
||||
* mess something up.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - If `last` is greater than the maximum addressable location of the
|
||||
* appropriate memory space, `last` should be set to the maximum addressable
|
||||
* location. This will allow users to use `(void *)-1` (or `(void
|
||||
* *)UINT16_MAX`) as the `last` location when they wish to read until the end
|
||||
* of addressable memory.
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// special --------------------------------------------------------------------
|
||||
|
||||
// === key_functions__toggle_capslock() ===
|
||||
/** functions/key_functions__toggle_capslock/description
|
||||
* Toggle the state of 'capslock'
|
||||
*
|
||||
* Notes:
|
||||
* - This requires special handling because neither of the shift keys may be
|
||||
* active when 'capslock' is pressed, or it will not register properly. This
|
||||
* function disables both shift keys, toggles 'capslock', and then restores
|
||||
* the state of both shift keys.
|
||||
*/
|
||||
|
||||
// === key_functions__type_byte_hex() ===
|
||||
/** functions/key_functions__type_byte_hex/description
|
||||
* Send the characters (2 of `[0-9A-F]`) corresponding to `byte` in base 16
|
||||
*
|
||||
* Arguments:
|
||||
* - `byte`: The byte to send a representation of
|
||||
*/
|
||||
|
||||
// === key_functions__type_string() ===
|
||||
/** functions/key_functions__type_string/description
|
||||
* Type the keycode (or "unicode sequence") for each character in `string`
|
||||
*
|
||||
* Arguments:
|
||||
* - `string`: A pointer to a valid UTF-8 string in PROGMEM
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - A "unicode sequence" is holding down "alt", typing "+", typing the 4
|
||||
* character hexadecimal unicode code for the specified character, then
|
||||
* releasing "alt". This is done for every character in `string` for which a
|
||||
* dedicated USB keycode has not been specified.
|
||||
*
|
||||
* - This function is, relative to the rest of life, extremely unportable.
|
||||
* Sorry about that: I looked for a better way to do things, but I couldn't
|
||||
* find one. It may be very convenient under certain circumstances though,
|
||||
* if one is willing to take a little time to understand what it's actually
|
||||
* doing, so I'm including it in the hope that it will be. Please see the
|
||||
* implementation (in the '.c' file) for which keycodes are being sent for a
|
||||
* given character.
|
||||
*
|
||||
*
|
||||
* Operating system considerations (for typing "unicode sequences"):
|
||||
*
|
||||
* - "Unicode sequences", as implemented, should work on OS X and Windows
|
||||
* (after enabling "hexidecimal code input" in the OS), but probably will not
|
||||
* work on Linux (2 out of 3 of the major OSs is better than nothing...). If
|
||||
* you're using Linux and you want to flip that around :) please modify the
|
||||
* function to send Linux friendly start and end sequences. See [this
|
||||
* Wikipedia article]
|
||||
* (http://en.wikipedia.org/wiki/Unicode_input#Hexadecimal_code_input) for
|
||||
* more information.
|
||||
*
|
||||
* - On Windows (per the Wikipedia article above): Make sure the registry key
|
||||
* `HKEY_CURRENT_USER\Control Panel\Input Method\EnableHexNumpad` has a
|
||||
* string value of `1`. If it does not, fix it, then reboot (or log off/on,
|
||||
* in Windows 7+)
|
||||
*
|
||||
* - On OS X: open "System Preferences", navigate to "Language & Text", select
|
||||
* the "Input Sources" tab (if it isn't selected already), and check the box
|
||||
* next to "Unicode Hex Input". Make sure this input method is active
|
||||
* whenever you use this function.
|
||||
* - I recommend disabling the default (in the US at least) "U.S." input
|
||||
* method, and just leaving this one active all the time. Note though
|
||||
* that this will render all the normal "Alt" special characters
|
||||
* unavailable.
|
||||
*
|
||||
*
|
||||
* Usage notes:
|
||||
*
|
||||
* - Characters (and strings) sent with this function do not automatically
|
||||
* repeat (as normal keys do).
|
||||
*
|
||||
* - Please pay very special attention to what this function is actually typing
|
||||
* if you are using any language besides English as the default in your OS,
|
||||
* or if you have characters that aren't 7-bit ASCII in your string and have
|
||||
* not enabled "hexidecimal code input" in your OS.
|
||||
*
|
||||
* - This function does not disable modifier keys, or any other keys, on entry:
|
||||
* keys are pressed and released as they would be if one was typing them
|
||||
* starting with no keys pressed. If you are holding down any keys when this
|
||||
* function is called, it *may* not do what you expect, depending on which
|
||||
* keys are being held down and which keys this function is trying to press
|
||||
* and release.
|
||||
*
|
||||
* - An easy way to pass a PROGMEM string to this function is to use the
|
||||
* `PSTR()` macro in `<avr/pgmspace.h>`, as in
|
||||
*
|
||||
* key_functions__type_string( PSTR( "❄" ) );
|
||||
*
|
||||
* or
|
||||
*
|
||||
* key_functions__type_string( PSTR(
|
||||
* "こんにちは世界 γειά σου κόσμε hello world ^_^" ) );
|
||||
*/
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "basic" section of "../key-functions.h"
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../../../../firmware/lib/usb.h"
|
||||
#include "../../../../firmware/lib/usb/usage-page/keyboard.h"
|
||||
#include "../key-functions.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void key_functions__press(uint8_t keycode) {
|
||||
usb__kb__set_key(true, keycode);
|
||||
}
|
||||
|
||||
void key_functions__release(uint8_t keycode) {
|
||||
usb__kb__set_key(false, keycode);
|
||||
}
|
||||
|
||||
void key_functions__toggle(uint8_t keycode) {
|
||||
if (usb__kb__read_key(keycode))
|
||||
usb__kb__set_key(false, keycode);
|
||||
else
|
||||
usb__kb__set_key(true, keycode);
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "device" section of "../../key-functions.h" for the
|
||||
* ATMega23U4
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <util/delay.h>
|
||||
#include "../../../../../firmware/lib/eeprom.h"
|
||||
#include "../../key-functions.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/sram_read/description
|
||||
* A tiny little helper function to let `key_functions__dump_sram_ihex()` work
|
||||
* the same way as the other `...dump...ihex()` functions.
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: The address of (i.e. a pointer to) the location from which to read
|
||||
*
|
||||
* Returns:
|
||||
* - success: The data stored at `pointer` in the SRAM memory space
|
||||
*/
|
||||
static uint8_t sram_read(void * from) {
|
||||
return *(uint8_t *)from;
|
||||
}
|
||||
|
||||
/** functions/progmem_read/description
|
||||
* A tiny little helper function to let `key_functions__dump_progmem_ihex()`
|
||||
* work the same way as the other `...dump...ihex()` functions.
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: The address of (i.e. a pointer to) the location from which to read
|
||||
*
|
||||
* Returns:
|
||||
* - success: The data stored at `pointer` in the PROGMEM memory space
|
||||
*/
|
||||
static uint8_t progmem_read(void * from) {
|
||||
return pgm_read_byte(from);
|
||||
}
|
||||
|
||||
/** functions/dump_ihex/description
|
||||
* Type, in ihex format, all data between `from` and `last`, inclusive.
|
||||
*
|
||||
* Arguments:
|
||||
* - `read`: A pointer to a function that takes a memory address and returns 1
|
||||
* byte of data (presumably from that memory address, in whatever address
|
||||
* space we're dealing with)
|
||||
* - `from`: A pointer to the location from which to start reading
|
||||
* - `last`: A pointer to the last location from which to read
|
||||
*
|
||||
* Notes:
|
||||
* - See [the Wikipedia article] (http://en.wikipedia.org/wiki/Intel_HEX) on
|
||||
* the Intel HEX (ihex) format.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - When the loop starts, `from` might be `0`, and `last` might be
|
||||
* `UINT16_MAX`, in which case `from == last+1`. We need to ignore this on
|
||||
* the first iteration, hence the do-while loop and the `from != last+1` part
|
||||
* of the first conditional. When this condition occurs after the first
|
||||
* iteration, it will be because `from` was incremented one past `last`, and
|
||||
* the loop should be terminated.
|
||||
* - Pointer comparisons in C are interesting... Specifically, `last-from+1 <
|
||||
* line_width` seems to cast `last-from+1` to the same type as `line_width`
|
||||
* (`uint8_t`) which causes problems since pointers on this platform are 16
|
||||
* bits. Casting either side of the expression to `uint16_t` seems to solve
|
||||
* the problem. It feels cleaner to me to cast the pointer side, because
|
||||
* comparison between pointers and integers is only implementation defined,
|
||||
* whereas comparison between two integers ought to be defined by the
|
||||
* standard.
|
||||
*/
|
||||
static void dump_ihex( uint8_t (*read)(void *),
|
||||
void * from,
|
||||
void * last ) {
|
||||
|
||||
if (from > last)
|
||||
return; // error
|
||||
|
||||
const uint8_t record_type = 0x00; // ihex record type = data
|
||||
uint8_t line_width = 0x10; // 16 bytes of data per line, by default
|
||||
|
||||
do {
|
||||
// - As long as `from != last+1`, `last-from+1` is the number of bytes
|
||||
// left to print
|
||||
if (from != last+1 && (uint16_t)(last-from+1) < line_width)
|
||||
line_width = last-from+1;
|
||||
|
||||
uint8_t checksum = line_width + record_type
|
||||
+ ((uint16_t)(from) >> 8)
|
||||
+ ((uint16_t)(from) & 0xFF);
|
||||
|
||||
key_functions__type_string( PSTR(":") );
|
||||
key_functions__type_byte_hex( line_width );
|
||||
key_functions__type_byte_hex( (uint16_t)(from) >> 8 );
|
||||
key_functions__type_byte_hex( (uint16_t)(from) & 0xFF );
|
||||
key_functions__type_byte_hex( record_type );
|
||||
|
||||
for (uint8_t l=0; l<line_width; l++) {
|
||||
uint8_t byte = (*read)(from++);
|
||||
checksum += byte;
|
||||
key_functions__type_byte_hex(byte);
|
||||
}
|
||||
|
||||
key_functions__type_byte_hex( 0x100 - checksum ); // 2's compliment
|
||||
key_functions__type_string( PSTR("\n") );
|
||||
|
||||
} while (from != last+1);
|
||||
|
||||
key_functions__type_string( PSTR(":00000001FF\n") ); // ihex EOF record
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/key_functions__jump_to_bootloader/description
|
||||
* Implementation notes:
|
||||
* - This code is from PJRC (slightly modified):
|
||||
* <http://www.pjrc.com/teensy/jump_to_bootloader.html>.
|
||||
*
|
||||
*/
|
||||
void key_functions__jump_to_bootloader(void) {
|
||||
// --- for all Teensy boards ---
|
||||
|
||||
cli();
|
||||
|
||||
// disable watchdog, if enabled
|
||||
// disable all peripherals
|
||||
UDCON = 1;
|
||||
USBCON = (1<<FRZCLK); // disable USB
|
||||
UCSR1B = 0;
|
||||
_delay_ms(5);
|
||||
|
||||
// --- Teensy 2.0 specific ---
|
||||
|
||||
EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
|
||||
TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
|
||||
DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
|
||||
PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
|
||||
asm volatile("jmp 0x7E00");
|
||||
}
|
||||
|
||||
void key_functions__dump_sram_ihex(void * from, void * last) {
|
||||
if (last > (void *)RAMEND)
|
||||
last = (void *)RAMEND;
|
||||
dump_ihex(&sram_read, from, last);
|
||||
}
|
||||
|
||||
void key_functions__dump_progmem_ihex(void * from, void * last) {
|
||||
if (last > (void *)FLASHEND)
|
||||
last = (void *)FLASHEND;
|
||||
dump_ihex(&progmem_read, from, last);
|
||||
}
|
||||
|
||||
void key_functions__dump_eeprom_ihex(void * from, void * last) {
|
||||
if (last > (void *)E2END)
|
||||
last = (void *)E2END;
|
||||
dump_ihex(&eeprom__read, from, last);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# key-functions options
|
||||
#
|
||||
# This file is meant to be included by the using '.../options.mk'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/*.c)
|
||||
SRC += $(wildcard $(CURDIR)/device/$(MCU).c)
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "special" section of "../key-functions.h"
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/pgmspace.h> // for `key_functions__type_string()`
|
||||
#include "../../../../firmware/lib/usb.h"
|
||||
#include "../../../../firmware/lib/usb/usage-page/keyboard.h"
|
||||
#include "../key-functions.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void key_functions__toggle_capslock(void) {
|
||||
// save the state of both shifts, and disable them
|
||||
struct {
|
||||
bool left_shift : 1;
|
||||
bool right_shift : 1;
|
||||
} state = {
|
||||
.left_shift = usb__kb__read_key( KEYBOARD__LeftShift ),
|
||||
.right_shift = usb__kb__read_key( KEYBOARD__RightShift ),
|
||||
};
|
||||
usb__kb__set_key( false, KEYBOARD__LeftShift );
|
||||
usb__kb__set_key( false, KEYBOARD__RightShift );
|
||||
usb__kb__send_report();
|
||||
|
||||
// toggle capslock
|
||||
usb__kb__set_key(true, KEYBOARD__CapsLock);
|
||||
usb__kb__send_report();
|
||||
usb__kb__set_key(false, KEYBOARD__CapsLock);
|
||||
usb__kb__send_report();
|
||||
|
||||
// restore the state of both shifts
|
||||
usb__kb__set_key( state.left_shift, KEYBOARD__LeftShift );
|
||||
usb__kb__set_key( state.right_shift, KEYBOARD__RightShift );
|
||||
usb__kb__send_report();
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: try using number pad numbers; see how it works
|
||||
*/
|
||||
// void key_functions__type_byte_hex(uint8_t byte) {
|
||||
// uint8_t c[2] = { byte >> 4, byte & 0xF };
|
||||
//
|
||||
// bool numlock_on = usb__kb__read_led('N');
|
||||
//
|
||||
// if (! numlock_on ) {
|
||||
// usb__kb__set_key(true, KEYPAD__NumLock_Clear);
|
||||
// usb__kb__send_report();
|
||||
// usb__kb__set_key(false, KEYPAD__NumLock_Clear);
|
||||
// }
|
||||
//
|
||||
// for (uint8_t i=0; i<2; i++) {
|
||||
// if (c[i] == 0) c[i] = KEYPAD__0_Insert;
|
||||
// else if (c[i] < 10) c[i] += KEYPAD__1_End-1;
|
||||
// else c[i] += KEYBOARD__a_A-10;
|
||||
//
|
||||
// usb__kb__set_key(true, c[i]);
|
||||
// usb__kb__send_report();
|
||||
// usb__kb__set_key(false, c[i]);
|
||||
// }
|
||||
//
|
||||
// if (! numlock_on ) {
|
||||
// usb__kb__set_key(true, KEYPAD__NumLock_Clear);
|
||||
// usb__kb__send_report();
|
||||
// usb__kb__set_key(false, KEYPAD__NumLock_Clear);
|
||||
// usb__kb__send_report();
|
||||
// }
|
||||
//
|
||||
// usb__kb__send_report();
|
||||
// }
|
||||
/** functions/key_functions__type_byte_hex/description
|
||||
* Implementation notes:
|
||||
*
|
||||
* - We have to call `usb__kb__send_report()` after each call to
|
||||
* `usb__kb__set_key()`; otherwise, if the high 4 bits is the same as the low
|
||||
* 4 bits, only one character will be typed.
|
||||
*/
|
||||
void key_functions__type_byte_hex(uint8_t byte) {
|
||||
uint8_t c[2] = { byte >> 4, byte & 0xF };
|
||||
|
||||
for (uint8_t i=0; i<2; i++) {
|
||||
if (c[i] == 0) c[i] = KEYBOARD__0_RightParenthesis;
|
||||
else if (c[i] < 10) c[i] += KEYBOARD__1_Exclamation-1;
|
||||
else c[i] += KEYBOARD__a_A-10;
|
||||
|
||||
usb__kb__set_key(true, c[i]);
|
||||
usb__kb__send_report();
|
||||
usb__kb__set_key(false, c[i]);
|
||||
usb__kb__send_report();
|
||||
}
|
||||
|
||||
usb__kb__send_report();
|
||||
}
|
||||
|
||||
/** functions/key_functions__type_string/description
|
||||
* Implementation notes:
|
||||
*
|
||||
* - We use `uint8_t` instead of `char` when iterating over `string` because
|
||||
* the signedness of `char` is implementation defined (and, actually, signed
|
||||
* by default with avr-gcc, which is not what we want if we're going to be
|
||||
* doing bitwise operations and comparisons).
|
||||
*
|
||||
* - We assume, for the most part, that the string is valid modified (i.e.
|
||||
* null-terminated) UTF-8. This should be a fairly safe assumption, since
|
||||
* all PROGMEM strings should be generated by the compiler :)
|
||||
*
|
||||
* - UTF-8 character format
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
* code points avail. bits byte 1 byte 2 byte 3 byte 4
|
||||
* --------------- ----------- -------- -------- -------- --------
|
||||
* 0x0000 - 0x007F 7 0xxxxxxx
|
||||
* 0x0080 - 0x07FF 11 110xxxxx 10xxxxxx
|
||||
* 0x0800 - 0xFFFF 16 1110xxxx 10xxxxxx 10xxxxxx
|
||||
* 0x010000 - 0x10FFFF 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* TODO:
|
||||
* - try to add start and end sequence for linux. maybe as a compilation
|
||||
* option?
|
||||
*/
|
||||
void key_functions__type_string(const char * string) {
|
||||
uint8_t c; // for storing the current byte of the character
|
||||
uint16_t c_full; // for storing the full character
|
||||
|
||||
// send string
|
||||
for (c = pgm_read_byte(string); c; c = pgm_read_byte(++string)) {
|
||||
|
||||
// get character
|
||||
if (c >> 7 == 0b0) {
|
||||
// a 1-byte utf-8 character
|
||||
c_full = c;
|
||||
|
||||
} else if (c >> 5 == 0b110) {
|
||||
// beginning of a 2-byte utf-8 character
|
||||
// assume the string is valid
|
||||
c_full = (c & 0x1F) << 6; c = pgm_read_byte(++string);
|
||||
c_full |= (c & 0x3F) << 0;
|
||||
|
||||
} else if (c >> 4 == 0b1110) {
|
||||
// beginning of a 3-byte utf-8 character
|
||||
// assume the string is valid
|
||||
c_full = (c & 0x0F) << 12; c = pgm_read_byte(++string);
|
||||
c_full |= (c & 0x3F) << 6; c = pgm_read_byte(++string);
|
||||
c_full |= (c & 0x3F) << 0;
|
||||
|
||||
} else if ((c >> 3) == 0b11110) {
|
||||
// beginning of a 4-byte utf-8 character
|
||||
// this character is too long, we can't send it
|
||||
// skip this byte, and the next 3
|
||||
string += 3;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
// ran across some invalid utf-8
|
||||
// ignore it, try again next time
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- (if possible) send regular keycode ---
|
||||
|
||||
if (c == c_full) {
|
||||
bool shifted = false;
|
||||
uint8_t keycode = 0;
|
||||
|
||||
if (c == 0x30) {
|
||||
keycode = KEYBOARD__0_RightParenthesis; // 0
|
||||
} else if (0x31 <= c && c <= 0x39) {
|
||||
keycode = KEYBOARD__1_Exclamation + c - 0x31; // 1..9
|
||||
} else if (0x41 <= c && c <= 0x5A) {
|
||||
shifted = true;
|
||||
keycode = KEYBOARD__a_A + c - 0x41; // A..Z
|
||||
} else if (0x61 <= c && c <= 0x7A) {
|
||||
keycode = KEYBOARD__a_A + c - 0x61; // a..z
|
||||
|
||||
} else switch (c) {
|
||||
// control characters
|
||||
case 0x08: keycode = KEYBOARD__DeleteBackspace; break; // BS
|
||||
case 0x09: keycode = KEYBOARD__Tab; break; // VT
|
||||
case 0x0A: keycode = KEYBOARD__ReturnEnter; break; // LF
|
||||
case 0x0D: keycode = KEYBOARD__ReturnEnter; break; // CR
|
||||
case 0x1B: keycode = KEYBOARD__Escape; break; // ESC
|
||||
// printable characters
|
||||
case 0x20: keycode = KEYBOARD__Spacebar; break; // ' '
|
||||
case 0x21: shifted = true;
|
||||
keycode = KEYBOARD__1_Exclamation; break; // !
|
||||
case 0x22: shifted = true;
|
||||
keycode = KEYBOARD__SingleQuote_DoubleQuote;
|
||||
break; // "
|
||||
case 0x23: shifted = true;
|
||||
keycode = KEYBOARD__3_Pound; break; // #
|
||||
case 0x24: shifted = true;
|
||||
keycode = KEYBOARD__4_Dollar; break; // $
|
||||
case 0x25: shifted = true;
|
||||
keycode = KEYBOARD__5_Percent; break; // %
|
||||
case 0x26: shifted = true;
|
||||
keycode = KEYBOARD__7_Ampersand; break; // &
|
||||
case 0x27: keycode = KEYBOARD__SingleQuote_DoubleQuote;
|
||||
break; // '
|
||||
case 0x28: shifted = true;
|
||||
keycode = KEYBOARD__9_LeftParenthesis;
|
||||
break; // (
|
||||
case 0x29: shifted = true;
|
||||
keycode = KEYBOARD__0_RightParenthesis;
|
||||
break; // )
|
||||
case 0x2A: shifted = true;
|
||||
keycode = KEYBOARD__8_Asterisk; break; // *
|
||||
case 0x2B: shifted = true;
|
||||
keycode = KEYBOARD__Equal_Plus; break; // +
|
||||
case 0x2C: keycode = KEYBOARD__Comma_LessThan; break; // ,
|
||||
case 0x2D: keycode = KEYBOARD__Dash_Underscore; break; // -
|
||||
case 0x2E: keycode = KEYBOARD__Period_GreaterThan;
|
||||
break; // .
|
||||
case 0x2F: keycode = KEYBOARD__Slash_Question; break; // /
|
||||
// ... numbers
|
||||
case 0x3A: shifted = true;
|
||||
keycode = KEYBOARD__Semicolon_Colon; break; // :
|
||||
case 0x3B: keycode = KEYBOARD__Semicolon_Colon; break; // ;
|
||||
case 0x3C: shifted = true;
|
||||
keycode = KEYBOARD__Comma_LessThan; break; // <
|
||||
case 0x3D: keycode = KEYBOARD__Equal_Plus; break; // =
|
||||
case 0x3E: shifted = true;
|
||||
keycode = KEYBOARD__Period_GreaterThan;
|
||||
break; // >
|
||||
case 0x3F: shifted = true;
|
||||
keycode = KEYBOARD__Slash_Question; break; // ?
|
||||
case 0x4D: shifted = true;
|
||||
keycode = KEYBOARD__2_At; break; // @
|
||||
// ... uppercase letters
|
||||
case 0x5B: keycode = KEYBOARD__LeftBracket_LeftBrace;
|
||||
break; // [
|
||||
case 0x5C: keycode = KEYBOARD__Backslash_Pipe; break; // '\'
|
||||
case 0x5D: keycode = KEYBOARD__RightBracket_RightBrace;
|
||||
break; // ]
|
||||
case 0x5E: shifted = true;
|
||||
keycode = KEYBOARD__6_Caret; break; // ^
|
||||
case 0x5F: shifted = true;
|
||||
keycode = KEYBOARD__Dash_Underscore; break; // _
|
||||
case 0x60: keycode = KEYBOARD__GraveAccent_Tilde;
|
||||
break; // `
|
||||
// ... lowercase letters
|
||||
case 0x7B: shifted = true;
|
||||
keycode = KEYBOARD__LeftBracket_LeftBrace;
|
||||
break; // {
|
||||
case 0x7C: shifted = true;
|
||||
keycode = KEYBOARD__Backslash_Pipe; break; // |
|
||||
case 0x7D: shifted = true;
|
||||
keycode = KEYBOARD__RightBracket_RightBrace;
|
||||
break; // }
|
||||
case 0x7E: shifted = true;
|
||||
keycode = KEYBOARD__GraveAccent_Tilde;
|
||||
break; // ~
|
||||
case 0x7F: keycode = KEYBOARD__DeleteForward; break; // DEL
|
||||
}
|
||||
|
||||
if (keycode) {
|
||||
// press keycode
|
||||
if (shifted) usb__kb__set_key(true, KEYBOARD__LeftShift);
|
||||
usb__kb__set_key(true, keycode);
|
||||
usb__kb__send_report();
|
||||
|
||||
// release keycode
|
||||
if (shifted) usb__kb__set_key(false, KEYBOARD__LeftShift);
|
||||
usb__kb__set_key(false, keycode);
|
||||
usb__kb__send_report();
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// --- (otherwise) send unicode sequence ---
|
||||
|
||||
// send start sequence
|
||||
usb__kb__set_key(true, KEYBOARD__LeftAlt ); usb__kb__send_report();
|
||||
usb__kb__set_key(true, KEYBOARD__Equal_Plus); usb__kb__send_report();
|
||||
usb__kb__set_key(false, KEYBOARD__Equal_Plus); usb__kb__send_report();
|
||||
|
||||
// send character
|
||||
key_functions__type_byte_hex( c_full >> 8 );
|
||||
key_functions__type_byte_hex( c_full & 0xFF );
|
||||
|
||||
// send end sequence
|
||||
usb__kb__set_key(false, KEYBOARD__LeftAlt); usb__kb__send_report();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Partial key descriptions for the "Boot Keyboard Required" keys (see
|
||||
* <../../../firmware/lib/usb/usage-page/keyboard.h>).
|
||||
*
|
||||
* This header is meant to be included, not as an interface to anything, but in
|
||||
* order to centralize a bit of code that would otherwise be duplicated by most
|
||||
* layouts.
|
||||
*
|
||||
* Prefixes: `keys__`, [none]
|
||||
*
|
||||
* Usage:
|
||||
* - `#define` `KEYS__DEFAULT` and `KEYS__SHIFTED` before `#include`ing.
|
||||
* - These macros should probably do what their names imply (i.e. define a
|
||||
* "default" or "shifted" key, respectively, whatever that means to the
|
||||
* implementing layout), but in a practical sense they can be used however
|
||||
* one wants. They're simply a really convenient way to generate almost
|
||||
* the same bit of code for a lot of (key_name, key_code) pairs.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEYS__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEYS__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "../../../firmware/lib/usb/usage-page/keyboard.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef KEYS__DEFAULT
|
||||
#error "You must define `KEYS__DEFAULT`"
|
||||
#endif
|
||||
#ifndef KEYS__SHIFTED
|
||||
#error "You must define `KEYS__SHIFTED`"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// letters
|
||||
KEYS__DEFAULT( a, KEYBOARD__a_A );
|
||||
KEYS__DEFAULT( b, KEYBOARD__b_B );
|
||||
KEYS__DEFAULT( c, KEYBOARD__c_C );
|
||||
KEYS__DEFAULT( d, KEYBOARD__d_D );
|
||||
KEYS__DEFAULT( e, KEYBOARD__e_E );
|
||||
KEYS__DEFAULT( f, KEYBOARD__f_F );
|
||||
KEYS__DEFAULT( g, KEYBOARD__g_G );
|
||||
KEYS__DEFAULT( h, KEYBOARD__h_H );
|
||||
KEYS__DEFAULT( i, KEYBOARD__i_I );
|
||||
KEYS__DEFAULT( j, KEYBOARD__j_J );
|
||||
KEYS__DEFAULT( k, KEYBOARD__k_K );
|
||||
KEYS__DEFAULT( l, KEYBOARD__l_L );
|
||||
KEYS__DEFAULT( m, KEYBOARD__m_M );
|
||||
KEYS__DEFAULT( n, KEYBOARD__n_N );
|
||||
KEYS__DEFAULT( o, KEYBOARD__o_O );
|
||||
KEYS__DEFAULT( p, KEYBOARD__p_P );
|
||||
KEYS__DEFAULT( q, KEYBOARD__q_Q );
|
||||
KEYS__DEFAULT( r, KEYBOARD__r_R );
|
||||
KEYS__DEFAULT( s, KEYBOARD__s_S );
|
||||
KEYS__DEFAULT( t, KEYBOARD__t_T );
|
||||
KEYS__DEFAULT( u, KEYBOARD__u_U );
|
||||
KEYS__DEFAULT( v, KEYBOARD__v_V );
|
||||
KEYS__DEFAULT( w, KEYBOARD__w_W );
|
||||
KEYS__DEFAULT( x, KEYBOARD__x_X );
|
||||
KEYS__DEFAULT( y, KEYBOARD__y_Y );
|
||||
KEYS__DEFAULT( z, KEYBOARD__z_Z );
|
||||
// --- (shifted) ---
|
||||
KEYS__SHIFTED( A, KEYBOARD__a_A );
|
||||
KEYS__SHIFTED( B, KEYBOARD__b_B );
|
||||
KEYS__SHIFTED( C, KEYBOARD__c_C );
|
||||
KEYS__SHIFTED( D, KEYBOARD__d_D );
|
||||
KEYS__SHIFTED( E, KEYBOARD__e_E );
|
||||
KEYS__SHIFTED( F, KEYBOARD__f_F );
|
||||
KEYS__SHIFTED( G, KEYBOARD__g_G );
|
||||
KEYS__SHIFTED( H, KEYBOARD__h_H );
|
||||
KEYS__SHIFTED( I, KEYBOARD__i_I );
|
||||
KEYS__SHIFTED( J, KEYBOARD__j_J );
|
||||
KEYS__SHIFTED( K, KEYBOARD__k_K );
|
||||
KEYS__SHIFTED( L, KEYBOARD__l_L );
|
||||
KEYS__SHIFTED( M, KEYBOARD__m_M );
|
||||
KEYS__SHIFTED( N, KEYBOARD__n_N );
|
||||
KEYS__SHIFTED( O, KEYBOARD__o_O );
|
||||
KEYS__SHIFTED( P, KEYBOARD__p_P );
|
||||
KEYS__SHIFTED( Q, KEYBOARD__q_Q );
|
||||
KEYS__SHIFTED( R, KEYBOARD__r_R );
|
||||
KEYS__SHIFTED( S, KEYBOARD__s_S );
|
||||
KEYS__SHIFTED( T, KEYBOARD__t_T );
|
||||
KEYS__SHIFTED( U, KEYBOARD__u_U );
|
||||
KEYS__SHIFTED( V, KEYBOARD__v_V );
|
||||
KEYS__SHIFTED( W, KEYBOARD__w_W );
|
||||
KEYS__SHIFTED( X, KEYBOARD__x_X );
|
||||
KEYS__SHIFTED( Y, KEYBOARD__y_Y );
|
||||
KEYS__SHIFTED( Z, KEYBOARD__z_Z );
|
||||
|
||||
// numbers
|
||||
KEYS__DEFAULT( 0, KEYBOARD__0_RightParenthesis );
|
||||
KEYS__DEFAULT( 1, KEYBOARD__1_Exclamation );
|
||||
KEYS__DEFAULT( 2, KEYBOARD__2_At );
|
||||
KEYS__DEFAULT( 3, KEYBOARD__3_Pound );
|
||||
KEYS__DEFAULT( 4, KEYBOARD__4_Dollar );
|
||||
KEYS__DEFAULT( 5, KEYBOARD__5_Percent );
|
||||
KEYS__DEFAULT( 6, KEYBOARD__6_Caret );
|
||||
KEYS__DEFAULT( 7, KEYBOARD__7_Ampersand );
|
||||
KEYS__DEFAULT( 8, KEYBOARD__8_Asterisk );
|
||||
KEYS__DEFAULT( 9, KEYBOARD__9_LeftParenthesis );
|
||||
// --- (shifted) ---
|
||||
KEYS__SHIFTED( parenR, KEYBOARD__0_RightParenthesis );
|
||||
KEYS__SHIFTED( exclam, KEYBOARD__1_Exclamation );
|
||||
KEYS__SHIFTED( at, KEYBOARD__2_At );
|
||||
KEYS__SHIFTED( pound, KEYBOARD__3_Pound );
|
||||
KEYS__SHIFTED( dollar, KEYBOARD__4_Dollar );
|
||||
KEYS__SHIFTED( percent, KEYBOARD__5_Percent );
|
||||
KEYS__SHIFTED( caret, KEYBOARD__6_Caret );
|
||||
KEYS__SHIFTED( amp, KEYBOARD__7_Ampersand );
|
||||
KEYS__SHIFTED( asterisk, KEYBOARD__8_Asterisk );
|
||||
KEYS__SHIFTED( parenL, KEYBOARD__9_LeftParenthesis );
|
||||
// --- (keypad) ---
|
||||
KEYS__DEFAULT( kp0, KEYPAD__0_Insert );
|
||||
KEYS__DEFAULT( kp1, KEYPAD__1_End );
|
||||
KEYS__DEFAULT( kp2, KEYPAD__2_DownArrow );
|
||||
KEYS__DEFAULT( kp3, KEYPAD__3_PageDown );
|
||||
KEYS__DEFAULT( kp4, KEYPAD__4_LeftArrow );
|
||||
KEYS__DEFAULT( kp5, KEYPAD__5 );
|
||||
KEYS__DEFAULT( kp6, KEYPAD__6_RightArrow );
|
||||
KEYS__DEFAULT( kp7, KEYPAD__7_Home );
|
||||
KEYS__DEFAULT( kp8, KEYPAD__8_UpArrow );
|
||||
KEYS__DEFAULT( kp9, KEYPAD__9_PageUp );
|
||||
|
||||
// function
|
||||
KEYS__DEFAULT( F1, KEYBOARD__F1 );
|
||||
KEYS__DEFAULT( F2, KEYBOARD__F2 );
|
||||
KEYS__DEFAULT( F3, KEYBOARD__F3 );
|
||||
KEYS__DEFAULT( F4, KEYBOARD__F4 );
|
||||
KEYS__DEFAULT( F5, KEYBOARD__F5 );
|
||||
KEYS__DEFAULT( F6, KEYBOARD__F6 );
|
||||
KEYS__DEFAULT( F7, KEYBOARD__F7 );
|
||||
KEYS__DEFAULT( F8, KEYBOARD__F8 );
|
||||
KEYS__DEFAULT( F9, KEYBOARD__F9 );
|
||||
KEYS__DEFAULT( F10, KEYBOARD__F10 );
|
||||
KEYS__DEFAULT( F11, KEYBOARD__F11 );
|
||||
KEYS__DEFAULT( F12, KEYBOARD__F12 );
|
||||
|
||||
// whitespace and symbols
|
||||
KEYS__DEFAULT( enter, KEYBOARD__ReturnEnter );
|
||||
KEYS__DEFAULT( space, KEYBOARD__Spacebar );
|
||||
KEYS__DEFAULT( tab, KEYBOARD__Tab );
|
||||
// ---
|
||||
KEYS__DEFAULT( bkslash, KEYBOARD__Backslash_Pipe );
|
||||
KEYS__DEFAULT( brktL, KEYBOARD__LeftBracket_LeftBrace );
|
||||
KEYS__DEFAULT( brktR, KEYBOARD__RightBracket_RightBrace );
|
||||
KEYS__DEFAULT( comma, KEYBOARD__Comma_LessThan );
|
||||
KEYS__DEFAULT( dash, KEYBOARD__Dash_Underscore );
|
||||
KEYS__DEFAULT( equal, KEYBOARD__Equal_Plus );
|
||||
KEYS__DEFAULT( grave, KEYBOARD__GraveAccent_Tilde );
|
||||
KEYS__DEFAULT( period, KEYBOARD__Period_GreaterThan );
|
||||
KEYS__DEFAULT( quote, KEYBOARD__SingleQuote_DoubleQuote );
|
||||
KEYS__DEFAULT( semicol, KEYBOARD__Semicolon_Colon );
|
||||
KEYS__DEFAULT( slash, KEYBOARD__Slash_Question );
|
||||
// --- (shifted) ---
|
||||
KEYS__SHIFTED( pipe, KEYBOARD__Backslash_Pipe );
|
||||
KEYS__SHIFTED( braceL, KEYBOARD__LeftBracket_LeftBrace );
|
||||
KEYS__SHIFTED( braceR, KEYBOARD__RightBracket_RightBrace );
|
||||
KEYS__SHIFTED( lessThan, KEYBOARD__Comma_LessThan );
|
||||
KEYS__SHIFTED( undersc, KEYBOARD__Dash_Underscore );
|
||||
KEYS__SHIFTED( plus, KEYBOARD__Equal_Plus );
|
||||
KEYS__SHIFTED( tilde, KEYBOARD__GraveAccent_Tilde );
|
||||
KEYS__SHIFTED( grtrThan, KEYBOARD__Period_GreaterThan );
|
||||
KEYS__SHIFTED( dblQuote, KEYBOARD__SingleQuote_DoubleQuote );
|
||||
KEYS__SHIFTED( colon, KEYBOARD__Semicolon_Colon );
|
||||
KEYS__SHIFTED( question, KEYBOARD__Slash_Question );
|
||||
// --- (keypad) ---
|
||||
KEYS__DEFAULT( kpEnter, KEYPAD__Enter );
|
||||
// ---
|
||||
KEYS__DEFAULT( kpDec, KEYPAD__Period_Delete );
|
||||
// ---
|
||||
KEYS__DEFAULT( kpAdd, KEYPAD__Plus );
|
||||
KEYS__DEFAULT( kpSub, KEYPAD__Minus );
|
||||
KEYS__DEFAULT( kpMul, KEYPAD__Asterisk );
|
||||
KEYS__DEFAULT( kpDiv, KEYPAD__Slash );
|
||||
|
||||
// text control
|
||||
KEYS__DEFAULT( arrowU, KEYBOARD__UpArrow );
|
||||
KEYS__DEFAULT( arrowD, KEYBOARD__DownArrow );
|
||||
KEYS__DEFAULT( arrowL, KEYBOARD__LeftArrow );
|
||||
KEYS__DEFAULT( arrowR, KEYBOARD__RightArrow );
|
||||
KEYS__DEFAULT( bs, KEYBOARD__DeleteBackspace );
|
||||
KEYS__DEFAULT( del, KEYBOARD__DeleteForward );
|
||||
KEYS__DEFAULT( end, KEYBOARD__End );
|
||||
KEYS__DEFAULT( esc, KEYBOARD__Escape );
|
||||
KEYS__DEFAULT( home, KEYBOARD__Home );
|
||||
KEYS__DEFAULT( ins, KEYBOARD__Insert );
|
||||
KEYS__DEFAULT( pageU, KEYBOARD__PageUp );
|
||||
KEYS__DEFAULT( pageD, KEYBOARD__PageDown );
|
||||
|
||||
// modifier
|
||||
KEYS__DEFAULT( altR, KEYBOARD__RightAlt );
|
||||
KEYS__DEFAULT( altL, KEYBOARD__LeftAlt );
|
||||
KEYS__DEFAULT( ctrlL, KEYBOARD__LeftControl );
|
||||
KEYS__DEFAULT( ctrlR, KEYBOARD__RightControl );
|
||||
KEYS__DEFAULT( guiL, KEYBOARD__LeftGUI );
|
||||
KEYS__DEFAULT( guiR, KEYBOARD__RightGUI );
|
||||
KEYS__DEFAULT( shiftL, KEYBOARD__LeftShift );
|
||||
KEYS__DEFAULT( shiftR, KEYBOARD__RightShift );
|
||||
|
||||
// lock
|
||||
KEYS__DEFAULT( caps, KEYBOARD__CapsLock );
|
||||
KEYS__DEFAULT( scrl, KEYBOARD__ScrollLock );
|
||||
// --- (keypad) ---
|
||||
KEYS__DEFAULT( num, KEYPAD__NumLock_Clear );
|
||||
|
||||
// special function
|
||||
KEYS__DEFAULT( app, KEYBOARD__Application );
|
||||
KEYS__DEFAULT( pause, KEYBOARD__Pause );
|
||||
KEYS__DEFAULT( prScr, KEYBOARD__PrintScreen );
|
||||
|
||||
// international and language
|
||||
KEYS__DEFAULT( nonUSBkslash, KEYBOARD__NonUS_Backslash_Pipe );
|
||||
KEYS__DEFAULT( nonUSPound, KEYBOARD__NonUS_Pound_Tilde );
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__KEYS__H
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Layer-stack interface
|
||||
*
|
||||
* Prefix: `layer_stack__`
|
||||
*
|
||||
* This file is meant to be included and used by the keyboard layout
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__LIB__LAYOUT__LAYER_STACK__H
|
||||
#define ERGODOX_FIRMWARE__LIB__LAYOUT__LAYER_STACK__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t layer_stack__peek (uint8_t offset);
|
||||
uint8_t layer_stack__push ( uint8_t offset,
|
||||
uint8_t layer_id,
|
||||
uint8_t layer_number );
|
||||
uint8_t layer_stack__pop_id (uint8_t layer_id);
|
||||
uint8_t layer_stack__find_id (uint8_t layer_id);
|
||||
uint8_t layer_stack__size (void);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__LIB__LAYOUT__LAYER_STACK__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
// === layer_stack__peek() ===
|
||||
/** functions/layer_stack__peek/description
|
||||
* Return the layer-number of the `offset`th element of the layer-stack
|
||||
*
|
||||
* Arguments:
|
||||
* - `offset`: the `offset` of the element to return (with the top element
|
||||
* being `offset = 0`)
|
||||
*
|
||||
* Returns:
|
||||
* - success: the layer-number of the `offset`th element (which may be `0`), or
|
||||
* `0` if `offset` was out of bounds
|
||||
*/
|
||||
|
||||
// === layer_stack__push() ===
|
||||
/** functions/layer_stack__push/description
|
||||
* Push the given element into the layer-stack on top of the element with the
|
||||
* given `offset` (or update the element with the given layer-id, if it already
|
||||
* exists)
|
||||
*
|
||||
* Arguments:
|
||||
* - `offset`: the `offset` of the element on top of which to push the given
|
||||
* element (with the top element being `offset = 0`)
|
||||
* - `layer_id`: the layer-id of the layer to push
|
||||
* - `layer_number`: the layer-number of the layer to push (ignored if not
|
||||
* pushing a new element)
|
||||
*
|
||||
* Returns:
|
||||
* - success: the `offset` of the element that was pushed (or updated)
|
||||
* - failure: `UINT8_MAX`
|
||||
*
|
||||
* Notes:
|
||||
* - If the given layer-id is not present in the stack, and a new element is
|
||||
* created and pushed on top of the element with the given `offset`, the new
|
||||
* element will now be at position `offset`, since `offset` is the distance
|
||||
* from the top of the stack
|
||||
* - If the given layer-id is present in the stack, the element with that
|
||||
* layer-id is updated; this preserves the expectation that all layer-id's in
|
||||
* the stack will be unique
|
||||
*/
|
||||
|
||||
// === layer_stack__pop_id() ===
|
||||
/** functions/layer_stack__pop_id/description
|
||||
* Pop the given element (by ID) out from the layer-stack
|
||||
*
|
||||
* Arguments:
|
||||
* - `layer_id`: the layer-id of the layer to pop (remove)
|
||||
*
|
||||
* Returns:
|
||||
* - success: the `offset` of the element that was popped (removed)
|
||||
* - failure: `UINT8_MAX`
|
||||
*/
|
||||
|
||||
// === layer_stack__find_id() ===
|
||||
/** functions/layer_stack__find_id/description
|
||||
* Find an element element by ID
|
||||
*
|
||||
* Arguments:
|
||||
* - `layer_id`: the layer-id of the layer to find
|
||||
*
|
||||
* Returns:
|
||||
* - success: the `offset` of the element that was found
|
||||
* - failure: `UINT8_MAX`
|
||||
*/
|
||||
|
||||
// === layer_stack__size ===
|
||||
/** functions/layer_stack__size/description
|
||||
* Return the current size (height) of the layer-stack
|
||||
*
|
||||
* Returns:
|
||||
* - success: the current size (height) of the layer-stack (`0` if empty)
|
||||
*/
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the layer-stack defined in "../layer-stack.h"
|
||||
*
|
||||
* Notes:
|
||||
* - This would be relatively trivial to implement using linked lists; but
|
||||
* while I'm sure it takes more PROGMEM to do it this way, it uses less SRAM,
|
||||
* and is also probably faster on average. I also think it's a nice example
|
||||
* of how to resize arrays in C based on demand. I tried to generalize the
|
||||
* functionality a little into a "flex-array" for "lib/data-types", in order
|
||||
* to make the same technique easier to use elsewhere. It turns out though
|
||||
* that doing this in a type-agnostic way leads to passing an inordinate
|
||||
* amount of information on the call stack for each function call (array
|
||||
* size, element size, ..., ...), which for small stacks defeats the purpose
|
||||
* (of saving SRAM), and is kind of inefficient and ugly anyway. But
|
||||
* sacrificing a bit of PROGMEM (and programmer time) to reimplement this
|
||||
* everywhere it's used seems worth it, since the amount of core code that
|
||||
* would be able to be generalized out anyway is relatively small and
|
||||
* straightforward.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "../layer-stack.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** macros/MIN_UNUSED/description
|
||||
* The minimum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MIN_UNUSED 0
|
||||
|
||||
/** macros/MAX_UNUSED/description
|
||||
* The maximum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MAX_UNUSED 4
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** types/element_t/description
|
||||
* The type of one layer-element in our layer-stack
|
||||
*
|
||||
* Struct members:
|
||||
* - `id`: The layer-id of this element
|
||||
* - `number`: The layer-number of this element
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
uint8_t number;
|
||||
} element_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** variables/stack/description
|
||||
* To hold the layer-stack and directly related metadata
|
||||
*
|
||||
* Struct members:
|
||||
* - `allocated`: The number of positions allocated
|
||||
* - `filled`: The number of positions filled
|
||||
* - `data`: A pointer to the array of layer-elements
|
||||
*
|
||||
* Notes:
|
||||
* - The maximum value of `uint8_t filled` is `UINT8_MAX` indicating a stack
|
||||
* with `UINT8_MAX` elements. This means that any valid offset or index will
|
||||
* be between `0` and `UINT8_MAX-1` inclusive, and that `UINT8_MAX` will
|
||||
* therefore always be an invalid value for an offset or index.
|
||||
*/
|
||||
static struct {
|
||||
uint8_t allocated;
|
||||
uint8_t filled;
|
||||
element_t * data;
|
||||
} stack;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/resize_stack/description
|
||||
* Resize the stack, so that the number of unused elements is between
|
||||
* `MIN_UNUSED` and `MAX_UNUSED`, inclusive
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We use a signed type for `unused` in case `filled` is greater than
|
||||
* `allocated`, as may happen when pushing a new element onto the stack.
|
||||
* This number should normally be much smaller than either `filled` or
|
||||
* `allocated`, so we don't need to worry about the maximum value being 2^7-1
|
||||
* instead of 2^8-1.
|
||||
* - The only time `realloc()` should fail is if we are trying to grow the
|
||||
* stack. See [the documentation]
|
||||
* (http://www.nongnu.org/avr-libc/user-manual/malloc.html)
|
||||
* for `malloc()` in avr-libc.
|
||||
*/
|
||||
static uint8_t resize_stack(void) {
|
||||
int8_t unused = stack.allocated - stack.filled;
|
||||
|
||||
if (MIN_UNUSED <= unused && unused <= MAX_UNUSED)
|
||||
return 0; // nothing to do
|
||||
|
||||
uint8_t new_allocated;
|
||||
if (UINT8_MAX >= stack.filled + (MIN_UNUSED+MAX_UNUSED)/2)
|
||||
new_allocated = stack.filled + (MIN_UNUSED+MAX_UNUSED)/2;
|
||||
else if (UINT8_MAX >= stack.filled + MIN_UNUSED)
|
||||
new_allocated = UINT8_MAX;
|
||||
else
|
||||
return 1; // unable to count the required number of elements
|
||||
|
||||
void * new_data = realloc( stack.data, sizeof(element_t) * new_allocated );
|
||||
if (!new_data)
|
||||
return 1; // error: `realloc()` failed (unable to grow stack)
|
||||
|
||||
stack.allocated = new_allocated;
|
||||
stack.data = new_data;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t layer_stack__peek(uint8_t offset) {
|
||||
if (offset > stack.filled-1)
|
||||
return 0; // default
|
||||
|
||||
return stack.data[stack.filled-1-offset].number;
|
||||
}
|
||||
|
||||
uint8_t layer_stack__push( uint8_t offset,
|
||||
uint8_t layer_id,
|
||||
uint8_t layer_number ) {
|
||||
|
||||
// if an element with the given layer-id already exists
|
||||
{
|
||||
uint8_t old_offset = layer_stack__find_id(layer_id);
|
||||
if (old_offset != UINT8_MAX) {
|
||||
stack.data[stack.filled-1-old_offset].number = layer_number;
|
||||
return old_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// add an element
|
||||
if (stack.filled == UINT8_MAX)
|
||||
return UINT8_MAX; // error: stack already full
|
||||
stack.filled++;
|
||||
uint8_t index = stack.filled-1-offset;
|
||||
if (index > stack.filled-1 || resize_stack()) {
|
||||
stack.filled--;
|
||||
return UINT8_MAX; // error: index out of bounds, or resize failed
|
||||
}
|
||||
|
||||
// shift up
|
||||
// - start with the top element (which is currently uninitialized), and
|
||||
// copy the element below it up
|
||||
// - continue until we copy the element currently at `index` (which will
|
||||
// then be duplicated at `index` and `index-1`)
|
||||
// - if the top element is currently at `index`, this will do nothing
|
||||
for (uint8_t i = stack.filled-1; i > index; i--)
|
||||
stack.data[i] = stack.data[i-1];
|
||||
|
||||
// set values
|
||||
stack.data[index].id = layer_id;
|
||||
stack.data[index].number = layer_number;
|
||||
|
||||
return offset; // success
|
||||
}
|
||||
|
||||
uint8_t layer_stack__pop_id(uint8_t layer_id) {
|
||||
uint8_t offset = layer_stack__find_id(layer_id);
|
||||
|
||||
if (offset == UINT8_MAX)
|
||||
return UINT8_MAX; // error: no element with given layer-id
|
||||
|
||||
uint8_t index = stack.filled-1-offset;
|
||||
|
||||
// shift down
|
||||
// - start with the element at `index`, and copy the element above it down
|
||||
// - continue until we copy the top element (which will then be duplicated
|
||||
// at indices `stack.filled-2` and `stack.filled-1`)
|
||||
// - if the top element is at `index`, this will do nothing
|
||||
for (uint8_t i = index; i < stack.filled-1; i++)
|
||||
stack.data[i] = stack.data[i+1];
|
||||
|
||||
// remove an element
|
||||
stack.filled--;
|
||||
resize_stack(); // we're shrinking the stack, so this should never fail
|
||||
|
||||
return offset; // success
|
||||
}
|
||||
|
||||
uint8_t layer_stack__find_id(uint8_t layer_id) {
|
||||
for (uint8_t i = 0; i < stack.filled; i++)
|
||||
if (stack.data[i].id == layer_id)
|
||||
return stack.filled-1-i; // offset
|
||||
return UINT8_MAX; // error: no element with given layer-id
|
||||
}
|
||||
|
||||
uint8_t layer_stack__size(void) {
|
||||
return stack.filled;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# layer-stack options
|
||||
#
|
||||
# This file is meant to be included by the using '.../options.mk'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/*.c)
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Timer interface
|
||||
*
|
||||
* Prefixes: `timer__`, `timer___`
|
||||
*
|
||||
*
|
||||
* Usage notes:
|
||||
* - A "tick" is an ill-defined unit of time. It may represent any occurrence,
|
||||
* even a randomly timed one; but it makes the most sense for it to count
|
||||
* something that occurs predictably, like the passing of a millisecond, or
|
||||
* the completion of a scan cycle.
|
||||
*
|
||||
* - To "tick" (as a verb) is to denote the passage of a "tick" of time by
|
||||
* performing the actions corresponding thereto (i.e. incrementing the
|
||||
* counter and running any scheduled events).
|
||||
*
|
||||
* - A "timer" is a collection of related `...get...()`, `...schedule...()`,
|
||||
* and `...tick...()` functions, all dealing with the same (private)
|
||||
* variables.
|
||||
*
|
||||
* - Table, for milliseconds
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
* number highest value in in in in
|
||||
* of bits (milliseconds) seconds minutes hours days
|
||||
* --------- ---------------- ----------- --------- -------- ------
|
||||
* 8 255 0.3 0.0 0.0 0.0
|
||||
* 16 65535 65.5 1.1 0.0 0.0
|
||||
* 32 4294967295 4294967.3 71582.8 1193.0 49.7
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* note: 32-bit values given for reference only
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__LIB__TIMER__H
|
||||
#define ERGODOX_FIRMWARE__LIB__TIMER__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
uint8_t timer__init (void);
|
||||
|
||||
uint16_t timer__get_cycles (void);
|
||||
uint16_t timer__get_keypresses (void);
|
||||
uint16_t timer__get_milliseconds (void);
|
||||
|
||||
uint8_t timer__schedule_cycles (uint16_t ticks, void(*function)(void));
|
||||
uint8_t timer__schedule_keypresses (uint16_t ticks, void(*function)(void));
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
void timer___tick_cycles (void);
|
||||
void timer___tick_keypresses (void);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__LIB__TIMER__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === timer__init() ===
|
||||
/** functions/timer__init/description
|
||||
* Initialize the timer
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Should be called exactly once by `main()` before entering the run loop.
|
||||
*/
|
||||
|
||||
// === (group) get...() ===
|
||||
/** functions/(group) get.../description
|
||||
* Return the number of "ticks" since the given timer was initialized
|
||||
* (mod 2^16)
|
||||
*
|
||||
* Members:
|
||||
* - `timer__get_cycles`: Counts the number of scan cycles
|
||||
* - `timer__get_keypresses`: Counts the number of applicable key presses
|
||||
* - `timer__get_milliseconds`: Counts real time milliseconds
|
||||
*
|
||||
* Returns:
|
||||
* - success: The number of "ticks" since the timer was initialized (mod 2^16)
|
||||
*
|
||||
*
|
||||
* Usage notes:
|
||||
*
|
||||
* - It's unnecessary to keep 16-bit resolution when storing the value returned
|
||||
* by a get function if you don't need it. Use variables of the smallest
|
||||
* type that can (*always*) hold the amount of time you'll be dealing with.
|
||||
*
|
||||
* - Use `end_time - start_time` for determining time difference. Since the
|
||||
* returned values are unsigned (and you should be storing them in unsigned
|
||||
* variables as well) this will work across overflows, for up to the maximum
|
||||
* amount of ticks representable by the type you're using. (See [this
|
||||
* answer] (http://stackoverflow.com/a/50632) on <http://stackoverflow.com/>
|
||||
* if you're curious as to why this works across overflows.)
|
||||
*
|
||||
*
|
||||
* Warnings:
|
||||
*
|
||||
* - Do not cast the return value of a get function directly. Instead, store
|
||||
* the return value in a smaller variable
|
||||
*
|
||||
* uint8_t start_time = timer__get_cycles()
|
||||
*
|
||||
* or cast the expression as a whole
|
||||
*
|
||||
* (uint8_t)( timer__get_cycles() - start_time )
|
||||
*
|
||||
* Casting directly within the end condition check of a `while` or `for` loop
|
||||
* does not produce the desired behavior. I don't know assembly well enough
|
||||
* to figure out what it's *actually* doing, but the code generated is longer
|
||||
* than when casting as shown, and it doesn't appear to be throwing away all
|
||||
* but the least significant 8 bits before calculation, as I'd expect.
|
||||
* Casting directly when assigning to a variable doesn't appear to do
|
||||
* anything, and casting directly outside end condition checks seems to work
|
||||
* at least sometimes, but those casts may as well be avoided for
|
||||
* consistency.
|
||||
*
|
||||
* - Be careful when subtracting (or doing anything, really, with multiple)
|
||||
* times, to make sure that if the values are not of the same type, the
|
||||
* expression is cast to the smallest type you're using. If `start_time` is
|
||||
* a `uint8_t` value,
|
||||
*
|
||||
* (uint8_t)( timer__get_milliseconds() - start_time )
|
||||
*
|
||||
* has a very different meaning than
|
||||
*
|
||||
* timer__get_milliseconds() - start_time
|
||||
*
|
||||
* except within the first 2^8 milliseconds of the timer being initialized.
|
||||
*/
|
||||
|
||||
// === (group) schedule ===
|
||||
/** functions/(group) schedule/description
|
||||
* Schedule `function` to run in the given number of "ticks"
|
||||
*
|
||||
* Members:
|
||||
* - `timer__schedule_cycles`
|
||||
* - `timer__schedule_keypresses`
|
||||
*
|
||||
* Arguments:
|
||||
* - `ticks`: The number of ticks to wait
|
||||
* - `function`: A pointer to the function to run
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
*
|
||||
* Usage notes:
|
||||
*
|
||||
* - If a function needs a longer wait time than is possible with a 16-bit
|
||||
* resolution counter, it can repeatedly schedule itself to run in, say, 1
|
||||
* minute (= 1000*60/5 cycles, assuming cycles take on average 5
|
||||
* milliseconds) (using the cycles timer), increment a counter each time, and
|
||||
* then only execute its body code after, say, 5 calls (for a 5 minute
|
||||
* delay).
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
// === timer___tick_cycles() ===
|
||||
/** functions/timer___tick_cycles/description
|
||||
* Increment the counter for the number of cycles, and perform scheduled tasks
|
||||
*
|
||||
* Meant to be used only by `main()`
|
||||
*/
|
||||
|
||||
// === timer___tick_keypresses() ===
|
||||
/** functions/timer___tick_keypresses/description
|
||||
* Increment the counter for the number of key-presses, and perform scheduled
|
||||
* tasks
|
||||
*
|
||||
* Meant to be used only by `kb__layout__exec_key()`
|
||||
*/
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the device specific portion of the timer interface defined in
|
||||
* ".../firmware/lib/timer.h" for the ATMega32U4
|
||||
*
|
||||
* See the accompanying '.md' file for further documentation.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include "../../timer.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if F_CPU != 16000000
|
||||
#error "Expecting different CPU frequency"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static struct {
|
||||
volatile uint16_t counter;
|
||||
} milliseconds;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t timer__init(void) {
|
||||
OCR0A = 250; // (ticks (of hardware timer) per millisecond)
|
||||
TCCR0A = 0b00000010; // (configure Timer/Counter 0)
|
||||
TCCR0B = 0b00000011; // (configure Timer/Counter 0)
|
||||
|
||||
TIMSK0 = 0b00000010; // (enable interrupt vector)
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
uint16_t timer__get_milliseconds(void) {
|
||||
return milliseconds.counter;
|
||||
}
|
||||
|
||||
ISR(TIMER0_COMPA_vect) {
|
||||
milliseconds.counter++;
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
## 8-bit Timer/Counter Register Description (see datasheet section 13.8)
|
||||
|
||||
TCCR0A : Timer/Counter 0 Control Register A
|
||||
.---------------------------------------------------------------.
|
||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|---------------------------------------------------------------|
|
||||
| COM0A | COM0A | COM0B | COM0B | Reserved | WGM1 | WGM0 |
|
||||
'---------------------------------------------------------------'
|
||||
|
||||
TCCR0B : Timer/Counter 0 Control Register B
|
||||
.---------------------------------------------------------------.
|
||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|---------------------------------------------------------------|
|
||||
| FOC0A | FOC0B | Reserved | WGM02 | CS02 | CS01 | CS00 |
|
||||
'---------------------------------------------------------------'
|
||||
|
||||
* We want:
|
||||
* `COM0A` = `0b00` : Normal port operation, `OC0A` disconnected
|
||||
* `COM0B` = `0b00` : Normal port operation, `OC0B` disconnected
|
||||
* `WGM` = `0b010` : CTC (see section 13.6.2 "Clear Timer on Compare
|
||||
Match (CTC) Mode")
|
||||
* `CS` = `0b011` : clk_i/o / 64 (from prescaler)
|
||||
|
||||
|
||||
TIMSK0 : Timer/Counter 0 Interrupt Mask Register
|
||||
.-----------------------------------------------------------------.
|
||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|-----------------------------------------------------------------|
|
||||
| Reserved | OCIE0B | OCIE0A | TOIE0 |
|
||||
'-----------------------------------------------------------------'
|
||||
|
||||
* We want:
|
||||
* `OCIE0A` = `0b1` : enable the Timer/Counter 0 Compare Match A
|
||||
interrupt
|
||||
|
||||
|
||||
* We also want to set `OCR0A` (Output Compare Register A) to `250` for the
|
||||
number of "ticks per millisecond" (see below)
|
||||
|
||||
* Since we're using CTC mode with `OCIE0A` enabled, we will be using the
|
||||
`TIMER0_COMPA_vect` interrupt vector (see
|
||||
[the avr-libc documentation on interrupts]
|
||||
(http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html)).
|
||||
|
||||
|
||||
## Other Notes
|
||||
|
||||
* References:
|
||||
|
||||
* [Newbie's Guide to AVR Timers]
|
||||
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106)
|
||||
: tutorial lby Deana Camera
|
||||
|
||||
|
||||
* For a CPU frequency of 16 MHz:
|
||||
|
||||
--------------------------------------------
|
||||
prescale value ticks per millisecond
|
||||
---------------- -----------------------
|
||||
1 16000
|
||||
8 2000
|
||||
64 250
|
||||
256 62.5
|
||||
1024 15.625
|
||||
--------------------------------------------
|
||||
|
||||
* So if we set the prescaler to 64, we can just barely get to a millisecond
|
||||
within the range of an 8-bit counter (2^8-1 = 255, and we need 250
|
||||
ticks).
|
||||
|
||||
* Setting `COM0A` and `COM0B` to `0` (or, rather, leaving them that way) is
|
||||
very important: other things may be operating on those pins (`PB7` and `PD0`
|
||||
respectively), and we don't want normal port functionality to be overridden.
|
||||
|
||||
|
||||
* Abbreviations:
|
||||
* `COM`: Compare
|
||||
* `CS`: Clock Select
|
||||
* `FOC`: Force Output Compare
|
||||
* `OCIE`: Output Compare Interrupt Enable
|
||||
* `OCR`: Output Compare Register
|
||||
* `TCCR: Timer/Counter Control Register
|
||||
* `TCCR`: Timer/Counter Control Register
|
||||
* `TIMSK`: Timer/Counter Interrupt Mask Register
|
||||
* `TOIE`: Timer/Counter Overflow Interrupt Enable
|
||||
* `WGM`: Waveform Generation Module
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# timer options
|
||||
#
|
||||
# This file is meant to be included by '.../firmware/makefile'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/*.c)
|
||||
SRC += $(wildcard $(CURDIR)/device/$(MCU).c)
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the device agnostic portion of the timer interface defined in
|
||||
* ".../firmware/lib/timer.h"
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "../timer.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
/** macros/MIN_UNUSED/description
|
||||
* The minimum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MIN_UNUSED 0
|
||||
|
||||
/** macros/MAX_UNUSED/description
|
||||
* The maximum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MAX_UNUSED 4
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
|
||||
/** types/event_t/description
|
||||
* To hold an event that should be run at some point in the future
|
||||
*
|
||||
* Struct members:
|
||||
* - `ticks`: The number of units of time to wait until running this event
|
||||
* - `function`: The event (the function to run)
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t ticks;
|
||||
void (*function)(void);
|
||||
} event_t;
|
||||
|
||||
/** types/list_t/description
|
||||
* To hold a list of events, with corresponding metadata
|
||||
*
|
||||
* Struct members:
|
||||
* - `allocated`: The number of positions allocated
|
||||
* - `filled`: The number of positions filled
|
||||
* - `data`: A list of events to run at some point in the future
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t allocated;
|
||||
uint8_t filled;
|
||||
event_t * data;
|
||||
} list_t;
|
||||
|
||||
/** types/timer_t/description
|
||||
* To hold all the variables needed by a timer
|
||||
*
|
||||
* Struct members:
|
||||
* - `counter`: How many "ticks" of this timer have occurred since it was
|
||||
* initialized (mod 2^16)
|
||||
* - `scheduled`: The list of events to be eventually run by this timer, with
|
||||
* corresponding metadata
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t counter;
|
||||
list_t scheduled;
|
||||
} timer_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variable manipulator functions ---------------------------------------------
|
||||
|
||||
/** functions/resize/description
|
||||
* Resize `list->data`, so that the number of unused elements is between
|
||||
* `MIN_UNUSED` and `MAX_UNUSED`, inclusive
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We use a signed type for `unused` in case `filled` is greater than
|
||||
* `allocated`, as may happen when appending a new event. This number should
|
||||
* normally be much smaller than either `filled` or `allocated`, so there is
|
||||
* no need to worry about using a signed type (with a smaller maximum value)
|
||||
* rather than an unsigned one.
|
||||
* - The only time `realloc()` should fail is if we are trying to grow the
|
||||
* stack. See [the documentation]
|
||||
* (http://www.nongnu.org/avr-libc/user-manual/malloc.html)
|
||||
* for `malloc()` in avr-libc.
|
||||
*/
|
||||
static uint8_t resize(list_t * list) {
|
||||
int8_t unused = list->allocated - list->filled;
|
||||
|
||||
if (MIN_UNUSED <= unused && unused <= MAX_UNUSED)
|
||||
return 0; // nothing to do
|
||||
|
||||
uint8_t new_allocated;
|
||||
if (UINT8_MAX >= list->filled + (MIN_UNUSED+MAX_UNUSED)/2)
|
||||
new_allocated = list->filled + (MIN_UNUSED+MAX_UNUSED)/2;
|
||||
else if (UINT8_MAX >= list->filled + MIN_UNUSED)
|
||||
new_allocated = UINT8_MAX;
|
||||
else
|
||||
return 1; // unable to count the required number of elements
|
||||
|
||||
void * new_data = realloc( list->data, sizeof(event_t) * new_allocated );
|
||||
if (!new_data)
|
||||
return 1; // error: `realloc()` failed (unable to grow list)
|
||||
|
||||
list->allocated = new_allocated;
|
||||
list->data = new_data;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** functions/append/description
|
||||
* Append a new event containing the passed information to `list->data`
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
* - `ticks`: The value to put into the event's corresponding field
|
||||
* - `function`: The value to put into the event's corresponding field
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
static uint8_t append(list_t * list, uint16_t ticks, void(*function)(void)) {
|
||||
if (!function)
|
||||
return 0; // nothing to do
|
||||
|
||||
if (list->filled == UINT8_MAX)
|
||||
return 1; // error: list already full
|
||||
list->filled++;
|
||||
if(resize(list)) {
|
||||
list->filled--;
|
||||
return 1; // resize failed
|
||||
}
|
||||
|
||||
list->data[list->filled - 1].ticks = ticks;
|
||||
list->data[list->filled - 1].function = function;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** function/pop/description
|
||||
* Remove the event at `index` from `list->data`
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
* - `index`: The index of the event to pop
|
||||
*/
|
||||
static void pop(list_t * list, uint8_t index) {
|
||||
// shift down
|
||||
// - start with the element at `index`, and copy the element above it down
|
||||
// - continue until we copy the top element (which will then be duplicated
|
||||
// at indices `list->filled - 2` and `list->filled - 1`)
|
||||
// - if the top element is at `index` (or `index` is out of bounds), this
|
||||
// will do nothing
|
||||
for (uint8_t i = index; i < list->filled - 1; i++)
|
||||
list->data[i] = list->data[i+1];
|
||||
|
||||
// remove an element
|
||||
list->filled--;
|
||||
resize(list); // we're shrinking the list, so this should never fail
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables ------------------------------------------------------------------
|
||||
|
||||
static timer_t cycles;
|
||||
static timer_t keypresses;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// front end functions --------------------------------------------------------
|
||||
|
||||
uint16_t timer__get_cycles(void) {
|
||||
return cycles.counter;
|
||||
}
|
||||
|
||||
uint16_t timer__get_keypresses(void) {
|
||||
return keypresses.counter;
|
||||
}
|
||||
|
||||
uint8_t timer__schedule_cycles(uint16_t ticks, void(*function)(void)) {
|
||||
return append(&cycles.scheduled, ticks, function);
|
||||
}
|
||||
|
||||
uint8_t timer__schedule_keypresses(uint16_t ticks, void(*function)(void)) {
|
||||
return append(&keypresses.scheduled, ticks, function);
|
||||
}
|
||||
|
||||
void timer___tick_cycles(void) {
|
||||
cycles.counter++;
|
||||
for (uint8_t i = 0; i < cycles.scheduled.filled; i++) {
|
||||
if (cycles.scheduled.data[i].ticks == 0) {
|
||||
(*cycles.scheduled.data[i].function)();
|
||||
pop(&cycles.scheduled, i);
|
||||
} else {
|
||||
cycles.scheduled.data[i].ticks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timer___tick_keypresses(void) {
|
||||
keypresses.counter++;
|
||||
for (uint8_t i = 0; i < keypresses.scheduled.filled; i++) {
|
||||
if (keypresses.scheduled.data[i].ticks == 0) {
|
||||
(*keypresses.scheduled.data[i].function)();
|
||||
pop(&keypresses.scheduled, i);
|
||||
} else {
|
||||
keypresses.scheduled.data[i].ticks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* The TWI (aka I²C) interface
|
||||
*
|
||||
* Prefix: `twi__`
|
||||
*
|
||||
* Functions are named after the basic TWI actions; see general documentation
|
||||
* on TWI for more information.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__TWI__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__TWI__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef OPT__TWI__FREQUENCY
|
||||
#error "OPT__TWI__FREQUENCY not defined"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void twi__init (void);
|
||||
uint8_t twi__start (void);
|
||||
void twi__stop (void);
|
||||
uint8_t twi__send (uint8_t data);
|
||||
uint8_t twi__read (uint8_t * data);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__TWI__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === OPT__TWI__FREQUENCY ===
|
||||
/** macros/OPT__TWI__FREQUENCY/description
|
||||
* The TWI Frequency, in Hz.
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === twi__init() ===
|
||||
/** functions/twi__init/description
|
||||
* Initialize TWI
|
||||
*
|
||||
* Notes:
|
||||
* - Should be called exactly once during keyboard startup
|
||||
*/
|
||||
|
||||
// === twi__start() ===
|
||||
/** functions/twi__start/description
|
||||
* Send a TWI Start signal
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: The TWI status code
|
||||
*/
|
||||
|
||||
// === twi__stop() ===
|
||||
/** functions/twi__stop/description
|
||||
* Send a TWI Stop signal
|
||||
*/
|
||||
|
||||
// === twi__send() ===
|
||||
/** functions/twi__send/description
|
||||
* Send `data` (which may actually be an instruction or a device or register
|
||||
* address) over the bus
|
||||
*
|
||||
* Arguments:
|
||||
* - `data`: The data to send
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: The TWI status code
|
||||
*/
|
||||
|
||||
// === twi__read() ===
|
||||
/** functions/twi__read/description
|
||||
* Read incoming data
|
||||
*
|
||||
* Arguments:
|
||||
* - `data`: A pointer to the location to read data to
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: The TWI status code
|
||||
*/
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Very simple Teensy 2.0 TWI library : code
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Very simple implementation for the Teensy 2.0 (ATmega32U4)
|
||||
*
|
||||
* - This is mostly straight from the datasheet, section 20.6.6, figure 20-11
|
||||
* (the code example in C), and section 20.8.1, figure 20-12
|
||||
|
@ -9,39 +15,40 @@
|
|||
* Some other (more complete) TWI libraries for the Teensy 2.0 (and other Atmel
|
||||
* processors):
|
||||
* - [i2cmaster] (http://homepage.hispeed.ch/peterfleury/i2cmaster.zip)
|
||||
* - written by [peter-fleury] (http://homepage.hispeed.ch/peterfleury/)
|
||||
* - written by [peter-fleury] (http://homepage.hispeed.ch/peterfleury/)
|
||||
* - [the arduino twi library]
|
||||
* (https://github.com/arduino/Arduino/tree/master/libraries/Wire/utility)
|
||||
* - look for an older version if you need one that doesn't depend on all the
|
||||
* other Arduino stuff
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// conditional compile
|
||||
#if MAKEFILE_BOARD == teensy-2-0
|
||||
// ----------------------------------------------------------------------------
|
||||
* - look for an older version if you need one that doesn't depend on all
|
||||
* the other Arduino stuff
|
||||
*/
|
||||
|
||||
|
||||
#include <util/twi.h>
|
||||
#include "./teensy-2-0.h"
|
||||
#include "../twi.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void twi_init(void) {
|
||||
/** macros/OPT__TWI__FREQUENCY/description
|
||||
* Implementation notes:
|
||||
* - The max speed for the ATmega32U4 is 400kHz (datasheet sec. 20.1)
|
||||
* - The max speed for the MCP23017 is 1.7MHz (datasheet pg. 1)
|
||||
* - The max speed for the MCP23018 is 3.4MHz (datasheet pg. 1)
|
||||
*/
|
||||
#if OPT__TWI__FREQUENCY > 400000
|
||||
#error "OPT__TWI__FREQUENCY must be <= 400000"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void twi__init(void) {
|
||||
// set the prescaler value to 0
|
||||
TWSR &= ~( (1<<TWPS1)|(1<<TWPS0) );
|
||||
// set the bit rate
|
||||
// - TWBR should be 10 or higher (datasheet section 20.5.2)
|
||||
// - TWI_FREQ should be 400000 (400kHz) max (datasheet section 20.1)
|
||||
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
|
||||
TWBR = ((F_CPU / OPT__TWI__FREQUENCY) - 16) / 2;
|
||||
}
|
||||
|
||||
uint8_t twi_start(void) {
|
||||
uint8_t twi__start(void) {
|
||||
// send start
|
||||
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA);
|
||||
// wait for transmission to complete
|
||||
|
@ -53,14 +60,14 @@ uint8_t twi_start(void) {
|
|||
return 0; // success
|
||||
}
|
||||
|
||||
void twi_stop(void) {
|
||||
void twi__stop(void) {
|
||||
// send stop
|
||||
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
|
||||
// wait for transmission to complete
|
||||
while (TWCR & (1<<TWSTO));
|
||||
}
|
||||
|
||||
uint8_t twi_send(uint8_t data) {
|
||||
uint8_t twi__send(uint8_t data) {
|
||||
// load data into the data register
|
||||
TWDR = data;
|
||||
// send data
|
||||
|
@ -75,7 +82,7 @@ uint8_t twi_send(uint8_t data) {
|
|||
return 0; // success
|
||||
}
|
||||
|
||||
uint8_t twi_read(uint8_t * data) {
|
||||
uint8_t twi__read(uint8_t * data) {
|
||||
// read 1 byte to TWDR, send ACK
|
||||
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
|
||||
// wait for transmission to complete
|
||||
|
@ -88,8 +95,3 @@ uint8_t twi_read(uint8_t * data) {
|
|||
return 0; // success
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
# Documentation : I²C : Teensy 2.0
|
||||
|
||||
## I²C Status Codes (for Master modes)
|
||||
|
||||
### Master Transmitter (datasheet section 20.8.1, table 20-3)
|
||||
|
@ -25,6 +23,6 @@
|
|||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (MIT) (see "license.md")
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# TWI options
|
||||
#
|
||||
# This file is meant to be included by the using '.../options.mk'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU).c)
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* The USB interface
|
||||
*
|
||||
* Prefixes: `usb__` `usb__kb__`
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// --- general ---
|
||||
|
||||
uint8_t usb__init (void);
|
||||
bool usb__is_configured (void);
|
||||
|
||||
// --- keyboard ---
|
||||
|
||||
uint8_t usb__kb__set_key (bool pressed, uint8_t keycode);
|
||||
bool usb__kb__read_key (uint8_t keycode);
|
||||
bool usb__kb__read_led (char led);
|
||||
uint8_t usb__kb__send_report (void);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// general --------------------------------------------------------------------
|
||||
|
||||
// === usb__init() ===
|
||||
/** functions/usb__init/description
|
||||
* Initialize USB for this device
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - Should be called exactly once by `main()`, and nothing else USB related
|
||||
* should be done until `usb__configured` is `true`
|
||||
*/
|
||||
|
||||
// === usb__is_configured ===
|
||||
/** functions/usb__is_configured/description
|
||||
* Check whether this device has been configured by the host
|
||||
*
|
||||
* Returns:
|
||||
* - `true`: if the device has been configured
|
||||
* - `false`: if the device has not been configured
|
||||
*
|
||||
* Notes:
|
||||
* - Should return `true` before calling any function other than `usb__init()`
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// keyboard -------------------------------------------------------------------
|
||||
|
||||
// === usb__kb__set_key() ===
|
||||
/** functions/usb__kb__set_key/description
|
||||
* Set the given keycode as 'pressed' ('on') or 'released' ('off') (device
|
||||
* side)
|
||||
*
|
||||
* Arguments:
|
||||
* - `pressed`: whether to set the keycode 'on' (`true`) or 'off' (`false`)
|
||||
* - `keycode`: the keycode to set
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: status code (unspecified, nonzero)
|
||||
*
|
||||
* Notes:
|
||||
* - Setting a keycode 'on' or 'off' does not send the report, and the host
|
||||
* will not know the state of the keycode until the report is sent. If a key
|
||||
* is set 'on' then 'off' without sending the report in between, the host
|
||||
* will know nothing about it.
|
||||
*/
|
||||
|
||||
// === usb__kb__read_key() ===
|
||||
/** functions/usb__kb__read_key/description
|
||||
* Check whether the given keycode is set to 'on' (device side)
|
||||
*
|
||||
* Arguments:
|
||||
* - `keycode`: the keycode to check
|
||||
*
|
||||
* Returns:
|
||||
* - `false`: if the state of the given keycode is set to 'off'
|
||||
* - `true`: if the state of the given keycode is set to 'on'
|
||||
*
|
||||
* Notes:
|
||||
* - The fact that a given keycode is set to some value here does not mean that
|
||||
* the host knows about it. The host will only know the current state of the
|
||||
* keycodes if the report has been sent since the last change.
|
||||
*/
|
||||
|
||||
// === usb__kb__read_led() ===
|
||||
/** functions/usb__kb__read_led/description
|
||||
* Check whether the given (logical) LED is set to 'on' (host side)
|
||||
*
|
||||
* Arguments:
|
||||
* - `led`:
|
||||
* - `N`: numlock
|
||||
* - `C`: capslock
|
||||
* - `S`: scroll lock
|
||||
* - `O`: compose
|
||||
* - `K`: kana
|
||||
*
|
||||
* Returns:
|
||||
* - `false`: if the state of the given (logical) LED is set to 'off'
|
||||
* - `true`: if the state of the given (logical) LED is set to 'on'
|
||||
*
|
||||
* Notes:
|
||||
* - No data is actually exchanged with the host by this function, but
|
||||
* maintaining the state of these (logical) LEDs (and informing the device
|
||||
* when they change) is the host's responsibility. The device only keeps
|
||||
* track of what the host tells it.
|
||||
*/
|
||||
|
||||
// === usb__kb__send_report() ===
|
||||
/** functions/usb__kb__send_report/description
|
||||
* Send the USB report to the host (update the host on the state of the
|
||||
* keycodes)
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: status code (unspecified, nonzero)
|
||||
*/
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "general" section of '.../firmware/lib/usb.h'
|
||||
*
|
||||
* This is currently a wrapper for the PJRC code
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "./keyboard/from-pjrc/usb_keyboard.h"
|
||||
#include "../../usb.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t usb__init(void) { usb_init(); return 0; }
|
||||
|
||||
bool usb__is_configured(void) { return usb_configured(); }
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the "keyboard" section of '.../firmware/lib/usb.h'
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../usage-page/keyboard.h"
|
||||
#include "./keyboard/from-pjrc/usb_keyboard.h"
|
||||
#include "../../usb.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t usb__kb__set_key(bool pressed, uint8_t keycode) {
|
||||
// no-op
|
||||
if (keycode == 0)
|
||||
return 1;
|
||||
|
||||
// modifier keys
|
||||
switch (keycode) {
|
||||
case KEYBOARD__LeftControl: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<0))
|
||||
: (keyboard_modifier_keys &= ~(1<<0));
|
||||
return 0;
|
||||
case KEYBOARD__LeftShift: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<1))
|
||||
: (keyboard_modifier_keys &= ~(1<<1));
|
||||
return 0;
|
||||
case KEYBOARD__LeftAlt: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<2))
|
||||
: (keyboard_modifier_keys &= ~(1<<2));
|
||||
return 0;
|
||||
case KEYBOARD__LeftGUI: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<3))
|
||||
: (keyboard_modifier_keys &= ~(1<<3));
|
||||
return 0;
|
||||
case KEYBOARD__RightControl: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<4))
|
||||
: (keyboard_modifier_keys &= ~(1<<4));
|
||||
return 0;
|
||||
case KEYBOARD__RightShift: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<5))
|
||||
: (keyboard_modifier_keys &= ~(1<<5));
|
||||
return 0;
|
||||
case KEYBOARD__RightAlt: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<6))
|
||||
: (keyboard_modifier_keys &= ~(1<<6));
|
||||
return 0;
|
||||
case KEYBOARD__RightGUI: (pressed)
|
||||
? (keyboard_modifier_keys |= (1<<7))
|
||||
: (keyboard_modifier_keys &= ~(1<<7));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all others
|
||||
for (uint8_t i=0; i<6; i++) {
|
||||
if (pressed) {
|
||||
if (keyboard_keys[i] == 0) {
|
||||
keyboard_keys[i] = keycode;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (keyboard_keys[i] == keycode) {
|
||||
keyboard_keys[i] = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool usb__kb__read_key(uint8_t keycode) {
|
||||
// no-op
|
||||
if (keycode == 0)
|
||||
return false;
|
||||
|
||||
// modifier keys
|
||||
switch (keycode) {
|
||||
case KEYBOARD__LeftControl: return (keyboard_modifier_keys & (1<<0));
|
||||
case KEYBOARD__LeftShift: return (keyboard_modifier_keys & (1<<1));
|
||||
case KEYBOARD__LeftAlt: return (keyboard_modifier_keys & (1<<2));
|
||||
case KEYBOARD__LeftGUI: return (keyboard_modifier_keys & (1<<3));
|
||||
case KEYBOARD__RightControl: return (keyboard_modifier_keys & (1<<4));
|
||||
case KEYBOARD__RightShift: return (keyboard_modifier_keys & (1<<5));
|
||||
case KEYBOARD__RightAlt: return (keyboard_modifier_keys & (1<<6));
|
||||
case KEYBOARD__RightGUI: return (keyboard_modifier_keys & (1<<7));
|
||||
}
|
||||
|
||||
// all others
|
||||
for (uint8_t i=0; i<6; i++)
|
||||
if (keyboard_keys[i] == keycode)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool usb__kb__read_led(char led) {
|
||||
switch(led) {
|
||||
case 'N': return keyboard_leds & (1<<0); // numlock
|
||||
case 'C': return keyboard_leds & (1<<1); // capslock
|
||||
case 'S': return keyboard_leds & (1<<2); // scroll lock
|
||||
case 'O': return keyboard_leds & (1<<3); // compose
|
||||
case 'K': return keyboard_leds & (1<<4); // kana
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t usb__kb__send_report(void) {
|
||||
return usb_keyboard_send();
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
The PJRC USB keyboard implementation
|
||||
|
||||
Originally from [PJRC] (http://pjrc.com/teensy/) : [usb_keyboard]
|
||||
(http://pjrc.com/teensy/usb_keyboard.zip)
|
||||
|
||||
Modifications are noted in the files.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -24,6 +24,49 @@
|
|||
// Version 1.0: Initial Release
|
||||
// Version 1.1: Add support for Teensy 2.0
|
||||
|
||||
|
||||
/** description
|
||||
* The PJRC USB keyboard implementation
|
||||
*
|
||||
* History:
|
||||
* - Modified 2013, Ben Blazak
|
||||
*
|
||||
* Notes:
|
||||
* - 'description' added
|
||||
* - `usb_keyboard_press()` removed
|
||||
* - `OPT__` macros added (and other code modified accordingly)
|
||||
* - `PROGMEM` code made `const`
|
||||
* - removed unused variable `t` from `ISR(USB_GEN_vect)`
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef OPT__USB__STR_MANUFACTURER
|
||||
#error "OPT__USB__STR_MANUFACTURER not defined"
|
||||
#endif
|
||||
#ifndef OPT__USB__STR_PRODUCT
|
||||
#error "OPT__USB__STR_PRODUCT not defined"
|
||||
#endif
|
||||
#ifndef OPT__USB__VENDOR_ID
|
||||
#error "OPT__USB__VENDOR_ID not defined"
|
||||
#endif
|
||||
#ifndef OPT__USB__PRODUCT_ID
|
||||
#error "OPT__USB__PRODUCT_ID not defined"
|
||||
#endif
|
||||
|
||||
/** macros/(group) USB/description
|
||||
* USB identifier information
|
||||
*
|
||||
* Members:
|
||||
* - `OPT__USB__STR_MANUFACTURER`
|
||||
* - `OPT__USB__STR_PRODUCT`
|
||||
* - `OPT__USB__VENDOR_ID`
|
||||
* - `OPT__USB__PRODUCT_ID`
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define USB_SERIAL_PRIVATE_INCLUDE
|
||||
#include "usb_keyboard.h"
|
||||
|
||||
|
@ -34,16 +77,16 @@
|
|||
**************************************************************************/
|
||||
|
||||
// You can change these to give your code its own name.
|
||||
#define STR_MANUFACTURER L"unspecified" // TODO
|
||||
#define STR_PRODUCT L"ErgoDox ergonomic keyboard"
|
||||
#define STR_MANUFACTURER OPT__USB__STR_MANUFACTURER
|
||||
#define STR_PRODUCT OPT__USB__STR_PRODUCT
|
||||
|
||||
|
||||
// Mac OS-X and Linux automatically load the correct drivers. On
|
||||
// Windows, even though the driver is supplied by Microsoft, an
|
||||
// INF file is needed to load the driver. These numbers need to
|
||||
// match the INF file.
|
||||
#define VENDOR_ID 0x1d50 // Openmoko, Inc.
|
||||
#define PRODUCT_ID 0x6028 // ErgoDox ergonomic keyboard
|
||||
#define VENDOR_ID OPT__USB__VENDOR_ID
|
||||
#define PRODUCT_ID OPT__USB__PRODUCT_ID
|
||||
|
||||
|
||||
// USB devices are supposed to implment a halt feature, which is
|
||||
|
@ -209,12 +252,12 @@ static const struct usb_string_descriptor_struct PROGMEM string2 = {
|
|||
|
||||
// This table defines which descriptor data is sent for each specific
|
||||
// request from the host (in wValue and wIndex).
|
||||
static struct descriptor_list_struct {
|
||||
static const struct descriptor_list_struct {
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
const uint8_t *addr;
|
||||
uint8_t length;
|
||||
} const PROGMEM descriptor_list[] = {
|
||||
} PROGMEM descriptor_list[] = {
|
||||
{0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
|
||||
{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
|
||||
{0x2200, KEYBOARD_INTERFACE, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)},
|
||||
|
@ -287,21 +330,6 @@ uint8_t usb_configured(void)
|
|||
return usb_configuration;
|
||||
}
|
||||
|
||||
|
||||
// perform a single keystroke
|
||||
int8_t usb_keyboard_press(uint8_t key, uint8_t modifier)
|
||||
{
|
||||
int8_t r;
|
||||
|
||||
keyboard_modifier_keys = modifier;
|
||||
keyboard_keys[0] = key;
|
||||
r = usb_keyboard_send();
|
||||
if (r) return r;
|
||||
keyboard_modifier_keys = 0;
|
||||
keyboard_keys[0] = 0;
|
||||
return usb_keyboard_send();
|
||||
}
|
||||
|
||||
// send the contents of keyboard_keys and keyboard_modifier_keys
|
||||
int8_t usb_keyboard_send(void)
|
||||
{
|
||||
|
@ -349,8 +377,7 @@ int8_t usb_keyboard_send(void)
|
|||
//
|
||||
ISR(USB_GEN_vect)
|
||||
{
|
||||
uint8_t intbits, i; // used to declare a variable `t` as well, but it
|
||||
// wasn't used ::Ben Blazak, 2012::
|
||||
uint8_t intbits, i;
|
||||
static uint8_t div4=0;
|
||||
|
||||
intbits = UDINT;
|
|
@ -0,0 +1,107 @@
|
|||
/** description
|
||||
* The PJRC USB keyboard interface
|
||||
*
|
||||
* See the '.c' file for copyright and such
|
||||
*
|
||||
* Modified 2013, Ben Blazak
|
||||
*
|
||||
* Should be included only by those files that wrap parts of it to conform with
|
||||
* '.../firmware/lib/usb.h'
|
||||
*
|
||||
* Notes:
|
||||
* - 'description' added
|
||||
* - `usb_keyboard_press()` removed
|
||||
* - the empty macros for `usb_debug_putchar()` and `usb_debug_flush_output()`
|
||||
* removed
|
||||
* - keycode macros removed
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef usb_serial_h__
|
||||
#define usb_serial_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void usb_init(void); // initialize everything
|
||||
uint8_t usb_configured(void); // is the USB port configured
|
||||
|
||||
int8_t usb_keyboard_send(void);
|
||||
extern uint8_t keyboard_modifier_keys;
|
||||
extern uint8_t keyboard_keys[6];
|
||||
extern volatile uint8_t keyboard_leds;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Everything below this point is only intended for usb_serial.c
|
||||
#ifdef USB_SERIAL_PRIVATE_INCLUDE
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#define EP_TYPE_CONTROL 0x00
|
||||
#define EP_TYPE_BULK_IN 0x81
|
||||
#define EP_TYPE_BULK_OUT 0x80
|
||||
#define EP_TYPE_INTERRUPT_IN 0xC1
|
||||
#define EP_TYPE_INTERRUPT_OUT 0xC0
|
||||
#define EP_TYPE_ISOCHRONOUS_IN 0x41
|
||||
#define EP_TYPE_ISOCHRONOUS_OUT 0x40
|
||||
|
||||
#define EP_SINGLE_BUFFER 0x02
|
||||
#define EP_DOUBLE_BUFFER 0x06
|
||||
|
||||
#define EP_SIZE(s) ((s) == 64 ? 0x30 : \
|
||||
((s) == 32 ? 0x20 : \
|
||||
((s) == 16 ? 0x10 : \
|
||||
0x00)))
|
||||
|
||||
#define MAX_ENDPOINT 4
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
#if defined(__AVR_AT90USB162__)
|
||||
#define HW_CONFIG()
|
||||
#define PLL_CONFIG() (PLLCSR = ((1<<PLLE)|(1<<PLLP0)))
|
||||
#define USB_CONFIG() (USBCON = (1<<USBE))
|
||||
#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
|
||||
#elif defined(__AVR_ATmega32U4__)
|
||||
#define HW_CONFIG() (UHWCON = 0x01)
|
||||
#define PLL_CONFIG() (PLLCSR = 0x12)
|
||||
#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
|
||||
#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
|
||||
#elif defined(__AVR_AT90USB646__)
|
||||
#define HW_CONFIG() (UHWCON = 0x81)
|
||||
#define PLL_CONFIG() (PLLCSR = 0x1A)
|
||||
#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
|
||||
#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
|
||||
#elif defined(__AVR_AT90USB1286__)
|
||||
#define HW_CONFIG() (UHWCON = 0x81)
|
||||
#define PLL_CONFIG() (PLLCSR = 0x16)
|
||||
#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
|
||||
#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
|
||||
#endif
|
||||
|
||||
// standard control endpoint request types
|
||||
#define GET_STATUS 0
|
||||
#define CLEAR_FEATURE 1
|
||||
#define SET_FEATURE 3
|
||||
#define SET_ADDRESS 5
|
||||
#define GET_DESCRIPTOR 6
|
||||
#define GET_CONFIGURATION 8
|
||||
#define SET_CONFIGURATION 9
|
||||
#define GET_INTERFACE 10
|
||||
#define SET_INTERFACE 11
|
||||
// HID (human interface device)
|
||||
#define HID_GET_REPORT 1
|
||||
#define HID_GET_IDLE 2
|
||||
#define HID_GET_PROTOCOL 3
|
||||
#define HID_SET_REPORT 9
|
||||
#define HID_SET_IDLE 10
|
||||
#define HID_SET_PROTOCOL 11
|
||||
// CDC (communication class device)
|
||||
#define CDC_SET_LINE_CODING 0x20
|
||||
#define CDC_GET_LINE_CODING 0x21
|
||||
#define CDC_SET_CONTROL_LINE_STATE 0x22
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# USB keyboard options
|
||||
#
|
||||
# This file is meant to be included by '.../firmware/makefile'
|
||||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU)/*.c)
|
||||
|
||||
ifeq '$(MCU)' 'atmega32u4'
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU)/keyboard/from-pjrc/*.c)
|
||||
endif
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* USB HID Consumer Codes (usage page 0x0C)
|
||||
*
|
||||
* Prefix: `CONSUMER__`
|
||||
*
|
||||
* Taken from [the HID Usage Tables pdf][1], Section 15, which can be found on
|
||||
* [the HID Page][2] at <http://www.usb.org>
|
||||
*
|
||||
* - Usage Types (from Section 3.4)
|
||||
* - Controls
|
||||
* - LC : Linear Control
|
||||
* - OOC : On/Off Control
|
||||
* - MC : Momentary Control
|
||||
* - OSC : One Shot Control
|
||||
* - RTC : Re-Trigger Control
|
||||
* - Data
|
||||
* - Sel : Selector
|
||||
* - SV : Static Value
|
||||
* - SF : Static Flag
|
||||
* - DF : Dynamic Flag
|
||||
* - DV : Dynamic Value
|
||||
* - Collection
|
||||
* - NAry : Named Array
|
||||
* - CA : Collection Application
|
||||
* - CL : Collection Logical
|
||||
* - CP : Collection Physical
|
||||
* - US : Usage Switch
|
||||
* - UM : Usage Modifier
|
||||
*
|
||||
* [1]: http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
|
||||
* [2]: http://www.usb.org/developers/hidpage
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__CONSUMER__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__CONSUMER__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Name ID Usage Type Section of HID Tables
|
||||
// --------------------------------------------- ------ ---------- ---------------------
|
||||
|
||||
// (Unassigned) 0x0000 // - -
|
||||
|
||||
#define CONSUMER__ConsumerControl 0x0001 // CA 15.1
|
||||
#define CONSUMER__NumericKeyPad 0x0002 // NAry 15.2
|
||||
#define CONSUMER__ProgrammableButtons 0x0003 // NAry 15.14
|
||||
#define CONSUMER__Microphone 0x0004 // CA 15.1
|
||||
#define CONSUMER__Headphone 0x0005 // CA 15.1
|
||||
#define CONSUMER__GraphicEqualizer 0x0006 // CA 15.1
|
||||
|
||||
// (Reserved) 0x0007..0x001F // - -
|
||||
|
||||
#define CONSUMER__Plus10 0x0020 // OSC 15.2
|
||||
#define CONSUMER__Plus100 0x0021 // OSC 15.2
|
||||
#define CONSUMER__AM_PM 0x0022 // OSC 15.2
|
||||
|
||||
// (Reserved) 0x0023..0x002F // - -
|
||||
|
||||
#define CONSUMER__Power 0x0030 // OOC 15.3
|
||||
#define CONSUMER__Reset 0x0031 // OSC 15.3
|
||||
#define CONSUMER__Sleep 0x0032 // OSC 15.3
|
||||
#define CONSUMER__SleepAfter 0x0033 // OSC 15.3
|
||||
#define CONSUMER__SleepMode 0x0034 // RTC 15.3
|
||||
#define CONSUMER__Illumination 0x0035 // OOC 15.3
|
||||
#define CONSUMER__FunctionButtons 0x0036 // NAry 15.3
|
||||
|
||||
// (Reserved) 0x0037..0x003F // - -
|
||||
|
||||
#define CONSUMER__Menu 0x0040 // OOC 15.4
|
||||
#define CONSUMER__MenuPick 0x0041 // OSC 15.4
|
||||
#define CONSUMER__MenuUp 0x0042 // OSC 15.4
|
||||
#define CONSUMER__MenuDown 0x0043 // OSC 15.4
|
||||
#define CONSUMER__MenuLeft 0x0044 // OSC 15.4
|
||||
#define CONSUMER__MenuRight 0x0045 // OSC 15.4
|
||||
#define CONSUMER__MenuEscape 0x0046 // OSC 15.4
|
||||
#define CONSUMER__MenuValueIncrease 0x0047 // OSC 15.4
|
||||
#define CONSUMER__MenuValueDecrease 0x0048 // OSC 15.4
|
||||
|
||||
// (Reserved) 0x0049..0x005F // - -
|
||||
|
||||
#define CONSUMER__DataOnScreen 0x0060 // OOC 15.5
|
||||
#define CONSUMER__ClosedCaption 0x0061 // OOC 15.5
|
||||
#define CONSUMER__ClosedCaptionSelect 0x0062 // OSC 15.5
|
||||
#define CONSUMER__VCR_TV 0x0063 // OOC 15.5
|
||||
#define CONSUMER__BroadcastMode 0x0064 // OSC 15.5
|
||||
#define CONSUMER__Snapshot 0x0065 // OSC 15.5
|
||||
#define CONSUMER__Still 0x0066 // OSC 15.5
|
||||
|
||||
// (Reserved) 0x0067..0x007F // - -
|
||||
|
||||
#define CONSUMER__Selection 0x0080 // NAry 15.6
|
||||
#define CONSUMER__AssignSelection 0x0081 // OSC 15.6
|
||||
#define CONSUMER__ModeStep 0x0082 // OSC 15.6
|
||||
#define CONSUMER__RecallLast 0x0083 // OSC 15.6
|
||||
#define CONSUMER__EnterChannel 0x0084 // OSC 15.6
|
||||
#define CONSUMER__OrderMovie 0x0085 // OSC 15.6
|
||||
#define CONSUMER__Channel 0x0086 // LC 15.6
|
||||
#define CONSUMER__MediaSelection 0x0087 // NAry 15.6
|
||||
#define CONSUMER__MediaSelectComputer 0x0088 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectTV 0x0089 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectWWW 0x008A // Sel 15.6
|
||||
#define CONSUMER__MediaSelectDVD 0x008B // Sel 15.6
|
||||
#define CONSUMER__MediaSelectTelephone 0x008C // Sel 15.6
|
||||
#define CONSUMER__MediaSelectProgramGuide 0x008D // Sel 15.6
|
||||
#define CONSUMER__MediaSelectVideoPhone 0x008E // Sel 15.6
|
||||
#define CONSUMER__MediaSelectGames 0x008F // Sel 15.6
|
||||
#define CONSUMER__MediaSelectMessages 0x0090 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectCD 0x0091 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectVCR 0x0092 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectTuner 0x0093 // Sel 15.6
|
||||
#define CONSUMER__Quit 0x0094 // OSC 15.6
|
||||
#define CONSUMER__Help 0x0095 // OOC 15.6
|
||||
#define CONSUMER__MediaSelectTape 0x0096 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectCable 0x0097 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectSatellite 0x0098 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectSecurity 0x0099 // Sel 15.6
|
||||
#define CONSUMER__MediaSelectHome 0x009A // Sel 15.6
|
||||
#define CONSUMER__MediaSelectCall 0x009B // Sel 15.6
|
||||
#define CONSUMER__ChannelIncrement 0x009C // OSC 15.6
|
||||
#define CONSUMER__ChannelDecrement 0x009D // OSC 15.6
|
||||
#define CONSUMER__MediaSelectSAP 0x009E // Sel 15.6
|
||||
|
||||
// (Reserved) 0x009F // - -
|
||||
|
||||
#define CONSUMER__VCRPlus 0x00A0 // OSC 15.6
|
||||
#define CONSUMER__Once 0x00A1 // OSC 15.6
|
||||
#define CONSUMER__Daily 0x00A2 // OSC 15.6
|
||||
#define CONSUMER__Weekly 0x00A3 // OSC 15.6
|
||||
#define CONSUMER__Monthly 0x00A4 // OSC 15.6
|
||||
|
||||
// (Reserved) 0x00A5..0x00AF // - -
|
||||
|
||||
#define CONSUMER__Play 0x00B0 // OOC 15.7
|
||||
#define CONSUMER__Pause 0x00B1 // OOC 15.7
|
||||
#define CONSUMER__Record 0x00B2 // OOC 15.7
|
||||
#define CONSUMER__FastForward 0x00B3 // OOC 15.7
|
||||
#define CONSUMER__Rewind 0x00B4 // OOC 15.7
|
||||
#define CONSUMER__ScanNextTrack 0x00B5 // OSC 15.7
|
||||
#define CONSUMER__ScanPreviousTrack 0x00B6 // OSC 15.7
|
||||
#define CONSUMER__Stop 0x00B7 // OSC 15.7
|
||||
#define CONSUMER__Eject 0x00B8 // OSC 15.7
|
||||
#define CONSUMER__RandomPlay 0x00B9 // OOC 15.7
|
||||
#define CONSUMER__SelectDisc 0x00BA // NAry 15.7
|
||||
#define CONSUMER__EnterDisc 0x00BB // MC 15.7
|
||||
#define CONSUMER__Repeat 0x00BC // OSC 15.7
|
||||
#define CONSUMER__Tracking 0x00BD // LC 15.7
|
||||
#define CONSUMER__TrackNormal 0x00BE // OSC 15.7
|
||||
#define CONSUMER__SlowTracking 0x00BF // LC 15.7
|
||||
#define CONSUMER__FrameForward 0x00C0 // RTC 15.7
|
||||
#define CONSUMER__FrameBack 0x00C1 // RTC 15.7
|
||||
#define CONSUMER__Mark 0x00C2 // OSC 15.8
|
||||
#define CONSUMER__ClearMark 0x00C3 // OSC 15.8
|
||||
#define CONSUMER__RepeatFromMark 0x00C4 // OOC 15.8
|
||||
#define CONSUMER__ReturnToMark 0x00C5 // OSC 15.8
|
||||
#define CONSUMER__SearchMarkForward 0x00C6 // OSC 15.8
|
||||
#define CONSUMER__SearchMarkBackwards 0x00C7 // OSC 15.8
|
||||
#define CONSUMER__CounterReset 0x00C8 // OSC 15.8
|
||||
#define CONSUMER__ShowCounter 0x00C9 // OSC 15.8
|
||||
#define CONSUMER__TrackingIncrement 0x00CA // RTC 15.7
|
||||
#define CONSUMER__TrackingDecrement 0x00CB // RTC 15.7
|
||||
#define CONSUMER__Stop_Eject 0x00CC // OSC 15.7
|
||||
#define CONSUMER__Play_Pause 0x00CD // OSC 15.7
|
||||
#define CONSUMER__Play_Skip 0x00CE // OSC 15.7
|
||||
|
||||
// (Reserved) 0x00CF..0x00DF // - -
|
||||
|
||||
#define CONSUMER__Volume 0x00E0 // LC 15.9.1
|
||||
#define CONSUMER__Balance 0x00E1 // LC 15.9.2
|
||||
#define CONSUMER__Mute 0x00E2 // OOC 15.9.1
|
||||
#define CONSUMER__Bass 0x00E3 // LC 15.9.3
|
||||
#define CONSUMER__Treble 0x00E4 // LC 15.9.4
|
||||
#define CONSUMER__BassBoost 0x00E5 // OOC 15.9.3
|
||||
#define CONSUMER__SurroundMode 0x00E6 // OSC 15.9.5
|
||||
#define CONSUMER__Loudness 0x00E7 // OOC 15.9.5
|
||||
#define CONSUMER__MPX 0x00E8 // OOC 15.9.5
|
||||
#define CONSUMER__VolumeIncrement 0x00E9 // RTC 15.9.1
|
||||
#define CONSUMER__VolumeDecrement 0x00EA // RTC 15.9.1
|
||||
|
||||
// (Reserved) 0x00EB..0x00EF // - -
|
||||
|
||||
#define CONSUMER__SpeedSelect 0x00F0 // OSC 15.10
|
||||
#define CONSUMER__PlaybackSpeed 0x00F1 // NAry 15.10
|
||||
#define CONSUMER__StandardPlay 0x00F2 // Sel 15.10
|
||||
#define CONSUMER__LongPlay 0x00F3 // Sel 15.10
|
||||
#define CONSUMER__ExtendedPlay 0x00F4 // Sel 15.10
|
||||
#define CONSUMER__Slow 0x00F5 // OSC 15.10
|
||||
|
||||
// (Reserved) 0x00F6..0x00FF // - -
|
||||
|
||||
#define CONSUMER__FanEnable 0x0100 // OOC 15.11
|
||||
#define CONSUMER__FanSpeed 0x0101 // LC 15.11
|
||||
#define CONSUMER__LightEnable 0x0102 // OOC 15.11
|
||||
#define CONSUMER__LightIlluminationLevel 0x0103 // LC 15.11
|
||||
#define CONSUMER__ClimateControlEnable 0x0104 // OOC 15.11
|
||||
#define CONSUMER__RoomTemperature 0x0105 // LC 15.11
|
||||
#define CONSUMER__SecurityEnable 0x0106 // OOC 15.11
|
||||
#define CONSUMER__FireAlarm 0x0107 // OSC 15.11
|
||||
#define CONSUMER__PoliceAlarm 0x0108 // OSC 15.11
|
||||
#define CONSUMER__Proximity 0x0109 // LC 15.11
|
||||
#define CONSUMER__Motion 0x010A // OSC 15.11
|
||||
#define CONSUMER__DuressAlarm 0x010B // OSC 15.11
|
||||
#define CONSUMER__HoldupAlarm 0x010C // OSC 15.11
|
||||
#define CONSUMER__MedicalAlarm 0x010D // OSC 15.11
|
||||
|
||||
// (Reserved) 0x010E..0x014F // - -
|
||||
|
||||
#define CONSUMER__BalanceRight 0x0150 // RTC 15.9.2
|
||||
#define CONSUMER__BalanceLeft 0x0151 // RTC 15.9.2
|
||||
#define CONSUMER__BassIncrement 0x0152 // RTC 15.9.3
|
||||
#define CONSUMER__BassDecrement 0x0153 // RTC 15.9.3
|
||||
#define CONSUMER__TrebleIncrement 0x0154 // RTC 15.9.4
|
||||
#define CONSUMER__TrebleDecrement 0x0155 // RTC 15.9.4
|
||||
|
||||
// (Reserved) 0x0156..0x015F // - -
|
||||
|
||||
#define CONSUMER__SpeakerSystem 0x0160 // CL 15.12.1
|
||||
#define CONSUMER__ChannelLeft 0x0161 // CL 15.12.1
|
||||
#define CONSUMER__ChannelRight 0x0162 // CL 15.12.1
|
||||
#define CONSUMER__ChannelCenter 0x0163 // CL 15.12.1
|
||||
#define CONSUMER__ChannelFront 0x0164 // CL 15.12.1
|
||||
#define CONSUMER__ChannelCenterFront 0x0165 // CL 15.12.1
|
||||
#define CONSUMER__ChannelSide 0x0166 // CL 15.12.1
|
||||
#define CONSUMER__ChannelSurround 0x0167 // CL 15.12.1
|
||||
#define CONSUMER__ChannelLowFrequencyEnhancement 0x0168 // CL 15.12.1
|
||||
#define CONSUMER__ChannelTop 0x0169 // CL 15.12.1
|
||||
#define CONSUMER__ChannelUnknown 0x016A // CL 15.12.1
|
||||
|
||||
// (Reserved) 0x016B..0x016F // - -
|
||||
|
||||
#define CONSUMER__SubChannel 0x0170 // LC 15.13
|
||||
#define CONSUMER__SubChannelIncrement 0x0171 // OSC 15.13
|
||||
#define CONSUMER__SubChannelDecrement 0x0172 // OSC 15.13
|
||||
#define CONSUMER__AlternateAudioIncrement 0x0173 // OSC 15.13
|
||||
#define CONSUMER__AlternateAudioDecrement 0x0174 // OSC 15.13
|
||||
|
||||
// (Reserved) 0x0175..0x017F // - -
|
||||
|
||||
#define CONSUMER__ApplicationLaunchButtons 0x0180 // NAry 15.15
|
||||
#define CONSUMER__AL_LaunchButtonConfigurationTool 0x0181 // Sel 15.15
|
||||
#define CONSUMER__AL_ProgrammableButtonConfiguration 0x0182 // Sel 15.15
|
||||
#define CONSUMER__AL_ConsumerControlConfiguration 0x0183 // Sel 15.15
|
||||
#define CONSUMER__AL_WordProcessor 0x0184 // Sel 15.15
|
||||
#define CONSUMER__AL_TextEditor 0x0185 // Sel 15.15
|
||||
#define CONSUMER__AL_Spreadsheet 0x0186 // Sel 15.15
|
||||
#define CONSUMER__AL_GraphicsEditor 0x0187 // Sel 15.15
|
||||
#define CONSUMER__AL_PresentationApp 0x0188 // Sel 15.15
|
||||
#define CONSUMER__AL_DatabaseApp 0x0189 // Sel 15.15
|
||||
#define CONSUMER__AL_EmailReader 0x018A // Sel 15.15
|
||||
#define CONSUMER__AL_Newsreader 0x018B // Sel 15.15
|
||||
#define CONSUMER__AL_Voicemail 0x018C // Sel 15.15
|
||||
#define CONSUMER__AL_Contacts_AddressBook 0x018D // Sel 15.15
|
||||
#define CONSUMER__AL_Calendar_Schedule 0x018E // Sel 15.15
|
||||
#define CONSUMER__AL_Task_ProjectManager 0x018F // Sel 15.15
|
||||
#define CONSUMER__AL_Log_Journal_Timecard 0x0190 // Sel 15.15
|
||||
#define CONSUMER__AL_Checkbook_Finance 0x0191 // Sel 15.15
|
||||
#define CONSUMER__AL_Calculator 0x0192 // Sel 15.15
|
||||
#define CONSUMER__AL_A_VCapture_Playback 0x0193 // Sel 15.15
|
||||
#define CONSUMER__AL_LocalMachineBrowser 0x0194 // Sel 15.15
|
||||
#define CONSUMER__AL_LAN_WANBrowser 0x0195 // Sel 15.15
|
||||
#define CONSUMER__AL_InternetBrowser 0x0196 // Sel 15.15
|
||||
#define CONSUMER__AL_RemoteNetworking_ISPConnect 0x0197 // Sel 15.15
|
||||
#define CONSUMER__AL_NetworkConference 0x0198 // Sel 15.15
|
||||
#define CONSUMER__AL_NetworkChat 0x0199 // Sel 15.15
|
||||
#define CONSUMER__AL_Telephony_Dialer 0x019A // Sel 15.15
|
||||
#define CONSUMER__AL_Logon 0x019B // Sel 15.15
|
||||
#define CONSUMER__AL_Logoff 0x019C // Sel 15.15
|
||||
#define CONSUMER__AL_Logon_Logoff 0x019D // Sel 15.15
|
||||
#define CONSUMER__AL_TerminalLock_Screensaver 0x019E // Sel 15.15
|
||||
#define CONSUMER__AL_ControlPanel 0x019F // Sel 15.15
|
||||
#define CONSUMER__AL_CommandLineProcessor_Run 0x01A0 // Sel 15.15
|
||||
#define CONSUMER__AL_Process_TaskManager 0x01A1 // Sel 15.15
|
||||
#define CONSUMER__AL_SelectTask_Application 0x01A2 // Sel 15.15
|
||||
#define CONSUMER__AL_NextTask_Application 0x01A3 // Sel 15.15
|
||||
#define CONSUMER__AL_PreviousTask_Application 0x01A4 // Sel 15.15
|
||||
#define CONSUMER__AL_PreemptiveHaltTask_Application 0x01A5 // Sel 15.15
|
||||
#define CONSUMER__AL_IntegratedHelpCenter 0x01A6 // Sel 15.15
|
||||
#define CONSUMER__AL_Documents 0x01A7 // Sel 15.15
|
||||
#define CONSUMER__AL_Thesaurus 0x01A8 // Sel 15.15
|
||||
#define CONSUMER__AL_Dictionary 0x01A9 // Sel 15.15
|
||||
#define CONSUMER__AL_Desktop 0x01AA // Sel 15.15
|
||||
#define CONSUMER__AL_SpellCheck 0x01AB // Sel 15.15
|
||||
#define CONSUMER__AL_GrammarCheck 0x01AC // Sel 15.15
|
||||
#define CONSUMER__AL_WirelessStatus 0x01AD // Sel 15.15
|
||||
#define CONSUMER__AL_KeyboardLayout 0x01AE // Sel 15.15
|
||||
#define CONSUMER__AL_VirusProtection 0x01AF // Sel 15.15
|
||||
#define CONSUMER__AL_Encryption 0x01B0 // Sel 15.15
|
||||
#define CONSUMER__AL_ScreenSaver 0x01B1 // Sel 15.15
|
||||
#define CONSUMER__AL_Alarms 0x01B2 // Sel 15.15
|
||||
#define CONSUMER__AL_Clock 0x01B3 // Sel 15.15
|
||||
#define CONSUMER__AL_FileBrowser 0x01B4 // Sel 15.15
|
||||
#define CONSUMER__AL_PowerStatus 0x01B5 // Sel 15.15
|
||||
#define CONSUMER__AL_ImageBrowser 0x01B6 // Sel 15.15
|
||||
#define CONSUMER__AL_AudioBrowser 0x01B7 // Sel 15.15
|
||||
#define CONSUMER__AL_MovieBrowser 0x01B8 // Sel 15.15
|
||||
#define CONSUMER__AL_DigitalRightsManager 0x01B9 // Sel 15.15
|
||||
#define CONSUMER__AL_DigitalWallet 0x01BA // Sel 15.15
|
||||
|
||||
// (Reserved) 0x01BB // - -
|
||||
|
||||
#define CONSUMER__AL_InstantMessaging 0x01BC // Sel 15.15
|
||||
#define CONSUMER__AL_OEMFeatures_Tips_TutorialBrowser 0x01BD // Sel 15.15
|
||||
#define CONSUMER__AL_OEMHelp 0x01BE // Sel 15.15
|
||||
#define CONSUMER__AL_OnlineCommunity 0x01BF // Sel 15.15
|
||||
#define CONSUMER__AL_EntertainmentContentBrowser 0x01C0 // Sel 15.15
|
||||
#define CONSUMER__AL_OnlineShoppingBrowser 0x01C1 // Sel 15.15
|
||||
#define CONSUMER__AL_SmartCardInformation_Help 0x01C2 // Sel 15.15
|
||||
#define CONSUMER__AL_MarketMonitor_FinanceBrowser 0x01C3 // Sel 15.15
|
||||
#define CONSUMER__AL_CustomizedCorporateNewsBrowser 0x01C4 // Sel 15.15
|
||||
#define CONSUMER__AL_OnlineActivityBrowser 0x01C5 // Sel 15.15
|
||||
#define CONSUMER__AL_Research_SearchBrowser 0x01C6 // Sel 15.15
|
||||
#define CONSUMER__AL_AudioPlayer 0x01C7 // Sel 15.15
|
||||
|
||||
// (Reserved) 0x01C8..0x01FF // - -
|
||||
|
||||
#define CONSUMER__GenericGUIApplicationControls 0x0200 // NAry 15.16
|
||||
#define CONSUMER__AC_New 0x0201 // Sel 15.16
|
||||
#define CONSUMER__AC_Open 0x0202 // Sel 15.16
|
||||
#define CONSUMER__AC_Close 0x0203 // Sel 15.16
|
||||
#define CONSUMER__AC_Exit 0x0204 // Sel 15.16
|
||||
#define CONSUMER__AC_Maximize 0x0205 // Sel 15.16
|
||||
#define CONSUMER__AC_Minimize 0x0206 // Sel 15.16
|
||||
#define CONSUMER__AC_Save 0x0207 // Sel 15.16
|
||||
#define CONSUMER__AC_Print 0x0208 // Sel 15.16
|
||||
#define CONSUMER__AC_Properties 0x0209 // Sel 15.16
|
||||
|
||||
#define CONSUMER__AC_Undo 0x021A // Sel 15.16
|
||||
#define CONSUMER__AC_Copy 0x021B // Sel 15.16
|
||||
#define CONSUMER__AC_Cut 0x021C // Sel 15.16
|
||||
#define CONSUMER__AC_Paste 0x021D // Sel 15.16
|
||||
#define CONSUMER__AC_SelectAll 0x021E // Sel 15.16
|
||||
#define CONSUMER__AC_Find 0x021F // Sel 15.16
|
||||
#define CONSUMER__AC_FindandReplace 0x0220 // Sel 15.16
|
||||
#define CONSUMER__AC_Search 0x0221 // Sel 15.16
|
||||
#define CONSUMER__AC_GoTo 0x0222 // Sel 15.16
|
||||
#define CONSUMER__AC_Home 0x0223 // Sel 15.16
|
||||
#define CONSUMER__AC_Back 0x0224 // Sel 15.16
|
||||
#define CONSUMER__AC_Forward 0x0225 // Sel 15.16
|
||||
#define CONSUMER__AC_Stop 0x0226 // Sel 15.16
|
||||
#define CONSUMER__AC_Refresh 0x0227 // Sel 15.16
|
||||
#define CONSUMER__AC_PreviousLink 0x0228 // Sel 15.16
|
||||
#define CONSUMER__AC_NextLink 0x0229 // Sel 15.16
|
||||
#define CONSUMER__AC_Bookmarks 0x022A // Sel 15.16
|
||||
#define CONSUMER__AC_History 0x022B // Sel 15.16
|
||||
#define CONSUMER__AC_Subscriptions 0x022C // Sel 15.16
|
||||
#define CONSUMER__AC_ZoomIn 0x022D // Sel 15.16
|
||||
#define CONSUMER__AC_ZoomOut 0x022E // Sel 15.16
|
||||
#define CONSUMER__AC_Zoom 0x022F // Sel 15.16
|
||||
#define CONSUMER__AC_FullScreenView 0x0230 // Sel 15.16
|
||||
#define CONSUMER__AC_NormalView 0x0231 // Sel 15.16
|
||||
#define CONSUMER__AC_ViewToggle 0x0232 // Sel 15.16
|
||||
#define CONSUMER__AC_ScrollUp 0x0233 // Sel 15.16
|
||||
#define CONSUMER__AC_ScrollDown 0x0234 // Sel 15.16
|
||||
#define CONSUMER__AC_Scroll 0x0235 // Sel 15.16
|
||||
#define CONSUMER__AC_PanLeft 0x0236 // Sel 15.16
|
||||
#define CONSUMER__AC_PanRight 0x0237 // Sel 15.16
|
||||
#define CONSUMER__AC_Pan 0x0238 // Sel 15.16
|
||||
#define CONSUMER__AC_NewWindow 0x0239 // Sel 15.16
|
||||
#define CONSUMER__AC_TileHorizontally 0x023A // Sel 15.16
|
||||
#define CONSUMER__AC_TileVertically 0x023B // Sel 15.16
|
||||
#define CONSUMER__AC_Format 0x023C // Sel 15.16
|
||||
#define CONSUMER__AC_Edit 0x023D // Sel 15.14
|
||||
#define CONSUMER__AC_Bold 0x023E // Sel 15.16
|
||||
#define CONSUMER__AC_Italics 0x023F // Sel 15.16
|
||||
#define CONSUMER__AC_Underline 0x0240 // Sel 15.16
|
||||
#define CONSUMER__AC_Strikethrough 0x0241 // Sel 15.16
|
||||
#define CONSUMER__AC_Subscript 0x0242 // Sel 15.16
|
||||
#define CONSUMER__AC_Superscript 0x0243 // Sel 15.16
|
||||
#define CONSUMER__AC_AllCaps 0x0244 // Sel 15.16
|
||||
#define CONSUMER__AC_Rotate 0x0245 // Sel 15.16
|
||||
#define CONSUMER__AC_Resize 0x0246 // Sel 15.16
|
||||
#define CONSUMER__AC_Fliphorizontal 0x0247 // Sel 15.16
|
||||
#define CONSUMER__AC_FlipVertical 0x0248 // Sel 15.16
|
||||
#define CONSUMER__AC_MirrorHorizontal 0x0249 // Sel 15.16
|
||||
#define CONSUMER__AC_MirrorVertical 0x024A // Sel 15.16
|
||||
#define CONSUMER__AC_FontSelect 0x024B // Sel 15.16
|
||||
#define CONSUMER__AC_FontColor 0x024C // Sel 15.16
|
||||
#define CONSUMER__AC_FontSize 0x024D // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyLeft 0x024E // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyCenterH 0x024F // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyRight 0x0250 // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyBlockH 0x0251 // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyTop 0x0252 // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyCenterV 0x0253 // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyBottom 0x0254 // Sel 15.16
|
||||
#define CONSUMER__AC_JustifyBlockV 0x0255 // Sel 15.16
|
||||
#define CONSUMER__AC_IndentDecrease 0x0256 // Sel 15.16
|
||||
#define CONSUMER__AC_IndentIncrease 0x0257 // Sel 15.16
|
||||
#define CONSUMER__AC_NumberedList 0x0258 // Sel 15.16
|
||||
#define CONSUMER__AC_RestartNumbering 0x0259 // Sel 15.16
|
||||
#define CONSUMER__AC_BulletedList 0x025A // Sel 15.16
|
||||
#define CONSUMER__AC_Promote 0x025B // Sel 15.16
|
||||
#define CONSUMER__AC_Demote 0x025C // Sel 15.16
|
||||
#define CONSUMER__AC_Yes 0x025D // Sel 15.16
|
||||
#define CONSUMER__AC_No 0x025E // Sel 15.16
|
||||
#define CONSUMER__AC_Cancel 0x025F // Sel 15.16
|
||||
#define CONSUMER__AC_Catalog 0x0260 // Sel 15.16
|
||||
#define CONSUMER__AC_Buy_Checkout 0x0261 // Sel 15.16
|
||||
#define CONSUMER__AC_AddtoCart 0x0262 // Sel 15.16
|
||||
#define CONSUMER__AC_Expand 0x0263 // Sel 15.16
|
||||
#define CONSUMER__AC_ExpandAll 0x0264 // Sel 15.16
|
||||
#define CONSUMER__AC_Collapse 0x0265 // Sel 15.16
|
||||
#define CONSUMER__AC_CollapseAll 0x0266 // Sel 15.16
|
||||
#define CONSUMER__AC_PrintPreview 0x0267 // Sel 15.16
|
||||
#define CONSUMER__AC_PasteSpecial 0x0268 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertMode 0x0269 // Sel 15.16
|
||||
#define CONSUMER__AC_Delete 0x026A // Sel 15.16
|
||||
#define CONSUMER__AC_Lock 0x026B // Sel 15.16
|
||||
#define CONSUMER__AC_Unlock 0x026C // Sel 15.16
|
||||
#define CONSUMER__AC_Protect 0x026D // Sel 15.16
|
||||
#define CONSUMER__AC_Unprotect 0x026E // Sel 15.16
|
||||
#define CONSUMER__AC_AttachComment 0x026F // Sel 15.16
|
||||
#define CONSUMER__AC_DeleteComment 0x0270 // Sel 15.16
|
||||
#define CONSUMER__AC_ViewComment 0x0271 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectWord 0x0272 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectSentence 0x0273 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectParagraph 0x0274 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectColumn 0x0275 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectRow 0x0276 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectTable 0x0277 // Sel 15.16
|
||||
#define CONSUMER__AC_SelectObject 0x0278 // Sel 15.16
|
||||
#define CONSUMER__AC_Redo_Repeat 0x0279 // Sel 15.16
|
||||
#define CONSUMER__AC_Sort 0x027A // Sel 15.16
|
||||
#define CONSUMER__AC_SortAscending 0x027B // Sel 15.16
|
||||
#define CONSUMER__AC_SortDescending 0x027C // Sel 15.16
|
||||
#define CONSUMER__AC_Filter 0x027D // Sel 15.16
|
||||
#define CONSUMER__AC_SetClock 0x027E // Sel 15.16
|
||||
#define CONSUMER__AC_ViewClock 0x027F // Sel 15.16
|
||||
#define CONSUMER__AC_SelectTimeZone 0x0280 // Sel 15.16
|
||||
#define CONSUMER__AC_EditTimeZones 0x0281 // Sel 15.16
|
||||
#define CONSUMER__AC_SetAlarm 0x0282 // Sel 15.16
|
||||
#define CONSUMER__AC_ClearAlarm 0x0283 // Sel 15.16
|
||||
#define CONSUMER__AC_SnoozeAlarm 0x0284 // Sel 15.16
|
||||
#define CONSUMER__AC_ResetAlarm 0x0285 // Sel 15.16
|
||||
#define CONSUMER__AC_Synchronize 0x0286 // Sel 15.16
|
||||
#define CONSUMER__AC_Send_Receive 0x0287 // Sel 15.16
|
||||
#define CONSUMER__AC_SendTo 0x0288 // Sel 15.16
|
||||
#define CONSUMER__AC_Reply 0x0289 // Sel 15.16
|
||||
#define CONSUMER__AC_ReplyAll 0x028A // Sel 15.16
|
||||
#define CONSUMER__AC_ForwardMsg 0x028B // Sel 15.16
|
||||
#define CONSUMER__AC_Send 0x028C // Sel 15.16
|
||||
#define CONSUMER__AC_AttachFile 0x028D // Sel 15.16
|
||||
#define CONSUMER__AC_Upload 0x028E // Sel 15.16
|
||||
#define CONSUMER__AC_Download_SaveTargetAs 0x028F // Sel 15.16
|
||||
#define CONSUMER__AC_SetBorders 0x0290 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertRow 0x0291 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertColumn 0x0292 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertFile 0x0293 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertPicture 0x0294 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertObject 0x0295 // Sel 15.16
|
||||
#define CONSUMER__AC_InsertSymbol 0x0296 // Sel 15.16
|
||||
#define CONSUMER__AC_SaveandClose 0x0297 // Sel 15.16
|
||||
#define CONSUMER__AC_Rename 0x0298 // Sel 15.16
|
||||
#define CONSUMER__AC_Merge 0x0299 // Sel 15.16
|
||||
#define CONSUMER__AC_Split 0x029A // Sel 15.16
|
||||
#define CONSUMER__AC_DisributeHorizontally 0x029B // Sel 15.16
|
||||
#define CONSUMER__AC_DistributeVertically 0x029C // Sel 15.16
|
||||
|
||||
// (Reserved) 0x029D..0xFFFF // - -
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__SRC__LIB__USB__USAGE_PAGE__CONSUMER__H
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* USB Generic Desktop Codes (usage page 0x01)
|
||||
*
|
||||
* Prefix: `GENERIC_DESKTOP__`
|
||||
*
|
||||
* Taken from [the HID Usage Tables pdf][1], Section 4, which can be found on
|
||||
* [the HID Page][2] at <http://www.usb.org>
|
||||
*
|
||||
* - Usage Types (from Section 3.4)
|
||||
* - Controls
|
||||
* - LC : Linear Control
|
||||
* - OOC : On/Off Control
|
||||
* - MC : Momentary Control
|
||||
* - OSC : One Shot Control
|
||||
* - RTC : Re-Trigger Control
|
||||
* - Data
|
||||
* - Sel : Selector
|
||||
* - SV : Static Value
|
||||
* - SF : Static Flag
|
||||
* - DF : Dynamic Flag
|
||||
* - DV : Dynamic Value
|
||||
* - Collection
|
||||
* - NAry : Named Array
|
||||
* - CA : Collection Application
|
||||
* - CL : Collection Logical
|
||||
* - CP : Collection Physical
|
||||
* - US : Usage Switch
|
||||
* - UM : Usage Modifier
|
||||
*
|
||||
* [1]: http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
|
||||
* [2]: http://www.usb.org/developers/hidpage
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__GENERIC_DESKTOP__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__GENERIC_DESKTOP__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Name ID Usage Type Section of HID Tables
|
||||
// -------------------------------------------------- ---- ---------- ---------------------
|
||||
|
||||
// (Undefined) 0x00 // - -
|
||||
|
||||
#define GENERIC_DESKTOP__Pointer 0x01 // CP 4.1
|
||||
#define GENERIC_DESKTOP__Mouse 0x02 // CA 4.1
|
||||
|
||||
// (Reserved) 0x03 // - -
|
||||
|
||||
#define GENERIC_DESKTOP__Joystick 0x04 // CA 4.1
|
||||
#define GENERIC_DESKTOP__GamePad 0x05 // CA 4.1
|
||||
#define GENERIC_DESKTOP__Keyboard 0x06 // CA 4.1
|
||||
#define GENERIC_DESKTOP__Keypad 0x07 // CA 4.1
|
||||
#define GENERIC_DESKTOP__MultiAxisController 0x08 // CA 4.1
|
||||
#define GENERIC_DESKTOP__TabletPCSystemControls 0x09 // CA 4.1
|
||||
|
||||
// (Reserved) 0x0A..0x2F // - -
|
||||
|
||||
#define GENERIC_DESKTOP__X 0x30 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Y 0x31 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Z 0x32 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Rx 0x33 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Ry 0x34 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Rz 0x35 // DV 4.2
|
||||
#define GENERIC_DESKTOP__Slider 0x36 // DV 4.3
|
||||
#define GENERIC_DESKTOP__Dial 0x37 // DV 4.3
|
||||
#define GENERIC_DESKTOP__Wheel 0x38 // DV 4.3
|
||||
#define GENERIC_DESKTOP__HatSwitch 0x39 // DV 4.3
|
||||
#define GENERIC_DESKTOP__CountedBuffer 0x3A // CL 4.6
|
||||
#define GENERIC_DESKTOP__ByteCount 0x3B // DV 4.6
|
||||
#define GENERIC_DESKTOP__MotionWakeup 0x3C // OSC 4.3
|
||||
#define GENERIC_DESKTOP__Start 0x3D // OOC 4.3
|
||||
#define GENERIC_DESKTOP__Select 0x3E // OOC 4.3
|
||||
|
||||
// (Reserved) 0x3F // - -
|
||||
|
||||
#define GENERIC_DESKTOP__Vx 0x40 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vy 0x41 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vz 0x42 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vbrx 0x43 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vbry 0x44 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vbrz 0x45 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__Vno 0x46 // DV 4.3.1
|
||||
#define GENERIC_DESKTOP__FeatureNotification 0x47 // DV,DF 4.8
|
||||
#define GENERIC_DESKTOP__ResolutionMultiplier 0x48 // DV -
|
||||
|
||||
// (Reserved) 0x49..0x7F // - -
|
||||
|
||||
#define GENERIC_DESKTOP__SystemControl 0x80 // CA 4.5
|
||||
#define GENERIC_DESKTOP__SystemPowerDown 0x81 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemSleep 0x82 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemWakeUp 0x83 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemContextMenu 0x84 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMainMenu 0x85 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemAppMenu 0x86 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuHelp 0x87 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuExit 0x88 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuSelect 0x89 // OSC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuRight 0x8A // RTC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuLeft 0x8B // RTC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuUp 0x8C // RTC 4.5
|
||||
#define GENERIC_DESKTOP__SystemMenuDown 0x8D // RTC 4.5
|
||||
#define GENERIC_DESKTOP__SystemColdRestart 0x8E // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemWarmRestart 0x8F // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__DPadUp 0x90 // OOC 4.7
|
||||
#define GENERIC_DESKTOP__DPadDown 0x91 // OOC 4.7
|
||||
#define GENERIC_DESKTOP__DPadRight 0x92 // OOC 4.7
|
||||
#define GENERIC_DESKTOP__DPadLeft 0x93 // OOC 4.7
|
||||
|
||||
// (Reserved) 0x94..0x9F // - -
|
||||
|
||||
#define GENERIC_DESKTOP__SystemDock 0xA0 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemUndock 0xA1 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemSetup 0xA2 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemBreak 0xA3 // OSC 4.9
|
||||
#define GENERIC_DESKTOP__SystemDebuggerBreak 0xA4 // OSC 4.9
|
||||
#define GENERIC_DESKTOP__ApplicationBreak 0xA5 // OSC 4.9
|
||||
#define GENERIC_DESKTOP__ApplicationDebuggerBreak 0xA6 // OSC 4.9
|
||||
#define GENERIC_DESKTOP__SystemSpeakerMute 0xA7 // OSC 4.5.1
|
||||
#define GENERIC_DESKTOP__SystemHibernate 0xA8 // OSC 4.5.1
|
||||
|
||||
// (Reserved) 0xA9..0xAF // - -
|
||||
|
||||
#define GENERIC_DESKTOP__SystemDisplayInvert 0xB0 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayInternal 0xB1 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayExternal 0xB2 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayBoth 0xB3 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayDual 0xB4 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayToggleIntExt 0xB5 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplaySwapPrimarySecondary 0xB6 // OSC 4.10
|
||||
#define GENERIC_DESKTOP__SystemDisplayLCDAutoscale 0xB7 // OSC 4.10
|
||||
|
||||
// (Reserved) 0xB8..0xFFFF // - -
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__SRC__LIB__USB__USAGE_PAGE__GENERIC_DESKTOP__H
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* USB Keyboard Key Codes (usage page 0x07)
|
||||
*
|
||||
* Prefixes: `KEYBOARD__`, `KEYPAD__`
|
||||
*
|
||||
* Taken from [the HID Usage Tables pdf][1], Section 10, which can be found on
|
||||
* [the HID Page][2] at <http://www.usb.org>
|
||||
*
|
||||
* - `Boot Keyboard Req.` indicates that the usage code is one that should be
|
||||
* supported by the listed types of keyboards (104-key, ...) on boot
|
||||
*
|
||||
* - `KEY_` indicates a Keyboard key
|
||||
* - `KEYPAD_` indicates a Keypad key
|
||||
* - Multiple names concatenated in CamelCase indicate a single value
|
||||
* - Multiple names separated by `_`s indicate shifted or alternate values
|
||||
*
|
||||
* [1]: http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
|
||||
* [2]: http://www.usb.org/developers/hidpage
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__KEYBOARD__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__KEYBOARD__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Name ID // PC Mac Unix Boot Keyboard Req.
|
||||
// ---------------------------- ---- -- --- ---- --------------------
|
||||
|
||||
// (Reserved) 0x00 // √ √ √ 84/101/104
|
||||
|
||||
#define KEYBOARD__ErrorRollOver 0x01 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__POSTFail 0x02 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__ErrorUndefined 0x03 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__a_A 0x04 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__b_B 0x05 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__c_C 0x06 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__d_D 0x07 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__e_E 0x08 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__f_F 0x09 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__g_G 0x0A // √ √ √ 84/101/104
|
||||
#define KEYBOARD__h_H 0x0B // √ √ √ 84/101/104
|
||||
#define KEYBOARD__i_I 0x0C // √ √ √ 84/101/104
|
||||
#define KEYBOARD__j_J 0x0D // √ √ √ 84/101/104
|
||||
#define KEYBOARD__k_K 0x0E // √ √ √ 84/101/104
|
||||
#define KEYBOARD__l_L 0x0F // √ √ √ 84/101/104
|
||||
#define KEYBOARD__m_M 0x10 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__n_N 0x11 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__o_O 0x12 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__p_P 0x13 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__q_Q 0x14 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__r_R 0x15 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__s_S 0x16 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__t_T 0x17 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__u_U 0x18 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__v_V 0x19 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__w_W 0x1A // √ √ √ 84/101/104
|
||||
#define KEYBOARD__x_X 0x1B // √ √ √ 84/101/104
|
||||
#define KEYBOARD__y_Y 0x1C // √ √ √ 84/101/104
|
||||
#define KEYBOARD__z_Z 0x1D // √ √ √ 84/101/104
|
||||
#define KEYBOARD__1_Exclamation 0x1E // √ √ √ 84/101/104
|
||||
#define KEYBOARD__2_At 0x1F // √ √ √ 84/101/104
|
||||
#define KEYBOARD__3_Pound 0x20 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__4_Dollar 0x21 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__5_Percent 0x22 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__6_Caret 0x23 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__7_Ampersand 0x24 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__8_Asterisk 0x25 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__9_LeftParenthesis 0x26 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__0_RightParenthesis 0x27 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__ReturnEnter 0x28 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Escape 0x29 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__DeleteBackspace 0x2A // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Tab 0x2B // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Spacebar 0x2C // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Dash_Underscore 0x2D // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Equal_Plus 0x2E // √ √ √ 84/101/104
|
||||
#define KEYBOARD__LeftBracket_LeftBrace 0x2F // √ √ √ 84/101/104
|
||||
#define KEYBOARD__RightBracket_RightBrace 0x30 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Backslash_Pipe 0x31 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__NonUS_Pound_Tilde 0x32 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Semicolon_Colon 0x33 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__SingleQuote_DoubleQuote 0x34 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__GraveAccent_Tilde 0x35 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Comma_LessThan 0x36 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Period_GreaterThan 0x37 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Slash_Question 0x38 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__CapsLock 0x39 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F1 0x3A // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F2 0x3B // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F3 0x3C // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F4 0x3D // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F5 0x3E // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F6 0x3F // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F7 0x40 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F8 0x41 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F9 0x42 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F10 0x43 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__F11 0x44 // √ √ √ 101/104
|
||||
#define KEYBOARD__F12 0x45 // √ √ √ 101/104
|
||||
#define KEYBOARD__PrintScreen 0x46 // √ √ √ 101/104
|
||||
#define KEYBOARD__ScrollLock 0x47 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Pause 0x48 // √ √ √ 101/104
|
||||
#define KEYBOARD__Insert 0x49 // √ √ √ 101/104
|
||||
#define KEYBOARD__Home 0x4A // √ √ √ 101/104
|
||||
#define KEYBOARD__PageUp 0x4B // √ √ √ 101/104
|
||||
#define KEYBOARD__DeleteForward 0x4C // √ √ √ 101/104
|
||||
#define KEYBOARD__End 0x4D // √ √ √ 101/104
|
||||
#define KEYBOARD__PageDown 0x4E // √ √ √ 101/104
|
||||
#define KEYBOARD__RightArrow 0x4F // √ √ √ 101/104
|
||||
#define KEYBOARD__LeftArrow 0x50 // √ √ √ 101/104
|
||||
#define KEYBOARD__DownArrow 0x51 // √ √ √ 101/104
|
||||
#define KEYBOARD__UpArrow 0x52 // √ √ √ 101/104
|
||||
|
||||
#define KEYPAD__NumLock_Clear 0x53 // √ √ √ 101/104
|
||||
#define KEYPAD__Slash 0x54 // √ √ √ 101/104
|
||||
#define KEYPAD__Asterisk 0x55 // √ √ √ 84/101/104
|
||||
#define KEYPAD__Minus 0x56 // √ √ √ 84/101/104
|
||||
#define KEYPAD__Plus 0x57 // √ √ √ 84/101/104
|
||||
#define KEYPAD__Enter 0x58 // √ √ √ 101/104
|
||||
#define KEYPAD__1_End 0x59 // √ √ √ 84/101/104
|
||||
#define KEYPAD__2_DownArrow 0x5A // √ √ √ 84/101/104
|
||||
#define KEYPAD__3_PageDown 0x5B // √ √ √ 84/101/104
|
||||
#define KEYPAD__4_LeftArrow 0x5C // √ √ √ 84/101/104
|
||||
#define KEYPAD__5 0x5D // √ √ √ 84/101/104
|
||||
#define KEYPAD__6_RightArrow 0x5E // √ √ √ 84/101/104
|
||||
#define KEYPAD__7_Home 0x5F // √ √ √ 84/101/104
|
||||
#define KEYPAD__8_UpArrow 0x60 // √ √ √ 84/101/104
|
||||
#define KEYPAD__9_PageUp 0x61 // √ √ √ 84/101/104
|
||||
#define KEYPAD__0_Insert 0x62 // √ √ √ 84/101/104
|
||||
#define KEYPAD__Period_Delete 0x63 // √ √ √ 84/101/104
|
||||
|
||||
#define KEYBOARD__NonUS_Backslash_Pipe 0x64 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__Application 0x65 // √ - √ 104
|
||||
#define KEYBOARD__Power 0x66 // - √ √ -
|
||||
|
||||
#define KEYPAD__Equal 0x67 // - √ - -
|
||||
|
||||
#define KEYBOARD__F13 0x68 // - √ - -
|
||||
#define KEYBOARD__F14 0x69 // - √ - -
|
||||
#define KEYBOARD__F15 0x6A // - √ - -
|
||||
#define KEYBOARD__F16 0x6B // - - - -
|
||||
#define KEYBOARD__F17 0x6C // - - - -
|
||||
#define KEYBOARD__F18 0x6D // - - - -
|
||||
#define KEYBOARD__F19 0x6E // - - - -
|
||||
#define KEYBOARD__F20 0x6F // - - - -
|
||||
#define KEYBOARD__F21 0x70 // - - - -
|
||||
#define KEYBOARD__F22 0x71 // - - - -
|
||||
#define KEYBOARD__F23 0x72 // - - - -
|
||||
#define KEYBOARD__F24 0x73 // - - - -
|
||||
#define KEYBOARD__Execute 0x74 // - - √ -
|
||||
#define KEYBOARD__Help 0x75 // - - √ -
|
||||
#define KEYBOARD__Menu 0x76 // - - √ -
|
||||
#define KEYBOARD__Select 0x77 // - - √ -
|
||||
#define KEYBOARD__Stop 0x78 // - - √ -
|
||||
#define KEYBOARD__Again 0x79 // - - √ -
|
||||
#define KEYBOARD__Undo 0x7A // - - √ -
|
||||
#define KEYBOARD__Cut 0x7B // - - √ -
|
||||
#define KEYBOARD__Copy 0x7C // - - √ -
|
||||
#define KEYBOARD__Paste 0x7D // - - √ -
|
||||
#define KEYBOARD__Find 0x7E // - - √ -
|
||||
#define KEYBOARD__Mute 0x7F // - - √ -
|
||||
#define KEYBOARD__VolumeUp 0x80 // - - √ -
|
||||
#define KEYBOARD__VolumeDown 0x81 // - - √ -
|
||||
#define KEYBOARD__LockingCapsLock 0x82 // - - √ -
|
||||
#define KEYBOARD__LockingNumLock 0x83 // - - √ -
|
||||
#define KEYBOARD__LockingScrollLock 0x84 // - - √ -
|
||||
|
||||
#define KEYPAD__Comma 0x85 // - - - -
|
||||
#define KEYPAD__EqualSign 0x86 // - - - -
|
||||
|
||||
#define KEYBOARD__International1 0x87 // - - - -
|
||||
#define KEYBOARD__International2 0x88 // - - - -
|
||||
#define KEYBOARD__International3 0x89 // - - - -
|
||||
#define KEYBOARD__International4 0x8A // - - - -
|
||||
#define KEYBOARD__International5 0x8B // - - - -
|
||||
#define KEYBOARD__International6 0x8C // - - - -
|
||||
#define KEYBOARD__International7 0x8D // - - - -
|
||||
#define KEYBOARD__International8 0x8E // - - - -
|
||||
#define KEYBOARD__International9 0x8F // - - - -
|
||||
#define KEYBOARD__LANG1 0x90 // - - - -
|
||||
#define KEYBOARD__LANG2 0x91 // - - - -
|
||||
#define KEYBOARD__LANG3 0x92 // - - - -
|
||||
#define KEYBOARD__LANG4 0x93 // - - - -
|
||||
#define KEYBOARD__LANG5 0x94 // - - - -
|
||||
#define KEYBOARD__LANG6 0x95 // - - - -
|
||||
#define KEYBOARD__LANG7 0x96 // - - - -
|
||||
#define KEYBOARD__LANG8 0x97 // - - - -
|
||||
#define KEYBOARD__LANG9 0x98 // - - - -
|
||||
#define KEYBOARD__AlternateErase 0x99 // - - - -
|
||||
#define KEYBOARD__SysReq_Attention 0x9A // - - - -
|
||||
#define KEYBOARD__Cancel 0x9B // - - - -
|
||||
#define KEYBOARD__Clear 0x9C // - - - -
|
||||
#define KEYBOARD__Prior 0x9D // - - - -
|
||||
#define KEYBOARD__Return 0x9E // - - - -
|
||||
#define KEYBOARD__Separator 0x9F // - - - -
|
||||
#define KEYBOARD__Out 0xA0 // - - - -
|
||||
#define KEYBOARD__Oper 0xA1 // - - - -
|
||||
#define KEYBOARD__Clear_Again 0xA2 // - - - -
|
||||
#define KEYBOARD__CrSel_Props 0xA3 // - - - -
|
||||
#define KEYBOARD__ExSel 0xA4 // - - - -
|
||||
|
||||
// (Reserved) 0xA5..0xAF // - - - -
|
||||
|
||||
#define KEYPAD__00 0xB0 // - - - -
|
||||
#define KEYPAD__000 0xB1 // - - - -
|
||||
|
||||
#define KEYBOARD__ThousandsSeparator 0xB2 // - - - -
|
||||
#define KEYBOARD__DecimalSeparator 0xB3 // - - - -
|
||||
#define KEYBOARD__CurrencyUnit 0xB4 // - - - -
|
||||
#define KEYBOARD__CurrencySubunit 0xB5 // - - - -
|
||||
|
||||
#define KEYPAD__LeftParenthesis 0xB6 // - - - -
|
||||
#define KEYPAD__RightParenthesis 0xB7 // - - - -
|
||||
#define KEYPAD__LeftBrace 0xB8 // - - - -
|
||||
#define KEYPAD__RightBrace 0xB9 // - - - -
|
||||
|
||||
#define KEYPAD__Tab 0xBA // - - - -
|
||||
#define KEYPAD__Backspace 0xBB // - - - -
|
||||
#define KEYPAD__A 0xBC // - - - -
|
||||
#define KEYPAD__B 0xBD // - - - -
|
||||
#define KEYPAD__C 0xBE // - - - -
|
||||
#define KEYPAD__D 0xBF // - - - -
|
||||
#define KEYPAD__E 0xC0 // - - - -
|
||||
#define KEYPAD__F 0xC1 // - - - -
|
||||
#define KEYPAD__XOR 0xC2 // - - - -
|
||||
#define KEYPAD__Caret 0xC3 // - - - -
|
||||
#define KEYPAD__Percent 0xC4 // - - - -
|
||||
#define KEYPAD__LessThan 0xC5 // - - - -
|
||||
#define KEYPAD__GreaterThan 0xC6 // - - - -
|
||||
#define KEYPAD__Ampersand 0xC7 // - - - -
|
||||
#define KEYPAD__AmpersandAmpersand 0xC8 // - - - -
|
||||
#define KEYPAD__Pipe 0xC9 // - - - -
|
||||
#define KEYPAD__PipePipe 0xCA // - - - -
|
||||
#define KEYPAD__Colon 0xCB // - - - -
|
||||
#define KEYPAD__Pound 0xCC // - - - -
|
||||
#define KEYPAD__Space 0xCD // - - - -
|
||||
#define KEYPAD__At 0xCE // - - - -
|
||||
#define KEYPAD__Exclamation 0xCF // - - - -
|
||||
#define KEYPAD__MemoryStore 0xD0 // - - - -
|
||||
#define KEYPAD__MemoryRecall 0xD1 // - - - -
|
||||
#define KEYPAD__MemoryClear 0xD2 // - - - -
|
||||
#define KEYPAD__MemoryAdd 0xD3 // - - - -
|
||||
#define KEYPAD__MemorySubtract 0xD4 // - - - -
|
||||
#define KEYPAD__MemoryMultiply 0xD5 // - - - -
|
||||
#define KEYPAD__MemoryDivide 0xD6 // - - - -
|
||||
#define KEYPAD__PlusMinus 0xD7 // - - - -
|
||||
#define KEYPAD__Clear 0xD8 // - - - -
|
||||
#define KEYPAD__ClearEntry 0xD9 // - - - -
|
||||
#define KEYPAD__Binary 0xDA // - - - -
|
||||
#define KEYPAD__Octal 0xDB // - - - -
|
||||
#define KEYPAD__Decimal 0xDC // - - - -
|
||||
#define KEYPAD__Hexadecimal 0xDD // - - - -
|
||||
|
||||
// (Reserved) 0xDE..0xDF // - - - -
|
||||
|
||||
#define KEYBOARD__LeftControl 0xE0 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__LeftShift 0xE1 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__LeftAlt 0xE2 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__LeftGUI 0xE3 // √ √ √ 104
|
||||
#define KEYBOARD__RightControl 0xE4 // √ √ √ 101/104
|
||||
#define KEYBOARD__RightShift 0xE5 // √ √ √ 84/101/104
|
||||
#define KEYBOARD__RightAlt 0xE6 // √ √ √ 101/104
|
||||
#define KEYBOARD__RightGUI 0xE7 // √ √ √ 104
|
||||
|
||||
// (Reserved) 0xE8..0xFFFF // - - - -
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__SRC__LIB__USB__USAGE_PAGE__KEYBOARD__H
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* USB LED Codes (usage page 0x08)
|
||||
*
|
||||
* Prefix: `LED__`
|
||||
*
|
||||
* Taken from [the HID Usage Tables pdf][1], Section 11, which can be found on
|
||||
* [the HID Page][2] at <http://www.usb.org>
|
||||
*
|
||||
* - Usage Types (from Section 3.4)
|
||||
* - Controls
|
||||
* - LC : Linear Control
|
||||
* - OOC : On/Off Control
|
||||
* - MC : Momentary Control
|
||||
* - OSC : One Shot Control
|
||||
* - RTC : Re-Trigger Control
|
||||
* - Data
|
||||
* - Sel : Selector
|
||||
* - SV : Static Value
|
||||
* - SF : Static Flag
|
||||
* - DF : Dynamic Flag
|
||||
* - DV : Dynamic Value
|
||||
* - Collection
|
||||
* - NAry : Named Array
|
||||
* - CA : Collection Application
|
||||
* - CL : Collection Logical
|
||||
* - CP : Collection Physical
|
||||
* - US : Usage Switch
|
||||
* - UM : Usage Modifier
|
||||
*
|
||||
* [1]: http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
|
||||
* [2]: http://www.usb.org/developers/hidpage
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__LED__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__LIB__USB__USAGE_PAGE__LED__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Name ID Usage Type Section of HID Tables
|
||||
// ---------------------------- ---- ---------- ---------------------
|
||||
|
||||
// (Undefined) 0x00 // - -
|
||||
|
||||
#define LED__NumLock 0x01 // OOC 11.1
|
||||
#define LED__CapsLock 0x02 // OOC 11.1
|
||||
#define LED__ScrollLock 0x03 // OOC 11.1
|
||||
#define LED__Compose 0x04 // OOC 11.1
|
||||
#define LED__Kana 0x05 // OOC 11.1
|
||||
#define LED__Power 0x06 // OOC 11.6
|
||||
#define LED__Shift 0x07 // OOC 11.1
|
||||
#define LED__DoNotDisturb 0x08 // OOC 11.2
|
||||
#define LED__Mute 0x09 // OOC 11.3
|
||||
#define LED__ToneEnable 0x0A // OOC 11.3
|
||||
#define LED__HighCutFilter 0x0B // OOC 11.3
|
||||
#define LED__LowCutFilter 0x0C // OOC 11.3
|
||||
#define LED__EqualizerEnable 0x0D // OOC 11.3
|
||||
#define LED__SoundFieldOn 0x0E // OOC 11.3
|
||||
#define LED__SurroundOn 0x0F // OOC 11.3
|
||||
#define LED__Repeat 0x10 // OOC 11.3
|
||||
#define LED__Stereo 0x11 // OOC 11.3
|
||||
#define LED__SamplingRateDetect 0x12 // OOC 11.3
|
||||
#define LED__Spinning 0x13 // OOC 11.4
|
||||
#define LED__CAV 0x14 // OOC 11.3
|
||||
#define LED__CLV 0x15 // OOC 11.3
|
||||
#define LED__RecordingFormatDetect 0x16 // OOC 11.4
|
||||
#define LED__OffHook 0x17 // OOC 11.2
|
||||
#define LED__Ring 0x18 // OOC 11.2
|
||||
#define LED__MessageWaiting 0x19 // OOC 11.2
|
||||
#define LED__DataMode 0x1A // OOC 11.2
|
||||
#define LED__BatteryOperation 0x1B // OOC 11.6
|
||||
#define LED__BatteryOK 0x1C // OOC 11.6
|
||||
#define LED__BatteryLow 0x1D // OOC 11.6
|
||||
#define LED__Speaker 0x1E // OOC 11.2
|
||||
#define LED__HeadSet 0x1F // OOC 11.2
|
||||
#define LED__Hold 0x20 // OOC 11.2
|
||||
#define LED__Microphone 0x21 // OOC 11.2
|
||||
#define LED__Coverage 0x22 // OOC 11.2
|
||||
#define LED__NightMode 0x23 // OOC 11.2
|
||||
#define LED__SendCalls 0x24 // OOC 11.2
|
||||
#define LED__CallPickup 0x25 // OOC 11.2
|
||||
#define LED__Conference 0x26 // OOC 11.2
|
||||
#define LED__Standby 0x27 // OOC 11.6
|
||||
#define LED__CameraOn 0x28 // OOC 11.3
|
||||
#define LED__CameraOff 0x29 // OOC 11.3
|
||||
#define LED__OnLine 0x2A // OOC 11.6
|
||||
#define LED__OffLine 0x2B // OOC 11.6
|
||||
#define LED__Busy 0x2C // OOC 11.6
|
||||
#define LED__Ready 0x2D // OOC 11.6
|
||||
#define LED__PaperOut 0x2E // OOC 11.5
|
||||
#define LED__PaperJam 0x2F // OOC 11.5
|
||||
#define LED__Remote 0x30 // OOC 11.6
|
||||
#define LED__Forward 0x31 // OOC 11.4
|
||||
#define LED__Reverse 0x32 // OOC 11.4
|
||||
#define LED__Stop 0x33 // OOC 11.4
|
||||
#define LED__Rewind 0x34 // OOC 11.4
|
||||
#define LED__FastForward 0x35 // OOC 11.4
|
||||
#define LED__Play 0x36 // OOC 11.4
|
||||
#define LED__Pause 0x37 // OOC 11.4
|
||||
#define LED__Record 0x38 // OOC 11.4
|
||||
#define LED__Error 0x39 // OOC 11.6
|
||||
#define LED__UsageSelectedIndicator 0x3A // US 11.6
|
||||
#define LED__UsageInUseIndicator 0x3B // US 11.6
|
||||
#define LED__UsageMultiModeIndicator 0x3C // UM 11.6
|
||||
#define LED__IndicatorOn 0x3D // Sel 11.6
|
||||
#define LED__IndicatorFlash 0x3E // Sel 11.6
|
||||
#define LED__IndicatorSlowBlink 0x3F // Sel 11.6
|
||||
#define LED__IndicatorFastBlink 0x40 // Sel 11.6
|
||||
#define LED__IndicatorOff 0x41 // Sel 11.6
|
||||
#define LED__FlashOnTime 0x42 // DV 11.6
|
||||
#define LED__SlowBlinkOnTime 0x43 // DV 11.6
|
||||
#define LED__SlowBlinkOffTime 0x44 // DV 11.6
|
||||
#define LED__FastBlinkOnTime 0x45 // DV 11.6
|
||||
#define LED__FastBlinkOffTime 0x46 // DV 11.6
|
||||
#define LED__UsageIndicatorColor 0x47 // UM 11.6
|
||||
#define LED__IndicatorRed 0x48 // Sel 11.6
|
||||
#define LED__IndicatorGreen 0x49 // Sel 11.6
|
||||
#define LED__IndicatorAmber 0x4A // Sel 11.6
|
||||
#define LED__GenericIndicator 0x4B // OOC 11.6
|
||||
#define LED__SystemSuspend 0x4C // OOC 11.6
|
||||
#define LED__ExternalPowerConnected 0x4D // OOC 11.6
|
||||
|
||||
// (Reserved) 0x4E..0xFFFF // - -
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__SRC__LIB__USB__USAGE_PAGE__LED__H
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* `main()`: tying it all together
|
||||
*
|
||||
* If you're just trying to get a feel for the source, I'd glance over this
|
||||
* file, then move on to ".../firmware/keyboard.h"
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "../firmware/keyboard.h"
|
||||
#include "../firmware/lib/timer.h"
|
||||
#include "../firmware/lib/usb.h"
|
||||
#include "./main.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** macros/OPT__DEBOUNCE_TIME/description
|
||||
* The minimum amount of time to wait between two scans of a key, in
|
||||
* milliseconds
|
||||
*
|
||||
* Notes:
|
||||
* - Cherry MX bounce time <= 5ms (at 16 in/sec actuation speed) (spec)
|
||||
* - From experience (after having this issue brought to my attention by
|
||||
* Thanatermesis), waiting longer can sometimes fix the issue of having some
|
||||
* keys unexpectedly seem to double-tap.
|
||||
*/
|
||||
#ifndef OPT__DEBOUNCE_TIME
|
||||
#error "OPT__DEBOUNCE_TIME not defined"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define main__is_pressed is_pressed
|
||||
#define main__was_pressed was_pressed
|
||||
#define main__row row
|
||||
#define main__col col
|
||||
#define main__flags flags
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool (* is_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS]
|
||||
= &( bool [OPT__KB__ROWS][OPT__KB__COLUMNS] ){};
|
||||
bool (* was_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS]
|
||||
= &( bool [OPT__KB__ROWS][OPT__KB__COLUMNS] ){};
|
||||
|
||||
uint8_t row;
|
||||
uint8_t col;
|
||||
|
||||
struct main__flags_t flags = { .update_leds = true };
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** functions/main/description
|
||||
* Initialize things, then loop forever
|
||||
*
|
||||
* There are a few interesting things that happen here, but most things happen
|
||||
* elsewhere. Have a look through the source, especially the documentation,
|
||||
* and especially in ".../firmware/keyboard/.../layout", to see what's going
|
||||
* on.
|
||||
*/
|
||||
int main(void) {
|
||||
static bool (*temp)[OPT__KB__ROWS][OPT__KB__COLUMNS]; // for swapping below
|
||||
static bool key_is_pressed;
|
||||
static bool key_was_pressed;
|
||||
|
||||
static uint8_t time_scan_started;
|
||||
|
||||
{
|
||||
// initialize everything; signal an error (at the end) if one occurs
|
||||
|
||||
bool error;
|
||||
|
||||
error = kb__init(); // initialize hardware (besides USB and timer)
|
||||
|
||||
kb__led__state__power_on();
|
||||
|
||||
error |= usb__init();
|
||||
while (!usb__is_configured());
|
||||
kb__led__delay__usb_init(); // give the OS time to load drivers, etc.
|
||||
|
||||
error |= timer__init();
|
||||
|
||||
kb__led__state__ready();
|
||||
|
||||
if (error) kb__led__delay__error();
|
||||
}
|
||||
|
||||
time_scan_started // on the first iteration, scan immediately
|
||||
= timer__get_milliseconds() - OPT__DEBOUNCE_TIME;
|
||||
|
||||
for (;;) {
|
||||
temp = is_pressed;
|
||||
is_pressed = was_pressed;
|
||||
was_pressed = temp;
|
||||
|
||||
// delay if necessary, then rescan
|
||||
while( (uint8_t)(timer__get_milliseconds()-time_scan_started)
|
||||
< OPT__DEBOUNCE_TIME );
|
||||
time_scan_started = timer__get_milliseconds();
|
||||
kb__update_matrix(*is_pressed);
|
||||
|
||||
// "execute" keys that have changed state
|
||||
for (row=0; row<OPT__KB__ROWS; row++) {
|
||||
for (col=0; col<OPT__KB__COLUMNS; col++) {
|
||||
key_is_pressed = (*is_pressed)[row][col];
|
||||
key_was_pressed = (*was_pressed)[row][col];
|
||||
|
||||
if (key_is_pressed != key_was_pressed)
|
||||
kb__layout__exec_key(key_is_pressed, row, col);
|
||||
}
|
||||
}
|
||||
|
||||
usb__kb__send_report(); // (even if nothing's changed)
|
||||
|
||||
// note: only use the `kb__led__logical...` functions here, since the
|
||||
// meaning of the physical LEDs should be controlled by the layout
|
||||
if (flags.update_leds) {
|
||||
#define read usb__kb__read_led
|
||||
#define on kb__led__logical_on
|
||||
#define off kb__led__logical_off
|
||||
read('N') ? on('N') : off('N'); // numlock
|
||||
read('C') ? on('C') : off('C'); // capslock
|
||||
read('S') ? on('S') : off('S'); // scroll lock
|
||||
read('O') ? on('O') : off('O'); // compose
|
||||
read('K') ? on('K') : off('K'); // kana
|
||||
#undef read
|
||||
#undef on
|
||||
#undef off
|
||||
}
|
||||
|
||||
timer___tick_cycles();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* The `main()` interface for the rest of the program
|
||||
*
|
||||
* Prefix: `main__`
|
||||
*
|
||||
* Certain variables are declared here so that other functions can see (and
|
||||
* perhaps modify) them, to accomplish things that may be difficult otherwise.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__FIRMWARE__MAIN__H
|
||||
#define ERGODOX_FIRMWARE__FIRMWARE__MAIN__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
struct main__flags_t {
|
||||
bool update_leds : 1;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
extern bool (* main__is_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS];
|
||||
extern bool (* main__was_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS];
|
||||
|
||||
extern uint8_t main__row;
|
||||
extern uint8_t main__col;
|
||||
|
||||
extern struct main__flags_t main__flags;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__FIRMWARE__MAIN__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === main__flags_t ===
|
||||
/** types/struct main__flags_t/description
|
||||
* See the documentation for `main__flags`
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === main__is_pressed ===
|
||||
/** variables/main__is_pressed/description
|
||||
* A matrix of `bool`s indicating whether the key at a given position is
|
||||
* currently pressed
|
||||
*/
|
||||
|
||||
// === main__was_pressed ===
|
||||
/** variables/main__was_pressed/description
|
||||
* A matrix of `bool`s indicating whether the key at a given position was
|
||||
* pressed on the previous scan
|
||||
*/
|
||||
|
||||
// === main__row ===
|
||||
/** variables/main__row/description
|
||||
* Indicates which row is currently being tested for changes of key state
|
||||
*/
|
||||
|
||||
// === main__col ===
|
||||
/** variables/main__col/description
|
||||
* Indicates which column is currently being tested for changes of key state
|
||||
*/
|
||||
|
||||
// === main__flags ===
|
||||
/** variables/main__flags/description
|
||||
* A collection of flags pertaining to the operation of `main()`
|
||||
*
|
||||
* Struct members:
|
||||
* - `update_leds`: A predicate indicating whether to update the keyboard LED
|
||||
* state based on the USB LED state.
|
||||
* - This is for taking over control the LEDs temporarily, as one may want
|
||||
* to do when in a special mode, etc. If you want to change the meaning
|
||||
* of the LEDs under normal use, the correct place to do that is in the
|
||||
* layout file, where the `kb__led__logical_...()` functions are defined
|
||||
* (see the documentation in that and related files for more
|
||||
* information).
|
||||
*/
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2012, 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (see "doc/license.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
## description
|
||||
# Makefile for the firmware
|
||||
#
|
||||
# See '.../firmware/keyboard/.../options.mk' for options
|
||||
#
|
||||
# Notes:
|
||||
# - '.h' file dependencies are automatically generated
|
||||
#
|
||||
# History:
|
||||
# - This makefile was originally (extensively) modified from the WinAVR
|
||||
# makefile template, mostly by removing stuff. The copy I used was from
|
||||
# [pjrc : usb_keyboard] (http://pjrc.com/teensy/usb_keyboard.zip).
|
||||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
KEYBOARD_NAME := ergodox
|
||||
# the name of the keyboard to build a firmware for
|
||||
|
||||
TARGET := firmware
|
||||
# default name for the '.hex', '.eep', and '.map' files we'll be generating
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
include_options = \
|
||||
$(eval INCLUDED := $(1) $(INCLUDED)) \
|
||||
$(eval CURDIRS := $(CURDIR) $(CURDIRS)) \
|
||||
$(eval CURDIR := $(ROOTDIR)/$(1)) \
|
||||
$(eval include $(CURDIR)/options.mk) \
|
||||
$(eval CURDIR := $(firstword $(CURDIRS))) \
|
||||
$(eval CURDIRS := $(wordlist 2,$(words $(CURDIRS)),$(CURDIRS)))
|
||||
|
||||
include_options_once = \
|
||||
$(if $(findstring $(1),$(INCLUDED)), \
|
||||
, \
|
||||
$(call include_options,$(1)))
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
.PHONY: default-target
|
||||
default-target: all
|
||||
# (to retain default behavior when included makefiles specify dependencies)
|
||||
|
||||
CURDIR := .
|
||||
ROOTDIR := .
|
||||
INCLUDED :=
|
||||
# for including sub-makefiles
|
||||
# - by default, `CURDIR` is initialized to (an absolute path to) the
|
||||
# current working directory
|
||||
|
||||
SRC :=
|
||||
CFLAGS :=
|
||||
LDFLAGS :=
|
||||
GENDEPFLAGS = # need to use delayed evaluation for this one
|
||||
# (initialize variables, so we can use `+=` below and in the included files)
|
||||
|
||||
|
||||
$(call include_options_once,keyboard/$(KEYBOARD_NAME))
|
||||
$(call include_options_once,lib/usb)
|
||||
$(call include_options_once,lib/timer)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/main.c)
|
||||
# (other source files included through the makefile included above)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
CFLAGS += -mmcu=$(MCU) # processor type; must match real life
|
||||
CFLAGS += -DF_CPU=$(F_CPU) # processor frequency; must match initialization
|
||||
# in source
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
CFLAGS += -std=gnu99 # use C99 plus GCC extensions
|
||||
CFLAGS += -Os # optimize for size
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
CFLAGS += -Wall # enable lots of common warnings
|
||||
CFLAGS += -Wstrict-prototypes # "warn if a function is declared or defined
|
||||
# without specifying the argument types"
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
CFLAGS += -fpack-struct # "pack all structure members together without holes"
|
||||
CFLAGS += -fshort-enums # "allocate to an 'enum' type only as many bytes as it
|
||||
# needs for the declared range of possible values"
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
CFLAGS += -ffunction-sections # \ "place each function or data into its own
|
||||
CFLAGS += -fdata-sections # / section in the output file if the
|
||||
# target supports arbitrary sections." for
|
||||
# linker optimizations, and discarding
|
||||
# unused code.
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
LDFLAGS += -Wl,-Map=$(TARGET).map,--cref # generate a link map, with a cross
|
||||
# reference table
|
||||
LDFLAGS += -Wl,--relax # for some linker optimizations
|
||||
LDFLAGS += -Wl,--gc-sections # discard unused functions and data
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
GENDEPFLAGS += -MMD -MP -MF $@.dep # generate dependency files
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
CC := avr-gcc
|
||||
OBJCOPY := avr-objcopy
|
||||
SIZE := avr-size
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
OBJ := $(SRC:%.c=%.o)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
.PHONY: all clean cleanall all-layouts
|
||||
|
||||
all: $(TARGET).hex $(TARGET).eep
|
||||
@echo
|
||||
@echo '---------------------------------------------------------------'
|
||||
@echo '------- done --------------------------------------------------'
|
||||
@echo
|
||||
$(SIZE) --target=$(BINARY_FORMAT) $(TARGET).hex
|
||||
@echo
|
||||
$(SIZE) --target=$(BINARY_FORMAT) $(TARGET).eep
|
||||
@echo
|
||||
@echo 'you can load "$(TARGET).hex" and "$(TARGET).eep" onto the'
|
||||
@echo 'Teensy using the Teensy loader'
|
||||
@echo
|
||||
@echo '---------------------------------------------------------------'
|
||||
@echo
|
||||
|
||||
clean:
|
||||
@echo
|
||||
@echo '--- cleaning ---'
|
||||
@make clean-all-files KEYBOARD_LAYOUT=$(KEYBOARD_LAYOUT)
|
||||
|
||||
cleanall:
|
||||
@echo
|
||||
@echo '--- cleaning ---'
|
||||
@for layout in $(KEYBOARD_LAYOUTS); do \
|
||||
make clean-all-files KEYBOARD_LAYOUT=$$layout; \
|
||||
done
|
||||
-@rm -vf $(KEYBOARD_LAYOUTS:%=firmware--%.hex)
|
||||
|
||||
all-layouts:
|
||||
@for layout in $(KEYBOARD_LAYOUTS); do \
|
||||
echo; \
|
||||
echo '--- cleaning ---'; \
|
||||
make clean-target-files all KEYBOARD_LAYOUT=$$layout; \
|
||||
mv $(TARGET).hex $(TARGET)--$$layout.hex; \
|
||||
done
|
||||
@echo
|
||||
@echo '--- cleaning ---'
|
||||
@make clean-target-files
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
.PHONY: clean-all-files clean-object-files clean-target-files
|
||||
|
||||
clean-all-files: clean-object-files clean-target-files
|
||||
|
||||
clean-object-files:
|
||||
-@rm -vf $(OBJ) \
|
||||
$(OBJ:%=%.dep)
|
||||
|
||||
clean-target-files:
|
||||
-@rm -vf $(TARGET).eep \
|
||||
$(TARGET).elf \
|
||||
$(TARGET).hex \
|
||||
$(TARGET).map
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
.SECONDARY:
|
||||
|
||||
%.hex: %.elf
|
||||
@echo
|
||||
@echo '--- making $@ ---'
|
||||
# from the WinAVR makefile template (modified)
|
||||
$(OBJCOPY) -O $(BINARY_FORMAT) \
|
||||
-R .eeprom -R .fuse -R .lock -R .signature \
|
||||
$< $@
|
||||
|
||||
%.eep: %.elf
|
||||
@echo
|
||||
@echo '--- making $@ ---'
|
||||
# from the WinAVR makefile template (modified)
|
||||
-$(OBJCOPY) -O $(BINARY_FORMAT) \
|
||||
-j .eeprom \
|
||||
--set-section-flags=.eeprom="alloc,load" \
|
||||
--change-section-lma .eeprom=0 \
|
||||
--no-change-warnings \
|
||||
$< $@ || exit 0
|
||||
|
||||
$(TARGET).elf: $(OBJ)
|
||||
@echo
|
||||
@echo '--- making $@ ---'
|
||||
$(CC) $(strip $(CFLAGS)) $(strip $(LDFLAGS)) $^ --output $@
|
||||
|
||||
%.o: %.c
|
||||
@echo
|
||||
@echo '--- making $@ ---'
|
||||
$(CC) -c $(strip $(CFLAGS)) $(strip $(GENDEPFLAGS)) $< -o $@
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
-include $(OBJ:%=%.dep)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!-- TODO: everything ... -->
|
||||
|
||||
## TODO
|
||||
- include versions for all build environment programs
|
||||
|
||||
- generate documentation
|
||||
- include a collection of all "namespace" prefixes, and where they belong
|
||||
- write a note in "main.c" about where that file is (and about other
|
||||
documentation that would help in understanding the code)
|
||||
- write a note about where options are described, how they're included, how
|
||||
i have makefiles set up, etc.
|
||||
- write a mediumly thorough note about my workflow (vim, ...)
|
||||
|
||||
- add a note in the docs somewhere that SDA and SCL need to have a pull-up
|
||||
resistor on them, or `mcp23018__init()` will hang (or something like that)
|
||||
|
||||
- add notes to directories, explaining why things are organized the way they
|
||||
are; specifically, why the directory structure is so deep
|
||||
|
||||
## Dependencies
|
||||
- the gnu avr toolchain
|
||||
- python 3
|
||||
- markdown `sudo pip install markdown`
|
||||
- git (for cleaning)
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012, 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
* [Notes on LEDs]
|
||||
(http://www.gizmology.net/LEDs.htm)
|
||||
Talks about different types of LEDs, mentioning typical brightnesses,
|
||||
voltages, and other intersting stuff.
|
||||
voltages, and other interesting stuff.
|
||||
|
||||
* [All About Circuits : Reference]
|
||||
(http://www.allaboutcircuits.com/vol_5/index.html)
|
||||
|
@ -110,6 +110,110 @@
|
|||
(http://stackoverflow.com/questions/4606301/gcc-why-the-lm-flag-is-needed-to-link-the-math-library)
|
||||
(on <http://stackoverflow.com/>)
|
||||
|
||||
* [cdecl]
|
||||
(http://cdecl.org)
|
||||
Nice little web interface to a really helpful (when you're dealing with
|
||||
confusing type signatures in C) command line program.
|
||||
|
||||
* [changing a 16-bit binary number into two 8-bit binary numbers]
|
||||
(http://arduino.cc/forum/index.php/topic,44830.0.html)
|
||||
(on <http://arduino.cc/>)
|
||||
|
||||
* [what happens in os when we dereference a null pointer in c]
|
||||
(http://stackoverflow.com/questions/12645647/what-happens-in-os-when-we-dereference-a-null-pointer-in-c)
|
||||
Doesn't apply to AVRs really (since what happens in implementation defined
|
||||
according to the C standard, and I didn't see AVRs mentioned; and AVRs don't
|
||||
typically run an OS anyway), but still quite interesting.
|
||||
|
||||
* [Signed to unsigned conversion in C - is it always safe?]
|
||||
(http://stackoverflow.com/questions/50605/signed-to-unsigned-conversion-in-c-is-it-always-safe)
|
||||
What happens if you return `-1` from a `uint8_t` function (or assign `-1` to
|
||||
a `uint8_t`)?
|
||||
|
||||
* [Mod Function and Negative Numbers]
|
||||
(http://mathforum.org/library/drmath/view/52343.html)
|
||||
`(-1)%5` in python returns `4` (just like it should)
|
||||
|
||||
* [Is it safe to free a `void *`?]
|
||||
(http://stackoverflow.com/a/2182522/2360353)
|
||||
Yes. The memory manager keeps track of the size of allocations - and the
|
||||
pointer you pass to `free()` is cast to `void *` before deallocation anyway.
|
||||
|
||||
* [Why exactly should I not call free() on variables not allocated by malloc()?]
|
||||
(http://stackoverflow.com/questions/2688377/why-exactly-should-i-not-call-free-on-variables-not-allocated-by-malloc)
|
||||
But it's not safe to call `free()` on non-`malloc()`ed things.
|
||||
|
||||
* [Why do we need C Unions?]
|
||||
(http://stackoverflow.com/questions/252552/why-do-we-need-c-unions)
|
||||
Some examples of what unions are good for.
|
||||
|
||||
* [Using and Abusing Unions]
|
||||
(http://critical.eschertech.com/2010/03/12/using-and-abusing-unions/)
|
||||
A good discussion on when to use unions and when not to.
|
||||
|
||||
* [C preprocessor, recursive macros]
|
||||
(http://stackoverflow.com/questions/5641836/c-preprocessor-recursive-macros)
|
||||
I'm not entirely sure I understand what's going on... but be it known that
|
||||
function like macros that expand into other function like macros are tricky
|
||||
business.
|
||||
|
||||
* [Declaring and Using Bit Fields in Structures]
|
||||
(http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=%2Fcom.ibm.vacpp6m.doc%2Flanguage%2Fref%2Fclrc03defbitf.htm)
|
||||
|
||||
* [Bitwise shifting in C]
|
||||
(http://stackoverflow.com/a/8422852/2360353)
|
||||
Shifting unsigned values is safe, left or right. Shifting signed values is
|
||||
not always safe.
|
||||
|
||||
* [The signedness of `char`]
|
||||
(http://stackoverflow.com/questions/2054939/char-is-signed-or-unsigned-by-default)
|
||||
Is undefined by the standard, and done differently in different compilers.
|
||||
avr-gcc appears to treat it as signed, by default, unless `-funsigned-char`
|
||||
is specified on the command line. But then you have a non-default default
|
||||
behavior... which doesn't strike me as particularly clean. Better to use
|
||||
`uint8_t`s when operating on things then, when possible, and to just not
|
||||
bitshift `char`s.
|
||||
|
||||
* [GCC docs : Variable Attributes]
|
||||
(http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html)
|
||||
How to byte align variables in GCC (among other things...)
|
||||
|
||||
* [Bit-fields in C99]
|
||||
(http://blog.reverberate.org/2009/06/bit-fields-in-c99.html)
|
||||
Bit-fields [can be dangerous]
|
||||
(http://avr.2057.n7.nabble.com/Bit-field-packing-order-changed-between-avrgcc-implementations-td19193.html).
|
||||
Most things are implementation defined, and [can change]
|
||||
(http://avr.2057.n7.nabble.com/Bit-field-packing-order-changed-between-avrgcc-implementations-td19193.html)
|
||||
even between different versions of the same compiler. It seems to me that if
|
||||
you depend on binary layout, you should take care of that manually. But the
|
||||
syntax is very nice compared to bit-shifting and bit-masking if you're going
|
||||
to do a lot of it, and there are many useful situations where they'll do
|
||||
exactly what you expect, so... use caution, but don't neglect them all
|
||||
together :) . Kind of like `goto`.
|
||||
|
||||
* [Pointer comparisons in C. Are they signed or unsigned?]
|
||||
(http://stackoverflow.com/questions/6702161/pointer-comparisons-in-c-are-they-signed-or-unsigned)
|
||||
Pointer comparisons in C are interesting... Have to be careful with them.
|
||||
|
||||
* [Saturating Addition in C]
|
||||
(http://stackoverflow.com/a/3431717)
|
||||
Saturated addition is kind of difficult in C. This is my favorite of the
|
||||
methods I saw.
|
||||
|
||||
### C++ Stuff
|
||||
|
||||
* [Google C++ Style Guide]
|
||||
(http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml)
|
||||
Useful overview of C++ stuff. I'd been away from it for long enough that I
|
||||
needed a review.
|
||||
|
||||
* [Haiku : Coding Guidelines]
|
||||
(https://www.haiku-os.org/development/coding-guidelines)
|
||||
|
||||
* [C++ Language Tutorial]
|
||||
(http://www.cplusplus.com/doc/tutorial/)
|
||||
(on <http://www.cplusplus.com/>)
|
||||
|
||||
### For the AVR
|
||||
|
||||
* [AVR Newbie guide]
|
||||
|
@ -157,6 +261,40 @@
|
|||
(http://www.fourwalledcubicle.com/AVRArticles.php), along with a few
|
||||
other good articles on AVR programming
|
||||
|
||||
* [Components of the AVR-GCC Toolchain]
|
||||
(http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/AVR_GCC_Tool_Collection)
|
||||
Nice picture, and short descriptions of the relevent command line tools.
|
||||
|
||||
* [A Very Simple Arduino Task Manager]
|
||||
(http://bleaklow.com/2010/07/20/a_very_simple_arduino_task_manager.html)
|
||||
A nice little write up on cooperative multitasking with an AVR.
|
||||
|
||||
* [A small discussion on AVR endianness]
|
||||
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=337752#337752)
|
||||
|
||||
* [Another discussion on AVR endianness]
|
||||
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=337747)
|
||||
Wherin it is well argued that AVRs are neither big endian nor little endian,
|
||||
since all atomic arithmetic operators work on 8-bit data types only.
|
||||
Apparently, where there are (other) native multi-byte things going on,
|
||||
endinness is inconsistent. It seems like most things though (or at least the
|
||||
most common things?) are little endian. The LSB of a `uint16_t`, for
|
||||
instance, is stored in the lower memory address (which is apparently
|
||||
consistent with the way GCC typically does things).
|
||||
|
||||
* [Wide characters and unicode on the AVR]
|
||||
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=64431&start=0)
|
||||
|
||||
* [`char` is signed, by default, in avr-gcc]
|
||||
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=119944&start=0)
|
||||
Apparently, treating `char` as signed is more common than treating it as
|
||||
unsigned. It can be changed to unsigned, by default, with a compiler option.
|
||||
|
||||
* [EmbeddedGurus: Experts on Embedded Software]
|
||||
(http://embeddedgurus.com)
|
||||
Seems like a good resource, with lots of general tips on embedded
|
||||
programming.
|
||||
|
||||
|
||||
## Protocol Stuff
|
||||
|
||||
|
@ -206,11 +344,33 @@
|
|||
(http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf)
|
||||
: pdf (from <http://www.usb.org/developers/hidpage>)
|
||||
|
||||
### UTF-8
|
||||
|
||||
* [Reading Unicode (UTF-8) in C]
|
||||
(http://zaemis.blogspot.com/2011/06/reading-unicode-utf-8-by-hand-in-c.html)
|
||||
A short discussion on the subject.
|
||||
|
||||
* [wikipedia: Hexadecimal Code Input (for the 3 major OSs)]
|
||||
(http://en.wikipedia.org/wiki/Unicode_input#Hexadecimal_code_input)
|
||||
Note that both OS X and Windows require a little OS side setup for this to
|
||||
work.
|
||||
|
||||
### Other
|
||||
|
||||
* [Journaling and Logging File Systems]
|
||||
(http://www.eecs.harvard.edu/~margo/cs161/notes/fs-logs-slides.pdf)
|
||||
PowerPoint-ish overview of a couple file system design strategies
|
||||
|
||||
|
||||
## Other People's Code
|
||||
|
||||
### Keyboard Firmware
|
||||
|
||||
* github: [cub-uanic / tmk_keyboard]
|
||||
(https://github.com/cub-uanic/tmk_keyboard)
|
||||
A port of Hasu's keyboard firmware to the ErgoDox, by Oleg Kostyuk
|
||||
("cub-uanic").
|
||||
|
||||
* zip: [Phantom Firmware from PrinsValium]
|
||||
(http://geekhack.org/attachment.php?attachmentid=38982&d=1327895092)
|
||||
Pretty basic (unless I'm missing nuances, which is entirely possible).
|
||||
|
@ -253,6 +413,15 @@
|
|||
* mentioned on [the designer's blog]
|
||||
(http://humblehacker.com/blog/)
|
||||
|
||||
* githug: [chrisandreae / kinesis-firmware]
|
||||
(https://github.com/chrisandreae/kinesis-firmware)
|
||||
Another firmware using the ATMega32u4.
|
||||
|
||||
* mentioned in [this post] (http://deskthority.net/post106621.html#p106621)
|
||||
by [vvp] (http://deskthority.net/vvp-u3812/) on
|
||||
<http://deskthority.net/>. he's thinking about using it on a hardware
|
||||
modded ErgoDox.
|
||||
|
||||
### USB Libraries
|
||||
|
||||
* [LUFA (2012) (Lightweight USB Framework for AVRs)]
|
||||
|
@ -407,9 +576,14 @@
|
|||
(which is the spec'ed high value for Cherry MX switches) would give us a max
|
||||
scan rate of 200Hz.
|
||||
|
||||
* [Cherry MX debounce times]
|
||||
(http://geekhack.org/index.php?topic=42385.msg861321#msg861321)
|
||||
Interesting discussion. Oscilloscope diagrams of bounce for a few different
|
||||
switches near the beginning :) .
|
||||
|
||||
### Other Awesome Keyboard Projects
|
||||
|
||||
* [My DIY keyboard collection ( or how I became a KB-geek...)]
|
||||
* [My DIY keyboard collection (or how I became a KB-geek...)]
|
||||
(http://deskthority.net/workshop-f7/my-diy-keyboard-collection-or-how-i-became-a-kb-geek-t2534.html)
|
||||
: post by [suka] (http://deskthority.net/suka-u434/)
|
||||
on <http://deskthority.net/>
|
||||
|
@ -461,10 +635,104 @@
|
|||
see the link; as that tool improves it will hopefully become much better),
|
||||
but it works and I like it :) .
|
||||
|
||||
### Web Stuff
|
||||
|
||||
* [Raphaël—JavaScript Library]
|
||||
(http://raphaeljs.com)
|
||||
A library for working with SVG (and VML, on IE I think) graphics - drawing
|
||||
them, animating them, adding events... Looks really nice :)
|
||||
|
||||
* also [on github] (https://github.com/DmitryBaranovskiy/raphael/)
|
||||
|
||||
* [An Introduction to the Raphaël JS Library]
|
||||
(http://net.tutsplus.com/tutorials/javascript-ajax/an-introduction-to-the-raphael-js-library/)
|
||||
A good tutorial, it looks like.
|
||||
|
||||
* [YAML (official site)]
|
||||
(http://www.yaml.org)
|
||||
YAML is a great data serilization language.
|
||||
|
||||
* [JS-YAML : online demo]
|
||||
(http://nodeca.github.com/js-yaml/)
|
||||
The demo page of a YAML parser written in JavaScript.
|
||||
|
||||
* [Lively Kernel]
|
||||
(http://www.lively-kernel.org)
|
||||
I read about this a while ago, and just remembered it again while thinking
|
||||
about how I would write a keymap making thing in javascript. Not sure how
|
||||
good it'd be for that specifically, but the concept is just so **awesome**!
|
||||
|
||||
### Hardware Mods to the ErgoDox (horribly incomplete list)
|
||||
|
||||
* [LEDs on the left (MCP23018) side]
|
||||
(http://geekhack.org/index.php?topic=22780.msg873819#msg873819)
|
||||
: post by [Unix Guru] (http://geekhack.org/index.php?action=profile;u=24565)
|
||||
on <http://geekhack.org/>
|
||||
|
||||
### Hardware and DIY Stuff (for the keyboard case, and such)
|
||||
|
||||
* [Working with Acrylic]
|
||||
(http://www.bcae1.com/plexi.htm)
|
||||
|
||||
* mentioned in [this post]
|
||||
(http://geekhack.org/index.php?topic=22780.msg782192#msg782192)
|
||||
by [sordna] (http://geekhack.org/index.php?action=profile;u=9426)
|
||||
(on <http://geekhack.org/>)
|
||||
|
||||
* also see [this post]
|
||||
(http://geekhack.org/index.php?topic=22780.msg782080#msg782080)
|
||||
by [kurplop] (http://geekhack.org/index.php?action=profile;u=17045)
|
||||
(on <http://geekhack.org/>)
|
||||
|
||||
* [Kurplop's finishing of the aluminum case (from Massdrop round 1)]
|
||||
(http://geekhack.org/index.php?topic=22780.msg854128#msg854128)
|
||||
|
||||
* [Geekhack: DIY resources]
|
||||
(http://geekhack.org/index.php?topic=33298.0)
|
||||
A nice short list of things one might want to know for keyboard hardware
|
||||
projects.
|
||||
|
||||
### Some Random Stuff
|
||||
|
||||
* [Tutorial: Metacompilers Part 1]
|
||||
(http://www.bayfronttechnologies.com/mc_tutorial.html)
|
||||
Probably doesn't have anything to do with microprocessors *or* web
|
||||
development (at least at the moment)... but the concept is really awesome :)
|
||||
. I included it here because, as I was writing in C, the thought "I could
|
||||
write a script to generate this code in Python *so* easily..." went through
|
||||
my head quite often.
|
||||
|
||||
* [Towards Moore's Law Software: Part 1 of 3]
|
||||
(http://www.moserware.com/2008/04/towards-moores-law-software-part-1-of-3.html)
|
||||
Great article on programming in general, and where it might be going in the
|
||||
future. Along the same lines as the Metacompilers tutorial, in that it talks
|
||||
about using domain specific languages. Also talks about different
|
||||
programming paradigms that are being explored right now.
|
||||
|
||||
* [What are the available interactive languages that run in tiny memory?]
|
||||
(http://stackoverflow.com/questions/1082751/what-are-the-available-interactive-languages-that-run-in-tiny-memory)
|
||||
(on <http://stackoverflow.com/>)
|
||||
Didn't really see anything other than ooc and FORTH that might work for an
|
||||
application like this firmware.
|
||||
|
||||
* [ooc]
|
||||
(http://ooc-lang.org)
|
||||
: a programming language targeting C99
|
||||
This project doesn't seem like it's been very active in the last year or
|
||||
so... not that it looks dead (and, acutally, I didn't check the IRC channel
|
||||
or forums or anything, so I could be completely wrong), just not very active.
|
||||
And the documentation's fairly incomplete (which is sad, it looks like a
|
||||
really cool language...).
|
||||
|
||||
* [Plover, the Open Source Steno Program]
|
||||
(http://plover.stenoknight.com)
|
||||
One of the only good reasons I've seen to want NKRO ;)
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (MIT) (see "license.md")
|
||||
Copyright © 2012, 2013, 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
cache/
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
This might eventually become a layout generator :) . Until then, it's an
|
||||
experiment.
|
||||
|
||||
## TODO:
|
||||
- update readme with useful information (once we've gotten anywhere)
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
## Elm Stuff
|
||||
|
||||
* [Official Documentation]
|
||||
(http://elm-lang.org)
|
||||
|
||||
* [Standard Libaries]
|
||||
(http://library.elm-lang.org/catalog/elm-lang-Elm/0.12.3/)
|
||||
* [Examples]
|
||||
(http://elm-lang.org/Examples.elm)
|
||||
* [Syntax: a Quick Tour]
|
||||
(http://elm-lang.org/learn/Syntax.elm)
|
||||
* [Types]
|
||||
(http://elm-lang.org/learn/Getting-started-with-Types.elm)
|
||||
* [Changes in Elm 0.10]
|
||||
(http://elm-lang.org/blog/announce/0.10.elm)
|
||||
|
||||
* [What does the “Just” syntax mean in Haskell?]
|
||||
(http://stackoverflow.com/questions/18808258/what-does-the-just-syntax-mean-in-haskell/18809252#18809252)
|
||||
Haskell is very similar to Elm in some ways, so I think this applies.
|
||||
|
||||
|
||||
|
||||
## Web Stuff
|
||||
|
||||
### JavaScript
|
||||
|
||||
* [How to include a JavaScript file in another JavaScript file?]
|
||||
(http://stackoverflow.com/questions/950087/how-to-include-a-javascript-file-in-another-javascript-file)
|
||||
Not as simple as one might wish... But still not that bad.
|
||||
|
||||
* [JavaScript 101]
|
||||
(http://learn.jquery.com/javascript-101/)
|
||||
JavaScript tutorial on the jQuery site :)
|
||||
|
||||
* [How Good C# Habits can Encourage Bad JavaScript Habits: Part 1]
|
||||
(http://appendto.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/)
|
||||
Actually didn't read this all the way through... :) but good information
|
||||
|
||||
* Referenced [on stackoverflow]
|
||||
(http://stackoverflow.com/a/5947280)
|
||||
|
||||
* [Set default value of javascript object attributes]
|
||||
(http://stackoverflow.com/questions/6600868/set-default-value-of-javascript-object-attributes)
|
||||
|
||||
* [A Plain English Guide to JavaScript Prototypes]
|
||||
(http://sporto.github.io/blog/2013/02/22/a-plain-english-guide-to-javascript-prototypes/)
|
||||
About the JavaScript object model. I guess objects can have defaults?
|
||||
|
||||
* [Do You Really Need jQuery?]
|
||||
(http://www.sitepoint.com/do-you-really-need-jquery/)
|
||||
For this project I can probably get away without it...
|
||||
|
||||
|
||||
#### Libraries
|
||||
|
||||
* [Raphaël]
|
||||
(http://raphaeljs.com)
|
||||
Cool graphics library :)
|
||||
|
||||
|
||||
## HTML
|
||||
|
||||
* [W3C Documentation]
|
||||
(http://www.w3schools.com/html/html5_intro.asp)
|
||||
|
||||
|
||||
## CSS
|
||||
|
||||
* [github: CSS file for the website of Ethan Schoonover]
|
||||
(https://github.com/altercation/ethanschoonover.com/blob/master/resources/css/style.css)
|
||||
Ethan Schoonover is the creator of the Solarized color palette.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
-------------------------------------------------------------------------------
|
||||
-- Copyright (c) 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
-- Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
-- Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
{-| Keyboard definitions
|
||||
-}
|
||||
|
||||
|
||||
module Boards where
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- values
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
{-| A list of the keyboards defined
|
||||
-}
|
||||
all : [Board]
|
||||
all = [ ergodox ]
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- types and default values
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
{-| A type for keys in our keyboard
|
||||
|
||||
Fields:
|
||||
- `name`: The name of the key (so we can reference it)
|
||||
- `size`: The width of the key (in multiples of the standard 1x1 key, which
|
||||
takes up 1 grid block)
|
||||
- `position`: The position of the center of the key on the grid, with `(0,0)`
|
||||
being the top left of the board, positive x extending right, and positive y
|
||||
extending down
|
||||
- `rotation`: The rotation of the key (in degrees)
|
||||
- `value`: A name corresponding to the assigned keycode for this key
|
||||
- `configuration`: The configuration of the board to which this key belongs
|
||||
- 0: This key belongs to all configurations
|
||||
-}
|
||||
type Key = { name: String
|
||||
, size: Float
|
||||
, position: (Float,Float)
|
||||
, rotation: Float
|
||||
, value: String
|
||||
, configuration: Int
|
||||
}
|
||||
|
||||
defaultKey : Key
|
||||
defaultKey = { name = ""
|
||||
, size = 1
|
||||
, position = (0,0)
|
||||
, rotation = 0
|
||||
, value = ""
|
||||
, configuration = 0
|
||||
}
|
||||
|
||||
|
||||
{-| A type for keyboards (`Keyboard` is already used, in Elm)
|
||||
|
||||
Fields:
|
||||
- `name`: The display name
|
||||
- `size`: The horizontal and vertical size of the board, in grid squares
|
||||
- `keys`: A list of keys, making up the keyboard
|
||||
-}
|
||||
type Board = { name: String
|
||||
, size: (Float,Float)
|
||||
, keys: [Key]
|
||||
}
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- board definitions
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
{-| Definition for the ErgoDox Keyboard
|
||||
|
||||
From the firmware source:
|
||||
|
||||
/* left hand, spatial positions */
|
||||
k50,k51,k52,k53,k54,k55,k56,
|
||||
k40,k41,k42,k43,k44,k45,k46,
|
||||
k30,k31,k32,k33,k34,k35,
|
||||
k20,k21,k22,k23,k24,k25,k26,
|
||||
k10,k11,k12,k13,k14,
|
||||
k05,k06,
|
||||
k15,k16,k04,
|
||||
k03,k02,k01,
|
||||
|
||||
/* right hand, spatial positions *
|
||||
k57,k58,k59,k5A,k5B,k5C,k5D,
|
||||
k47,k48,k49,k4A,k4B,k4C,k4D,
|
||||
k38,k39,k3A,k3B,k3C,k3D,
|
||||
k27,k28,k29,k2A,k2B,k2C,k2D,
|
||||
k19,k1A,k1B,k1C,k1D,
|
||||
k07,k08,
|
||||
k09,k17,k18,
|
||||
k0C,k0B,k0A )
|
||||
-}
|
||||
|
||||
|
||||
ergodox : Board
|
||||
ergodox = { name = "ErgoDox", size = (18,8), keys = [
|
||||
|
||||
-- left hand, roughly from upper left to bottom right
|
||||
|
||||
{ defaultKey | name <- "k50", position <- (0.25,0), size <- 1.5 }
|
||||
, { defaultKey | name <- "k51", position <- (1.5,0) }
|
||||
, { defaultKey | name <- "k52", position <- (2.5,0) }
|
||||
, { defaultKey | name <- "k53", position <- (3.5,0) }
|
||||
, { defaultKey | name <- "k54", position <- (4.5,0) }
|
||||
, { defaultKey | name <- "k55", position <- (5.5,0) }
|
||||
, { defaultKey | name <- "k56", position <- (6.5,0) }
|
||||
|
||||
, { defaultKey | name <- "k40", position <- (0.25,1), size <- 1.5 }
|
||||
, { defaultKey | name <- "k41", position <- (1.5,1) }
|
||||
, { defaultKey | name <- "k42", position <- (2.5,1) }
|
||||
, { defaultKey | name <- "k43", position <- (3.5,1) }
|
||||
, { defaultKey | name <- "k44", position <- (4.5,1) }
|
||||
, { defaultKey | name <- "k45", position <- (5.5,1) }
|
||||
, { defaultKey | name <- "k46", position <- (6.5,1.25), size <- 1.5, rotation <- 90 }
|
||||
|
||||
, { defaultKey | name <- "k30", position <- (0.25,2), size <- 1.5 }
|
||||
, { defaultKey | name <- "k31", position <- (1.5,2) }
|
||||
, { defaultKey | name <- "k32", position <- (2.5,2) }
|
||||
, { defaultKey | name <- "k33", position <- (3.5,2) }
|
||||
, { defaultKey | name <- "k34", position <- (4.5,2) }
|
||||
, { defaultKey | name <- "k35", position <- (5.5,2) }
|
||||
|
||||
, { defaultKey | name <- "k20", position <- (0.25,3), size <- 1.5 }
|
||||
, { defaultKey | name <- "k21", position <- (1.5,3) }
|
||||
, { defaultKey | name <- "k22", position <- (2.5,3) }
|
||||
, { defaultKey | name <- "k23", position <- (3.5,3) }
|
||||
, { defaultKey | name <- "k24", position <- (4.5,3) }
|
||||
, { defaultKey | name <- "k25", position <- (5.5,3) }
|
||||
, { defaultKey | name <- "k26", position <- (6.5,2.75), size <- 1.5, rotation <- 90 }
|
||||
|
||||
, { defaultKey | name <- "k10", position <- (0.5,4) }
|
||||
, { defaultKey | name <- "k11", position <- (1.5,4) }
|
||||
, { defaultKey | name <- "k12", position <- (2.5,4) }
|
||||
, { defaultKey | name <- "k13", position <- (3.5,4) }
|
||||
, { defaultKey | name <- "k14", position <- (4.5,4) }
|
||||
|
||||
, { defaultKey | name <- "k05", position <- (6.5,5) }
|
||||
, { defaultKey | name <- "k06", position <- (7.5,5) }
|
||||
|
||||
, { defaultKey | name <- "k15", position <- (5.5,6), configuration <- 2 }
|
||||
, { defaultKey | name <- "k16", position <- (6.5,6), configuration <- 2 }
|
||||
, { defaultKey | name <- "k04", position <- (7.5,6) }
|
||||
|
||||
, { defaultKey | name <- "k03", position <- (5.5,6.5), size <- 2, rotation <- 90, configuration <- 1 }
|
||||
, { defaultKey | name <- "k02", position <- (6.5,6.5), size <- 2, rotation <- 90, configuration <- 1 }
|
||||
, { defaultKey | name <- "k03", position <- (5.5,7), configuration <- 2 }
|
||||
, { defaultKey | name <- "k02", position <- (6.5,7), configuration <- 2 }
|
||||
, { defaultKey | name <- "k01", position <- (7.5,7) }
|
||||
|
||||
-- right hand, roughly from upper left to bottom right
|
||||
|
||||
, { defaultKey | name <- "k57", position <- (10.5,0) }
|
||||
, { defaultKey | name <- "k58", position <- (11.5,0) }
|
||||
, { defaultKey | name <- "k59", position <- (12.5,0) }
|
||||
, { defaultKey | name <- "k5A", position <- (13.5,0) }
|
||||
, { defaultKey | name <- "k5B", position <- (14.5,0) }
|
||||
, { defaultKey | name <- "k5C", position <- (15.5,0) }
|
||||
, { defaultKey | name <- "k5D", position <- (16.75,0), size <- 1.5 }
|
||||
|
||||
, { defaultKey | name <- "k47", position <- (10.5,1.25), size <- 1.5, rotation <- -90 }
|
||||
, { defaultKey | name <- "k48", position <- (11.5,1) }
|
||||
, { defaultKey | name <- "k49", position <- (12.5,1) }
|
||||
, { defaultKey | name <- "k4A", position <- (13.5,1) }
|
||||
, { defaultKey | name <- "k4B", position <- (14.5,1) }
|
||||
, { defaultKey | name <- "k4C", position <- (15.5,1) }
|
||||
, { defaultKey | name <- "k4D", position <- (16.75,1), size <- 1.5 }
|
||||
|
||||
, { defaultKey | name <- "k38", position <- (11.5,2) }
|
||||
, { defaultKey | name <- "k39", position <- (12.5,2) }
|
||||
, { defaultKey | name <- "k3A", position <- (13.5,2) }
|
||||
, { defaultKey | name <- "k3B", position <- (14.5,2) }
|
||||
, { defaultKey | name <- "k3C", position <- (15.5,2) }
|
||||
, { defaultKey | name <- "k3D", position <- (16.75,2), size <- 1.5 }
|
||||
|
||||
, { defaultKey | name <- "k27", position <- (10.5,2.75), size <- 1.5, rotation <- -90 }
|
||||
, { defaultKey | name <- "k28", position <- (11.5,3) }
|
||||
, { defaultKey | name <- "k29", position <- (12.5,3) }
|
||||
, { defaultKey | name <- "k2A", position <- (13.5,3) }
|
||||
, { defaultKey | name <- "k2B", position <- (14.5,3) }
|
||||
, { defaultKey | name <- "k2C", position <- (15.5,3) }
|
||||
, { defaultKey | name <- "k2D", position <- (16.75,3), size <- 1.5 }
|
||||
|
||||
, { defaultKey | name <- "k19", position <- (12.5,4) }
|
||||
, { defaultKey | name <- "k1A", position <- (13.5,4) }
|
||||
, { defaultKey | name <- "k1B", position <- (14.5,4) }
|
||||
, { defaultKey | name <- "k1C", position <- (15.5,4) }
|
||||
, { defaultKey | name <- "k1D", position <- (16.5,4) }
|
||||
|
||||
, { defaultKey | name <- "k07", position <- (9.5,5) }
|
||||
, { defaultKey | name <- "k08", position <- (10.5,5) }
|
||||
|
||||
, { defaultKey | name <- "k09", position <- (9.5,6) }
|
||||
, { defaultKey | name <- "k17", position <- (10.5,6), configuration <- 2 }
|
||||
, { defaultKey | name <- "k18", position <- (11.5,6), configuration <- 2 }
|
||||
|
||||
, { defaultKey | name <- "k0C", position <- (9.5,7) }
|
||||
, { defaultKey | name <- "k0B", position <- (10.5,6.5), size <- 2, rotation <- -90, configuration <- 1 }
|
||||
, { defaultKey | name <- "k0A", position <- (11.5,6.5), size <- 2, rotation <- -90, configuration <- 1 }
|
||||
, { defaultKey | name <- "k0B", position <- (10.5,7), configuration <- 2 }
|
||||
, { defaultKey | name <- "k0A", position <- (11.5,7), configuration <- 2 }
|
||||
|
||||
]}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
-------------------------------------------------------------------------------
|
||||
-- Copyright (c) 2014 Ben Blazak <benblazak.dev@gmail.com>
|
||||
-- Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
-- Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
{-| A web-based UI for generating keyboard layouts for the firmware
|
||||
-}
|
||||
|
||||
|
||||
import Window
|
||||
import Dict
|
||||
|
||||
import Boards
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- values and signals
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
{-| The number of columns and rows in our grid
|
||||
|
||||
Notes:
|
||||
- The list gives the aspect ratio (of width to height)
|
||||
- The number gives the scale of the aspect ratio, relative to the window size
|
||||
-}
|
||||
[columns, rows] = map ((*) 5) [4,3]
|
||||
|
||||
|
||||
{-| The edge length of a square in our grid
|
||||
-}
|
||||
edgeSignal : Signal Float
|
||||
edgeSignal =
|
||||
let function = \(width,height) -> min (toFloat width / columns)
|
||||
(toFloat height / rows)
|
||||
in lift function Window.dimensions
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- groups of forms
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
background : (Int, Int) -> Float -> [Form]
|
||||
background (width,height) edge =
|
||||
let (w,h) = (toFloat width, toFloat height)
|
||||
|
||||
ruleLines = True
|
||||
lineStyle = dashed lightBlue
|
||||
ruleLineStyle = dotted charcoal
|
||||
|
||||
-- x and y represent the horizontal and vertical scaling factors
|
||||
line (x,y) style offset =
|
||||
segment (-x*w/2,-y*h/2) (x*w/2,y*h/2)
|
||||
|> traced style
|
||||
|> move (y*offset*edge, x*offset*edge)
|
||||
|
||||
lines (x,y) =
|
||||
let dimension = if x == 0 then w else h
|
||||
in map (\n -> line (x,y) lineStyle (toFloat n/2)
|
||||
|> alpha (1 / (toFloat (mod (n+1) 2 + 1) * 3)))
|
||||
[floor (-dimension/edge)*2 .. floor (dimension/edge)*2]
|
||||
|
||||
vLines = lines (0,1)
|
||||
hLines = lines (1,0)
|
||||
|
||||
rLines =
|
||||
let boxLine (x,y) offset = line (x,y) ruleLineStyle offset
|
||||
in [ boxLine (0,1) (-columns/2), boxLine (1,0) (-rows/2)
|
||||
-- , boxLine (0,1) 0, boxLine (1,0) 0
|
||||
, boxLine (0,1) (columns/2), boxLine (1,0) (rows/2)
|
||||
]
|
||||
|
||||
in hLines ++ vLines ++ if ruleLines then rLines else []
|
||||
|
||||
|
||||
keyboardTestDraw : Float -> [Form]
|
||||
keyboardTestDraw edge =
|
||||
let lineStyle =
|
||||
let temp = solid lightBlue
|
||||
in { temp | width <- 5, cap <- Round, join <- Smooth }
|
||||
|
||||
-- grab just 1 Board, and use only 1 configuration
|
||||
keyboard = head <| filter (\n -> n.name == "ErgoDox") Boards.all
|
||||
configuration = 1
|
||||
|
||||
-- s = scaling factor (from 1 square of the grid)
|
||||
-- p = position of the center of the rectangle on the grid
|
||||
formKey k =
|
||||
let (sizeX, sizeY) = (k.size*edge, edge)
|
||||
(posX, posY) = ((fst k.position)*edge, (snd k.position)*edge)
|
||||
in map (move (posX, posY) . rotate (degrees k.rotation))
|
||||
[ rect sizeX sizeY |> outlined lineStyle
|
||||
, toForm <| plainText k.name
|
||||
]
|
||||
|
||||
-- convert positions to be relative to center of the page
|
||||
keys =
|
||||
let convertPosition (x,y) = ( x - (fst keyboard.size) / 2,
|
||||
-y + (snd keyboard.size) / 2 )
|
||||
in map (\n -> { n | position <- convertPosition n.position })
|
||||
keyboard.keys
|
||||
|> filter ( \n -> n.configuration == 0
|
||||
|| n.configuration == configuration )
|
||||
|
||||
in foldl1 (++) (map formKey keys)
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- putting it all together
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
scene : (Int, Int) -> Float -> Element
|
||||
scene (width,height) edge =
|
||||
let backgroundColor = lightGray
|
||||
in layers [ collage width height [] |> color backgroundColor
|
||||
, collage width height (background (width,height) edge)
|
||||
, collage width height (keyboardTestDraw edge)
|
||||
]
|
||||
|
||||
|
||||
main : Signal Element
|
||||
main = scene <~ Window.dimensions ~ edgeSignal
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<title>layout-gen</title>
|
||||
<link rel='stylesheet' type='text/css' href='style.css'>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
|
||||
<script type='text/javascript' src='main.js'></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
// colors
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - From <http://ethanschoonover.com/solarized>
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* light dark
|
||||
* --------------------------- ---------------------------
|
||||
* base03 ........................... background ................
|
||||
* base02 ........................... background highlights .....
|
||||
* base01 optional emphasized content comments ..................
|
||||
* base00 body text ................. ...........................
|
||||
* base0 ........................... body text .................
|
||||
* base1 comments .................. optional emphasized content
|
||||
* base2 background highlights ..... ...........................
|
||||
* base3 background ................ ...........................
|
||||
* ```
|
||||
*/
|
||||
var solarized = {
|
||||
base03 : '#002b36',
|
||||
base02 : '#073642',
|
||||
base01 : '#586e75',
|
||||
base00 : '#657b83',
|
||||
base0 : '#839496',
|
||||
base1 : '#93a1a1',
|
||||
base2 : '#eee8d5',
|
||||
base3 : '#fdf6e3',
|
||||
yellow : '#b58900',
|
||||
orange : '#cb4b16',
|
||||
red : '#dc322f',
|
||||
magenta : '#d33682',
|
||||
violet : '#6c71c4',
|
||||
blue : '#268bd2',
|
||||
cyan : '#2aa198',
|
||||
green : '#859900',
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - Names taken from Elm:
|
||||
* <http://library.elm-lang.org/catalog/elm-lang-Elm/0.12.3/Color>
|
||||
* - Real names from Tango:
|
||||
* <http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines>
|
||||
* ```
|
||||
* yellow : butter
|
||||
* orange : orange
|
||||
* brown : chocolate
|
||||
* green : chameleon
|
||||
* blue : sky blue
|
||||
* purple : plum
|
||||
* red : scarlet red
|
||||
* gray : aluminum (light)
|
||||
* charcoal : aluminum (dark)
|
||||
* ```
|
||||
*/
|
||||
var tango = {
|
||||
lightYellow : '#fce94f', yellow : '#edd400', darkYellow : '#c4a000',
|
||||
lightOrange : '#fcaf3e', orange : '#f57900', darkOrange : '#ce5c00',
|
||||
lightBrown : '#e9b96e', brown : '#c17d11', darkBrown : '#8f5902',
|
||||
lightGreen : '#8ae234', green : '#73d216', darkGreen : '#4e9a06',
|
||||
lightBlue : '#729fcf', blue : '#3465a4', darkBlue : '#204a87',
|
||||
lightPurple : '#ad7fa8', purple : '#75507b', darkPurple : '#5c3566',
|
||||
lightRed : '#ef2929', red : '#cc0000', darkRed : '#a40000',
|
||||
lightGray : '#eeeeec', gray : '#d3d7cf', darkGray : '#babdb6',
|
||||
lightCharcoal : '#888a85', charcoal : '#555753', darkCharcoal : '#2e3436',
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// keyboards
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* ```
|
||||
* var keyboards = {
|
||||
* id: { // keyboard name
|
||||
* size: [1,1], // the number of 1x keys it would take to cover the board
|
||||
* keys: {
|
||||
* id: { // configuration name
|
||||
* id: { // key name
|
||||
* size: 1, // 1 = 1x key, 2 = 2x key, etc.
|
||||
* position: [0,0], // of the top left of the key
|
||||
* rotation: 0, // in degrees
|
||||
* value: 'transp', // name of the assigned key-function
|
||||
* },
|
||||
* // ... more keys
|
||||
* },
|
||||
* // ... more configurations
|
||||
* // - `all`: keys belong to all configurations
|
||||
* // - [other]: keys belong to the named configuration; at least one
|
||||
* // other configuration must be specified; a configuration may be
|
||||
* // empty (then only the `all` keys are in that configuration)
|
||||
* },
|
||||
* },
|
||||
* // ... more keyboards
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
var keyboards = {};
|
||||
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - From the firmware source:
|
||||
* ```
|
||||
* // left hand, spatial positions
|
||||
* k50,k51,k52,k53,k54,k55,k56,
|
||||
* k40,k41,k42,k43,k44,k45,k46,
|
||||
* k30,k31,k32,k33,k34,k35,
|
||||
* k20,k21,k22,k23,k24,k25,k26,
|
||||
* k10,k11,k12,k13,k14,
|
||||
* k05,k06,
|
||||
* k15,k16,k04,
|
||||
* k03,k02,k01,
|
||||
*
|
||||
* // right hand, spatial positions
|
||||
* k57,k58,k59,k5A,k5B,k5C,k5D,
|
||||
* k47,k48,k49,k4A,k4B,k4C,k4D,
|
||||
* k38,k39,k3A,k3B,k3C,k3D,
|
||||
* k27,k28,k29,k2A,k2B,k2C,k2D,
|
||||
* k19,k1A,k1B,k1C,k1D,
|
||||
* k07,k08,
|
||||
* k09,k17,k18,
|
||||
* k0C,k0B,k0A )
|
||||
* ```
|
||||
*/
|
||||
keyboards['ErgoDox'] = {
|
||||
size: [18,8],
|
||||
keys: {
|
||||
all: {
|
||||
|
||||
// left hand, roughly from upper left to bottom right
|
||||
|
||||
k50: { position: [0,0], size: 1.5 },
|
||||
k51: { position: [1.5,0] },
|
||||
k52: { position: [2.5,0] },
|
||||
k53: { position: [3.5,0] },
|
||||
k54: { position: [4.5,0] },
|
||||
k55: { position: [5.5,0] },
|
||||
k56: { position: [6.5,0] },
|
||||
|
||||
k40: { position: [0,1], size: 1.5 },
|
||||
k41: { position: [1.5,1] },
|
||||
k42: { position: [2.5,1] },
|
||||
k43: { position: [3.5,1] },
|
||||
k44: { position: [4.5,1] },
|
||||
k45: { position: [5.5,1] },
|
||||
k46: { position: [6.25,1.25], size: 1.5, rotation: -90 },
|
||||
|
||||
k30: { position: [0,2], size: 1.5 },
|
||||
k31: { position: [1.5,2] },
|
||||
k32: { position: [2.5,2] },
|
||||
k33: { position: [3.5,2] },
|
||||
k34: { position: [4.5,2] },
|
||||
k35: { position: [5.5,2] },
|
||||
|
||||
k20: { position: [0,3], size: 1.5 },
|
||||
k21: { position: [1.5,3] },
|
||||
k22: { position: [2.5,3] },
|
||||
k23: { position: [3.5,3] },
|
||||
k24: { position: [4.5,3] },
|
||||
k25: { position: [5.5,3] },
|
||||
k26: { position: [6.25,2.75], size: 1.5, rotation: -90 },
|
||||
|
||||
k10: { position: [0.5,4] },
|
||||
k11: { position: [1.5,4] },
|
||||
k12: { position: [2.5,4] },
|
||||
k13: { position: [3.5,4] },
|
||||
k14: { position: [4.5,4] },
|
||||
|
||||
k05: { position: [6.5,5] },
|
||||
k06: { position: [7.5,5] },
|
||||
|
||||
k04: { position: [7.5,6] },
|
||||
|
||||
k01: { position: [7.5,7] },
|
||||
|
||||
// right hand, roughly from upper left to bottom right
|
||||
|
||||
k57: { position: [10.5,0] },
|
||||
k58: { position: [11.5,0] },
|
||||
k59: { position: [12.5,0] },
|
||||
k5A: { position: [13.5,0] },
|
||||
k5B: { position: [14.5,0] },
|
||||
k5C: { position: [15.5,0] },
|
||||
k5D: { position: [16.5,0], size: 1.5 },
|
||||
|
||||
k47: { position: [10.25,1.25], size: 1.5, rotation: 90 },
|
||||
k48: { position: [11.5,1] },
|
||||
k49: { position: [12.5,1] },
|
||||
k4A: { position: [13.5,1] },
|
||||
k4B: { position: [14.5,1] },
|
||||
k4C: { position: [15.5,1] },
|
||||
k4D: { position: [16.5,1], size: 1.5 },
|
||||
|
||||
k38: { position: [11.5,2] },
|
||||
k39: { position: [12.5,2] },
|
||||
k3A: { position: [13.5,2] },
|
||||
k3B: { position: [14.5,2] },
|
||||
k3C: { position: [15.5,2] },
|
||||
k3D: { position: [16.5,2], size: 1.5 },
|
||||
|
||||
k27: { position: [10.25,2.75], size: 1.5, rotation: 90 },
|
||||
k28: { position: [11.5,3] },
|
||||
k29: { position: [12.5,3] },
|
||||
k2A: { position: [13.5,3] },
|
||||
k2B: { position: [14.5,3] },
|
||||
k2C: { position: [15.5,3] },
|
||||
k2D: { position: [16.5,3], size: 1.5 },
|
||||
|
||||
k19: { position: [12.5,4] },
|
||||
k1A: { position: [13.5,4] },
|
||||
k1B: { position: [14.5,4] },
|
||||
k1C: { position: [15.5,4] },
|
||||
k1D: { position: [16.5,4] },
|
||||
|
||||
k07: { position: [9.5,5] },
|
||||
k08: { position: [10.5,5] },
|
||||
|
||||
k09: { position: [9.5,6] },
|
||||
|
||||
k0C: { position: [9.5,7] },
|
||||
},
|
||||
|
||||
'Long Thumbs': {
|
||||
k03: { position: [5,6.5], size: 2, rotation: -90 },
|
||||
k02: { position: [6,6.5], size: 2, rotation: -90 },
|
||||
|
||||
k0B: { position: [10,6.5], size: 2, rotation: 90 },
|
||||
k0A: { position: [11,6.5], size: 2, rotation: 90 },
|
||||
},
|
||||
|
||||
'Split Thumbs': {
|
||||
k15: { position: [5.5,6] },
|
||||
k16: { position: [6.5,6] },
|
||||
|
||||
k03: { position: [5.5,7] },
|
||||
k02: { position: [6.5,7] },
|
||||
|
||||
k17: { position: [10.5,6] },
|
||||
k18: { position: [11.5,6] },
|
||||
|
||||
k0B: { position: [10.5,7] },
|
||||
k0A: { position: [11.5,7] },
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// test: functions and event bindings
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// TODO:
|
||||
// - need to actually plan the organization of the key data...
|
||||
// - how shall we handle switching between different layers?
|
||||
// - we should probably do all the styling in css...
|
||||
|
||||
var drawKey = function(key) {
|
||||
var size = key.size || 1;
|
||||
var position = key.position || [0,0];
|
||||
var rotation = key.rotation || 0;
|
||||
|
||||
key.raphael = {};
|
||||
|
||||
key.raphael.rect =
|
||||
paper.rect( position[0]*keysize,
|
||||
position[1]*keysize,
|
||||
size*keysize,
|
||||
keysize )
|
||||
.attr({ 'fill': tango.lightBlue,
|
||||
'stroke': tango.lightGray })
|
||||
.transform('r'+rotation);
|
||||
|
||||
key.raphael.text =
|
||||
paper.text( position[0]*keysize + size*keysize/2,
|
||||
position[1]*keysize + keysize/2,
|
||||
key.value )
|
||||
.attr({ 'font-family': 'monospace' })
|
||||
.transform( 'r' + rotation +
|
||||
's' + Math.min( size*8/key.value.length, 8/3) );
|
||||
|
||||
key.raphael.button =
|
||||
paper.rect( position[0]*keysize,
|
||||
position[1]*keysize,
|
||||
size*keysize,
|
||||
keysize )
|
||||
.attr({ 'fill': '#fff',
|
||||
'stroke': 'none',
|
||||
'opacity': 0 })
|
||||
.transform('r'+rotation)
|
||||
.mouseover(function() {
|
||||
key.raphael.button.attr({ 'opacity': .1 });
|
||||
})
|
||||
.mouseout(function() {
|
||||
key.raphael.button.attr({ 'opacity': 0 });
|
||||
})
|
||||
.mouseup(function() {
|
||||
key.raphael.rect.attr({ 'fill': tango.lightOrange });
|
||||
});
|
||||
}
|
||||
|
||||
var updateKey = function(key) {
|
||||
var size = key.size || 1;
|
||||
var rotation = key.rotation || 0;
|
||||
|
||||
key.raphael.text
|
||||
.attr({'text': key.value})
|
||||
.transform( 'r' + rotation +
|
||||
's' + Math.min( size*8/key.value.length, 8/3) );
|
||||
}
|
||||
|
||||
|
||||
// main() (kind of) -----------------------------------------------------------
|
||||
|
||||
window.onload = function() {
|
||||
// style
|
||||
document.body.style.background = tango.lightGray;
|
||||
|
||||
// setup
|
||||
keyboard = keyboards['ErgoDox'];
|
||||
configuration = 'Long Thumbs';
|
||||
|
||||
keysize = 990/keyboard['size'][0];
|
||||
|
||||
paper = Raphael( 0, 0, keyboard.size[0]*keysize+10,
|
||||
keyboard.size[1]*keysize+10 );
|
||||
|
||||
// draw bounding box
|
||||
paper.rect( 0, 0,
|
||||
keyboard.size[0]*keysize+10,
|
||||
keyboard.size[1]*keysize+10,
|
||||
5 )
|
||||
.attr('stroke', tango.lightBlue);
|
||||
|
||||
// draw keys
|
||||
for (var id in keyboard.keys[configuration]) {
|
||||
var key = keyboard.keys[configuration][id];
|
||||
key.position[0] += 5/keysize;
|
||||
key.position[1] += 5/keysize;
|
||||
key.value = id;
|
||||
drawKey(key);
|
||||
}
|
||||
for (var id in keyboard.keys.all) {
|
||||
var key = keyboard.keys['all'][id];
|
||||
key.position[0] += 5/keysize;
|
||||
key.position[1] += 5/keysize;
|
||||
key.value = id;
|
||||
drawKey(key);
|
||||
}
|
||||
}
|
||||
|
118
makefile
118
makefile
|
@ -1,118 +0,0 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# makefile for the ergoDOX project
|
||||
#
|
||||
# This should produce a single file (probably in an archive format) for
|
||||
# distribution, containing everything people will need to use the software.
|
||||
#
|
||||
# DEPENDENCIES: This is unabashedly dependant on (the GNU implementation of)
|
||||
# various Unix commands, and therefore probably won't work in a Windows
|
||||
# environment (besides maybe cygwin). Sorry... I don't know a good portable
|
||||
# way to write it.
|
||||
#
|
||||
# TODO:
|
||||
# - include doc files (and maybe render them in html)
|
||||
# - include the UI stuff (once it's done)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
# Released under The MIT License (MIT) (see "license.md")
|
||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
include src/makefile-options
|
||||
|
||||
# which layouts to compile (will override the variable in src/makefile-options)
|
||||
# --- default
|
||||
LAYOUT := qwerty-kinesis-mod
|
||||
# --- all
|
||||
LAYOUTS := qwerty-kinesis-mod dvorak-kinesis-mod colemak-symbol-mod
|
||||
|
||||
# system specific stuff
|
||||
UNAME := $(shell uname)
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DATE_PROG := gdate
|
||||
else
|
||||
DATE_PROG := date
|
||||
endif
|
||||
|
||||
CURRENT_DATE := $(shell $(DATE_PROG) --rfc-3339 s)
|
||||
|
||||
# git info
|
||||
GIT_BRANCH := $(shell git branch -l | grep '*' | cut -c 3-)
|
||||
GIT_COMMIT_DATE := $(shell git log -n 1 --pretty --date=iso | grep 'Date' | cut -c 9- )
|
||||
GIT_COMMIT_ID := $(shell git log -n 1 | grep 'commit' | cut -c 8-)
|
||||
|
||||
# name to use for the final distribution file or package
|
||||
TARGET := ergodox-firmware--$(GIT_BRANCH)--$(shell $(DATE_PROG) -d "$(GIT_COMMIT_DATE)" +'%Y%m%dT%H%M%S')--$(shell echo $(GIT_COMMIT_ID) | cut -c 1-7)--$(LAYOUT)
|
||||
|
||||
# directories
|
||||
BUILD := build
|
||||
ROOT := $(BUILD)/$(TARGET)
|
||||
SCRIPTS := build-scripts
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
.PHONY: all clean checkin build-dir firmware dist zip zip-all
|
||||
|
||||
all: dist
|
||||
|
||||
clean:
|
||||
git clean -dX # remove ignored files and directories
|
||||
-rm -r '$(BUILD)'
|
||||
|
||||
checkin:
|
||||
-git commit -a
|
||||
|
||||
build-dir:
|
||||
-rm -r '$(BUILD)/$(TARGET)'*
|
||||
-mkdir -p '$(BUILD)/$(TARGET)'
|
||||
|
||||
firmware:
|
||||
cd src; $(MAKE) LAYOUT=$(LAYOUT) all
|
||||
|
||||
$(ROOT)/firmware.%: firmware
|
||||
cp 'src/firmware.$*' '$@'
|
||||
|
||||
|
||||
$(ROOT)/firmware--ui-info.json: $(SCRIPTS)/gen-ui-info.py checkin
|
||||
( ./'$<' \
|
||||
--current-date '$(shell $(DATE_PROG) --rfc-3339 s)' \
|
||||
--git-commit-date '$(GIT_COMMIT_DATE)' \
|
||||
--git-commit-id '$(GIT_COMMIT_ID)' \
|
||||
--map-file-path '$(BUILD)/$(TARGET)/firmware.map' \
|
||||
--source-code-path 'src' \
|
||||
--matrix-file-path 'src/keyboard/$(KEYBOARD)/matrix.h' \
|
||||
--layout-file-path \
|
||||
'src/keyboard/$(KEYBOARD)/layout/$(LAYOUT).c' \
|
||||
) > '$@'
|
||||
|
||||
$(ROOT)/firmware--layout.html: \
|
||||
$(SCRIPTS)/gen-layout.py \
|
||||
$(ROOT)/firmware--ui-info.json
|
||||
\
|
||||
( ./'$<' \
|
||||
--ui-info-file '$(ROOT)/firmware--ui-info.json' \
|
||||
) > '$@'
|
||||
|
||||
|
||||
dist: \
|
||||
checkin \
|
||||
build-dir \
|
||||
$(ROOT)/firmware.hex \
|
||||
$(ROOT)/firmware.eep \
|
||||
$(ROOT)/firmware.map \
|
||||
$(ROOT)/firmware--ui-info.json \
|
||||
$(ROOT)/firmware--layout.html
|
||||
|
||||
zip: dist
|
||||
( cd '$(BUILD)/$(TARGET)'; \
|
||||
zip '../$(TARGET).zip' \
|
||||
-r * .* \
|
||||
-x '..*' )
|
||||
|
||||
zip-all:
|
||||
for layout in $(LAYOUTS); do \
|
||||
make LAYOUT=$$layout zip; \
|
||||
done
|
||||
|
332
readme.md
332
readme.md
|
@ -1,311 +1,35 @@
|
|||
# [ergodox-firmware][]: Firmware for the [ErgoDox keyboard][]
|
||||
# rev-2 (`partial-rewrite`)
|
||||
|
||||
The official website is [ergodox.org] (http://www.ergodox.org).
|
||||
Also see the [geekhack]
|
||||
(http://geekhack.org/showthread.php?22780-Interest-Check-Custom-split-ergo-keyboard)
|
||||
and [deskthority]
|
||||
(http://deskthority.net/workshop-f7/split-ergonomic-keyboard-project-t1753.html)
|
||||
discussion threads.
|
||||
This branch is currently under development, and all parts are subject to
|
||||
change. I'll do my very best to keep the latest commit working properly
|
||||
though.
|
||||
|
||||
[ergodox-firmware]: https://github.com/benblazak/ergodox-firmware
|
||||
[ergodox keyboard]: http://ergodox.org/
|
||||
Source level documentation is being written with the code. Other documentation
|
||||
(including a better readme) will be written afterwards.
|
||||
|
||||
|
||||
## About This File
|
||||
|
||||
If you're viewing this on github, please note that directory links will only
|
||||
work if you're viewing this from the directory, and file links will only work
|
||||
if you're viewing this as a file. This is true for all the '.md' files here.
|
||||
The limitation is due to the way github addresses directories and files, and
|
||||
the fact that Markdown doesn't have any way (that I know of) to rewrite the
|
||||
URLs as would be required.
|
||||
|
||||
|
||||
## Features (on the ErgoDox)
|
||||
* 6KRO
|
||||
* Teensy 2.0, MCP23018 I/O expander
|
||||
* ~167 Hz scan rate (last time I measured it) (most of which is spent
|
||||
communicating via I²C)
|
||||
* firmware level layers
|
||||
|
||||
|
||||
## About This Project
|
||||
|
||||
If you're just looking for binaries, they can be downloaded [here]
|
||||
[dropbox-download-page].
|
||||
|
||||
If you're just trying to compile, jump to the How To: [Compile the Source Code]
|
||||
(#compile-the-source-code) section.
|
||||
|
||||
Open issues, feature requests, and such are tracked [on github]
|
||||
(/benblazak/ergodox-firmware/issues).
|
||||
|
||||
This project is still definitely a work in progress, but it's getting towards
|
||||
something I'll be happy with when the keyboard finally goes into group buy.
|
||||
The 'master' branch should always contain the most recent "stable" release of
|
||||
the code. The 'dev' branch may have new things, but it may also have
|
||||
expiremental or not yet fixed things. Code on the 'master' branch should also
|
||||
tend to be more thoroughly tested. Please see the source (and especially the
|
||||
accompanying '.md' files) for documentation. And [references.md]
|
||||
(references.md) contains lots of good links, along with descriptions.
|
||||
|
||||
|
||||
## About This Project (more technical)
|
||||
|
||||
If you're looking to hack on the source, or just feel like reading it:
|
||||
|
||||
* The [makefile] (./makefile) and [build-scripts] (./build-scripts) folder in
|
||||
the toplevel directory are for building a collection of files for easy
|
||||
distribution. They are not guaranteed to work on non-Unix systems, and may
|
||||
be (read: are) more hackish than the stuff in [src] (./src). They help me
|
||||
out though.
|
||||
* [src/lib] (src/lib) is for generally useful stuff relating to the firmware.
|
||||
[src/lib-other] (src/lib-other) is for generally useful stuff that I didn't
|
||||
write myself. The TWI and USB libraries are in there, along with the files
|
||||
containing key press and release functions.
|
||||
* [src/keyboard] (src/keyboard) is for keyboard specific stuff. All the chip
|
||||
initialization code is there, along with the layout files, the software
|
||||
matrix to hardware matrix mapping, and hardware specific documentation.
|
||||
* [src/main.c] (src/main.c) ties it all together, and provides a few higher
|
||||
level functions that are useful in the key press and release functions.
|
||||
|
||||
|
||||
A few concepts that might be different:
|
||||
|
||||
* The layer stack
|
||||
|
||||
When activated, layers are pushed onto the top of a stack. When deactivated,
|
||||
layers are popped out from wherever they are in the stack. Layers may be
|
||||
active in the stack more than once. When a keypress occures, the top layer
|
||||
is used to determine what actions to take.
|
||||
|
||||
* Keypresses are functions
|
||||
|
||||
Each time a key is pressed, the "press" function assigned to that key on the
|
||||
current layer is called. When the key is released, the "release" function
|
||||
(from the same layer the key was on when it was pressed) is called. These
|
||||
functions may do pretty much anything - from sending multiple different
|
||||
keypresses to the host, to changing the firmware state in some way, to
|
||||
activating a new layer. They may also be "transparent", i.e. execute the
|
||||
function assigned to the key on the layer one down from the top layer
|
||||
(allowing for layers that are effectively "masks" over whatever layer was
|
||||
active before them).
|
||||
|
||||
|
||||
## Dependencies (for building from source)
|
||||
|
||||
* See the PJRC [Getting Started] (http://pjrc.com/teensy/first_use.html) page
|
||||
for instructions on how to set up an AVR programming environment (be sure to
|
||||
click through all the subsections (in the navigation bar on the left), as
|
||||
there is essential information in each one). This project uses C (not
|
||||
Arduino), and Make. I'm compiling with GNU tools under OS X, but other
|
||||
environments (especially Linux, appropriately set up, or [WinAVR]
|
||||
(http://winavr.sourceforge.net/) under Windows) should work too.
|
||||
|
||||
* I also assume that you are using [git] (http://git-scm.com/) (for `make
|
||||
clean`).
|
||||
|
||||
|
||||
|
||||
## HowTo
|
||||
|
||||
Most of these instructions (or notes, rather) are meant for people who's
|
||||
googling skills and patience are directly proportional to the amount of C
|
||||
programming they don't already know :) . I've done my best to organize and
|
||||
comment things though, so I hope that just about anyone who manages to find
|
||||
their way all the way here will be able to figure out a good deal from context.
|
||||
|
||||
|
||||
### Load Firmware onto the Teensy
|
||||
(beginner)
|
||||
|
||||
Before beginning, make sure:
|
||||
|
||||
* Your Teensy is plugged into your computer via USB
|
||||
|
||||
* You have the appropriate version of [the Teensy loader application]
|
||||
(http://www.pjrc.com/teensy/loader.html) from PJRC installed.
|
||||
|
||||
* You know what the "reset button" (a.k.a. the "tiny pushbutton") on the Teensy
|
||||
is. See the "HalfKay Bootloader Mode" section of the [Teensy First Use]
|
||||
(http://www.pjrc.com/teensy/first_use.html) page on the PJRC website.
|
||||
|
||||
After getting set up:
|
||||
|
||||
* Run the Teensy loader program.
|
||||
* This will give you a window, as shown on the website.
|
||||
|
||||
* Click the "Auto" button on the upper right hand side of the window.
|
||||
* The button will light up brighter green.
|
||||
* This tells the loader program to load its current file whenever the
|
||||
Teensy is ready for it.
|
||||
|
||||
* Locate your '.eep' and '.hex' files.
|
||||
* If you don't have any, you can grab the latest ones [here]
|
||||
[dropbox-download-page] (in a '.zip' file). Choose the newest file who's
|
||||
name contains the name of the keymap you want (look for "qwerty" if
|
||||
you're not sure).
|
||||
|
||||
* Drag and drop the '.eep' file onto the Teensy loader window.
|
||||
* The information bar at the bottom of the window should now read
|
||||
"firmware.eep" followed by the percentage of the Teensy memory that will
|
||||
be used by this file.
|
||||
|
||||
* Press and release the Teensy reset button (a.k.a. the "tiny pushbutton").
|
||||
* The Teensy loader should inform you that it is loading the file. Wait
|
||||
until it's done: it shouldn't take long.
|
||||
|
||||
* Drag and drop the '.hex' file onto the Teensy loader window.
|
||||
|
||||
* Press and release the Teensy reset button (a.k.a. the "tiny pushbutton").
|
||||
|
||||
* Your firmware is now loaded! Press a few buttons on your keyboard to make
|
||||
sure everything worked out all right, and enjoy :)
|
||||
**Pull requests for this branch will almost certainly not be accepted at this
|
||||
time.**
|
||||
Sorry. It's a bit hard to explain exactly why. Perhaps it will be
|
||||
understandable if I just say that I've been working on this project for quite a
|
||||
while now, and at this point rev-2 is kind of my baby. One of my goals for it
|
||||
is to not only have it work and have decent features, but be well designed and
|
||||
well documented to the point that someone starting out where I did could pick
|
||||
it up and learn without having to write their own. Until it grows up, and I
|
||||
send it out into the real world by moving it to the master branch, I'm likely
|
||||
to feel very protective of it.
|
||||
|
||||
Notes:
|
||||
* Gyuri Horak (dyuri) implemented NKRO and mouse keys in [his branch]
|
||||
(https://github.com/dyuri/ergodox-firmware) of rev-2. Not merged, but I plan
|
||||
to implement the features later, if I can. See [Pull Request #28]
|
||||
(https://github.com/benblazak/ergodox-firmware/pull/28).
|
||||
* Oleg Kostyuk (cub-uanic) [ported the TMK firmware]
|
||||
(https://github.com/cub-uanic/tmk_keyboard)
|
||||
(written by "hasu") to the ErgoDox!
|
||||
|
||||
* It may not be necessary to load the '.eep' file (if the file is 0 bytes, and
|
||||
the Teensy doesn't have anything loaded into its EEPROM already, it doesn't
|
||||
make any difference), but it's good to do so anyway, just to be safe. It
|
||||
won't hurt anything either way.
|
||||
|
||||
* Now that your firmware is loaded, there should be a keyboard shortcut you can
|
||||
press instead of the Teensy reset button. See the documentation on your
|
||||
layout for more information.
|
||||
|
||||
|
||||
### Change the Direction of the Diodes
|
||||
(intermediate)
|
||||
|
||||
That is, how to change whether the rows or the columns are being driven. This can be done for each side of the keyboard independently.
|
||||
|
||||
* See [src/keyboard/ergodox/options.h] (src/keyboard/ergodox/options.h)
|
||||
|
||||
* After this, you'll need to recompile. See the [About This Project]
|
||||
(#about-this-project) section above.
|
||||
|
||||
|
||||
### Compile the Source Code
|
||||
(brief notes)
|
||||
|
||||
Note: This relates to compiling in the [src] (src) directory. The toplevel
|
||||
build process (for generating the ui-info file and such) isn't really intended
|
||||
to be portable; but you could probably get it working without *too* much
|
||||
trouble, if you're familiar with programming in a Unix environment. I'd
|
||||
suggest looking through the toplevel Makefile, as a staring point, if that's
|
||||
your goal.
|
||||
|
||||
* Read the [Dependencies] (#dependencies-for-building-from-source) section.
|
||||
|
||||
* Take a quickish glance at the [About This Project (more technical)]
|
||||
(#about-this-project-more-technical) section.
|
||||
|
||||
* Navigate to the [src] (src) directory (*not* the toplevel directory) in a
|
||||
terminal, and type `make`.
|
||||
|
||||
* If everything worked, the '.hex' and '.eep' files will be in the [src] (src)
|
||||
directory (where you currently are).
|
||||
|
||||
|
||||
### Create a New Keymap
|
||||
|
||||
* Files to reference:
|
||||
* Keycode macros: see the file
|
||||
[src/lib/usb/usage-page/keyboard--short-names.h]
|
||||
(src/lib/usb/usage-page/keyboard--short-names.h).
|
||||
* See [keyboard.h] (src/lib/usb/usage-page/keyboard.h) for the actual
|
||||
values, with references to the specification.
|
||||
* Keyboard functions: see all files in the folder
|
||||
[src/lib/key-functions/public] (src/lib/key-functions/public).
|
||||
* Template layout files: see the QWERTY keymap source files in the folder
|
||||
[src/keyboard/ergodox/layout] (src/keyboard/ergodox/layout)
|
||||
* Currently [qwerty-kinesis-mod.c]
|
||||
(src/keyboard/ergodox/layout/qwerty-kinesis-mod.c) and
|
||||
[qwerty-kinesis-mod.h]
|
||||
(src/keyboard/ergodox/layout/qwerty-kinesis-mod.h)).
|
||||
* You'll probably want to make a copy of each to use as a template.
|
||||
|
||||
* You will need to set the `LAYOUT` variable in [src/makefile-options]
|
||||
(src/makefile-options) to the base name of your new layout files before you
|
||||
recompile. ('.h' files may be called what you wish, but '.c' files must all
|
||||
have the same prefix (i.e. "base name") or they won't be compiled).
|
||||
|
||||
* Among other things, the '.h' layout file defines the macros that control the
|
||||
meaning of each of the LEDs on the keyboard (capslock, etc.). They may be
|
||||
changed freely (or removed, to disable that LED).
|
||||
|
||||
* The '.c' layout file defines the values (keycode|value, press function,
|
||||
release function) assigned to each key, for each layer.
|
||||
* If a "press" function is set to NULL for a given layer, nothing will be
|
||||
called when the key is pressed on that layer. Likewise for "release"
|
||||
functions. If both are set to `NULL`, nothing will happen when the key
|
||||
is pressed in either event, so it doesn't matter what the keycode|value
|
||||
is for that layer - but you should probably pick something like `0` and
|
||||
stick to it, just for clarity.
|
||||
* The default number of layers is 10 (defined in
|
||||
[default--matrix-control.h]
|
||||
(src/keyboard/ergodox/layout/default--matrix-control.h) - you can
|
||||
override it in the layout's '.h' file, if you like). You don't have to
|
||||
define all of them in the '.c' layout file, just the ones you want (C
|
||||
sets the uninitialized portions of the matrices to 0).
|
||||
* Make sure that in layer-0 **nothing is transparent** (see [About this
|
||||
Project (more technical)] (#about-this-project-more-technical). Behavior
|
||||
is undefined if this occurs (off the top of my head, it'll probably cause
|
||||
an infinite loop - and you'll have to reset your keyboard (unplug it and
|
||||
plug it in again)).
|
||||
* Be careful how you assign things. Pay **close attention** to the
|
||||
possible combinations of keypresses that could occur. It's perfectly
|
||||
possible, for example, to make a layout that can shift to layer 2 (or
|
||||
some layer that doesn't even exist) and can't shift back, or that fails
|
||||
to tell the host when keys are released. It's fairly unlikely that
|
||||
anything bad will happen if you mess up (though you could theoretically
|
||||
generate a sequence of keypresses that accidentally do very bad things on
|
||||
your machine), but it's important to have the possibility in mind.
|
||||
Please consider yourself warned :) .
|
||||
|
||||
|
||||
### Add Code for a Different Type of Keyboard
|
||||
|
||||
* All the function prototypes and macro definitions required by the rest of the
|
||||
code should be in the following files (using the ergodox code as an example):
|
||||
* [controller.h] (src/keyboard/ergodox/controller.h)
|
||||
* [layout.h] (src/keyboard/ergodox/layout.h) (which in the ergodox code
|
||||
only exists to include
|
||||
[layout/default--led-control.h]
|
||||
(src/keyboard/ergodox/layout/default--led-control.h) and
|
||||
[layout/default--matrix-control.h]
|
||||
(src/keyboard/ergodox/layout/default--matrix-control.h))
|
||||
* [matrix.h] (src/keyboard/ergodox/matrix.h)
|
||||
|
||||
* (In broad terms, you'll need to define functions that initialize and update
|
||||
the matrix, LED control macros, macros defining matrix dimensions, and
|
||||
keycode | key-lookup macros (or functions).)
|
||||
|
||||
* You will need to set the `KEYBOARD` variable in [src/makefile-options]
|
||||
(src/makefile-options) to the name of your new keyboard folder before you
|
||||
recompile. You may also wish to change some of the other options in that
|
||||
file.
|
||||
|
||||
* The '.h' files listed above *must* exist, with those names, in the toplevel
|
||||
of your keyboard's directory (e.g. in `src/keyboard/new-keyboard-name`).
|
||||
They may include other '.h' files if you wish to put various definitions or
|
||||
prototypes in other places. They will be included (with the help of some
|
||||
handy macros) in the corresponding files in [src/keyboard] (src/keyboard),
|
||||
which are in turn the files included by "main" and the keyboard functions.
|
||||
|
||||
* If you change the way things are included, be careful for circular includes.
|
||||
"main", the keyboard functions, and the keyboard code all need various
|
||||
parts of each other.
|
||||
|
||||
* Make sure to keep your eye on the `SRC` variable in [src/makefile]
|
||||
(src/makefile), to make sure all your '.c' files are getting compiled.
|
||||
|
||||
|
||||
|
||||
[dropbox-download-page]: https://www.dropbox.com/sh/8bbol6fkvydmtmg/QLudrdEyc9
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
Released under The MIT License (MIT) (see "license.md")
|
||||
Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
Status:
|
||||
* (updated 2014-04-12) School and chores and life in general still taking most
|
||||
of my time. I still plan to finish everything I've already talked about
|
||||
though: macro recording, (attempt) USB features, layout examples, and other
|
||||
documentation.
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* controller specific exports
|
||||
*
|
||||
* Files for different keyboards are used by modifying a variable in the
|
||||
* Makefile
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "../lib/variable-include.h"
|
||||
#define INCLUDE EXP_STR( ./MAKEFILE_KEYBOARD/controller.h )
|
||||
#include INCLUDE
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller specific code
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "./matrix.h"
|
||||
#include "./controller/mcp23018--functions.h"
|
||||
#include "./controller/teensy-2-0--functions.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/* returns
|
||||
* - success: 0
|
||||
* - error: number of the function that failed
|
||||
*/
|
||||
uint8_t kb_init(void) {
|
||||
if (teensy_init()) // must be first
|
||||
return 1;
|
||||
if (mcp23018_init()) // must be second
|
||||
return 2;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/* returns
|
||||
* - success: 0
|
||||
* - error: number of the function that failed
|
||||
*/
|
||||
uint8_t kb_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) {
|
||||
if (teensy_update_matrix(matrix))
|
||||
return 1;
|
||||
if (mcp23018_update_matrix(matrix))
|
||||
return 2;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller specific exports
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef KEYBOARD__ERGODOX__CONTROLLER_h
|
||||
#define KEYBOARD__ERGODOX__CONTROLLER_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "./matrix.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
#include "./controller/teensy-2-0--led.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
uint8_t kb_init(void);
|
||||
uint8_t kb_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller : MCP23018 specific exports : functions
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef KEYBOARD__ERGODOX__CONTROLLER__MCP23018__FUNCTIONS_h
|
||||
#define KEYBOARD__ERGODOX__CONTROLLER__MCP23018__FUNCTIONS_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../matrix.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
#define MCP23018_TWI_ADDRESS 0b0100000
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
uint8_t mcp23018_init(void);
|
||||
uint8_t mcp23018_update_matrix( bool matrix[KB_ROWS][KB_COLUMNS] );
|
||||
|
||||
#endif
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller: MCP23018 specific code
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <util/twi.h>
|
||||
#include "../../../lib/twi.h" // `TWI_FREQ` defined in "teensy-2-0.c"
|
||||
#include "../options.h"
|
||||
#include "../matrix.h"
|
||||
#include "./mcp23018--functions.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// check options
|
||||
#if (MCP23018__DRIVE_ROWS && MCP23018__DRIVE_COLUMNS) \
|
||||
|| !(MCP23018__DRIVE_ROWS || MCP23018__DRIVE_COLUMNS)
|
||||
#error "See 'Pin drive direction' in 'options.h'"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// register addresses (see "mcp23018.md")
|
||||
#define IODIRA 0x00 // i/o direction register
|
||||
#define IODIRB 0x01
|
||||
#define GPPUA 0x0C // GPIO pull-up resistor register
|
||||
#define GPPUB 0x0D
|
||||
#define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT)
|
||||
#define GPIOB 0x13
|
||||
#define OLATA 0x14 // output latch register
|
||||
#define OLATB 0x15
|
||||
|
||||
// TWI aliases
|
||||
#define TWI_ADDR_WRITE ( (MCP23018_TWI_ADDRESS<<1) | TW_WRITE )
|
||||
#define TWI_ADDR_READ ( (MCP23018_TWI_ADDRESS<<1) | TW_READ )
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/* returns:
|
||||
* - success: 0
|
||||
* - failure: twi status code
|
||||
*
|
||||
* notes:
|
||||
* - `twi_stop()` must be called *exactly once* for each twi block, the way
|
||||
* things are currently set up. this may change in the future.
|
||||
*/
|
||||
uint8_t mcp23018_init(void) {
|
||||
uint8_t ret;
|
||||
|
||||
// set pin direction
|
||||
// - unused : input : 1
|
||||
// - input : input : 1
|
||||
// - driving : output : 0
|
||||
twi_start();
|
||||
ret = twi_send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi_send(IODIRA);
|
||||
#if MCP23018__DRIVE_ROWS
|
||||
twi_send(0b11111111); // IODIRA
|
||||
twi_send(0b11000000); // IODIRB
|
||||
#elif MCP23018__DRIVE_COLUMNS
|
||||
twi_send(0b10000000); // IODIRA
|
||||
twi_send(0b11111111); // IODIRB
|
||||
#endif
|
||||
twi_stop();
|
||||
|
||||
// set pull-up
|
||||
// - unused : on : 1
|
||||
// - input : on : 1
|
||||
// - driving : off : 0
|
||||
twi_start();
|
||||
ret = twi_send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi_send(GPPUA);
|
||||
#if MCP23018__DRIVE_ROWS
|
||||
twi_send(0b11111111); // GPPUA
|
||||
twi_send(0b11000000); // GPPUB
|
||||
#elif MCP23018__DRIVE_COLUMNS
|
||||
twi_send(0b10000000); // GPPUA
|
||||
twi_send(0b11111111); // GPPUB
|
||||
#endif
|
||||
twi_stop();
|
||||
|
||||
// set logical value (doesn't matter on inputs)
|
||||
// - unused : hi-Z : 1
|
||||
// - input : hi-Z : 1
|
||||
// - driving : hi-Z : 1
|
||||
twi_start();
|
||||
ret = twi_send(TWI_ADDR_WRITE);
|
||||
if (ret) goto out; // make sure we got an ACK
|
||||
twi_send(OLATA);
|
||||
twi_send(0b11111111); //OLATA
|
||||
twi_send(0b11111111); //OLATB
|
||||
|
||||
out:
|
||||
twi_stop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* returns:
|
||||
* - success: 0
|
||||
* - failure: twi status code
|
||||
*/
|
||||
#if KB_ROWS != 6 || KB_COLUMNS != 14
|
||||
#error "Expecting different keyboard dimensions"
|
||||
#endif
|
||||
uint8_t mcp23018_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) {
|
||||
uint8_t ret, data;
|
||||
|
||||
// initialize things, just to make sure
|
||||
// - it's not appreciably faster to skip this, and it takes care of the
|
||||
// case when the i/o expander isn't plugged in during the first
|
||||
// init()
|
||||
ret = mcp23018_init();
|
||||
|
||||
// if there was an error
|
||||
if (ret) {
|
||||
// clear our part of the matrix
|
||||
for (uint8_t row=0; row<=5; row++)
|
||||
for (uint8_t col=0; col<=6; col++)
|
||||
matrix[row][col] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// update our part of the matrix
|
||||
|
||||
#if MCP23018__DRIVE_ROWS
|
||||
for (uint8_t row=0; row<=5; row++) {
|
||||
// set active row low : 0
|
||||
// set other rows hi-Z : 1
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOB);
|
||||
twi_send( 0xFF & ~(1<<(5-row)) );
|
||||
twi_stop();
|
||||
|
||||
// read column data
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOA);
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_READ);
|
||||
twi_read(&data);
|
||||
twi_stop();
|
||||
|
||||
// update matrix
|
||||
for (uint8_t col=0; col<=6; col++) {
|
||||
matrix[row][col] = !( data & (1<<col) );
|
||||
}
|
||||
}
|
||||
|
||||
// set all rows hi-Z : 1
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOB);
|
||||
twi_send(0xFF);
|
||||
twi_stop();
|
||||
|
||||
#elif MCP23018__DRIVE_COLUMNS
|
||||
for (uint8_t col=0; col<=6; col++) {
|
||||
// set active column low : 0
|
||||
// set other columns hi-Z : 1
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOA);
|
||||
twi_send( 0xFF & ~(1<<col) );
|
||||
twi_stop();
|
||||
|
||||
// read row data
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOB);
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_READ);
|
||||
twi_read(&data);
|
||||
twi_stop();
|
||||
|
||||
// update matrix
|
||||
for (uint8_t row=0; row<=5; row++) {
|
||||
matrix[row][col] = !( data & (1<<(5-row)) );
|
||||
}
|
||||
}
|
||||
|
||||
// set all columns hi-Z : 1
|
||||
twi_start();
|
||||
twi_send(TWI_ADDR_WRITE);
|
||||
twi_send(GPIOA);
|
||||
twi_send(0xFF);
|
||||
twi_stop();
|
||||
|
||||
#endif
|
||||
|
||||
// /update our part of the matrix
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
return ret; // success
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller : Teensy 2.0 specific exports : functions
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__FUNCTIONS_h
|
||||
#define KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__FUNCTIONS_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../matrix.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
uint8_t teensy_init(void);
|
||||
uint8_t teensy_update_matrix( bool matrix[KB_ROWS][KB_COLUMNS] );
|
||||
|
||||
#endif
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller : Teensy 2.0 specific exports : LED control
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__LED_h
|
||||
#define KEYBOARD__ERGODOX__CONTROLLER__TEENSY_2_0__LED_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h> // for the register macros
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
#define _kb_led_1_on() (DDRB |= (1<<5))
|
||||
#define _kb_led_1_off() (DDRB &= ~(1<<5))
|
||||
#define _kb_led_1_set(n) (OCR1A = (uint8_t)(n))
|
||||
#define _kb_led_1_set_percent(n) (OCR1A = (uint8_t)((n) * 0xFF))
|
||||
|
||||
#define _kb_led_2_on() (DDRB |= (1<<6))
|
||||
#define _kb_led_2_off() (DDRB &= ~(1<<6))
|
||||
#define _kb_led_2_set(n) (OCR1B = (uint8_t)(n))
|
||||
#define _kb_led_2_set_percent(n) (OCR1B = (uint8_t)((n) * 0xFF))
|
||||
|
||||
#define _kb_led_3_on() (DDRB |= (1<<7))
|
||||
#define _kb_led_3_off() (DDRB &= ~(1<<7))
|
||||
#define _kb_led_3_set(n) (OCR1C = (uint8_t)(n))
|
||||
#define _kb_led_3_set_percent(n) (OCR1C = (uint8_t)((n) * 0xFF))
|
||||
|
||||
|
||||
#define _kb_led_all_on() do { \
|
||||
_kb_led_1_on(); \
|
||||
_kb_led_2_on(); \
|
||||
_kb_led_3_on(); \
|
||||
} while(0)
|
||||
|
||||
#define _kb_led_all_off() do { \
|
||||
_kb_led_1_off(); \
|
||||
_kb_led_2_off(); \
|
||||
_kb_led_3_off(); \
|
||||
} while(0)
|
||||
|
||||
#define _kb_led_all_set(n) do { \
|
||||
_kb_led_1_set(n); \
|
||||
_kb_led_2_set(n); \
|
||||
_kb_led_3_set(n); \
|
||||
} while(0)
|
||||
|
||||
#define _kb_led_all_set_percent(n) do { \
|
||||
_kb_led_1_set_percent(n); \
|
||||
_kb_led_2_set_percent(n); \
|
||||
_kb_led_3_set_percent(n); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : controller: Teensy 2.0 specific code
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
// for "lib/twi.h"
|
||||
#define TWI_FREQ 400000
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
#include "../../../lib/twi.h"
|
||||
#include "../options.h"
|
||||
#include "../matrix.h"
|
||||
#include "./teensy-2-0--functions.h"
|
||||
#include "./teensy-2-0--led.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// check options
|
||||
#if (TEENSY__DRIVE_ROWS && TEENSY__DRIVE_COLUMNS) \
|
||||
|| !(TEENSY__DRIVE_ROWS || TEENSY__DRIVE_COLUMNS)
|
||||
#error "See 'Pin drive direction' in 'options.h'"
|
||||
#endif
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// processor frequency (from <http://www.pjrc.com/teensy/prescaler.html>)
|
||||
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
|
||||
#define CPU_16MHz 0x00
|
||||
#define CPU_8MHz 0x01
|
||||
#define CPU_4MHz 0x02
|
||||
#define CPU_2MHz 0x03
|
||||
#define CPU_1MHz 0x04
|
||||
#define CPU_500kHz 0x05
|
||||
#define CPU_250kHz 0x06
|
||||
#define CPU_125kHz 0x07
|
||||
#define CPU_62kHz 0x08
|
||||
|
||||
|
||||
/*
|
||||
* pin macros
|
||||
* - note: you can move the `UNUSED`, `ROW`, and `COLUMN` pins around, but be
|
||||
* sure to keep the set of all the pins listed constant. other pins are not
|
||||
* movable, and either are referenced explicitly or have macros defined for
|
||||
* them elsewhere.
|
||||
* - note: if you change pin assignments, please be sure to update
|
||||
* "teensy-2-0.md", and the '.svg' circuit diagram.
|
||||
*/
|
||||
|
||||
// --- unused
|
||||
#define UNUSED_0 C, 7
|
||||
#define UNUSED_1 D, 7
|
||||
#define UNUSED_2 D, 4 // hard to use with breadboard (on the end)
|
||||
#define UNUSED_3 D, 5 // hard to use with breadboard (on the end)
|
||||
#define UNUSED_4 E, 6 // hard to use with breadboard (internal)
|
||||
|
||||
// --- rows
|
||||
#define ROW_0 F, 7
|
||||
#define ROW_1 F, 6
|
||||
#define ROW_2 F, 5
|
||||
#define ROW_3 F, 4
|
||||
#define ROW_4 F, 1
|
||||
#define ROW_5 F, 0
|
||||
|
||||
// --- columns
|
||||
#define COLUMN_7 B, 0
|
||||
#define COLUMN_8 B, 1
|
||||
#define COLUMN_9 B, 2
|
||||
#define COLUMN_A B, 3
|
||||
#define COLUMN_B D, 2
|
||||
#define COLUMN_C D, 3
|
||||
#define COLUMN_D C, 6
|
||||
|
||||
// --- helpers
|
||||
#define SET |=
|
||||
#define CLEAR &=~
|
||||
|
||||
#define _teensypin_write(register, operation, pin_letter, pin_number) \
|
||||
do { \
|
||||
((register##pin_letter) operation (1<<(pin_number))); \
|
||||
_delay_us(1); /* allow pins time to stabilize */ \
|
||||
} while(0)
|
||||
#define teensypin_write(register, operation, pin) \
|
||||
_teensypin_write(register, operation, pin)
|
||||
|
||||
#define _teensypin_read(pin_letter, pin_number) \
|
||||
((PIN##pin_letter) & (1<<(pin_number)))
|
||||
#define teensypin_read(pin) \
|
||||
_teensypin_read(pin)
|
||||
|
||||
#define teensypin_write_all_unused(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, UNUSED_0); \
|
||||
teensypin_write(register, operation, UNUSED_1); \
|
||||
teensypin_write(register, operation, UNUSED_2); \
|
||||
teensypin_write(register, operation, UNUSED_3); \
|
||||
teensypin_write(register, operation, UNUSED_4); } \
|
||||
while(0)
|
||||
|
||||
#define teensypin_write_all_row(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, ROW_0); \
|
||||
teensypin_write(register, operation, ROW_1); \
|
||||
teensypin_write(register, operation, ROW_2); \
|
||||
teensypin_write(register, operation, ROW_3); \
|
||||
teensypin_write(register, operation, ROW_4); \
|
||||
teensypin_write(register, operation, ROW_5); } \
|
||||
while(0)
|
||||
|
||||
#define teensypin_write_all_column(register, operation) \
|
||||
do { \
|
||||
teensypin_write(register, operation, COLUMN_7); \
|
||||
teensypin_write(register, operation, COLUMN_8); \
|
||||
teensypin_write(register, operation, COLUMN_9); \
|
||||
teensypin_write(register, operation, COLUMN_A); \
|
||||
teensypin_write(register, operation, COLUMN_B); \
|
||||
teensypin_write(register, operation, COLUMN_C); \
|
||||
teensypin_write(register, operation, COLUMN_D); } \
|
||||
while(0)
|
||||
|
||||
|
||||
/*
|
||||
* update macros
|
||||
*/
|
||||
#define update_rows_for_column(matrix, column) \
|
||||
do { \
|
||||
/* set column low (set as output) */ \
|
||||
teensypin_write(DDR, SET, COLUMN_##column); \
|
||||
/* read rows 0..5 and update matrix */ \
|
||||
matrix[0x0][0x##column] = ! teensypin_read(ROW_0); \
|
||||
matrix[0x1][0x##column] = ! teensypin_read(ROW_1); \
|
||||
matrix[0x2][0x##column] = ! teensypin_read(ROW_2); \
|
||||
matrix[0x3][0x##column] = ! teensypin_read(ROW_3); \
|
||||
matrix[0x4][0x##column] = ! teensypin_read(ROW_4); \
|
||||
matrix[0x5][0x##column] = ! teensypin_read(ROW_5); \
|
||||
/* set column hi-Z (set as input) */ \
|
||||
teensypin_write(DDR, CLEAR, COLUMN_##column); \
|
||||
} while(0)
|
||||
|
||||
#define update_columns_for_row(matrix, row) \
|
||||
do { \
|
||||
/* set row low (set as output) */ \
|
||||
teensypin_write(DDR, SET, ROW_##row); \
|
||||
/* read columns 7..D and update matrix */ \
|
||||
matrix[0x##row][0x7] = ! teensypin_read(COLUMN_7); \
|
||||
matrix[0x##row][0x8] = ! teensypin_read(COLUMN_8); \
|
||||
matrix[0x##row][0x9] = ! teensypin_read(COLUMN_9); \
|
||||
matrix[0x##row][0xA] = ! teensypin_read(COLUMN_A); \
|
||||
matrix[0x##row][0xB] = ! teensypin_read(COLUMN_B); \
|
||||
matrix[0x##row][0xC] = ! teensypin_read(COLUMN_C); \
|
||||
matrix[0x##row][0xD] = ! teensypin_read(COLUMN_D); \
|
||||
/* set row hi-Z (set as input) */ \
|
||||
teensypin_write(DDR, CLEAR, ROW_##row); \
|
||||
} while(0)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/* returns
|
||||
* - success: 0
|
||||
*/
|
||||
uint8_t teensy_init(void) {
|
||||
// CPU speed : should match F_CPU in makefile
|
||||
#if F_CPU != 16000000
|
||||
#error "Expecting different CPU frequency"
|
||||
#endif
|
||||
CPU_PRESCALE(CPU_16MHz);
|
||||
|
||||
// onboard LED
|
||||
// (tied to GND for hardware convenience)
|
||||
DDRD &= ~(1<<6); // set D(6) as input
|
||||
PORTD &= ~(1<<6); // set D(6) internal pull-up disabled
|
||||
|
||||
// (tied to Vcc for hardware convenience)
|
||||
DDRB &= ~(1<<4); // set B(4) as input
|
||||
PORTB &= ~(1<<4); // set B(4) internal pull-up disabled
|
||||
|
||||
// keyboard LEDs (see "PWM on ports OC1(A|B|C)" in "teensy-2-0.md")
|
||||
_kb_led_all_off(); // (just to put the pins in a known state)
|
||||
TCCR1A = 0b10101001; // set and configure fast PWM
|
||||
TCCR1B = 0b00001001; // set and configure fast PWM
|
||||
|
||||
// I2C (TWI)
|
||||
twi_init(); // on pins D(1,0)
|
||||
|
||||
// unused pins
|
||||
teensypin_write_all_unused(DDR, CLEAR); // set as input
|
||||
teensypin_write_all_unused(PORT, SET); // set internal pull-up enabled
|
||||
|
||||
// rows and columns
|
||||
teensypin_write_all_row(DDR, CLEAR); // set as input (hi-Z)
|
||||
teensypin_write_all_column(DDR, CLEAR); // set as input (hi-Z)
|
||||
#if TEENSY__DRIVE_ROWS
|
||||
teensypin_write_all_row(PORT, CLEAR); // pull-up disabled
|
||||
teensypin_write_all_column(PORT, SET); // pull-up enabled
|
||||
#elif TEENSY__DRIVE_COLUMNS
|
||||
teensypin_write_all_row(PORT, SET); // pull-up enabled
|
||||
teensypin_write_all_column(PORT, CLEAR); // pull-up disabled
|
||||
#endif
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/* returns
|
||||
* - success: 0
|
||||
*/
|
||||
#if KB_ROWS != 6 || KB_COLUMNS != 14
|
||||
#error "Expecting different keyboard dimensions"
|
||||
#endif
|
||||
|
||||
uint8_t teensy_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) {
|
||||
#if TEENSY__DRIVE_ROWS
|
||||
update_columns_for_row(matrix, 0);
|
||||
update_columns_for_row(matrix, 1);
|
||||
update_columns_for_row(matrix, 2);
|
||||
update_columns_for_row(matrix, 3);
|
||||
update_columns_for_row(matrix, 4);
|
||||
update_columns_for_row(matrix, 5);
|
||||
#elif TEENSY__DRIVE_COLUMNS
|
||||
update_rows_for_column(matrix, 7);
|
||||
update_rows_for_column(matrix, 8);
|
||||
update_rows_for_column(matrix, 9);
|
||||
update_rows_for_column(matrix, A);
|
||||
update_rows_for_column(matrix, B);
|
||||
update_rows_for_column(matrix, C);
|
||||
update_rows_for_column(matrix, D);
|
||||
#endif
|
||||
|
||||
return 0; // success
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* ergoDOX : layout exports
|
||||
*
|
||||
* Different layouts are included by modifying a variable in the makefile.
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (MIT) (see "license.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef KEYBOARD__ERGODOX__LAYOUT_h
|
||||
#define KEYBOARD__ERGODOX__LAYOUT_h
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// include the appropriate keyboard layout header
|
||||
#include "../../lib/variable-include.h"
|
||||
#define INCLUDE EXP_STR( ./layout/MAKEFILE_KEYBOARD_LAYOUT.h )
|
||||
#include INCLUDE
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue