“The Lord Of The M" Controllers (aka TLOTM) - call for ideas

User avatar
idollar
i$

25 Jul 2024, 12:15

Hello,

Note: At the end of this post you will find a section with various references that I will keep updating

Here is my first post to the “The Lord Of The M"Controllers (aka TLOTM).
Spoiler:

"One Controller to rule them all (Ms), One Controller to find them, One Controller to bring them all and in the darkness bind them." :D


You may know that I am not very active in DT. This does not mean that I try to follow what is going on in “our business”.
I believe that it is time to move from pure keyboard hardware to the only part which I have not tried to master; controllers and their firmware.

So in this post I am calling for ideas on a project which I am thinking about. Let me know your opinions, please.

The basic concept is as follows:
  • Build a Model M controller – compatible with all IBM Model Ms and Unicomps
  • QMK & VIAL enabled
  • RP2040 (TBD - chip or board) based
  • Document and Document. Document even more ! I will probably write a document as xwhatsit did.
  • Eventually implement a group-buy.

Three initial challenges:
  • Section 1.- The number of rows and columns is variable
  • Section 2.- Size and position of the actual controller change from one keyboard to the other
  • Section 3.- The number of LEDs is also variable
  • Section 4.- (optional) solenoid support 😊


Let’s analyse each challenge amd provide preliminary solutions (initial top level design):

Section 1.- The number of rows and columns is variable
Spoiler:

For instance: MAX GPIO needed = [MAX(cols) = 17] + [MAX(rows) = 20] + [LEDs = 3] = 40

Conclusion 1: The number of GPIO in the RP2040 is 30 which means that we will shift registers for rows or columns.

For reference, purdeaandrei’s MiniRazz https://github.com/purdeaandrei/MiniRazz/tree/master) solved this problem with a mixed solution at the reading side (COLS):
  • He pings ROWS using RP2040 GPIOs
  • He reads COLS using a mixed solution: 1 byte direct GPIOs 1 byte PISO register (74HC165)
    • RP2040_CPI00 = COL_F
    • RP2040_CPI01 = COL_E
    • RP2040_CPI02 = COL_D
    • RP2040_CPI03 = COL_C
    • RP2040_CPI04 = COL_B
    • RP2040_CPI05 = COL_A
    • RP2040_CPI06 = COL_9
    • RP2040_CPI07 = COL_8
    • 74HC165_0 = COL_7
    • 74HC165_1 = COL_6
    • 74HC165_2 = COL_5
    • 74HC165_3 = COL_4
    • 74HC165_4 = COL_3
    • 74HC165_5 = COL_2
    • 74HC165_6 = COL_1
    • 74HC165_7 = COL_0
I would rather use the shift registers to ping the matrix. Pinging means shifting a "1" (or "0") across the rows/columns to be tested. This can be easily done using Serial In - Paralell Out (SIPO) registers.
Three 74HC595 are needed to address up to 24 rows or columns.
(Note: MIN(MAX(rows), MAX(cols)) = 17 which is greater than 16. The Mini-m has 17 rows therefore we need 3 shift registers)/

The process to ping would be as follows:
  • Initialise the shift registers with logical “1”.
  • To start the scan we push a “0” in the serial port.
  • To move to the next position we just need to push a “1”
(note: I assume pull ups, so 1=3.3v=”logical 0”)


We will need just three RP2040 GPIOs to manage the pinging: :
  • Serial interface
  • Clock
  • Enable
So we have left some 29 – 3 (serial) – 3(leds) = 23 which is sufficient to read the MAX(row)=20.

Using two of the 3 extra GPIOs could allow to implement and I2C interface.

Preliminary Conclusion 2: GPIO and Matrix assignment:

Pinging rows reading columns:

Read
  • RP2040_CPI00 = COL_x00 (byte0)
  • RP2040_CPI01 = COL_x01 (byte0)
  • RP2040_CPI02 = COL_x02 (byte0)
  • RP2040_CPI03 = COL_x03 (byte0)
  • RP2040_CPI04 = COL_x04 (byte0)
  • RP2040_CPI05 = COL_x05 (byte0)
  • RP2040_CPI06 = COL_x06 (byte0)
  • RP2040_CPI07 = COL_x07 (byte0)
  • RP2040_CPI08 = COL_x08 (byte1)
  • RP2040_CPI09 = COL_x09 (byte1)
  • RP2040_CPI10 = COL_x0A (byte1)
  • RP2040_CPI11 = COL_x0B (byte1)
  • RP2040_CPI12 = COL_x0C (byte1)
  • RP2040_CPI13 = COL_x0D (byte1)
  • RP2040_CPI14 = COL_x0E (byte1)
  • RP2040_CPI15 = COL_x0F (byte1)
  • RP2040_CPI16 = COL_x10 (byte2)
  • RP2040_CPI17 = COL_x11 (byte2)
  • RP2040_CPI18 = COL_x12 (byte2)
  • RP2040_CPI19 = COL_x13 (byte2)
Write (Ping)
  • RP2040_CPI20 = PING_SERIAL
  • RP2040_CPI21 = PING_CLOCK
  • RP2040_CPI22 = PING_LATCH
  • RP2040_CPI23 = PING_CLEAR_REGISTERS
  • 74HC165_A_0 = ROW_x00
  • 74HC595_A_1 = ROW_x01
  • 74HC595_A_2 = ROW_x02
  • 74HC595_A_3 = ROW_x03
  • 74HC595_A_4 = ROW_x04
  • 74HC595_A_5 = ROW_x05
  • 74HC595_A_6 = ROW_x06
  • 74HC595_A_7 = ROW_x07
  • 74HC165_B_0 = ROW_x08
  • 74HC595_B_1 = ROW_x09
  • 74HC595_B_2 = ROW_x0A
  • 74HC595_B_3 = ROW_x0B
  • 74HC595_B_4 = ROW_x0C
  • 74HC595_B_5 = ROW_x0D
  • 74HC595_B_6 = ROW_x0E
  • 74HC595_B_7 = ROW_x0F
  • 74HC165_C_0 = ROW_x10
  • 74HC595_C_1 = ROW_x11
  • 74HC595_C_2 = ROW_x12
  • 74HC595_C_3 = ROW_x13
  • 74HC595_C_4 = ROW_x14 (not needed but available)
  • 74HC595_C_5 = ROW_x15 (not needed but available)
  • 74HC595_C_6 = ROW_x16 (not needed but available)
  • 74HC595_C_7 = ROW_x17 (not needed but available)


LEDs
  • RP2040_CPI24 = LEDS_SERIAL
  • RP2040_CPI25 = LEDS_CLOCK
  • RP2040_CPI26 = LEDS_LATCH
  • RP2040_CPI27 = LEDS_CLEAR_REGISTERS
I2C
  • RP2040_CPI28 = I2C_SDA
  • RP2040_CPI29 = I2C_SCL
Section 2.- Size and position of the actual controller change from one keyboard to the other
Spoiler:

The solution which I am thinking here is to design a mother and daughter boards.
  • The mother will be at the bottom, with all the electronic components. It shall also have right and left USB connectors to allow the different keyboard cases configurations (location TBC).
  • The daughter will sit on top and will implement the interface to the keyboard matrix, placing the connectors in the right place.
Section 3.- The number of LEDs is variable
Spoiler:

Refer to GPIO assignment above.
We could also use two registers in cascade (74HC595) to allow up to 8 LEDs.
These would be very useful to show the current layer for instance.

A solution with LEDs mounted on a surface that would fall below the actual keys, would allow to light them under the keys.
Section 4.- (optional) solenoid support
Spoiler:

I need to study the Rico's solution used in the Leiden Jar based on I2C (https://geekhack.org/index.php?topic=117555.0 - reply #13). We could apply the same concept.
-----------------------

References:
Spoiler:
Last edited by idollar on 27 Jul 2024, 06:37, edited 23 times in total.

User avatar
idollar
i$

25 Jul 2024, 12:49

The RP2040 would look something like this:
Controller_v0.2.gif
Controller_v0.2.gif (303.14 KiB) Viewed 7471 times


The shift registers would be like this:
Shift Registers v_0.2.gif
Shift Registers v_0.2.gif (410.91 KiB) Viewed 7471 times
I thought that once we start with registers, for a couple of euros extra we could control many LEDs. So we can also shift the LED control.

