aboutsummaryrefslogtreecommitdiff
path: root/docs/feature_split_keyboard.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/feature_split_keyboard.md')
-rw-r--r--docs/feature_split_keyboard.md150
1 files changed, 139 insertions, 11 deletions
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 4ebf585f5..27df46a82 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
@@ -90,7 +89,13 @@ You can configure the firmware to read a pin on the controller to determine hand
90#define SPLIT_HAND_PIN B7 89#define SPLIT_HAND_PIN B7
91``` 90```
92 91
93This will read the specified pin. If it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side. 92This will read the specified pin. By default, if it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side.
93
94This behaviour can be flipped by adding this to you `config.h` file:
95
96```c
97#define SPLIT_HAND_PIN_LOW_IS_LEFT
98```
94 99
95#### Handedness by Matrix Pin 100#### Handedness by Matrix Pin
96 101
@@ -169,7 +174,7 @@ Because not every split keyboard is identical, there are a number of additional
169#define USE_I2C 174#define USE_I2C
170``` 175```
171 176
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. 177This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
173 178
174```c 179```c
175#define SOFT_SERIAL_PIN D0 180#define SOFT_SERIAL_PIN D0
@@ -193,20 +198,143 @@ If you're having issues with serial communication, you can change this value, as
193* **`5`**: about 20kbps 198* **`5`**: about 20kbps
194 199
195```c 200```c
196#define SPLIT_MODS_ENABLE 201#define FORCED_SYNC_THROTTLE_MS 100
202```
203
204This 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.
205
206```c
207#define SPLIT_MAX_CONNECTION_ERRORS 10
208```
209This sets the maximum number of failed communication attempts (one per scan cycle) from the master part before it assumes that no slave part is connected. This makes it possible to use a master part without the slave part connected.
210
211Set to 0 to disable the disconnection check altogether.
212
213```c
214#define SPLIT_CONNECTION_CHECK_TIMEOUT 500
197``` 215```
216How long (in milliseconds) the master part should block all connection attempts to the slave after the communication has been flagged as disconnected (see `SPLIT_MAX_CONNECTION_ERRORS` above).
198 217
199This enables transmitting modifier state (normal, weak and oneshot) to the non 218One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again.
200primary side of the split keyboard. This adds a few bytes of data to the split 219
201communication protocol and may impact the matrix scan speed when enabled. 220Set to 0 to disable this throttling of communications while disconnected. This can save you a couple of bytes of firmware size.
202The purpose of this feature is to support cosmetic use of modifer state (e.g.
203displaying status on an OLED screen).
204 221
205```c 222```c
206#define SPLIT_TRANSPORT_MIRROR 223#define SPLIT_TRANSPORT_MIRROR
207``` 224```
208 225
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). 226This 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.
227
228```c
229#define SPLIT_LAYER_STATE_ENABLE
230```
231
232This 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.
233
234```c
235#define SPLIT_LED_STATE_ENABLE
236```
237
238This 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.
239
240```c
241#define SPLIT_MODS_ENABLE
242```
243
244This 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.
245
246```c
247#define SPLIT_WPM_ENABLE
248```
249
250This 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.
251
252```c
253#define SPLIT_OLED_ENABLE
254```
255
256This enables transmitting the current OLED on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
257
258```c
259#define SPLIT_ST7565_ENABLE
260```
261
262This enables transmitting the current ST7565 on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
263
264### Custom data sync between sides :id=custom-data-sync
265
266QMK'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.
267
268To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_:
269
270```c
271// for keyboard-level data sync:
272#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B
273// or, for user:
274#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C
275```
276
277These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example:
278
279```c
280typedef struct _master_to_slave_t {
281 int m2s_data;
282} master_to_slave_t;
283
284typedef struct _slave_to_master_t {
285 int s2m_data;
286} slave_to_master_t;
287
288void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) {
289 const master_to_slave_t *m2s = (const master_to_slave_t*)in_data;
290 slave_to_master_t *s2m = (slave_to_master_t*)out_data;
291 s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
292}
293
294void keyboard_post_init_user(void) {
295 transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler);
296}
297```
298
299The 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:
300
301```c
302void housekeeping_task_user(void) {
303 if (is_keyboard_master()) {
304 // Interact with slave every 500ms
305 static uint32_t last_sync = 0;
306 if (timer_elapsed32(last_sync) > 500) {
307 master_to_slave_t m2s = {6};
308 slave_to_master_t s2m = {0};
309 if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
310 last_sync = timer_read32();
311 dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
312 } else {
313 dprint("Slave sync failed!\n");
314 }
315 }
316 }
317}
318```
319
320!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur.
321
322If only one-way data transfer is needed, helper methods are provided:
323
324```c
325bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
326bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer);
327bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
328```
329
330By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required:
331
332```c
333// Master to slave:
334#define RPC_M2S_BUFFER_SIZE 48
335// Slave to master:
336#define RPC_S2M_BUFFER_SIZE 48
337```
210 338
211### Hardware Configuration Options 339### Hardware Configuration Options
212 340