Compare commits

...

226 Commits

Author SHA1 Message Date
Ben Blazak ebb353b3f2 adding todos and ideas; mostly from thanatermesis 2014-10-11 15:28:41 -07:00
Ben Blazak 73341cd9a1 (added placeholder css stylesheet) 2014-07-31 19:51:17 -07:00
Ben Blazak 737428aec8 (minor cleanup; starting some changes in layout files) 2014-06-21 21:05:29 -07:00
Ben Blazak 84fa10fb5f changing debounce back to 5ms by default
to see how it works -- now testing with my original cherry blue
prototype board (with no case)
2014-06-16 15:34:50 -07:00
Ben Blazak d384925813 (added todo) 2014-06-10 19:54:53 -07:00
Ben Blazak cff9b31a76 layout-gen: just playing around :) 2014-06-10 19:39:59 -07:00
Ben Blazak a67d215524 (just looking at layout-gen some more) 2014-06-02 23:56:23 -07:00
Ben Blazak 8342b0be46 layout-gen: still testing stuff
will probably have to take a break for a while

javascript looks promising :) .  this application just lends itself so
well to state!  or maybe it's because i'm not used to programming in a
pure functional language like Elm...  Elm is fun though :)

decided not to write a second test for Elm.  partly because i didn't
have time.  what i wanted to try was doing things based on the Elm
calculator example.  I haven't tried using buttons yet, and they might
work out.  then again, it might turn out to be a whole lot easier in
javascript.  and then it would be a bit more portable as well...

so i suppose i'm leaning towards javascript.  we'll see :)
2014-06-02 01:39:31 -07:00
Ben Blazak 620221bd20 ui: going to try using elm a bit differently 2014-06-01 01:50:24 -07:00
Ben Blazak 203cb66b26 small cleanup; gonna try javascript 2014-05-31 22:53:50 -07:00
Ben Blazak f3635dc579 got it to draw the keyboard :) really slow 2014-05-31 22:49:28 -07:00
Ben Blazak c32f396e31 background's just about done! :) 2014-05-30 02:04:58 -07:00
Ben Blazak 5e931a7481 starting an experiment with a layout generator
this will likely be in development for a long time, if it ever amounts
to anything at all
2014-05-29 23:16:44 -07:00
Ben Blazak f980028d82 cleaned up a bit; minor bugfix 2014-05-27 23:05:00 -07:00
Ben Blazak 74c685a8b4 fixing up the ...exec_key...() functions
just need to implement eeprom_macro's in a layout now, to test them!
2014-05-27 21:42:01 -07:00
Ben Blazak c5afdb9659 (working: eeprom-macro: looked over public functions)
almost done with this module!
2014-05-27 15:44:31 -07:00
Ben Blazak 24f4aca7c8 (working: eeprom-macro: went over compress()) 2014-05-27 14:33:40 -07:00
Ben Blazak e8ad0d91c0 (working: eeprom-macro)
rough draft done!  need to finish -- and check it *thoroughly* to make
sure it works (and to clean it up) :)
2014-05-26 23:28:08 -07:00
Ben Blazak b099061e6a (working: eeprom-macro) 2014-05-26 20:29:08 -07:00
Ben Blazak 0fd92b5840 (working: eeprom-macro)
there are a few warnings during compile, but only about functions not
being used.  it's okay: they'll be used soon enough.
2014-05-26 15:54:13 -07:00
Ben Blazak 49e8f32a60 (working: eeprom-macro + minor updates) 2014-05-26 14:42:50 -07:00
Ben Blazak 5647b58120 (working: eeprom-macro: fixed typo) 2014-05-25 19:45:48 -07:00
Ben Blazak 5ee88643fd (working: eeprom-macro) 2014-05-25 19:44:55 -07:00
Ben Blazak 5d35f2ed44 (working: eeprom-macro; woo! finished compress(); will test later)
really, it'd be hard to test any of this stuff until i have it
implemented in the layout; so it might be a while.
2014-05-25 12:46:13 -07:00
Ben Blazak d2d0e1ffab (small aesthetic change) 2014-05-24 22:05:41 -07:00
Ben Blazak 79fec2cff0 (working: eeprom-macro; making progress!) 2014-05-24 21:54:41 -07:00
Ben Blazak 47038151ce (working: eeprom-macro) 2014-05-24 16:30:53 -07:00
Ben Blazak a353e8a5d1 little bit of cleanup 2014-05-24 16:30:36 -07:00
Ben Blazak c50b98922c changing the default scan rate to 100Hz
Thanatermesis (via email) told me that this worked for him, fixing his
issue of having some keys unexpectedly double tap (though, I think he
might have gone down to 50Hz, or something).  since he mentioned it,
i've been noticing my keys double tapping sometimes (on one of my brown
boards, with an acrylic case... it didn't seem to happen at all with my
blue caseless board...), so i decided to give this a try :)
2014-05-23 09:53:03 -07:00
Ben Blazak 881bdf6f94 working on eeprom_macro: wrote find_key_action() 2014-05-22 01:34:02 -07:00
Ben Blazak 3219738831 quick status update 2014-04-12 14:42:07 -07:00
Ben Blazak 1ed4e02e5d (forgot to mention my source) 2014-03-24 22:16:49 -07:00
Ben Blazak faf96737a0 added todo (keyboard debugging tip) 2014-03-24 22:14:20 -07:00
Ben Blazak 3aec8246ea small layout changes (also, qwerty--ben is now the default) 2014-03-08 13:50:43 -08:00
Ben Blazak 57edc24c49 (removed some latent unnecessary semicolons) 2014-02-17 23:36:02 -08:00
Ben Blazak b5101045be light layer leds only when locking 2014-02-09 12:00:15 -08:00
Ben Blazak b58439380a added a qwerty layout for myself; fixed a couple includes 2014-01-23 19:55:37 -08:00
Ben Blazak db52b78324 starting to work on an arensito type layout 2014-01-22 21:54:36 -08:00
Ben Blazak e83f2a3325 messing about with layouts :)
- changed how things are organized
- LEDs now indicate layers!  layers 1 and 2 have a corresponding LED,
  along with capslock (which also kinda needs one)
