Designing and building a keyboard
yager.io
yager.io
[1]: https://github.com/technomancy/atreus [2]: http://technomancy.us/173
> The fact that I2C communication blocks immediately is important. Button debouncing is done by counting the number of "frames" for which a key is in a certain state.
The obvious solution for this is to synchronise the I2C clock to the matrix scanning loop, so you receive/send one bit each time through the loop. Even better, do away with the I2C overhead completely and use a continuous serial protocol between the two halves.
I could do it with FP if someone made a Haskell to AVR compiler :)
>Am I the only one who finds this quite a lot more complex than it needs to be?
What functionality would you propose stripping?
>The obvious solution for this is to synchronise the I2C clock to the matrix scanning loop, so you receive/send one bit each time through the loop.
That's the wrong solution. The main loop takes some milliseconds. Communication at that rate would be intolerably slow. And how is that any better than natively clocked I2C? I'd have to re-implement the entire protocol in software to get that kind of control.
>do away with the I2C overhead completely and use a continuous serial protocol between the two halves.
What overhead? Any overhead introduced by I2C is very minimal.
What benefit would a "continuous" (do you mean asynchronous?) serial protocol have? There are also a number of downsides: 1. It wouldn't work well with the symmetric halves design. The cable would have to switch the TX and RX lines, and no common cable does that. 2. It wouldn't support more than two keyboards at once, unless I implemented a shared-line serial protocol from scratch, and then why not just use I2C? And using an asynchronous serial protocol doesn't solve the board synchronization problem.
The new version of the board also breaks out the UART header for people who want to do exactly this, but the UART is not a better solution.
The complexity here is not in the functionality. It's in the design and implementation.
> The main loop takes some milliseconds.
The MCU has a 16MHz single-cycle core. That means a 62.5ns instruction cycle, or 16,000 instructions in 1ms. What makes it require >16,000 instructions every time through the loop?
> What benefit would a "continuous" (do you mean asynchronous?) serial protocol have?
I do mean a continuous one, with the master driving the clock at the frequency of the main loop, and the slave(s) synchronising their transmissions with it. One of the biggest advantages I see is that you can make use of unused positions in the key matrix, so that they can be scanned at the same time as all the others. You just need to know which one, and instead of applying debouncing/up/down, accumulate the bits of scancode that are sent in and pass them through.
> 1. It wouldn't work well with the symmetric halves design. The cable would have to switch the TX and RX lines, and no common cable does that.
GPIOs are bidirectional. You can do the crossover configuration entirely in software, since the current design already detects whether it's plugged in from USB.
> 2. It wouldn't support more than two keyboards at once
How would you do this with the current design? They're symmetrical, but I don't see any way to chain or hub them together (unless you use a USB hub to connect as many independently as you want...), so I think that's a bit of a moot point. But if you are thinking of a star topology for this "keyboard area network"(!), using another unused key position in the matrix of the master for more extension boards would work. Each board could act as a hub for more, but this quickly gets into "why would you ever want to do that" territory.
> unless I implemented a shared-line serial protocol from scratch, and then why not just use I2C?
Because I2C was defined with different assumptions in mind.
I'd love to hear some proposals for simplifying the implementation.
>What makes it require >16,000 instructions every time through the loop?
Mostly USB library stuff, I think. Been a while since I profiled it.
>I do mean a continuous one...
Can you further explain this paragraph? I don't know what you mean by "unused positions in the key matrix".
>You can do the crossover configuration entirely in software
Like I said: I'd have no implement the protocol from scratch, in software. The hardware UART does not support this functionality.
>How would you do this with the current design?
You can splice a TRRS cable or buy a TRRS splitter. The new version of the board actually has a dual-jack USB-A connector to make daisy-chaining even easier.
Again, not sure what you mean by "unused key position in the matrix".
>Because I2C was defined with different assumptions in mind.
Which were?
The one thing that stands out is that many of the classes only have one instance created, and they're classes that don't have much functionality in them. Returning structures from functions requires a copy, in this case the array of button states, and copies of them are also created inside of functions like ButtonDebouncer::update. Only one of these state arrays is ever needed, so make only one...
The actual matrix-scan loop in HardwareController could also be simplified greatly; this usually just involves shifting a 1 bit through the row/column driver output port register(s) and reading the input port(s). There's no need to turn all the rows/columns off each time, since getting the next col/row is only a matter of changing which one is being driven. Getting the button number shouldn't require a multiplication (I'll admit I was pretty shocked when I saw that!) either, since each iteration of the inner loop is the button after the previous one, so it can be done with one counter.
> Mostly USB library stuff, I think. Been a while since I profiled it.
I'm more familiar with PS/2 keyboard protocol which is much simpler, but USB shouldn't be that much more complex to send/receive info once everything has been initialised (and as I understand from working with USB controllers in non-keyboard devices, the SIE does much of the actual transfer work in hardware.)
> Can you further explain this paragraph? I don't know what you mean by "unused positions in the key matrix".
There can be row/column combinations which don't have keyswitches at their intersection, but are scanned anyway. You can connect other units so that they act like keyswitches when they send information.
> Like I said: I'd have no implement the protocol from scratch, in software. The hardware UART does not support this functionality.
Of course.
> Which were?
A non-continuous clock. Transfers that occur in small bursts instead of one continuous serial stream.
(I have studied the IBM keyboard firmware, and worked with one for an 8051-based controller before.)
To maintain proper levels of abstraction.
>Returning structures from functions requires a copy,
Which is extremely fast, simple, side-effect-free, and likely optimized away by RVO.
>The actual matrix-scan loop in HardwareController could also be simplified greatly;
Nothing you described would actually be an optimization. Turning off all the rows/columns takes 1 cycle.
>Getting the button number shouldn't require a multiplication
Nit-picking. This is fast and easy to read. I'm not going to worry about the few nanoseconds required for a multiplication.
That's also not an issue of "simplification"; it's just premature optimization.
>but USB shouldn't be that much more complex
PJRC wrote that library. Not sure what they do.
>There can be row/column combinations which don't have keyswitches at their intersection, but are scanned anyway. You can connect other units so that they act like keyswitches when they send information.
Why would I do that? Part of the design is that each individual board is responsible for translating button presses to USB keycode deltas. This allows for better distribution of responsibility, and also the boards can be connected in any configuration/order and maintain the same behavior.
>Transfers that occur in small bursts instead of one continuous serial stream.
What's the downside to this? The bursts occur way faster than human perception could hope to notice.
Besides, if you want to be pedantic, any serial protocol communicates in "bursts".
Abstraction is only a tool, a means to an end; not the end in itself.
> Which is extremely fast, simple, side-effect-free, and likely optimized away by RVO.
Copying is one of the fastest things a processor can do, but that's not a reason to do it unnecessarily, even if the compiler optimiser could simplify it.
> That's also not an issue of "simplification"; it's just premature optimization.
Really? I suppose that if you consider abstraction the ultimate goal, you might think that shorter, simpler code would be "premature optimization"...
> Part of the design is that each individual board is responsible for translating button presses to USB keycode deltas.
That functionality remains the same; the receiving board just collects each bit of the scancode and passes it through directly to the USB. The sending board is still the one doing the matrix scan and position-to-keycode translation. The receiving board is just a passthrough.
> What's the downside to this? The bursts occur way faster than human perception could hope to notice.
It adds complexity to the software, because it has to deal with start/stop conditions, etc.
> Besides, if you want to be pedantic, any serial protocol communicates in "bursts".
Not if the clock is continuous; once the receiver and sender are synchronised, the sender can e.g. keep transmitting 0 bytes when there is nothing to send, and keycodes (which are all nonzero) otherwise.
I've been looking for a split ergonomic type mechanical keyboard, but I haven't found any. The author just went about building what I was looking for.
I personally got started with microcontrollers and digital electronics by reading "The Microcontroller Idea Book" by Jan Axelson. It's a bit dated, but very well-written, and for me it was the first time that hardware really "clicked". I still refer to it from time to time for ideas for interfacing I/O devices.
http://geekhack.org/index.php?board=117.0
I built my board (http://technomancy.us/173) primarily building on knowledge from geekhack.org and the #geekhack channel on freenode.
Came up with the following requirements:
- Must be split into a left hand and a right hand.
- No wires! (since it largely defeats the first point)
- All the regular relative raised key/ergonomic issues.
- Mechanical, obviously.
- Since the point is essentially arbitrarily positioned keyboard hands for touch typing, there's little purpose in having the letters on the keys: they're no longer necessarily right in front of you to look at.
- Optional: can either free-stand successfully or have some various means to allow it to be mechanically attached to various things/surfaces/mechanisms: Say if i want to have a lazy-boy chair with each arm being one of the keyboards.
So get to work HN :P
I don't know if it's a personal bias after using a Thinkpad keyboard over the years, but I feel like scissors switches don't put as much stress on my hands and require less effort.
I've used a Thinkpad as well as other laptop's keyboards over the years, and the Thinkpad ones usually do feel a bit more solid and accurate. They're still a bit too stiff for me, however, as my regular desktop's keyboard is an extremely soft rubber dome one (~40g actuation force). I've tried clicky keyboards, but never really liked them. It's definitely a personal preference.
What keyboard do you use?
Does the fact that the keys aren't staggered make it significantly better or worse to type on?
Depends who you ask! Personally, I think it's a bit more comfortable to type on than a staggered layout.
Some people love the matrix layout. Some people are rather put off by it.
[1] - like the Ergodox or my own Atreus design does: https://secure.flickr.com/photos/technomancy/tags/atreus
Many people use one hand to type on the mobile phone (thumb to be more exact). However, the design of the keyboard on the mobile screen does not align well with the natural movement of the thumb. It is painful and hideous.
I believe an Arc keyboard on the mobile screen would be the best design. Anyone working on something similar?
Sadly keyboards are often blocked by patents.
Of course, I confess that I have been lucky enough to never have any problems from one. I fully understand those that need it for medical reasons. I just don't know as that I would choose it if I didn't need it. (Rather, I know I would not.)
On the back side of the circuit board, there are 6 thermally isolated solder pads. I put them there so that I could potentially solder nuts to each of the pads, and then bolt the PCB to a case.
Hopefully this encourages more people to have a shot at building something interesting, and write about it.
Thanks a lot.
Flexible (polyamide) PCBs are already available, with FR4 stiffeners where components are to be soldered. You could assemble one flat, bend to desired shape and attach to a form.
Now you got me thinking!!! :D