aboutsummaryrefslogtreecommitdiff
path: root/docs/feature_split_keyboard.md
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2021-06-18 09:10:06 +1000
committerGitHub <noreply@github.com>2021-06-18 09:10:06 +1000
commit172e6a703041363decd6fc829542f33180c13beb (patch)
treea5d4afaa672ab44826865fd76b201e3899083192 /docs/feature_split_keyboard.md
parentef92c9ee2cf4745637635ec1895399e4f013914c (diff)
downloadqmk_firmware-172e6a703041363decd6fc829542f33180c13beb.tar.gz
qmk_firmware-172e6a703041363decd6fc829542f33180c13beb.zip
Extensible split data sync (#11930)
* Extensible split data sync capability through transactions. - Split common transport has been split up between the transport layer and data layer. - Split "transactions" model used, with convergence between I2C and serial data definitions. - Slave matrix "generation count" is used to determine if the full slave matrix needs to be retrieved. - Encoders get the same "generation count" treatment. - All other blocks of data are synchronised when a change is detected. - All transmissions have a globally-configurable deadline before a transmission is forced (`FORCED_SYNC_THROTTLE_MS`, default 100ms). - Added atomicity for all core-synced data, preventing partial updates - Added retries to AVR i2c_master's i2c_start, to minimise the number of failed transactions when interrupts are disabled on the slave due to atomicity checks. - Some keyboards have had slight modifications made in order to ensure that they still build due to firmware size restrictions. * Fixup LED_MATRIX compile. * Parameterise ERROR_DISCONNECT_COUNT.
Diffstat (limited to 'docs/feature_split_keyboard.md')
-rw-r--r--docs/feature_split_keyboard.md114
1 files changed, 104 insertions, 10 deletions
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 4ebf585f5..603c387c2 100644
--- a/docs/feature_split_keyboard.md
+++ b/docs/feature_split_keyboard.md
@@ -8,8 +8,7 @@ QMK Firmware has a generic implementation that is usable by any board, as well a
8 8
9For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. 9For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards.
10 10
11!> ARM is not yet fully supported for Split Keyboards and has many limitations. Progress is being made, but we have not yet reached 100% feature parity. 11!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported.
12
13 12
14## Compatibility Overview 13## Compatibility Overview
15 14
@@ -169,7 +168,7 @@ Because not every split keyboard is identical, there are a number of additional
169#define USE_I2C 168#define USE_I2C
170``` 169```
171 170
172This enables I<sup>2</sup>C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I<sup>2</sup>C-based devices. 171This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
173 172
174```c 173```c
175#define SOFT_SERIAL_PIN D0 174#define SOFT_SERIAL_PIN D0
@@ -193,20 +192,115 @@ If you're having issues with serial communication, you can change this value, as
193* **`5`**: about 20kbps 192* **`5`**: about 20kbps
194 193
195```c 194```c
196#define SPLIT_MODS_ENABLE 195#define FORCED_SYNC_THROTTLE_MS 100
197``` 196```
198 197
199This enables transmitting modifier state (normal, weak and oneshot) to the non 198This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync.
200primary side of the split keyboard. This adds a few bytes of data to the split
201communication protocol and may impact the matrix scan speed when enabled.
202The purpose of this feature is to support cosmetic use of modifer state (e.g.
203displaying status on an OLED screen).
204 199
205```c 200```c
206#define SPLIT_TRANSPORT_MIRROR 201#define SPLIT_TRANSPORT_MIRROR
207``` 202```
208 203
209This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. This adds a few bytes of data to the split communication protocol and may impact the matrix scan speed when enabled. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to Keypresses). 204This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
205
206```c
207#define SPLIT_LAYER_STATE_ENABLE
208```
209
210This enables syncing of the layer state between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the currently active layer. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
211
212```c
213#define SPLIT_LED_STATE_ENABLE
214```
215
216This enables syncing of the Host LED status (caps lock, num lock, etc) between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the Host LED status. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
217
218```c
219#define SPLIT_MODS_ENABLE
220```
221
222This enables transmitting modifier state (normal, weak and oneshot) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifer state (e.g. displaying status on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
223
224```c
225#define SPLIT_WPM_ENABLE
226```
227
228This enables transmitting the current WPM to the slave side of the split keyboard. The purpose of this feature is to support cosmetic use of WPM (e.g. displaying the current value on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
229
230### Custom data sync between sides :id=custom-data-sync
231
232QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
233
234To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_:
235
236```c
237// for keyboard-level data sync:
238#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B
239// or, for user:
240#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C
241```
242
243These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example:
244
245```c
246typedef struct _master_to_slave_t {
247 int m2s_data;
248} master_to_slave_t;
249
250typedef struct _slave_to_master_t {
251 int s2m_data;
252} slave_to_master_t;
253
254void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) {
255 const master_to_slave_t *m2s = (const master_to_slave_t*)in_data;
256 slave_to_master_t *s2m = (slave_to_master_t*)out_data;
257 s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
258}
259
260void keyboard_post_init_user(void) {
261 transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler);
262}
263```
264
265The master side can then invoke the slave-side handler - for normal keyboard functionality to be minimally affected, any keyboard- or user-level code attempting to sync data should be throttled:
266
267```c
268void housekeeping_task_user(void) {
269 if (is_keyboard_master()) {
270 // Interact with slave every 500ms
271 static uint32_t last_sync = 0;
272 if (timer_elapsed32(last_sync) > 500) {
273 master_to_slave_t m2s = {6};
274 slave_to_master_t s2m = {0};
275 if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
276 last_sync = timer_read32();
277 dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
278 } else {
279 dprint("Slave sync failed!\n");
280 }
281 }
282 }
283}
284```
285
286!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur.
287
288If only one-way data transfer is needed, helper methods are provided:
289
290```c
291bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
292bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer);
293bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
294```
295
296By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required:
297
298```c
299// Master to slave:
300#define RPC_M2S_BUFFER_SIZE 48
301// Slave to master:
302#define RPC_S2M_BUFFER_SIZE 48
303```
210 304
211### Hardware Configuration Options 305### Hardware Configuration Options
212 306