- this organization of things is not final.  but i hope (and think) it's
  a little better :)
2014-01-21 18:08:33 -08:00
Ben Blazak fdc5d836c3 (thinking) 2014-01-19 18:01:42 -08:00
Ben Blazak 0c0efafd7b (small doc update) 2014-01-19 17:48:56 -08:00
Ben Blazak 4a5014d3a3 changed all-layouts, and the clean targets 2014-01-19 17:46:50 -08:00
Ben Blazak 90f598ebe5 removed makefile dependency on git; fixed *.dep generation 2014-01-15 13:25:33 -08:00
Ben Blazak bcdbc2e37e added a test layout, and an `all-layouts` target to the makefile 2014-01-13 20:25:49 -08:00
Ben Blazak 24884d470a removed comment
static variables are initialized to 0 by the compiler, per the standard
2014-01-04 21:57:40 -08:00
Ben Blazak d607d7de3e misc; mostly docs, function stubs, and thinking 2013-12-30 01:13:23 -08:00
Ben Blazak 9df12828d0 mostly small stuff, documentation, and thinking 2013-12-29 20:15:44 -08:00
Ben Blazak eec0923dca (removed a few comments) 2013-12-27 17:35:09 -08:00
Ben Blazak b41a716631 working on eemprom-macro :) 2013-12-27 17:29:19 -08:00
Ben Blazak f0fcfca4ef UNTESTED: still playing with eeprom-macro
originally, it seemed that the little endian version of write...(), and
the big endian version of read...() would be the easiest.  this was
true, when i first wrote the functions.  after optimization, the big
endian write...() got much better, but for some reason, i couldn't quite
crack the little endian read...().  perhaps i'm missing something.  in
any case, the big endian versions of both look quite good to me now, so
they're what i'll be going with.  too bad the little endian versions
didn't quite work out... but i was expecting to go with the big endian
versions anyway, since we'll be reading much more often than writing.

// - sizes for each function (in bytes) (with optimizations on):
//
//                            function  frame  stack
//       read  big endian         154      3     10
//       read  little endian      200      2     10
//       write big endian         160      0      6
//       write little endian      172      0      5

also, note that while optimizing, i was looking only at compiled
function size, and frame and stack usage: actual profiling would have
been a bit difficult, and i don't feel like writing versions of the
algorithms for testing on my laptop right now (though, i might later,
for fun, if i can give it as an assignment to the class i hopefully TA
for next semester - hehe).  i tried to pay attention to the amount of
code inside the loops though, when i was changing things, and i'm under
the impression that the AVR has fairly predictably timed assembly
instructions (1 or 2 cycles each?), so compiled function size seemed
like a good proxy.