I keep the "CLEAR_REGISTER" common to the PING and LED shift register. This would allow to clear all of them in case is needed (e.g. initialisation)
Last edited by idollar on 25 Jul 2024, 17:49, edited 8 times in total.

User avatar
clickykeyboards

25 Jul 2024, 15:28

This is a great idea.

@ clickykeyboards, we often get inquiries (2-4/month) over the past 20 years from people that are new to the hobby looking to get their 1980s IBM model M keyboards to easily interface with 2024 computers. The other day, I met a Cherry MX enthusiast after work who was picking up their first model M keyboard and they had many questions about "modern" options for vintage keyboard hardware.

I am seeing a growing number of requests of people looking for native USB-C connectivity (primary request) and for key remapping/programmability (secondary request). While there are some who are willing to DIY from a raw list of materials, there are many more people who are just looking for pre-built, turnkey solutions.

When the TOLTM project is fully completed, I would be more than happy to forward many customers to obtain the alternate controller.
Or serve as a trusted resource for those who want someone else to install the new controller and/or clean/restore model M keyboard for them.
https://www.clickykeyboards.com/product ... ly-repair/

User avatar
depletedvespene

25 Jul 2024, 23:48

I very much like this idea. Well implemented, it should be able to unconflate both \| key positions and allow Model M keyboards with TIE Enter + Sun ("HHKB") Backspace keys.

User avatar
idollar
i$

26 Jul 2024, 12:47

It ocurred to me that if we add a couple of analog comparators (LM339A) and a DAC (MCP4921) to the daughter board, we can transform the Model M controller into a Model F one.

We would need to free some pins from the columns but this is not an issue as they will be exposed to the daughter board anyhow.

The firmware should make use of these as they are used in xwhatsit's controller.
I have not analysed how the new Leyden Jar works. But should be somehow similar.
In the case of the Model Fs, timing is critical as the change is registerd as a voltage pick at the time the flipper falls into the PCB (capacitive).

User avatar
idollar
i$

26 Jul 2024, 14:17

<post related with the idea above on the daugher board making TLOTM model F compatible>

I believe that I understood how Rico (Leyden Jar Controller) sets the reference/calibration voltage in his model F controller:
  • He replaced the MCP4921 DAC used by xwhatsit by a MCP4716A0T DAC.
    There are two main differences:
    • the MCP4716A0T-E/CH uses an i2C interface to set its values
    • the MCP4921 is a 12bits DAC while the MCP4716A0T is 10 bits
  • Rico connected the MCP4716A0T to the I2C interface and used the "i2c_master" library to address it
  • Rico also uses I2C to address the expansion cards (Solenoid + LEDs)
I have also seen that he is using "pioasm" to generate some assembly code ... I need to investigate. It is problably related to reading the value from the LM339A comparators as this is very time sensitive.

References in this post:

User avatar
DMA

07 Aug 2024, 22:57

You have a mistake in your math, idollar.
Largest dimensions are 20x11, largest matrix is 17x11 OR 20x8 - amusingly, both are 28 pins. 28+3 = 31, if you sacrifice ScrLk (which nobody uses now anyway) - you'll be able to squeeze into 30 GPIOs RP2040 has.

Re: "tall" matrix - just call the longer side "columns" and use the shift registers for those. This way you'll need 14 GPIOs tops.
Re: LEDs - you have 16 GPIOs available now, just direct-drive those.

Re: core+carrier boards - yeah, a solid approach: you'll have to place some components there anyway, so why not displace your shift registers to the carrier. However, I would bring the register to the core board - carrier will be just routing signals, so will be way easier to make a custom carrier board - or even just solder the core board directly to the matrix.

User avatar
idollar
i$

08 Aug 2024, 11:32

Thanks a lot DMA !
DMA wrote:
07 Aug 2024, 22:57
You have a mistake in your math, idollar.
Largest dimensions are 20x11, largest matrix is 17x11 OR 20x8 - amusingly, both are 28 pins. 28+3 = 31, if you sacrifice ScrLk (which nobody uses now anyway) - you'll be able to squeeze into 30 GPIOs RP2040 has.

