dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Bidirectional I2C communications for a split keyboard

Wed Sep 15, 2021 8:25 am

I've documented the process around implementing the I2C comms in my Pico based Keyboard firmware. Might be useful for people who are looking for a general bidirectional primary <-> secondary communication method for Picos (or other microcontrollers).

https://www.dairequinlan.com/2021/08/on-communication/

Firmware code is here https://github.com/dairequinlan/mechware

User avatar
Gavinmc42
Posts: 6194
Joined: Wed Aug 28, 2013 3:31 am

Re: Bidirectional I2C communications for a split keyboard

Wed Sep 15, 2021 8:40 am

Nice keyboard.
Haven't seen that keyboard layout website before.
Thinking something like the Atreus is for me.

Anyone sell keyboard hole punches?
Got lots of 1.5mm brass sheet lying around.

I am looking at using those keyswitch connectors so they can be swapped without resoldering.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Thu Sep 16, 2021 12:02 pm

Yeah both my keyboard layouts have been pretty conservative, the split one is like that so that it can be slotted back together again and just be a normal keyboard. There are another couple of posts on my site detailing the first build. Lots of tools online to make it straightforward to get everything you need. If you want a PCB (for hotswap switches) there's tooling for that too somewhere.

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Fri Sep 17, 2021 10:12 am

Actually, back on topic, I briefly mention some of the problems I had trying to get the I2C connection working in that blog post. Coming from a position of almost complete ignorance of I2C, I tried a bunch of different things before I settled on my final solution, which is probably not entirely optimal.

Ultimately I needed bidirectional communications between the primary and secondary device (master/slave in I2C notation). The Primary has to send events to the secondary, but also read events from it. From the Primary, I had tried the i2c_get_read_available at one point, and also, when that didn’t work, the i2c_read_timeout_us with a sufficiently small timeout that if there was nothing to read the Primary would just continue on its way. Neither worked, the ‘read_available’ was always 0, the read_timeout would hang IIRC. They seemed like things to _try_ but aren't documented particularly well as to usage and what not.

What I ended up doing, steered by some I2C examples on the forum here, was have the read and write requests from the Primary trigger IRQ handlers on the Secondary, and in those IRQ handlers dump events onto the bus or read events from the bus respectively. There were also a couple of queues involved because the actual event _handling_ happens outside the IRQ handlers.

The complication was that if I wanted to send multiple events from the Secondary to the Primary there didn't seem to be a good way of doing that, so I send any actual events I have queued up, and then send a special 'empty' event to signify that there aren't anymore, and then the primary quits requesting them. I'm pretty sure there's a better way of doing this.

User avatar
Gavinmc42
Posts: 6194
Joined: Wed Aug 28, 2013 3:31 am

Re: Bidirectional I2C communications for a split keyboard

Sat Sep 18, 2021 5:02 am

Some i2c chips use an interrupt output as well.
The slave has new data and pulls an interrupt low on the master.
The master then does a register read.
Power, i2c and interrupt, 5 wires.

What about SPI?
Just keep polling for changes?

How do the Ergo-Dox type splits do it?
I have seen some use 10 wire RJ45, probably in a 5x5 or 4x6 scan pattern.

https://github.com/qmk/qmk_firmware
Will it port to Pico?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

hippy
Posts: 10748
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Bidirectional I2C communications for a split keyboard

Sat Sep 18, 2021 11:45 am

dairequinlan wrote:
Wed Sep 15, 2021 8:25 am
Might be useful for people who are looking for a general bidirectional primary <-> secondary communication method for Picos (or other microcontrollers).
To me "bidirectional I2C" means either side can be master, the other slave, and it's possible to switch which is master while running. While your set-up can pass data in both directions it seems to me it is still using a unidirectional fixed master and slave configuration.

Have I misunderstood ?

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Sun Sep 19, 2021 6:23 pm

