You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.5 KiB

I2C interface to the firmware

To control the operation of the keyboard's firmware, the firmware
exposes some "registers" that the user can read/write using the
two kinds of I2C transfers.

1) set values of consecutive block of registers starting from REG_ADDR:


2) read values of consecutive block of registers starting from REG_ADDR:


Device address is 0x15.


General ranges:
0x00 - 0x1f - Read-only status range
0x20 - 0x2f - Writable keyboard control range
0x70 - 0xf4 - Flashing interface
0xff - Debug log

0x00: Device ID1 (0x4b)
0x01: Device ID2 (0x42)
0x02: Firmware revision
0x03: Firmware features
bit 0: USB debugger
bit 1: Flashing mode
bit 2: Self-test features
bit 3: Stock firmware flag (only stock firmware should have this set)

0x06: bits 3-0: number of rows, bits 7-4 number of cols
0x07: CRC8 of keyboard data from 0x08-0x13
0x08: Keyboard data for column 1
0x13: Keyboard data for column 12 (up to number of cols, in this case 12)

0x20: System configuration
bit 0: disable KB scanning (1: scanning disabled, 0: scanning enabled)
bit 1: poll mode
1: don't rely on row change detection, poll the matrix periodically
(prevents MCU power down)
0: power down the MCU when no key is pressed, and rely on change
detection on row inupts to wake the MCU up
bit 2: enable USB debug interface
1: enabled
0: disabled

0x21: System command
Writing values into this register causes the firmware to perform
certain one-shot actions:

0x52 ('R') - reset the MCU
0x63 ('c') - run a column self-test
0x72 ('r') - run a row self-test
(results for both tests will be stored in test-result

The register is set to 0x00 or 0xff after the operation completes.
0xff means error.

0x22: Writing value 0x53 ('S') to this register stops the main app from
jumping to the user app.

0x70: 128B block of EEPROM data (either read from code memory or to be
... written)

0xf0: target address low byte
0xf1: target address high byte

0xf2: CRC8 calculated for the 128B block of data from 0x70-0xef
- this must be written by the user when preparing data for write
operation, MCU checks the CRC8 of the data and compares it against
the value in this register before starting the execution of
0x57 command
- this is updated by the MCU after reading the data from flash memory
so the user can check the checksum against the data read from

0xf3: Flashing mode unlock key
- writing 0x46 to this register unlocks the flashing mode.
- this register is cleared after command completion

0xf4: Flashing control
Writing various commands to this register makes the MCU execute them,
if the MCU is not executing the previous command. Available commands:

0x52 - read a block of data from code ROM
0x57 - write a block of data to code ROM
0x45 - erase the code rom memory block (128B)
0x43 - confirm the validity of the firmware and enable redirection
to it from the main app (this redirection is automatically
disabled before executing the 0x57 command)

This register will ignore further commands as long as the last operation
is still in progress. This register will contain the code of the
currently executed operation, and will be cleared after the operation

If the operation fails, this register will contain value 0xff. If it
succeeds it will contain value 0x00.

0xff: Debug log
- reading from this register returns next character of the debug log
or 0
- register address is not auto-advanced for the 0xff address, so you
can read any number of bytes from 0xff to get the debug log text
- debug log stores results of self-tests


User can modify register 0x20 to choose how the firmware should operate.
The settings are not persistent across resets.

To read the keyboard matrix status, the user can perform a 13B read transaction
from address 0x07. Data for each column start at 0x08, 0x07 contains CRC8 checksum
of the 12 bytes starting from 0x08.

You can verify the data by calculating CRC8 on the last 12 bytes and compare it
with the 1st byte.

Bit 0 corresponds to row 1, bit 5 to row 6, bits 6 and 7 are always 0.


The firmware is split into 3 parts:

0x0000 - 0x2000: Stock USB bootloader
0x2000 - 0x4000: Stock FOSS firmware (flashable from stock bootloader)
0x4000 - 0x8000: User app (optional, flashable over I2C from stock FOSS firmware)

It is necessary to execute stock firmware to flash the user firmware. Stock
firmware will normally re-direct execution to user firmware if user firmware
is flashed.

After MCU powerup or reset, stock firmware will listen for 1s on I2C interface,
before passing control to the user firmware. If 0x53 is not written to register
0x22 during that time, this will prevent execution of the user's firmware.

If the user firmware execution is not prevented in this way, stock firmware
will redirect interrupt vectors from 0x0000+offset to 0x4000+offset and jump
to 0x4000, which will start execution of user's firmware. User firmware must
not change last byte of IRAM, because it is used by the stock firmware's
interrupt redirection mechanism.

User's firmware should always implement some way to reset the MCU via I2C
interface. This will make development and flashing easier. Failing that,
I2C flashing tool also allows for manual entry to stock firmware which
is done by holding the power key for > 12s to force power cycle on the MCU.

Flashing steps:

1) Write 128 B of data to 0x70-0xef and data CRC8 to 0xf2
2) Write address to 0xf0 (low) and 0xf1 (high byte)
(only range from 0x4000 to 0x7fff) is writeable
3) Unlock by writing 0x46 to register 0xf3
4) Write command 0x57 to 0xf4
5) Poll 0xf4 for result (either 0x00 or 0xff)
... repeat 1-5 for all memory locations to be flashed
6) Write command 0x43 to reg 0xf4
7) Poll 0xf4 for result (either 0x00 or 0xff)
8) Reset the MCU by writing command 0x52 to reg 0x21


1) Write command 'r' or 'c' to reg 0x21
2) Read repeatedly from register 0xff to get the self-test results
in human readable text form