done experimenting with this for now.  yay! :) .  i'll remove the extra
code in the next commit.
2013-12-23 22:26:43 -08:00
Ben Blazak 55e96d5413 UNTESTED: still experimenting 2013-12-23 19:13:53 -08:00
Ben Blazak 6b9d6916b2 UNTESTED: experimenting with big- and little-endian serializations 2013-12-23 17:21:02 -08:00
Ben Blazak 6990d46c60 finished the ...dump...ihex() functions 2013-12-21 13:54:22 -08:00
Ben Blazak 9622b1af1d finished `dump_ihex()` i think :) except for documentation 2013-12-20 13:07:02 -08:00
Ben Blazak 90f6b2c867 small changes; and working on the `...dump...ihex()` functions 2013-12-18 23:49:01 -08:00
Ben Blazak d5ae5e003b working; need to re-read some code before continuing 2013-12-18 16:32:39 -08:00
Ben Blazak bae1ae882a going to remove some stuff 2013-12-18 15:04:27 -08:00
Ben Blazak a90b6103a7 eeprom-macro: added some address definitions 2013-12-14 15:52:39 -08:00
Ben Blazak 14cdab0868 (added toplevel .gitignore) 2013-12-14 14:28:07 -08:00
Ben Blazak 9553dca085 fixed small typo (thanks Rema!) 2013-10-21 10:22:28 -07:00
Ben Blazak f8c5d7eb3c changed the numPush etc. macros
now pressing and releasing both numlock keys
2013-09-14 11:47:17 -07:00
Ben Blazak ab74c80758 added note about numlock macros 2013-09-14 11:25:45 -07:00
Ben Blazak c7350e1435 (still working -- compiles and runs though) 2013-09-13 09:28:11 -07:00
Ben Blazak 7c44123178 doc update 2013-09-04 15:21:51 -07:00
Ben Blazak 5fb869cf60 working on eeprom-macro (still) 2013-08-22 21:49:07 -07:00
Ben Blazak f630e3c6f5 (working; mostly on the eeprom-macro implementation) 2013-08-20 01:56:26 -07:00
Ben Blazak d73b58e23d (planning) 2013-08-19 14:08:38 -07:00
Ben Blazak 3cd6d819df (added todo) 2013-08-18 14:35:09 -07:00
Ben Blazak ec59f2ecd0 planning, rambling, ... 2013-08-18 09:29:48 -07:00
Ben Blazak d8c3572ed1 planning, mostly - again, lol 2013-08-18 00:52:25 -07:00
Ben Blazak f9e473b929 miscounted some bits... fixed now :) 2013-08-16 00:23:12 -07:00
Ben Blazak 43b304c66d added `eeprom__block_read()` 2013-08-15 18:28:40 -07:00
Ben Blazak 725e08e8c7 added `...fill()`, and (tried to) generalize things a tiny tiny bit 2013-08-15 16:59:57 -07:00
Ben Blazak ef054fa0c7 doc update (especially project status in toplevel readme) 2013-08-13 16:51:38 -07:00
Ben Blazak 242d04c9f1 (still working) 2013-08-05 09:16:54 -07:00
Ben Blazak fbbd4570d4 updated readme 2013-08-04 13:11:58 -07:00
Ben Blazak 6a13099a71 working on lib/layout/eeprom-macro 2013-08-04 13:03:21 -07:00
Ben Blazak e4d12764a8 (modified readme) 2013-08-03 19:17:19 -07:00
Ben Blazak 9ba3061fcb (minor: changed a file name) 2013-08-03 19:15:36 -07:00
Ben Blazak 6a85102a3b Merge branch 'partial-rewrite' of github.com:benblazak/ergodox-firmware into partial-rewrite
ges an updated upstream into a topic branch.
2013-08-03 19:15:02 -07:00
Ben Blazak be0c70cf15 Create README.md 2013-08-03 19:05:41 -07:00
Ben Blazak 2f1eec1040 (working on lib/layout/eeprom-macro) 2013-07-26 22:28:20 -07:00
Ben Blazak 6a4d062f7d (fixed small error; oops) 2013-07-25 15:43:15 -07:00
Ben Blazak f90d02f9c3 changed recording interface for eeprom-macros's 2013-07-25 15:05:18 -07:00
Ben Blazak 0bf0181ffa changed the semantics of eeprom__copy()
the direction in which the data is copied is now well defined.  this
allows us to copy a large block a small distance (where the block's new
address range overlaps with its old one), in either direction (to a
higher block of addresses or a lower one), in a well defined and
documented manner.
2013-07-24 14:20:39 -07:00
Ben Blazak a32a9b59d6 reorganized the keyboard layouts; made a template 2013-07-24 13:47:47 -07:00
Ben Blazak 2f628df002 (fixed a comment) 2013-07-23 22:45:44 -07:00
Ben Blazak c07f7c56e9 finished removing linked lists from the project
the memory overhead of using malloc() is significant, as is the memory
overhead of having a next pointer in each node. dynamic arrays are not
overly difficult to implement, and it's my opinion at the moment that
they should be preferred.
2013-07-23 22:39:54 -07:00
Ben Blazak a72e6ba4bc lib/timer now using dynamic arrays instead of linked lists 2013-07-23 22:37:22 -07:00
Ben Blazak 57c43089ec fixed a simple bug; oops 2013-07-23 22:36:14 -07:00
Ben Blazak c6944a095e switched lib/eeprom from linked lists to dynamic arrays 2013-07-23 11:14:22 -07:00
Ben Blazak 9796e78efb (messing with error checking in resize_stack() again) 2013-07-23 10:58:37 -07:00
Ben Blazak a05c0e42bd (minor: formatting) 2013-07-22 16:31:47 -07:00
Ben Blazak b5ffe83a38 changed resize_stack() slightly 2013-07-21 15:30:28 -07:00
Ben Blazak 6727ed89b9 added error checking and stopped implicitly converting -1 2013-07-21 14:59:48 -07:00
Ben Blazak 69252d682e (minor aesthetic) 2013-07-14 16:04:57 -07:00
Ben Blazak 9ace7653c1 rewrote the layer-stack implementation
i was going to pull the functionality out into a "flex-array" type, and
use that, but it turned out not to generalize very well.  in the
process, i think i found a cleaner (and smaller) way to do what i was
already doing, so i rewrote the implementation.  i'm planning to rewrite
everything currently using linked-lists to use this type of dynamically
sized array, eventually, in order to save SRAM, but we'll see how that
goes.
2013-07-12 16:59:52 -07:00
Ben Blazak 82c6cbf4f0 added todo 2013-07-11 22:05:49 -07:00
Ben Blazak 585503ac14 (...) 2013-06-21 17:34:43 -07:00
Ben Blazak 68649988cc renamed, to be a little more consistent (i hope) 2013-06-21 17:34:03 -07:00
Ben Blazak 952c2792ed extended `key_functions__type_string()` to use keycodes
when possible, and "unicode sequences" otherwise.  made the function
enormous, but also much more useful :)
2013-06-14 15:52:47 -07:00
Ben Blazak a89e6528f2 mostly aesthetic updates 2013-06-14 00:31:06 -07:00
Ben Blazak 36edee2eb7 '.c' files should now include the header that defines their functions 2013-06-13 23:41:26 -07:00
Ben Blazak 559fe97d7e finished removing `timer__schedule_milliseconds()` 2013-06-13 16:38:29 -07:00
Ben Blazak aec1bac3bb removed `timer__schedule_milliseconds()`
since we weren't executing scheduled functions inside the interrupt
vector, and therefore had to wait to execute a cycle's worth of
scheduled "millisecond tasks" at the end of each cycle anyway, it was
kind of a misleading function to have around. i'd keep it around and
change the functionality back to running scheduled tasks inside the
interrupt vector, except i feel that that should be strongly discouraged
2013-06-13 16:32:41 -07:00
Ben Blazak 4d50761b0d now allocating `list__list_t`s as static vars instead of pointers
and a small documentation update
2013-06-13 16:22:49 -07:00
Ben Blazak c24adb542c working mostly on lib/eeprom; hopefully done :) 2013-06-13 16:14:42 -07:00
Ben Blazak 0a9f4ec61d makefile: improved the way options.mk files are included 2013-06-11 19:47:42 -07:00
Ben Blazak b8156b4329 (lib/eeprom: fixed casting error) 2013-06-11 00:08:31 -07:00
Ben Blazak f6252243cc (lib/eeprom-macro: working) 2013-06-11 00:06:36 -07:00
Ben Blazak 30da4f3d31 (lib/eeprom: doc and makefile update) 2013-06-10 23:53:34 -07:00
Ben Blazak be8c826e90 added my own little EEPROM library
the library provided, as far as i can tell, does not allow for *only*
erasing or *only* writing bytes, only for doing both at the same time.
2013-06-10 23:38:09 -07:00
Ben Blazak d280078435 (still working) 2013-06-10 14:21:38 -07:00
Ben Blazak 0f72931b12 (lib/eeprom-macro: still working)
never mind what i was talking about in the last few commits.  i'm not
sure if i'll be able to get fault tolerance logic designed well or not,
but after all this, it appears that i really really want to try, lol.
i'm thinking about how i might implement logging right now.
2013-06-07 21:14:03 -07:00
Ben Blazak 950bddd579 lib/eeprom-macro: still planning! getting closer and closer..... 2013-06-07 01:27:25 -07:00
Ben Blazak 9ce41f8bc5 lib/eeprom-macro: one possibility 2013-06-06 20:26:26 -07:00
Ben Blazak f96b75a4f8 lib/eeprom-macro: still planning o_o
going to retain a degree of fault tolerance... but probably the more
old school kind, where if there's a power loss at a bad time, we know
about it, and can usually correct the problem, but it takes a while
(fsck style)
2013-06-06 17:38:05 -07:00
Ben Blazak b9ff36161e working on "lib/eeprom-macro": still planning...!
would it be better to just assume power won't fail (and writes will
always be successful), and tell people that if there's a macro they
really care about, they really should write it in the source, and
recompile?