Gavinmc42 wrote:
Sat Sep 18, 2021 5:02 am
Some i2c chips use an interrupt output as well.
The slave has new data and pulls an interrupt low on the master.
The master then does a register read.
Power, i2c and interrupt, 5 wires.
Ah right, yes that's interesting. I guess it's an ad hoc thing though ? I.E. outside the scope of the I2C spec ? Or is it part of the protocol ? Extra wire per device though for the interrupts, may as well be using SPI :-)
What about SPI?
Just keep polling for changes?
Didn't want to. I wanted to potentially be able to put other devices on the bus and have them communicate with the master and each other (via the master) using this same event protocol I'm using. I haven't _quite_ gotten there yet.
How do the Ergo-Dox type splits do it?
I have seen some use 10 wire RJ45, probably in a 5x5 or 4x6 scan pattern.
https://github.com/qmk/qmk_firmware
Will it port to Pico?
Yeah QMK is probably the most popular OS firmware for keyboards. Tons of features. It does support splits, either through I2C or UART I think. And yes there are other splits that just pass the entire matrix wiring across from one half to the other. I've seen one that uses a HDMI cable, which has ... 22? 26? lines or something so wouldn't even really matter at that point what size the board was. I don't think I'd be up to soldering a HDMI socket though :-D

Will QMK port to Pico ? Probably , I'd imagine there are people working on it. I wanted to write my own. It's more fun that way.

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Sun Sep 19, 2021 6:26 pm

hippy wrote:
Sat Sep 18, 2021 11:45 am
dairequinlan wrote:
Wed Sep 15, 2021 8:25 am
Might be useful for people who are looking for a general bidirectional primary <-> secondary communication method for Picos (or other microcontrollers).
To me "bidirectional I2C" means either side can be master, the other slave, and it's possible to switch which is master while running. While your set-up can pass data in both directions it seems to me it is still using a unidirectional fixed master and slave configuration.

Have I misunderstood ?
nope, that's pretty much exactly what it does. The master writes to the slave causing an IRQ on the slave, or the master polls the slave for data, again causing an IRQ on the slave. Pretty simple from the POV of the master, little more convoluted on the slave, particularly if you want to anything remotely time consuming with the data.

User avatar
Gavinmc42
Posts: 6194
Joined: Wed Aug 28, 2013 3:31 am

Re: Bidirectional I2C communications for a split keyboard

Mon Sep 20, 2021 1:04 am

I.E. outside the scope of the I2C spec ? Or is it part of the protocol ? Extra wire per device though for the interrupts, may as well be using SPI
I2C interrupts is something I don't use, as I am always polling for data.
Keyboard i2c with interrupt is a way to signal to the master new data is ready.

Lots of i2c chips have it, can be used or ignored.
Some i2c chips do a lot of on chip preprocessing of data and pull the interrupt out low when the data is ready.
4 or 5 wires won't make that much difference.

For keyboards interrupts save having tight polling cycles.
High poll rates, N key rollover etc gamers need for speed.

You could use UARTs as the receiver block in the master can interrupt when it gets new data.
3, or 4 wires can be used for that.
Full duplex TX/RX for bidirectional coms and/or bootloader software updates.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Tue Sep 21, 2021 8:35 am

Gavinmc42 wrote:
Mon Sep 20, 2021 1:04 am
I.E. outside the scope of the I2C spec ? Or is it part of the protocol ? Extra wire per device though for the interrupts, may as well be using SPI
I2C interrupts is something I don't use, as I am always polling for data.
Keyboard i2c with interrupt is a way to signal to the master new data is ready.
Right, interesting, definitely something to consider in the future, though again I'd probably just end up using SPI if I had to use a per-device interrupt. I guess the Interrupt line could be multiplexed as well, so you'd know SOMETHING on the bus had data and you could poll each one.

For data from the secondary half I have a 12ms scan and debounce delay on the switches, and then potentially another 4ms to wait for a scan on the primary, so ~16ms for a keypress to make its way to the primary half and get written to the USB host. I'm sure twitch streamers would give out, for me it does the job :-)

rhulme
Posts: 4
Joined: Fri Oct 08, 2021 9:54 am

Re: Bidirectional I2C communications for a split keyboard

Fri Oct 08, 2021 2:45 pm