Re: "tall" matrix - just call the longer side "columns" and use the shift registers for those. This way you'll need 14 GPIOs tops.
Re: LEDs - you have 16 GPIOs available now, just direct-drive those.
Please comment, any idea is welcome:

Actually my approach, after some thingking is somehow different:
  • We need I2C (At lease I need it, I will probably attach an optionak display to the keyboard)
  • We need x4 GPIOs to drive the pinging (rows) = Serial + Clock + Latch + Clear
  • We need x4 GPIOs to drive the LEDs = Serial + Clock + Latch + Clear
Which totals to 2 + 4 + 4 = 10

The remaning ones can be used to manage columns -- direct drive -- (30-10) = 20
DMA wrote:
07 Aug 2024, 22:57
Re: core+carrier boards - yeah, a solid approach: you'll have to place some components there anyway, so why not displace your shift registers to the carrier. However, I would bring the register to the core board - carrier will be just routing signals, so will be way easier to make a custom carrier board - or even just solder the core board directly to the matrix.
Yes, correct. This is the idea. all components will be located in the main board, including the shift registers.
The daughter board (carrier board) will just route the connections to the board to the correct location, exposing the required physical interface.

Let me know any comment

i$

User avatar
taylorswiftttttt

09 Aug 2024, 01:01

...
Last edited by taylorswiftttttt on 26 Sep 2024, 08:58, edited 1 time in total.

User avatar
DMA

09 Aug 2024, 03:00

idollar wrote:
08 Aug 2024, 11:32
Actually my approach, after some thingking is somehow different:
  • We need I2C (At lease I need it, I will probably attach an optionak display to the keyboard)
  • We need x4 GPIOs to drive the pinging (rows) = Serial + Clock + Latch + Clear
  • We need x4 GPIOs to drive the LEDs = Serial + Clock + Latch + Clear
Which totals to 2 + 4 + 4 = 10

The remaning ones can be used to manage columns -- direct drive -- (30-10) = 20
I'd direct-drive LEDs, actually. You'll never need more than 11 "columns" anyway - and for all cases except unicomp mini-m, just 8. That leaves you with I2C(2) + rows(4) = 6. Even with 11 direct-drive columns you still have 13 GPIOs available - way more than enough.
idollar wrote:
08 Aug 2024, 11:32
DMA wrote:
07 Aug 2024, 22:57
Re: core+carrier boards - yeah, a solid approach: you'll have to place some components there anyway, so why not displace your shift registers to the carrier. However, I would bring the register to the core board - carrier will be just routing signals, so will be way easier to make a custom carrier board - or even just solder the core board directly to the matrix.
Yes, correct. This is the idea. all components will be located in the main board, including the shift registers.
I misread your post then. Somehow I understood that shift registers are displaced to carrier, because carrier is board-specific, and so you only place as many shift registers as you need.
idollar wrote:
08 Aug 2024, 11:32
The daughter board (carrier board)
aaand that's why. In my book, daughterboards are plugged into motherboard, not the other way around.
So, I would have board-specific carrier board - containing both USB and matrix connectors (because USB placement is as variable as the connector placement, if not more) - with core board soldered into it akin to ESP32 modules (or plugged in somehow - although you might remember how inconvenient IDE cables were (and those are 40-pin) - so you might want to reconsider :) )

User avatar
webwit
Wild Duck

09 Aug 2024, 11:54

Should have been called Lord Of The Pings.

User avatar
idollar
i$

16 Aug 2024, 15:13

Dears,

I send you an update on the status of the controller.

I have added the following:
  • Drop in replacement of the original model M controller.
  • USB-A connector exposed where the original connector is.
  • J5 connector for debugging purposes do not need to be populated
  • J6/J7 expansion header which could be used for a solenoid ?
  • J8 I2C connector for future expansion boards
  • LEDs resistors will not be needed if no LEDs are connected
  • Connectors for daughter boards can be ignored if used for a model M only
  • Accessible reset and bootsel buttons
  • Connectors for reset and bootsel close to the USB, to allow wiring out some cables for testing without opening the keyboard
