Dec 20, 2023

I2C split of P2PPCB mainboard Charlotte

QMK split keyboard interface is usually UART except AVR. But I chose I2C to mainboard Charlotte.

Qwiic (3.3V 4-pin I2C connector) is necessary for many accessories like OLED display or rotary encoder. If I adopt UART, the mainboard should have another connector. The connector occupies a certain area and makes the mainboard bigger. But mainboads should be small, really small, and of course, really really cheap (this is a big lesson of mainboard Alice).

This is a story of I2C split implementation on RP2040.

Structure

I2C is a protocol between single master and multiple slave. Master-to-master is not available. So a hand (usually connected to PC via USB) should be a master and the other hand should be a slave.

P2PPCB mainboard Charlotte has RP2040. QMK on RP2040 uses ChibiOS. ChibiOS doesn't support I2C slave. So the implementation of I2C slave is inevitably a dirty hack.

ChibiOS build system has "Community HAL" mechanism. "keyboard/p2ppcb/charlotte/hal_community.h"  (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/keyboards/p2ppcb/charlotte/hal_community.h) can be included from the tail of "hal.h"  (https://github.com/qmk/ChibiOS/blob/0062927e3058a8b5ef587234bbd98d42fb4e595e/os/hal/include/hal.h#L344). The "hal_community.h" includes "charlotte_i2c.h" (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/keyboards/p2ppcb/charlotte/charlotte_i2c.h). "charlotte_i2c.h" includes "hal_i2c.h" (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/keyboards/p2ppcb/charlotte/charlotte_i2c.h#L61), but it is already once included in "hal.h"! (https://github.com/qmk/ChibiOS/blob/0062927e3058a8b5ef587234bbd98d42fb4e595e/os/hal/include/hal.h#L313). Yeah, this is a dirty hack.

"charlotte_i2c.c" and "charlotte_i2c_lld.c" structure are mimics ChibiOS's. "keyboards/p2ppcb/charlotte/i2c_master.c" (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/keyboards/p2ppcb/charlotte/i2c_master.c) is not far from "platforms/chibios/drivers/i2c_master.c" (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/platforms/chibios/drivers/i2c_master.c). "keyboards/p2ppcb/charlotte/i2c_slave.h" is almost a copy of "platforms/avr/drivers/i2c_slave.h" (https://github.com/hajimen/qmk_firmware/blob/p2ppcb/platforms/avr/drivers/i2c_slave.h).

In short, the structure is a patchwork from existing codebase.

Hardware

RP2040 has dedicated hardware for I2C. It is IP of Synopsys, DesignWare DW_apb_i2c. This is a big state machine and (needless to say) the states can be changed by inputs from I2C lines. Writing bitbang I2C code is a piece of cake because the states never fluctuate unless CPU changes them. But DW_apb_i2c is... a hell. Of course it raises interrupts too. ISR is another hell. "__not_in_flash_func" macro is fatally important to make ISR efficient enough.

Synopsys DesignWare DW_apb_i2c Databook describes everything in 292 pages PDF. If you need to debug "charlotte_i2c_lld.c", you should fully know the 292 pages. It might look roundabout, but "Haste makes waste" in this case.

Other MCUs

DW_apb_i2c may be used by other MCUs someday. Actually this post is mostly for the day and for the person who is going to implement I2C split.

The code in keyboard/p2ppcb/charlotte itself is dedicated for RP2040. But the dependency is not deep. Just find equivalents of "I2C_IC_*", "PAL_MODE_*", and "GP*" and replace. ChibiOS dependency is deep. In this case, it might be better to consider bitbang I2C. I haven't seen non-blocking I2C code of DW_apb_i2c except mine.

No comments:

Post a Comment