Osborne keyboard and KMK
Posted: 11 Jan 2021, 03:46
Back in the before-times (last year), there was a discussion of keyboard firmware systems and CircuitPython came up.
In the meantime, there are mechanical keyboard kits available that use CircuitPython and the KMK project to define a QMK-like library for them.
Now, these libraries aren't primarily designed for what I want, which is converting random vintage keyboards with a wide variety of protocol interfaces to USB. By default, I use QMK and it isn't primarily for this, either. Hasu's original TMK was more oriented that way. Still, there are around a dozen converters in QMK's repo (count in my branch is more like forty). So, what I have to say isn't meant to propose any actual changes in objectives for any of these systems.
As far as I know, all CircuitPython capable boards are 3V. 3V for vintage keyboards is a challenge. One could always add a level shifter, but if breadboarding rather than designing an SMT PCB, that adds a lot of real estate. Except sometimes one is needed for RS-232 voltages, in which case we're all set.
Some keyboards have all the outputs open collector, due to differences at the time between TTL and CMOS. These might work with 3V, but not without risk.
But what about keyboards without active electronics at all? Specifically, with just a matrix of switches that the terminal scanned directly?
Which brings me to this Osborne 1 keyboard. I don't know where that red CTRL key came from. I suppose it's a replacement, but where did someone find one with the right cross-mount? Anyway, I think I found a seller online with something closer to the original and I will fix it.
There are pictures of the insides in this post from a few years ago.
The Technical Manual on Bitsavers has schematics and details of the matrix.
Even though it's a direct matrix, the keyboard designers went to the trouble of connecting the two shift keys and all the numpad keys to corresponding main typewriter section keys. So while there are up transitions, there aren't as many key codes available as it might seem. Also there isn't a backspace key. So I figured to use the left arrow for that and the up/down arrows for layer shifts to get more codes.
Here is a straightforward converter using an Adafruit ItsyBitsy M4 Express, a board about the size of a Teensy and cheaper than one, too. Specifying the matrix pins and key mappings in Python code is straightforward. But the converter only sort-of works. The root problem is that there aren't any diodes in the Osborne matrix, a common problem with older (cheaper) boards. Consequently, you can't type shift-' to get " or shift-[ to get ] (or {) or ctrl-ESC. These are all pairs of keys in row zero. It's even worse if you switch the scan direction ("diode orientation") to ROWS: now shift T, G, and B and ctrl E, D, and C don't work!
The QMK default matrix scanning routine works like open collector scanning did in vintage firmware. The inputs (columns, usually, but not necessarily) have pull-up resistors (inside the MCU in QMK, in hardware in the old days). One of the outputs (rows) at a time is connected to ground. A pressed switch will cause the corresponding input (column) to sink current and sense low, while the pull-ups cause the rest to sense high. The rest of the outputs are kept at high impedance (hi-Z) by setting them as inputs (with additional pull-ups in QMK, but that's not actually essential).
The KMK matrix scanner is different. First off, it sets the selected output high and senses a pressed key as a high input. To some extent, this is an arbitrary choice. It does mean that the inputs need pull-down resistors not pull-up. Which can be inconvenient. While AVRs only did pull-up, these 32-bit MCUs can do both. But, for example, GPIO extenders like the MCP23008 also only do pull-up. There is even an example of using an MCP23017, but it has to be for the outputs (columns in this case); it would fail for the inputs.
More of a problem is what it does with the outputs other than the selected one. They are set low. Which means that without diodes they can compete with the one that is selected.
Now, of course, one way to fix this is to add some diodes. Specifically, a diode per row, if scanning by rows. That doesn't fix all ghosting, which needs them per switch, but it does solve the shift-key problem. Here is the same converter on a breadboard with the necessary diodes. But it's also possible by adding software: a scanner that keeps the non-selected outputs at hi-Z. I also switched the polarity to what I'm used to. Which does mean that if the with-diodes version were permanent, they would need to be reversed.
Naturally, that's just one-time work. This new class works fine for another matrix keyboard, like the TI-99/4A.
In the meantime, there are mechanical keyboard kits available that use CircuitPython and the KMK project to define a QMK-like library for them.
Now, these libraries aren't primarily designed for what I want, which is converting random vintage keyboards with a wide variety of protocol interfaces to USB. By default, I use QMK and it isn't primarily for this, either. Hasu's original TMK was more oriented that way. Still, there are around a dozen converters in QMK's repo (count in my branch is more like forty). So, what I have to say isn't meant to propose any actual changes in objectives for any of these systems.
As far as I know, all CircuitPython capable boards are 3V. 3V for vintage keyboards is a challenge. One could always add a level shifter, but if breadboarding rather than designing an SMT PCB, that adds a lot of real estate. Except sometimes one is needed for RS-232 voltages, in which case we're all set.
Some keyboards have all the outputs open collector, due to differences at the time between TTL and CMOS. These might work with 3V, but not without risk.
But what about keyboards without active electronics at all? Specifically, with just a matrix of switches that the terminal scanned directly?
Which brings me to this Osborne 1 keyboard. I don't know where that red CTRL key came from. I suppose it's a replacement, but where did someone find one with the right cross-mount? Anyway, I think I found a seller online with something closer to the original and I will fix it.
There are pictures of the insides in this post from a few years ago.
The Technical Manual on Bitsavers has schematics and details of the matrix.
Even though it's a direct matrix, the keyboard designers went to the trouble of connecting the two shift keys and all the numpad keys to corresponding main typewriter section keys. So while there are up transitions, there aren't as many key codes available as it might seem. Also there isn't a backspace key. So I figured to use the left arrow for that and the up/down arrows for layer shifts to get more codes.
Here is a straightforward converter using an Adafruit ItsyBitsy M4 Express, a board about the size of a Teensy and cheaper than one, too. Specifying the matrix pins and key mappings in Python code is straightforward. But the converter only sort-of works. The root problem is that there aren't any diodes in the Osborne matrix, a common problem with older (cheaper) boards. Consequently, you can't type shift-' to get " or shift-[ to get ] (or {) or ctrl-ESC. These are all pairs of keys in row zero. It's even worse if you switch the scan direction ("diode orientation") to ROWS: now shift T, G, and B and ctrl E, D, and C don't work!
The QMK default matrix scanning routine works like open collector scanning did in vintage firmware. The inputs (columns, usually, but not necessarily) have pull-up resistors (inside the MCU in QMK, in hardware in the old days). One of the outputs (rows) at a time is connected to ground. A pressed switch will cause the corresponding input (column) to sink current and sense low, while the pull-ups cause the rest to sense high. The rest of the outputs are kept at high impedance (hi-Z) by setting them as inputs (with additional pull-ups in QMK, but that's not actually essential).
The KMK matrix scanner is different. First off, it sets the selected output high and senses a pressed key as a high input. To some extent, this is an arbitrary choice. It does mean that the inputs need pull-down resistors not pull-up. Which can be inconvenient. While AVRs only did pull-up, these 32-bit MCUs can do both. But, for example, GPIO extenders like the MCP23008 also only do pull-up. There is even an example of using an MCP23017, but it has to be for the outputs (columns in this case); it would fail for the inputs.
More of a problem is what it does with the outputs other than the selected one. They are set low. Which means that without diodes they can compete with the one that is selected.
Now, of course, one way to fix this is to add some diodes. Specifically, a diode per row, if scanning by rows. That doesn't fix all ghosting, which needs them per switch, but it does solve the shift-key problem. Here is the same converter on a breadboard with the necessary diodes. But it's also possible by adding software: a scanner that keeps the non-selected outputs at hi-Z. I also switched the polarity to what I'm used to. Which does mean that if the with-diodes version were permanent, they would need to be reversed.
Naturally, that's just one-time work. This new class works fine for another matrix keyboard, like the TI-99/4A.