at least initially... and then if i decide that there needs to be fault
tolerance in the library, i can implement some logging of some sort.  it
probably wouldn't take *that* much more space, actually, even to do it
pretty generally, than what i'm trying to do right now... dunno.

but under normal use, it should be safe enough to live dangerously...

and i'm not going to implement a way for people who don't want to get a
nice avr toolchain going to put macros in after compilation anyway (since
it'd be easier to just write a UI... and hopefully other people haven't
lost interest, and will still do that for me... :) lol ).  so the eeprom
is not really a place that permanent macros *shoud* be, anyways...

ya... i think i should do that

</random thoughts> ... i'm tired
2013-06-06 02:35:23 -07:00
Ben Blazak 1f4dd59ea1 added reference 2013-06-06 01:58:36 -07:00
Ben Blazak a49110b2fc doc update 2013-06-05 21:43:54 -07:00
Ben Blazak 3959e83f38 doc update 2013-06-05 21:21:07 -07:00
Ben Blazak e5c3490dd6 (intermediate checkin; working on lib/eeprom-macro stuff still) 2013-06-05 14:59:19 -07:00
Ben Blazak 58dd5ab239 (todo and doc update) 2013-06-05 01:36:29 -07:00
Ben Blazak 3c99158e55 (added TODO) 2013-06-05 00:50:25 -07:00
Ben Blazak b7e03df76b working; especially on eeprom-macro stuff 2013-06-04 01:42:11 -07:00
Ben Blazak 0ab9483b94 (small doc update) 2013-06-03 00:48:32 -07:00
Ben Blazak 56cbc8cb1b lib/eeprom-macro : debating implementation 2013-06-01 23:56:05 -07:00
Ben Blazak 0c23c45627 (smallest style change ever) 2013-06-01 22:56:53 -07:00
Ben Blazak 8867e1b9d2 starting to implement lib/eeprom-macro 2013-06-01 17:32:05 -07:00
Ben Blazak 8b5daa9d92 (more minor planning updates) 2013-05-31 11:16:11 -07:00
Ben Blazak 3a2af3a9dd still planning for lib/eeprom_macro 2013-05-31 02:54:20 -07:00
Ben Blazak 3582dfbc65 small doc update 2013-05-31 02:54:10 -07:00
Ben Blazak e82c23b21c bug fix: just fixed a holdover from a long time ago
which just now started to make compilation fail
2013-05-29 02:08:17 -07:00
Ben Blazak 681d51ffb1 planning for eeprom_macro stuff :) - plans kinda messy still 2013-05-29 02:05:06 -07:00
Ben Blazak 10959ee7b9 small update 2013-05-29 02:04:40 -07:00
Ben Blazak 29446dfb24 finished implementing `...send_unicode_sequence()`
(for now, as always - :) )
2013-05-28 01:38:59 -07:00
Ben Blazak 115ef0ff4f TEST: going to remove some notes and clean `...send_unicode...()` up 2013-05-27 13:57:40 -07:00
Ben Blazak d8e62debf7 TEST: more notes (and updated)
will fix everything when i'm not so tired :)
2013-05-27 01:16:27 -07:00
Ben Blazak ad6a934f3a TEST: contains some notes, for future reference 2013-05-26 23:51:46 -07:00
Ben Blazak 1905f05263 TEST: this works, but i think i'm going to simplify it 2013-05-26 01:11:37 -07:00
Ben Blazak 79cda0e3c4 working on the scheduling offset logic 2013-05-25 12:20:41 -07:00
Ben Blazak db74d1b26a (added a note) 2013-05-25 01:43:11 -07:00
Ben Blazak 76eaa117ce (mostly small doc and comment changes) 2013-05-24 23:53:20 -07:00
Ben Blazak 4e1bd12990 (small doc change) 2013-05-24 21:52:46 -07:00
Ben Blazak 71d541be2f made lists use pointers again :/ didn't know what i was doing...
and fixed a tiny little logic bug in ...exec_key()