Pending
  • Is the interface for the original Model M LEDs suitable ? It may need some rework.
  • Add connector with "shield", "GND", "+3.3v" and "+5.0v" to feed the daughter board.


I still need to check and debug it.

i$
Schema.jpg
Schema.jpg (2.35 MiB) Viewed 5346 times
Front.jpg
Front.jpg (125.09 KiB) Viewed 5346 times
Back.jpg
Back.jpg (78.31 KiB) Viewed 5346 times

pandrew

26 Sep 2024, 19:15

Hey,

I have a few comments to add I'm noticing.
  • The Mini M matrix is 16x12 (not 17x11). (There was a bug on Sharktastica's Mini M matrix simulator site, which is now fixed, the simulator was okay, there was just an issue with how the matrix was displayed)
  • It is not a good idea to use push-pull drivers on outputs, when the matrix you are driving doesn't have diodes.
If you are driving rows, and reading columns, and your user presses two keys on the same column at the same time, then you are effectively shorting two rows together. If you use push-pull drivers rather than open-drain, then you end up shorting together two strong outputs, which can result in a number of undesired behaviors:
  • A strong "1" and a strong "0" shorted together will produce a voltage somewhere inbetween, which will make the reading of the columns unreliable.
  • Tens of milliamps of current going through the membrane. This may result in damage over a long time.
  • Strong outputs shorted together can overstress those outputs over time and result in them degrading.
In fact this is the exact mistake that Unicomp made with the first version of their pico-based Mini M controller, and that's why they had to add the "Mike Smith" board on top of the "Justify" controller.

If you want to do your pin-multiplexing on the output, then a couple of options I can think of are:
  • Add diodes to each row -- this may not work very well at 3.3V supply. Diodes have a significant forward voltage drop. You should do the math and make sure that VOLmax_design + Vdiode_forward + Vmembrane_drop < VILmax (note; VOLmax_design, would be the maximum output-low voltage for the given design, you don't have to use the VOLmax from the datasheet, cause you won't be maxing out the current). V_membrane_drop would be the voltage drop over the keyboard membrane, which might change over time as the keyboard get older and the membrane contact points could start having higher resistance). Of course similar issue can happen with one-diode-per-key keyboards, so I'm not saying it's impossible to do a good design with diodes, just saying pay attention, and do the math.
  • Instead of push-pull drivers use GPIO expanders which can set GPIOs as inputs, or can otherwise do open-drain outputs.
  • Use shift registers that have always open-drain outputs, (if they exist?)
  • What Unicomp did on the Mike Smith board was to use LVC125A 3-state outputs. I believe they drove the 1A/2A/3A/4A pins as always low, while driving the negated 1OE/2OE/3OE/4OE pins from the push-pull drivers that they had on the main board. (In their case these are LVC138A decoders, but the same concept applies if they would have used 595 shift registers)
Last edited by pandrew on 28 Sep 2024, 01:38, edited 1 time in total.

pandrew

26 Sep 2024, 19:28

Also, consider the RP2350B. It has a lot more outputs. Unfortunately its GPIOs have a known bug, however I believe they will function just fine for keyboard purposes. Also maybe consider just reusing the PGA2350 board or another similar project: https://shop.pimoroni.com/products/pga2 ... 2629229651 with this one could design a controller that can be assembled with just through-hole soldering. Akin to m-star, but better. I don't think QMK supports RP2350 yet, but I don't think it would take a lot of time for that to happen, I see people already doing prep-work for that.

User avatar
DMA

01 Oct 2024, 07:31

@pandrew I won't be too concerned about degrading GPIOs - a) usually source current is less than sink current and b) dI/dt kills the transistors, and does that pretty reliably - so you'll either be fine or fry your sourcing GPIO, but long-term not much will happen. Frying the membrane is of more concern.
Re: diodes on drivers - good idea, but a) 0.7V voltage drop won't really be much of a concern, and b) if it's a concern, just use Schottky diodes, they have 0.15-0.2V drop.

pandrew

02 Oct 2024, 03:58

