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.md122
1 files changed, 111 insertions, 11 deletions
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 4ebf585f5..428d581ca 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,115 @@ 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
197``` 202```
198 203
199This enables transmitting modifier state (normal, weak and oneshot) to the non 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.
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 205
205```c 206```c
206#define SPLIT_TRANSPORT_MIRROR 207#define SPLIT_TRANSPORT_MIRROR
207``` 208```
208 209
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). 210This 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.
211
212```c
213#define SPLIT_LAYER_STATE_ENABLE
214```
215
216This 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.
217
218```c
219#define SPLIT_LED_STATE_ENABLE
220```
221
222This 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.
223
224```c
225#define SPLIT_MODS_ENABLE
226```
227
228This 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.
229
230```c
231#define SPLIT_WPM_ENABLE
232```
233
234This 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.
235
236### Custom data sync between sides :id=custom-data-sync
237
238QMK'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.
239
240To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_:
241
242```c
243// for keyboard-level data sync:
244#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B
245// or, for user:
246#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C
247```
248
249These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example:
250
251```c
252typedef struct _master_to_slave_t {
253 int m2s_data;
254} master_to_slave_t;
255
256typedef struct _slave_to_master_t {
257 int s2m_data;
258} slave_to_master_t;
259
260void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) {
261 const master_to_slave_t *m2s = (const master_to_slave_t*)in_data;
262 slave_to_master_t *s2m = (slave_to_master_t*)out_data;
263 s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
264}
265
266void keyboard_post_init_user(void) {
267 transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler);
268}
269```
270
271The 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:
272
273```c
274void housekeeping_task_user(void) {
275 if (is_keyboard_master()) {
276 // Interact with slave every 500ms
277 static uint32_t last_sync = 0;
278 if (timer_elapsed32(last_sync) > 500) {
279 master_to_slave_t m2s = {6};
280 slave_to_master_t s2m = {0};
281 if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
282 last_sync = timer_read32();
283 dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
284 } else {
285 dprint("Slave sync failed!\n");
286 }
287 }
288 }
289}
290```
291
292!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur.
293
294If only one-way data transfer is needed, helper methods are provided:
295
296```c
297bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
298bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer);
299bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
300```
301
302By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required:
303
304```c
305// Master to slave:
306#define RPC_M2S_BUFFER_SIZE 48
307// Slave to master:
308#define RPC_S2M_BUFFER_SIZE 48
309```
210 310
211### Hardware Configuration Options 311### Hardware Configuration Options
212 312