and now scheduling works as expected! :D for all three timers!!!
2013-05-24 21:48:45 -07:00
Ben Blazak 58255dc29b messing with timers again; working on sticky keys 2013-05-24 21:10:03 -07:00
Ben Blazak f2acad70a4 (small doc change) 2013-05-24 15:18:07 -07:00
Ben Blazak e560e476f7 changed list fn's to accept lists instead of list pointers 2013-05-24 15:03:45 -07:00
Ben Blazak 1197f4d682 (forgot this; split platform dependent/agnostic timer code) 2013-05-24 12:35:51 -07:00
Ben Blazak d3e9291d49 (in the middle of a lot of misc changes) 2013-05-24 12:35:08 -07:00
Ben Blazak 4628061b53 fixed probable timing bug in timer functions; documentation 2013-05-17 16:03:34 -07:00
Ben Blazak 193f443604 comments, and a small function name fix 2013-05-17 03:23:20 -07:00
Ben Blazak 0b6103d6c3 finished messing with timer :) (for now)
main.c is now out of the timer business (except for "ticking" cycles...
since there really is no other way to guarantee something runs exactly
once per scan cycle)
2013-05-17 03:02:20 -07:00
Ben Blazak 7371362576 intermediate checkin: working on timer
was about to start handling the scheduling for cycle counted events the
same way as for real time scheduled events, but i think i'll switch over
to using `<util/atomic.h>`, if i can
2013-05-16 21:52:18 -07:00
Ben Blazak 758e95b17e documentation; and minor LED functionality in main() 2013-05-11 13:10:33 -07:00
Ben Blazak b5b5db12a7 (minor comments)
also, tested `timer__schedule()` and it works too!  at least for 1
function.  soooooo cool :D
2013-05-09 21:11:30 -07:00
Ben Blazak 203aaf415b timers working!
keyboard scanning at almost exactly 200Hz (or maybe exactly... as close
as i can measure)

