mirror of
https://github.com/grymoire/i2c_puppet-Linux.git
synced 2025-07-19 23:39:49 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
c44f9546e3 | |||
d5b4fe9053 | |||
c1cadbde4b | |||
7ab4cd3388 | |||
f82ff3fdf6 | |||
4f4567d10f | |||
28e64926f3 | |||
ff3c64865e |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
|||||||
[submodule "3rdparty/pico-sdk"]
|
[submodule "3rdparty/pico-sdk"]
|
||||||
path = 3rdparty/pico-sdk
|
path = 3rdparty/pico-sdk
|
||||||
url = https://github.com/raspberrypi/pico-sdk.git
|
url = https://github.com/raspberrypi/pico-sdk.git
|
||||||
[submodule "pico-sdk"]
|
|
||||||
path = pico-sdk
|
|
||||||
url = https://github.com/raspberrypi/pico-sdk.git
|
|
||||||
|
295
README.md
295
README.md
@ -1,7 +1,19 @@
|
|||||||
# I2C Puppet
|
# I2C Puppet
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Checkout
|
## Checkout
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
git clone https://github.com/solderparty/i2c_puppet
|
git clone https://github.com/solderparty/i2c_puppet
|
||||||
cd i2c_puppet
|
cd i2c_puppet
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
@ -10,13 +22,292 @@
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
See the `boards` directory for a list of available boards.
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DPICO_BOARD=bbq20kbd_breakout -DCMAKE_BUILD_TYPE=Debug ..
|
cmake -DPICO_BOARD=bbq20kbd_breakout -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
make
|
make
|
||||||
|
|
||||||
## Add rules
|
## Vendor USB Class
|
||||||
|
|
||||||
sudo cp tools/99-i2c_puppet.rules /lib/udev/rules.d/
|
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:
|
||||||
|
|
||||||
|
sudo cp etc/99-i2c_puppet.rules /lib/udev/rules.d/
|
||||||
sudo udevadm control --reload
|
sudo udevadm control --reload
|
||||||
sudo udevadm trigger
|
sudo udevadm trigger
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
- [Arduino](https://github.com/solderparty/arduino_bbq10kbd)
|
||||||
|
- [CircuitPython](https://github.com/solderparty/arturo182_CircuitPython_BBQ10Keyboard)
|
||||||
|
- [Rust (Embedded-HAL)](https://crates.io/crates/bbq10kbd)
|
||||||
|
|
||||||
|
## Protocol
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See `REG_CF2` for additional settings.
|
||||||
|
|
||||||
|
| Bit | Name | Description |
|
||||||
|
| ------ |:----------------:| ------------------------------------------------------------------:|
|
||||||
|
| 7 | CFG_USE_MODS | Should Alt, Sym and the Shift keys modify the keys being reported. |
|
||||||
|
| 6 | CFG_REPORT_MODS | Should Alt, Sym and the Shift keys be reported as well. |
|
||||||
|
| 5 | CFG_PANIC_INT | Currently not implemented. |
|
||||||
|
| 4 | CFG_KEY_INT | Should an interrupt be generated when a key is pressed. |
|
||||||
|
| 3 | CFG_NUMLOCK_INT | Should an interrupt be generated when Num Lock is toggled. |
|
||||||
|
| 2 | CFG_CAPSLOCK_INT | Should an interrupt be generated when Caps Lock is toggled. |
|
||||||
|
| 1 | CFG_OVERFLOW_INT | Should an interrupt be generated when a FIFO overflow happens. |
|
||||||
|
| 0 | CFG_OVERFLOW_ON | When a FIFO overflow happens, should the new entry still be pushed, overwriting the oldest one. If 0 then new entry is lost. |
|
||||||
|
|
||||||
|
Defaut value:
|
||||||
|
`CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS`
|
||||||
|
|
||||||
|
### Interrupt status register (REG_INT = 0x03)
|
||||||
|
|
||||||
|
When an interrupt happens, the register can be read to check what caused the interrupt. It's 1 byte in size.
|
||||||
|
|
||||||
|
| Bit | Name | Description |
|
||||||
|
| ------ |:----------------:| -----------------------------------------------------------:|
|
||||||
|
| 7 | N/A | Currently not implemented. |
|
||||||
|
| 6 | INT_TOUCH | The interrupt was generated by a trackpad motion. |
|
||||||
|
| 5 | INT_GPIO | The interrupt was generated by a input GPIO changing level. |
|
||||||
|
| 4 | INT_PANIC | Currently not implemented. |
|
||||||
|
| 3 | INT_KEY | The interrupt was generated by a key press. |
|
||||||
|
| 2 | INT_NUMLOCK | The interrupt was generated by Num Lock. |
|
||||||
|
| 1 | INT_CAPSLOCK | The interrupt was generated by Caps Lock. |
|
||||||
|
| 0 | INT_OVERFLOW | The interrupt was generated by FIFO overflow. |
|
||||||
|
|
||||||
|
After reading the register, it has to manually be reset to `0x00`.
|
||||||
|
|
||||||
|
For `INT_GPIO` check the bits in `REG_GIN` to see which GPIO triggered the interrupt. The GPIO interrupt must first be enabled in `REG_GIC`.
|
||||||
|
|
||||||
|
### Key status register (REG_KEY = 0x04)
|
||||||
|
|
||||||
|
This register contains information about the state of the fifo as well as modified keys. It is 1 byte in size.
|
||||||
|
|
||||||
|
| Bit | Name | Description |
|
||||||
|
| ------ |:----------------:| -----------------------------------------------:|
|
||||||
|
| 7 | N/A | Currently not implemented. |
|
||||||
|
| 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.
|
||||||
|
|
||||||
|
Default value: `0xFF`.
|
||||||
|
|
||||||
|
### Debounce configuration register (REG_DEB = 0x06)
|
||||||
|
|
||||||
|
Currently not implemented.
|
||||||
|
|
||||||
|
Default value: 10
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
### GPIO input pull enable register (REG_PUE = 0x0C)
|
||||||
|
|
||||||
|
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`)
|
||||||
|
|
||||||
|
### GPIO value register (REG_GIO = 0x0E)
|
||||||
|
|
||||||
|
This register contains the values 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.
|
||||||
|
|
||||||
|
If a pin is configured as an output (via `REG_DIR`), writing to this register will change the value of that pin.
|
||||||
|
|
||||||
|
Reading from this register will return the values for both input and output pins.
|
||||||
|
|
||||||
|
Default value: Depends on pin values
|
||||||
|
|
||||||
|
### GPIO interrupt config register (REG_GIC = 0x0F)
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
Default value: `0x00`
|
||||||
|
|
||||||
|
### Key hold threshold configuration (REG_HLD = 0x11)
|
||||||
|
|
||||||
|
This register can be read and written to, it is 1 byte in size.
|
||||||
|
|
||||||
|
The value of this register (expressed in units of 10ms) is used to determine if a "press and hold" state should be entered.
|
||||||
|
|
||||||
|
If a key is held down longer than the value, it enters the "press and hold" state.
|
||||||
|
|
||||||
|
Default value: 30 (300ms)
|
||||||
|
|
||||||
|
### Device I2C address (REG_ADR = 0x12)
|
||||||
|
|
||||||
|
The address that the device shows up on the I2C bus under. This register can be read and written to, it is 1 byte in size.
|
||||||
|
|
||||||
|
The change is applied as soon as a new value is written to the register. The next communication must be performed on the new address.
|
||||||
|
|
||||||
|
The address is not saved after a reset.
|
||||||
|
|
||||||
|
Default value: `0x1F`
|
||||||
|
|
||||||
|
### Interrupt duration (REG_IND = 0x13)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The value of this register is expressed in ms.
|
||||||
|
|
||||||
|
Default value: 1 (1ms)
|
||||||
|
|
||||||
|
### The configuration register 2 (REG_CF2 = 0x14)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See `REG_CFG` for additional settings.
|
||||||
|
|
||||||
|
| Bit | Name | Description |
|
||||||
|
| ------ |:----------------:| ------------------------------------------------------------------:|
|
||||||
|
| 7 | N/A | Currently not implemented. |
|
||||||
|
| 6 | N/A | Currently not implemented. |
|
||||||
|
| 5 | N/A | Currently not implemented. |
|
||||||
|
| 4 | N/A | Currently not implemented. |
|
||||||
|
| 3 | N/A | Currently not implemented. |
|
||||||
|
| 2 | CF2_USB_MOUSE_ON | Should trackpad events be sent over USB HID. |
|
||||||
|
| 1 | CF2_USB_KEYB_ON | Should key events be sent over USB HID. |
|
||||||
|
| 0 | CF2_TOUCH_INT | Should trackpad events generate interrupts. |
|
||||||
|
|
||||||
|
Default value: `CF2_TOUCH_INT | CF2_USB_KEYB_ON | CF2_USB_MOUSE_ON`
|
||||||
|
|
||||||
|
### Trackpad X Position(REG_TOX = 0x15)
|
||||||
|
|
||||||
|
This is a read-only register, it is 1 byte in size.
|
||||||
|
|
||||||
|
Trackpad X-axis position delta since the last time this register was read.
|
||||||
|
|
||||||
|
The value reported is signed and can be in the range of (-128 to 127).
|
||||||
|
|
||||||
|
When the value of this register is read, it is afterwards reset back to 0.
|
||||||
|
|
||||||
|
It is recommended to read the value of this register often, or data loss might occur.
|
||||||
|
|
||||||
|
Default value: 0
|
||||||
|
|
||||||
|
### Trackpad Y position (REG_TOY = 0x16)
|
||||||
|
|
||||||
|
This is a read-only register, it is 1 byte in size.
|
||||||
|
|
||||||
|
Trackpad Y-axis position delta since the last time this register was read.
|
||||||
|
|
||||||
|
The value reported is signed and can be in the range of (-128 to 127).
|
||||||
|
|
||||||
|
When the value of this register is read, it is afterwards reset back to 0.
|
||||||
|
|
||||||
|
It is recommended to read the value of this register often, or data loss might occur.
|
||||||
|
|
||||||
|
Default value: 0
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
v1.0:
|
||||||
|
- Initial release
|
||||||
|
|
||||||
|
See here for the legacy project's history: https://github.com/solderparty/bbq10kbd_i2c_sw#version-history
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION_MAJOR 1
|
#define VERSION_MAJOR 1
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 1
|
||||||
|
|
||||||
#define KEY_FIFO_SIZE 31 // number of keys in the public FIFO
|
#define KEY_FIFO_SIZE 31 // number of keys in the public FIFO
|
||||||
|
@ -114,8 +114,9 @@ static void transition_to(struct list_item * const p_item, const enum key_state
|
|||||||
if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) {
|
if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) {
|
||||||
const bool shift = (self.mods[KEY_MOD_ID_SHL] || self.mods[KEY_MOD_ID_SHR]) | self.capslock;
|
const bool shift = (self.mods[KEY_MOD_ID_SHL] || self.mods[KEY_MOD_ID_SHR]) | self.capslock;
|
||||||
const bool alt = self.mods[KEY_MOD_ID_ALT] | self.numlock;
|
const bool alt = self.mods[KEY_MOD_ID_ALT] | self.numlock;
|
||||||
|
const bool is_button = (key <= KEY_BTN_RIGHT1) || ((key >= KEY_BTN_LEFT2) && (key <= KEY_BTN_RIGHT2));
|
||||||
|
|
||||||
if (alt) {
|
if (alt && !is_button) {
|
||||||
key = p_entry->alt;
|
key = p_entry->alt;
|
||||||
} else if (!shift && (key >= 'A' && key <= 'Z')) {
|
} else if (!shift && (key >= 'A' && key <= 'Z')) {
|
||||||
key = (key + ' ');
|
key = (key + ' ');
|
||||||
|
@ -47,6 +47,13 @@ static int64_t timer_task(alarm_id_t id, void *user_data)
|
|||||||
|
|
||||||
static void key_cb(char key, enum key_state state)
|
static void key_cb(char key, enum key_state state)
|
||||||
{
|
{
|
||||||
|
// Don't send mods over USB
|
||||||
|
if ((key == KEY_MOD_SHL) ||
|
||||||
|
(key == KEY_MOD_SHR) ||
|
||||||
|
(key == KEY_MOD_ALT) ||
|
||||||
|
(key == KEY_MOD_SYM))
|
||||||
|
return;
|
||||||
|
|
||||||
if (tud_hid_n_ready(USB_ITF_KEYBOARD) && reg_is_bit_set(REG_ID_CF2, CF2_USB_KEYB_ON)) {
|
if (tud_hid_n_ready(USB_ITF_KEYBOARD) && reg_is_bit_set(REG_ID_CF2, CF2_USB_KEYB_ON)) {
|
||||||
uint8_t conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
|
uint8_t conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
|
||||||
conv_table['\n'][1] = HID_KEY_ENTER; // Fixup: Enter instead of Return
|
conv_table['\n'][1] = HID_KEY_ENTER; // Fixup: Enter instead of Return
|
||||||
|
130
etc/i2c_puppet.py
Normal file
130
etc/i2c_puppet.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import usb
|
||||||
|
|
||||||
|
|
||||||
|
_REG_VER = 0x01 # fw version
|
||||||
|
_REG_CFG = 0x02 # config
|
||||||
|
_REG_INT = 0x03 # interrupt status
|
||||||
|
_REG_KEY = 0x04 # key status
|
||||||
|
_REG_BKL = 0x05 # backlight
|
||||||
|
_REG_DEB = 0x06 # debounce cfg
|
||||||
|
_REG_FRQ = 0x07 # poll freq cfg
|
||||||
|
_REG_RST = 0x08 # reset
|
||||||
|
_REG_FIF = 0x09 # fifo
|
||||||
|
_REG_BK2 = 0x0A # backlight 2
|
||||||
|
_REG_DIR = 0x0B # gpio direction
|
||||||
|
_REG_PUE = 0x0C # gpio input pull enable
|
||||||
|
_REG_PUD = 0x0D # gpio input pull direction
|
||||||
|
_REG_GIO = 0x0E # gpio value
|
||||||
|
_REG_GIC = 0x0F # gpio interrupt config
|
||||||
|
_REG_GIN = 0x10 # gpio interrupt status
|
||||||
|
_REG_HLD = 0x11 # key hold time cfg (in 10ms units)
|
||||||
|
_REG_ADR = 0x12 # i2c puppet address
|
||||||
|
_REG_IND = 0x13 # interrupt pin assert duration
|
||||||
|
_REG_CF2 = 0x14 # config 2
|
||||||
|
_REG_TOX = 0x15 # touch delta x since last read, at most (-128 to 127)
|
||||||
|
_REG_TOY = 0x16 # touch delta y since last read, at most (-128 to 127)
|
||||||
|
|
||||||
|
_WRITE_MASK = 1 << 7
|
||||||
|
|
||||||
|
CFG_OVERFLOW_ON = 1 << 0
|
||||||
|
CFG_OVERFLOW_INT = 1 << 1
|
||||||
|
CFG_CAPSLOCK_INT = 1 << 2
|
||||||
|
CFG_NUMLOCK_INT = 1 << 3
|
||||||
|
CFG_KEY_INT = 1 << 4
|
||||||
|
CFG_PANIC_INT = 1 << 5
|
||||||
|
CFG_REPORT_MODS = 1 << 6
|
||||||
|
CFG_USE_MODS = 1 << 7
|
||||||
|
|
||||||
|
CF2_TOUCH_INT = 1 << 0
|
||||||
|
CF2_USB_KEYB_ON = 1 << 1
|
||||||
|
CF2_USB_MOUSE_ON = 1 << 2
|
||||||
|
|
||||||
|
INT_OVERFLOW = 1 << 0
|
||||||
|
INT_CAPSLOCK = 1 << 1
|
||||||
|
INT_NUMLOCK = 1 << 2
|
||||||
|
INT_KEY = 1 << 3
|
||||||
|
INT_PANIC = 1 << 4
|
||||||
|
INT_GPIO = 1 << 5
|
||||||
|
INT_TOUCH = 1 << 6
|
||||||
|
|
||||||
|
KEY_CAPSLOCK = 1 << 5
|
||||||
|
KEY_NUMLOCK = 1 << 6
|
||||||
|
KEY_COUNT_MASK = 0x1F
|
||||||
|
|
||||||
|
DIR_OUTPUT = 0
|
||||||
|
DIR_INPUT = 1
|
||||||
|
|
||||||
|
PUD_DOWN = 0
|
||||||
|
PUD_UP = 1
|
||||||
|
|
||||||
|
|
||||||
|
class I2CPuppet:
|
||||||
|
def __init__(self, vid=0x1209, pid=0xB182):
|
||||||
|
self._buffer = bytearray(2)
|
||||||
|
self._dev = usb.core.find(idVendor=vid, idProduct=pid)
|
||||||
|
|
||||||
|
if self._dev is None:
|
||||||
|
raise Exception('Device with vid:pid %04X:%04X not found!' % (vid, pid))
|
||||||
|
|
||||||
|
conf = self._dev.get_active_configuration()
|
||||||
|
itf = usb.util.find_descriptor(conf, bInterfaceClass=usb.CLASS_VENDOR_SPEC)
|
||||||
|
|
||||||
|
self._ep_out = usb.util.find_descriptor(itf, custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
|
||||||
|
self._ep_in = usb.util.find_descriptor(itf, custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)
|
||||||
|
|
||||||
|
if (self._ep_out is None) or (self._ep_in is None):
|
||||||
|
raise Exception('Vendor IN or OUT endpoint not found!')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self):
|
||||||
|
ver = self._read_register(_REG_VER)
|
||||||
|
return (ver >> 4, ver & 0x0F)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status(self):
|
||||||
|
return self._read_register(_REG_KEY)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def backlight(self):
|
||||||
|
return self._read_register(_REG_BKL) / 255
|
||||||
|
|
||||||
|
@backlight.setter
|
||||||
|
def backlight(self, value):
|
||||||
|
self._write_register(_REG_BKL, int(255 * value))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def address(self):
|
||||||
|
return self._read_register(_REG_ADR)
|
||||||
|
|
||||||
|
@address.setter
|
||||||
|
def address(self, value):
|
||||||
|
self._write_register(_REG_ADR, value)
|
||||||
|
|
||||||
|
def _read_register(self, reg):
|
||||||
|
self._buffer[0] = reg
|
||||||
|
self._dev.write(self._ep_out, self._buffer[:1])
|
||||||
|
|
||||||
|
return self._dev.read(self._ep_in, 1)[0]
|
||||||
|
|
||||||
|
def _write_register(self, reg, value):
|
||||||
|
self._buffer[0] = reg | _WRITE_MASK
|
||||||
|
self._buffer[1] = value
|
||||||
|
self._dev.write(self._ep_out, self._buffer)
|
||||||
|
|
||||||
|
def _update_register_bit(self, reg, bit, value):
|
||||||
|
|
||||||
|
reg_val = self._read_register(reg)
|
||||||
|
old_val = reg_val
|
||||||
|
|
||||||
|
if value:
|
||||||
|
reg_val |= (1 << bit)
|
||||||
|
else:
|
||||||
|
reg_val &= ~(1 << bit)
|
||||||
|
|
||||||
|
if reg_val != old_val:
|
||||||
|
self._write_register(reg, reg_val)
|
||||||
|
|
||||||
|
def _get_register_bit(self, reg, bit):
|
||||||
|
return self._read_register(reg) & (1 << bit) != 0
|
||||||
|
|
||||||
|
keyboard_backlight = backlight
|
Reference in New Issue
Block a user