dairequinlan wrote:
Tue Sep 21, 2021 8:35 am
Gavinmc42 wrote:
Mon Sep 20, 2021 1:04 am
I.E. outside the scope of the I2C spec ? Or is it part of the protocol ? Extra wire per device though for the interrupts, may as well be using SPI
I2C interrupts is something I don't use, as I am always polling for data.
Keyboard i2c with interrupt is a way to signal to the master new data is ready.
Right, interesting, definitely something to consider in the future, though again I'd probably just end up using SPI if I had to use a per-device interrupt. I guess the Interrupt line could be multiplexed as well, so you'd know SOMETHING on the bus had data and you could poll each one.
I'm a little late to this but I don't think you can say that if each slave is going to have a separate interrupt line, you might as well use SPI. SPI needs a separate chip select line just to select the slave the master wishes to speak to. If a slave needs to indicate it has data, you would still need another interrupt line per slave. So you'd go from 3 lines (GND, SCL, SDA) plus 1 interrupt line per slave to 4 lines (GND, SCK, MISO, MOSI) plus 2 (CS, Interrupt) per slave.

If keeping the number of lines to a minimum is critical, you could certainly use a single open-drain interrupt line that any slave could assert (as you suggested) and then just query each slave in turn. Obviously you'd have to check whether the increase in latency is acceptable.

Since you mentioned in your write-up that you had some difficulties getting it working (coming from nothing that's perfectly understandable), maybe this helps (if you haven't already got a better understanding):

In terms of data transfer, the i2c specification only defines how the master can write data to a slave and how it can tell a slave it expects data. It doesn't define any kind of handshaking or indication of how much data will be sent/received. If required, that kind of thing has to happen at a higher level.

Calling 'i2c_get_read_available' makes no sense at a base i2c level on the master because nothing is available until the master has explicitly tried to read something and it can't say "give me what you have" because it controls the clock and it decides when it's read enough. It can't even detect whether the slave is actively sending data or not. If the slave doesn't drive the SDA line, the master will just read 0xff and it can't know whether that's valid data or not. That, again, would have to be defined at the protocol level.

I find very often that the most intuitive way to implement a protocol is to think of the slave as a memory chip. I can read and I can write but I want to read and write from/to specific locations. So the first thing I need to do is tell the slave the location (so I have the "write" the address), then either read or write the data. Writing the address implies setting a pointer inside the slave so it knows where the next operation should be performed. After that every read or write will cause the pointer to be incremented.

Whether the "address" actually represents an offset within memory or a command to switch to a different mode or something else doesn't really matter.

Of course, it doesn't have to be implemented like that and you could just have a completely read-only interface (e.g. your keyboard can only return scan codes, not accept new LED states) but personally I would be wary of implementing that kind of interface for anything but the simplest of devices as the master cannot choose what data it would like (maybe it usually only wants scan codes but initially also wants the ability to query the slave's capabilities).

I'll leave it here because I've already written more than I originally intended to but I hope it helps either you or someone else. i2c is not that hard ;)

dairequinlan
Posts: 45
Joined: Tue Feb 23, 2021 3:18 pm

Re: Bidirectional I2C communications for a split keyboard

Fri Oct 08, 2021 3:50 pm

Much appreciated. Yes, much of my initial confusion was around thinking that I2C was a little more high level than it actually is. Once I realised that I had to effectively build out a protocol to suit what I wanted to do it went fairly smoothly.

I found it easiest to think of as a kind of message bus, the Master can write to to the Slave without impediment, but can also poll the Slave for any messages it has queued up. The slave will ALWAYS respond with either a string of messages, terminated by a specific 'no-op' message, OR just the no-op message indicating that its queue is empty. The slave maintains a queue of received messages (which are taken off the queue by any interested process running on the slave) and a queue of pending messages which are delivered to the master on demand.

This has been working pretty flawlessly for the last 4 or 5 months with nary a hang or missed keystroke so I'm pretty happy with it.

rhulme
Posts: 4
Joined: Fri Oct 08, 2021 9:54 am

Re: Bidirectional I2C communications for a split keyboard

Fri Oct 08, 2021 4:37 pm

dairequinlan wrote:
Fri Oct 08, 2021 3:50 pm
This has been working pretty flawlessly for the last 4 or 5 months with nary a hang or missed keystroke so I'm pretty happy with it.
Excellent! That is the most important thing :)

Return to “SDK”