possible to schedule things to run in 'n' scan cycles!  haven't tested
scheduling things to run in 'n' milliseconds, but the code is so similar
that it should work too.  :D
2013-05-09 20:41:40 -07:00
Ben Blazak 1deed9d9ac fixed a bug... fix in source will be checked in later
what was happening was, the bottommost led would indicate keyboard
startup, but wouldn't turn on after that.  turns out, i had been setting
timer/counter 0 to output on that pin, unnecessarily - because i didn't
understand how to set up timers o_o .  complicated little things.
2013-05-09 19:54:56 -07:00
Ben Blazak a3445a539b (fixed a type error.. oops) 2013-05-09 04:26:49 -07:00
Ben Blazak f74293d630 (putting ideas into comments..) 2013-05-09 04:22:17 -07:00
Ben Blazak 36ed9d1974 timing changes
main() no longer waits an extra millisecond between scan cycles, just to
be safe.  before, i was thinking that perhaps the
timer__get_millisecond() call might catch the tail end of whatever
millisecond it was getting, and then the scan cycle would be anywhere
between 4ms and 5ms.  but since we wait for our OPT__DEBOUNCE_TIME to be
up *directly before* getting the new time, we shouldn't be catching the
tail end of anything.  we should be getting as close as practically
possible to *exactly* OPT__DEBOUNCE_TIME milliseconds.

the timer__ functions now use and return 16-bit values instead of 32-bit
ones.  the rational is that 16-bit counters for milliseconds are good up
to ~1 minute, which should be enough for most things.  if a greater
length of time is needed, a function can reschedule it self, and only
execute it's body code after being called a certain number of times.
also, i'm considering making a main__ timer, that counts scan cycles
instead of milliseconds.  that would have the advantage of having lower
resolution (so, less overhead) and being able to schedule functions to
run and such without executing anything in an interrupt vector.
2013-05-09 03:57:21 -07:00
Ben Blazak a03b9ec2a1 (documentation) 2013-05-09 03:57:08 -07:00
Ben Blazak ff2e90997d mostly comments; decided to leave layer-stack as is 2013-05-09 03:11:29 -07:00
Ben Blazak 49f3d4bdd5 re-added linked lists :D; and minor aesthetic changes 2013-05-09 02:02:48 -07:00
Ben Blazak 65ecf576d3 (minor doc updates) 2013-05-07 14:01:39 -07:00
Ben Blazak 414158768b finished documentation and such about timer
especially relating to the unexpected behavior i was getting last night
2013-05-07 13:53:15 -07:00
Ben Blazak f031c99c1d fixed a bug... :/
in the while loop in main() that busywaits until we can scan again, the
compiler was optimizing out the function call, it seems like, when i
wrote `(uint8_t)timer__get_milliseconds()`; if i cast the whole
expression (not just the function) to `(volatile uint8_t)`, or if i just
didn't cast anything at all, it worked.  not sure why the compiler would
optimize the function call out like that though, even if it was cast...
this happened when i put it in a for loop too.  i need to research it
just a little more, and write a warning about it in the timer
documentation.
2013-05-07 03:01:21 -07:00
Ben Blazak 9724cf9331 documentation update 2013-05-07 01:27:57 -07:00
Ben Blazak a7d11e0af2 finished timer code; now using in main()
untested, but it should work.  i'll test before pushing to github, and
update if i messed anything up
2013-05-06 23:25:41 -07:00
Ben Blazak 5e1877cb82 misc; and working on the timer function 2013-05-06 02:31:52 -07:00
Ben Blazak 75e0336f6b (minor doc update) 2013-05-05 18:27:34 -07:00
Ben Blazak 467615a4b6 (removed a cast from a call to realloc()) 2013-04-26 17:32:41 -07:00
Ben Blazak ca56ea4647 (minor: changed USB vendor and product name slightly) 2013-04-26 02:26:01 -07:00
Ben Blazak 20f9737b49 started work on lib/timer; and some housekeeping 2013-04-26 02:16:15 -07:00
Ben Blazak 580c5e75a9 (misc changes; additions for planning) 2013-04-23 20:12:27 -07:00
Ben Blazak 1130549da0 (small comment update)
Also, I just tested the scan rate without debounce (lol, should have
thought of that before) and it's about 471Hz.  This is much faster than
the 200Hz (every 5ms) that we need to limit ourselves to due to the
switches needing a 5ms debounce time.  It should be trivial to tune the
scan rate to closer to 200Hz once I get lib/timer implemented.
2013-04-23 13:23:01 -07:00
Ben Blazak eab3d541bf (minor change to a comment) 2013-04-23 00:49:36 -07:00
Ben Blazak f8f8bef3c1 (a little planning, and organization) 2013-04-19 23:06:13 -07:00
Ben Blazak f9dfb539d1 moved `_layout` to PROGMEM
where it should have been (and i thought it was) before ... oops :)
2013-04-19 22:30:14 -07:00
Ben Blazak 058981cb83 fixed (or... worked around) a silly macro bug 2013-04-19 16:47:32 -07:00
Ben Blazak a210660b24 now releasing keys on the same layer they were pressed
lol - i'd forgotten to do this before
2013-04-19 16:46:42 -07:00
Ben Blazak 81e7e32dbe (fixed a name, to go with the last commit) 2013-04-19 16:46:00 -07:00
Ben Blazak a6ec78cd34 fixed a comment, and made a few var names more consistent 2013-04-19 16:44:57 -07:00
Ben Blazak 316f54596e working! cleaned up a bit and found a silly bug
one bug left to fix before it's actually doing what it's supposed to

