This is a port of the old [BB Q10 Keyboard-to-I2C Software](https://github.com/solderparty/bbq10kbd_i2c_sw) to the RP2040, expanded with new features, while still staying backwards compatible.
The target product/keyboard for this software is the BB Q20 keyboard, which adds a trackpad to the mix.
On the features side, this software adds USB support, the keyboard acts as a USB keyboard, and the trackpad acts as a USB mouse.
On the I2C side, you can access the key presses, the trackpad state, you can control some of the board GPIOs, as well as the backlight.
See [Protocol](#protocol) for details of the I2C puppet.
The code depends on the Raspberry Pi Pico SDK, which is added as a submodule. Because the Pico SDK includes TinyUSB as a module, it is not recommended to do a recursive submodule init, and rather follow these steps:
You can configure the software over USB in a similar way you would do it over I2C. You can access the same registers (like the backlight register) using the USB Vendor Class.
If you don't want to prefix all config interactions with `sudo`, you can copy the included udev rule:
To interact with the internal registers of the keyboard over USB, use the `i2c_puppet.py` script included in the `etc` folder.
just import it, create a `I2C_Puppet` object, and you can interact with the keyboard in the same you would do using the I2C interface and the CircuitPython class linked below.
## Implementations
Here are libraries that allow I2C interaction with the boards running this software. Not all libraries might support all the features.
The device uses I2C slave interface to communicate, the address can be configured in `app/config/conf_app.h`, the default is `0x1F`.
You can read the values of all the registers, the number of returned bytes depends on the register.
It's also possible to write to the registers, to do that, apply the write mask `0x80` to the register ID (for example, the backlight register `0x05` becomes `0x85`).
### The FW Version register (REG_VER = 0x01)
Data written to this register is discarded. Reading this register returns 1 byte, the first nibble contains the major version and the second nibble contains the minor version of the firmware.
### The configuration register (REG_CFG = 0x02)
This register can be read and written to, it's 1 byte in size.
This register is a bit map of various settings that can be changed to customize the behaviour of the firmware.
| 6 | KEY_NUMLOCK | Is Num Lock on at the moment. |
| 5 | KEY_CAPSLOCK | Is Caps Lock on at the moment. |
| 0-4 | KEY_COUNT | Number of items in the FIFO waiting to be read. |
### Backlight control register (REG_BKL = 0x05)
Internally a PWM signal is generated to control the keyboard backlight, this register allows changing the brightness of the backlight. It is 1 byte in size, `0x00` being off and `0xFF` being the brightest.
### Poll frequency configuration register (REG_FRQ = 0x07)
Currently not implemented.
Default value: 5
### Chip reset register (REG_RST = 0x08)
Reading or writing to this register will cause a SW reset of the chip.
### FIFO access register (REG_FIF = 0x09)
This register can be used to read the top of the key FIFO. It returns two bytes, a key state and a key code.
Possible key states:
| Value | State |
| ------ |:-----------------------:|
| 1 | Pressed |
| 2 | Pressed and Held |
| 3 | Released |
### Secondary backlight control register (REG_BK2 = 0x0A)
Internally a PWM signal is generated to control a secondary backlight (for example, a screen), this register allows changing the brightness of the backlight. It is 1 byte in size, `0x00` being off and `0xFF` being the brightest.
Default value: `0xFF`.
### GPIO direction register (REG_DIR = 0x0B)
This register controls the direction of the GPIO expander pins, each bit corresponding to one pin. It is 1 byte in size.
The actual pin[7..0] to MCU pin assignment depends on the board, see `<board>.h` of the board for the assignments.
Any bit set to `1` means the GPIO is configured as input, any bit set to `0` means the GPIO is configured as output.
Default value: `0xFF` (all GPIOs configured as input)
If a GPIO is configured as an input (using `REG_DIR`), a optional pull-up/pull-down can be enabled.
This register controls the pull enable status, each bit corresponding to one pin. It is 1 byte in size.
The actual pin[7..0] to MCU pin assignment depends on the board, see `<board>.h` of the board for the assignments.
Any bit set to `1` means the input pull for that pin is enabled, any bit set to `0` means the input pull for that pin is disabled.
The direction of the pull is done in `REG_PUD`.
When a pin is configured as output, its bit in this register has no effect.
Default value: 0 (all pulls disabled)
### GPIO input pull direction register (REG_PUD = 0x0D)
If a GPIO is configured as an input (using `REG_DIR`), a optional pull-up/pull-down can be configured.
The pull functionality is enabled using `REG_PUE` and the direction of the pull is configured using this register, each bit corresponding to one pin. This register is 1 byte in size.
The actual pin[7..0] to MCU pin assignment depends on the board, see `<board>.h` of the board for the assignments.
Any bit set to `1` means the input pull is set to pull-up, any bit set to `0` means the input pul is set to pull-down.
When a pin is configured as output, its bit in this register has no effect.
Default value: `0xFF` (all pulls set to pull-up, if enabled in `REG_PUE` and set to input in `REG_DIR`)
If a GPIO is configured as an input (using `REG_DIR`), an interrupt can be configured to trigger when the pin's value changes.
This register controls the interrupt, each bit corresponding to one pin.
The actual pin[7..0] to MCU pin assignment depends on the board, see `<board>.h` of the board for the assignments.
Any bit set to `1` means the input pin will trigger and interrupt when changing value, any bit set to `0` means no interrupt for that pin is triggered.
When an interrupt happens, the GPIO that triggered the interrupt can be determined by reading `REG_GIN`. Additionally, the `INT_GPIO` bit will be set in `REG_INT`.
Default value: `0x00`
### GPIO interrupt status register (REG_GIN = 0x10)
When an interrupt happens, the register can be read to check which GPIO caused the interrupt, each bit corresponding to one pin. This register is 1 byte in size.
The actual pin[7..0] to MCU pin assignment depends on the board, see `<board>.h` of the board for the assignments.
After reading the register, it has to manually be reset to `0x00`.
The value of this register determines how long the INT/IRQ pin is held LOW after an interrupt event happens.This register can be read and written to, it is 1 byte in size.