DMA wrote:
01 Oct 2024, 07:31
@pandrew I won't be too concerned about degrading GPIOs - a) usually source current is less than sink current and b) dI/dt kills the transistors, and does that pretty reliably - so you'll either be fine or fry your sourcing GPIO, but long-term not much will happen. Frying the membrane is of more concern.
If you press all the keys on a single input line, then you'll be shorting together up to 24 outputs on the above schematic, only one of which would be driving 0, and 23 would be driving 1. That is almost the same as driving a 0 directly into 3.3V. Well, not quite, the membrane's resistance will limit it somewhat. Probably won't kill the chip immediately, but I think it could degrade things over time, cause the current will most likely be above absolute maximum ratings. That is if you put something heavy on the keyboard pressing all keys, and leave it turned on for a long time. Otherwise probably not a real concern.
DMA wrote:
01 Oct 2024, 07:31
Re: diodes on drivers - good idea, but a) 0.7V voltage drop won't really be much of a concern, and b) if it's a concern, just use Schottky diodes, they have 0.15-0.2V drop.
Let's assume we're driving zeros from 74HC595, while the RP2040 runs at IOVDD=3.3V and samples.

VILmax for RP2040 is 0.8V. (I don't know if they measure it with or without hysteresis. If this is a spec which is measured without hysteresis enabled, then enabling hysteresis makes this worse, 0.6V or lower. But if this is a specification that considers hysteresis, then disabling hysteresis, could make the actual VILmax larger, and improve our headroom. It's not clear from the datasheet which one it is, it would be interesting to do some experiments to see how the real world compares to the datasheets. I think values such as VILmax are most of the time really statistically determined rule of thumb values, so an experiment might not point clearly to either conclusion. Anyway, for now let's ignore the question of hysteresis, and assume VILmax of 0.8V is needed for low levels to be reliably detected.)

If using RP2040 internal pull-ups, each pull-up can be as low as 50 kOhm, and having up to 20 of those in parallel Can result in 1mA of current, that the 74HC595 will need to sink.

I can't find clear graphs for how much the shift register's VOLmax will be at 1mA current, running at 3.3V, but a TI datasheet says at 6mA and running at 4.5V VCC, at 25degC it can be 0.26V and can be worse up to 0.4V at wide temperature range.

So I don't know, let's work with 0.07V for now (i.e. I expect VOLmax to be linearish to the current, so 0.4V * 1mA / 6mA = 0.07V)...

So the equation you'll have to satisfy is:
VOLmax(@1mA current) + Vforward + Vmembrane_drop < VILmax
  • Let's say you have a normal diode that drops 0.7V, that leaves you with a maximum Vmembrane_drop = 0.03V. A simplistic Ohm's law calculation results that the membrane can't have more than 30 ohms (with 20 keys connected to 1 driving line pressed). Not sure I'd be 100% comfortable with that. (For reference: on a relatively fresh Mini M I measured around 50 ohm pressing one key, and 35 ohm pressing two keys. Extrapolating to 20 keys, it'd be around 20 ohms, not a very scientific measurement, but this doesn't make me comfortable enough, it's way too close. Resistances could be worse on an aging membrane, or an over-the-numpad style controller.)
  • Let's say you have a schottky diode that will drop 0.2V, that leaves you with a maximum Vmembrane_drop of
    0.53V. Applying ohm's law, 0.53V / 0.001A = 530 ohms. I think this would be safe, I don't think the membrane
    would go to such high resistances unless it got serious water damage.
Now will it work with normal diodes if you try it on a one-off project? I say probably. It's unlikely that you'll end up stretching all of your numbers to the worst case, but I wouldn't trust it to be reliable unless I can come up with some way to reasonably relax some of the above numbers.

Also coming back to hysteresis, it bothers me that it's not very clear from the datasheet how hysteresis is intended to affect all of this. I think with my current knowledge I would say:
  • using schottky diodes with hysteresis disabled - will work
  • using schottky diodes with hysteresis enabled - will probably work, if hysteresis removes 0.2V of headroom, then the maximum mebrane resistance would have to be 330 ohm, which is still reasonable
  • using normal diodes with hysteresis disabled - i don't know, I would assume risky until proven otherwise.
  • using normal diodes with hysteresis enabled - way too risky for my taste.

Post Reply

Return to “Workshop”