scanning at about 140Hz :D , and only slightly bigger than the old
firmware (though, with many fewer layers compiled in...) (also, the
winavr makefile gets the hex to be smaller somehow; i should probably
look into that)
2013-04-19 14:58:09 -07:00
Ben Blazak 0774d0dd09 cleaned up a bunch and worked on the makefile; now compiling! :D 2013-04-19 05:21:29 -07:00
Ben Blazak f47ab1315e makefile working; doesn't compile yet 2013-04-18 19:55:24 -07:00
Ben Blazak a12a83b555 (messing with makefiles; not working yet) 2013-04-18 19:22:30 -07:00
Ben Blazak a5e2c20a84 working on the layer-stack; finished! (for now)
- did not move any of the layer-stack code into code for a  'flex-array'
  type, or something similar.  i'm thinking that most of the things the
  layer-stack does aren't sufficiently generalizable. for instance, one
  of the biggest general functions in the layer-stack implementation is
  `_shift_elements()`; but shifing elements is only something you want
  to do when you're messing with elements not on the the top of the
  stack (which breaks the definition of a general stack).  so i think
  i'll leave things as they are for now.  the functionality can always
  be split out later if it turns out to be needed elsewhere.
2013-04-18 17:12:58 -07:00
Ben Blazak e4b1717657 changed 'KEY__' to 'KEYBOARD__' for keycode macros 2013-04-18 13:32:37 -07:00
Ben Blazak bf13abc274 working on .../lib/usb/usage-page/ 2013-04-18 02:54:44 -07:00
Ben Blazak 98970c0698 (still working on the layer-stack) 2013-04-12 00:01:02 -07:00
Ben Blazak e3df599b85 (updated references) 2013-04-10 23:09:17 -07:00
Ben Blazak 30ad5663d2 (still working on the layer-stack) 2013-04-09 19:49:38 -07:00
Ben Blazak 9ac5701e7e (minor TODO fix) 2013-04-09 13:22:01 -07:00
Ben Blazak e8bec2c0a2 (working on the layer-stack) 2013-04-09 04:01:58 -07:00
Ben Blazak 43d849606a (working) 2013-04-07 22:51:41 -07:00
Ben Blazak 75a987d5a8 (more progress) 2013-04-06 23:37:16 -07:00
Ben Blazak 03c07e0a99 (working; got a lot done in keys.c.h and with the layout!) 2013-04-05 23:17:05 -07:00
Ben Blazak f7ba0b6497 (still working on keys and stuff) 2013-04-05 00:46:58 -07:00
Ben Blazak 76c4bb6c1f (working on keys still, and how to write them) 2013-04-04 17:19:20 -07:00
Ben Blazak c6d9875d5d (working on key definitions) 2013-04-04 01:20:19 -07:00
Ben Blazak b1b04419e1 (working) 2013-04-03 15:58:46 -07:00
Ben Blazak fb55fcfb7d (nighttime checkin: going to sleep) 2013-04-01 02:59:12 -07:00
Ben Blazak 2964e68840 wrote kf__chord__*__progmem() :) 2013-04-01 01:49:36 -07:00
Ben Blazak e036276dd0 (going to change a function) 2013-04-01 00:38:47 -07:00
Ben Blazak 02fd5471ae (intermediate) 2013-03-29 15:48:39 -07:00
Ben Blazak 7eec5fb237 (lots of work) 2013-03-26 23:16:24 -07:00
Ben Blazak b37f69824c (removed some stuff) 2013-03-18 22:07:02 -07:00
Ben Blazak b63e4ef0f0 (about to remove some things) 2013-03-18 21:31:39 -07:00
Ben Blazak 38e3ff49f8 (still working) 2013-02-18 18:01:42 -08:00
Ben Blazak a4696d8641 (intermediate checkin) 2013-02-01 02:18:27 -08:00
Ben Blazak ad0ff14ca6 (intermediate checkin) 2013-01-29 21:59:20 -08:00
Ben Blazak 1db6d4dd37 (intermediate checkin) 2013-01-29 21:34:46 -08:00
Ben Blazak 94e18d611d (intermediate checkin) 2013-01-27 00:00:47 -08:00
Ben Blazak 35bfca3662 (intermediate checkin) 2013-01-25 00:04:05 -08:00
Ben Blazak 0129fcae47 (intermediate checkin; things are coming along!) 2013-01-22 16:33:07 -08:00
Ben Blazak be26a411f2 (intermediate checkin) 2013-01-21 23:33:08 -08:00
Ben Blazak f70568c1c0 intermediate checkin 2013-01-10 03:08:25 -08:00
Ben Blazak 9bb6198878 intermediate checkin 2013-01-09 02:29:30 -08:00
Ben Blazak c85dfd2aa6 intermediate checkin: LOTS of things still changing 2013-01-08 19:05:26 -08:00
Ben Blazak b409cf1b1e (still working on the rewrite) 2012-12-29 01:47:38 -08:00
Ben Blazak b5e3c41751 minor doc update 2012-12-26 21:34:02 -08:00
Ben Blazak f6aa18498a very much in progress, rewriting in C
- decided to see if i could keep it in just C... i think that'll be
  easier, long run.  it does need a rewrite though, in a few respects

- this mid-stride checkin is because i'm leaving to visit santa barbara
  tomorrow (just for the day) and i want to make sure 1) my code is
  backed up, and 2) my code is available in case i wish to show it to
  anyone (lol :) since i'm not bringing my laptop)
2012-12-26 21:25:17 -08:00
Ben Blazak 88111197c3 (still working) 2012-12-22 01:00:21 -08:00
Ben Blazak 5ed574cd85 removed some more of the old files
(i may put some back, eventually, but right now i'm trying to clean
stuff out)
2012-12-20 15:24:43 -08:00
Ben Blazak 778e400868 started rewriting in C++ :) 2012-12-20 15:04:13 -08:00
134 changed files with 9881 additions and 8169 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/local - *

View File

@ -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()

View File

@ -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()

View File

@ -1,3 +0,0 @@
function keyclick(position, layer) {
}

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 94 KiB

View File

@ -1,2 +0,0 @@
This directory is for projects closely related to the firmware.

View File

@ -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 &copy; 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

View File

@ -1,7 +1,8 @@
*.o
*.dep
*.eep
*.elf
*.hex
*.map
*.o
*.o.dep

307
firmware/keyboard.h Normal file
View File

@ -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()`.
*/

View File

@ -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
}

View File

@ -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 &copy; 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>

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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&sup2;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 &copy; 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>

View File

@ -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
}

View File

@ -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

View File

@ -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&sup2;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&sup2;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 &copy; 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>

View File

@ -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 ),
// ............................................................................
};

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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)

View File

@ -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
};
}

View File

@ -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) }}

View File

@ -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();
}

View File

@ -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 &copy; 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>

View File

@ -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];

View File

@ -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];

View File

@ -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 ),
// ............................................................................
};

View File

@ -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"

View File

@ -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 &copy; 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>

View File

@ -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 ),
// ............................................................................
};

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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)

183
firmware/lib/eeprom.h Normal file
View File

@ -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()`.
*/

View File

@ -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
}

View File

@ -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)

View File

@ -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]
*/

View File

@ -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;
}

View File

@ -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)

View File

@ -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 ^_^" ) );
*/

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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();
}
}

230
firmware/lib/layout/keys.h Normal file
View File

@ -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

View File

@ -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)
*/

View File

@ -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;
}

View File

@ -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)

198
firmware/lib/timer.h Normal file
View File

@ -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()`
*/

View File

@ -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++;
}

View File

@ -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 &copy; 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>

View File

@ -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)

223
firmware/lib/timer/timer.c Normal file
View File

@ -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--;
}
}
}

111
firmware/lib/twi.h Normal file
View File

@ -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&sup2;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
*/

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -1,5 +1,3 @@
# Documentation : I&sup2;C : Teensy 2.0
## I&sup2;C Status Codes (for Master modes)
### Master Transmitter (datasheet section 20.8.1, table 20-3)
@ -25,6 +23,6 @@
-------------------------------------------------------------------------------
Copyright &copy; 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>

View File

@ -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)

150
firmware/lib/usb.h Normal file
View File

@ -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)
*/

View File

@ -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(); }

View File

@ -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();
}

View File

@ -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 &copy; 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>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

144
firmware/main.c Normal file
View File

@ -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;
}

104
firmware/main.h Normal file
View File

@ -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).
*/

212
firmware/makefile Normal file
View File

@ -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)

32
firmware/readme.md Normal file
View File

@ -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 &copy; 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>

View File

@ -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 &copy; 2012 Ben Blazak <benblazak.dev@gmail.com>
Released under The MIT License (MIT) (see "license.md")
Copyright &copy; 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>

3
layout-gen/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
cache/

6
layout-gen/readme.md Normal file
View File

@ -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)

83
layout-gen/references.md Normal file
View File

@ -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 &copy; 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>

View File

@ -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 }
]}

View File

@ -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

View File

@ -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>

362
layout-gen/test-js/main.js Normal file
View File

@ -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);
}
}

View File

118
makefile
View File

@ -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
View File

@ -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&sup2;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 &copy; 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.

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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