aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--keyboards/handwired/hexon38/config.h60
-rw-r--r--keyboards/handwired/hexon38/hexon38.c3
-rw-r--r--keyboards/handwired/hexon38/hexon38.h17
-rw-r--r--keyboards/handwired/hexon38/keymaps/default/keymap.c407
-rw-r--r--keyboards/handwired/hexon38/readme.md11
-rw-r--r--keyboards/handwired/hexon38/rules.mk64
6 files changed, 562 insertions, 0 deletions
diff --git a/keyboards/handwired/hexon38/config.h b/keyboards/handwired/hexon38/config.h
new file mode 100644
index 000000000..23eb51e01
--- /dev/null
+++ b/keyboards/handwired/hexon38/config.h
@@ -0,0 +1,60 @@
1// see https://github.com/pepaslabs/hexon38
2
3#pragma once
4
5#include "config_common.h"
6
7/* USB Device descriptor parameter */
8#define VENDOR_ID 0xFEED
9#define PRODUCT_ID 0x6060
10#define DEVICE_VER 0x0001
11#define MANUFACTURER pepaslabs
12#define PRODUCT hexon38
13#define DESCRIPTION "A handmade non-split ergonomic 38-key keyboard, inspired by the lil38. See https://github.com/pepaslabs/hexon38."
14
15/* key matrix size */
16#define MATRIX_ROWS 4
17#define MATRIX_COLS 12
18
19/* key matrix pins */
20#define MATRIX_ROW_PINS { B0, F0, B2, F4 }
21#define MATRIX_COL_PINS { C6, D3, D2, D1, D0, B7, F6, F7, B6, B5, B4, D7 }
22#define UNUSED_PINS
23
24/* COL2ROW or ROW2COL */
25#define DIODE_DIRECTION ROW2COL
26
27/* number of backlight levels */
28
29#ifdef BACKLIGHT_PIN
30#define BACKLIGHT_LEVELS 0
31#endif
32
33/* Set 0 if debouncing isn't needed */
34#define DEBOUNCING_DELAY 5
35
36
37/* key combination for command */
38#define IS_COMMAND() ( \
39 keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
40)
41
42#ifdef RGB_DI_PIN
43#define RGBLIGHT_ANIMATIONS
44#define RGBLED_NUM 0
45#define RGBLIGHT_HUE_STEP 8
46#define RGBLIGHT_SAT_STEP 8
47#define RGBLIGHT_VAL_STEP 8
48#endif
49
50
51// Disabled features:
52
53/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
54//#define LOCKING_SUPPORT_ENABLE
55
56/* Locking resynchronize hack */
57//#define LOCKING_RESYNC_ENABLE
58
59/* prevent stuck modifiers */
60//#define PREVENT_STUCK_MODIFIERS
diff --git a/keyboards/handwired/hexon38/hexon38.c b/keyboards/handwired/hexon38/hexon38.c
new file mode 100644
index 000000000..d830adef3
--- /dev/null
+++ b/keyboards/handwired/hexon38/hexon38.c
@@ -0,0 +1,3 @@
1// see https://github.com/pepaslabs/hexon38
2
3#include "hexon38.h"
diff --git a/keyboards/handwired/hexon38/hexon38.h b/keyboards/handwired/hexon38/hexon38.h
new file mode 100644
index 000000000..f98f460fa
--- /dev/null
+++ b/keyboards/handwired/hexon38/hexon38.h
@@ -0,0 +1,17 @@
1// see https://github.com/pepaslabs/hexon38
2
3#pragma once
4
5#include "quantum.h"
6
7#define LAYOUT( \
8 K002, K003, K004, K005, K006, K007, K008, K009, \
9 K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, \
10 K200, K201, K202, K203, K204, K207, K208, K209, K210, K211, \
11 K302, K303, K304, K305, K306, K307, K308, K309 \
12) { \
13 { KC_NO, KC_NO, K002, K003, K004, K005, K006, K007, K008, K009, KC_NO, KC_NO }, \
14 { K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111 }, \
15 { K200, K201, K202, K203, K204, KC_NO, KC_NO, K207, K208, K209, K210, K211 }, \
16 { KC_NO, KC_NO, K302, K303, K304, K305, K306, K307, K308, K309, KC_NO, KC_NO } \
17}
diff --git a/keyboards/handwired/hexon38/keymaps/default/keymap.c b/keyboards/handwired/hexon38/keymaps/default/keymap.c
new file mode 100644
index 000000000..c3805991f
--- /dev/null
+++ b/keyboards/handwired/hexon38/keymaps/default/keymap.c
@@ -0,0 +1,407 @@
1// see https://github.com/pepaslabs/hexon38
2
3#include "hexon38.h"
4
5#define A_ KC_A
6#define B_ KC_B
7#define C_ KC_C
8#define D_ KC_D
9#define E_ KC_E
10#define F_ KC_F
11#define G_ KC_G
12#define H_ KC_H
13#define I_ KC_I
14#define J_ KC_J
15#define K_ KC_K
16#define L_ KC_L
17#define M_ KC_M
18#define N_ KC_N
19#define O_ KC_O
20#define P_ KC_P
21#define Q_ KC_Q
22#define R_ KC_R
23#define S_ KC_S
24#define T_ KC_T
25#define U_ KC_U
26#define V_ KC_V
27#define W_ KC_W
28#define X_ KC_X
29#define Y_ KC_Y
30#define Z_ KC_Z
31
32// Dual-role keys: modifier when held, alpha when tapped.
33#define A_CTL CTL_T(KC_A)
34#define S_ALT ALT_T(KC_S)
35#define D_GUI GUI_T(KC_D)
36#define F_SFT SFT_T(KC_F)
37#define J_SFT SFT_T(KC_J)
38#define K_GUI GUI_T(KC_K)
39#define L_ALT ALT_T(KC_L)
40#define COLN_CTL CTL_T(KC_SCLN)
41
42#define ______ KC_TRNS
43#define LSHIFT KC_LSHIFT
44#define RSHIFT KC_RSHIFT
45#define COMMA KC_COMM
46#define SLASH KC_SLSH
47#define SPACE KC_SPC
48#define TAB KC_TAB
49#define BKSPC KC_BSPC
50#define ENTER KC_ENT
51#define PERIOD KC_DOT
52
53#define BASE_LAYER LAYOUT
54#define BLANK_LAYER LAYOUT
55
56
57const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
58
59 BASE_LAYER(
60// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
61 W_ , E_ , R_ , T_ , Y_ , U_ , I_ , O_ ,
62//|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
63 Q_ , A_CTL , S_ALT , D_GUI , F_SFT , G_ , H_ , J_SFT , K_GUI , L_ALT ,COLN_CTL, P_ ,
64//|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
65 B_ , Z_ , X_ , C_ , V_ , M_ , COMMA , PERIOD , SLASH , N_ ,
66//`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
67
68// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
69 LSHIFT , SPACE , TAB , DEBUG , SPACE , BKSPC , ENTER , RSHIFT
70// `--------+--------+--------+--------' `--------+--------+--------+--------'
71),
72
73 BLANK_LAYER(
74// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
75 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
76//|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
77 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
78//|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
79 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
80//`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
81
82// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
83 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______
84// `--------+--------+--------+--------' `--------+--------+--------+--------'
85)
86
87};
88
89// a linked list of pending key events (press or release) which we haven't processed yet.
90struct _pending_key_t {
91 uint16_t keycode;
92 keyrecord_t record;
93 struct _pending_key_t *next;
94};
95typedef struct _pending_key_t pending_key_t;
96
97// worst case is 10 down strokes and 1 up stroke before we can start disambiguating.
98#define RINGSIZE 11
99
100// a ring buffer and linked list to store pending key events (presses and releases).
101// (basically, this is a fixed-allocation linked list.)
102struct _kring_t {
103 // the actual key events.
104 pending_key_t items[RINGSIZE];
105 // the index of the oldest item, or -1 if no items.
106 int8_t ifirst;
107 // the index of the most recently added item, or -1 if no items.
108 int8_t ilast;
109 // the number of items in the ring.
110 uint8_t count;
111 // the head of the linked list.
112 pending_key_t *head;
113};
114typedef struct _kring_t kring_t;
115
116// safe accessor to the i-th item of the linked list (returns pointer or NULL).
117pending_key_t* kring_get(kring_t *ring, uint8_t i) {
118 if (i >= ring->count) {
119 return NULL;
120 }
121 uint8_t j = (ring->ifirst + i) % RINGSIZE;
122 return &(ring->items[j]);
123}
124
125// return the last key in the list of buffered keys.
126pending_key_t* kring_last(kring_t *ring) {
127 if (ring->count == 0) {
128 return NULL;
129 }
130 return kring_get(ring, ring->count - 1);
131}
132
133// remove the oldest item from the ring (the head of the list).
134void kring_pop(kring_t *ring) {
135 if (ring->count > 0) {
136 ring->ifirst += 1;
137 ring->ifirst %= RINGSIZE;
138 ring->head = ring->head->next;
139 ring->count -= 1;
140 }
141}
142
143// add an item to the ring (append to the list).
144void kring_append(kring_t *ring, uint16_t keycode, keyrecord_t *record) {
145 if (ring->count >= RINGSIZE) {
146 // uh oh, we overflowed the capacity of our buffer :(
147 return;
148 }
149
150 // if the ring is empty, insert at index 0.
151 if (ring->count == 0) {
152 ring->count += 1;
153 ring->ifirst = 0;
154 ring->ilast = 0;
155 ring->head = &(ring->items[0]);
156 }
157 // else, append it onto the end.
158 else {
159 ring->count += 1;
160 ring->ilast += 1;
161 ring->ilast %= RINGSIZE;
162 }
163
164 // the index at which we should insert this item.
165 int8_t i = ring->ilast;
166
167 // insert the item.
168 ring->items[i].keycode = keycode;
169 ring->items[i].record.event = record->event;
170#ifndef NO_ACTION_TAPPING
171 ring->items[i].record.tap = record->tap;
172#endif
173 ring->items[i].next = NULL;
174
175 // update the previous item to point to this item.
176 if (ring->count > 1) {
177 kring_get(ring, ring->count - 2)->next = &(ring->items[i]);
178 }
179}
180
181kring_t g_pending;
182
183void matrix_init_user(void) {
184 g_pending.ifirst = -1;
185 g_pending.ilast = -1;
186 g_pending.count = 0;
187 g_pending.head = NULL;
188}
189
190void matrix_scan_user(void) {}
191
192/*
193a_ a-: emit a
194a_ b_ b- a-: emit SHIFT+b
195a_ b_ a- b-: emit a, b
196dual1down, dual1up -> norm1down, norm1up
197dual1down, norm2down, norm2up -> mod1down, norm2down, norm2up
198dual1down, norm2down, dual1up -> norm1down, norm2down, norm1up
199dual1down, dual2down, norm3down, norm3up -> mod1down, mod2down, norm3down, norm3up
200so, a dual key can't be disambiguated until the next keyup of a keydown (not including keyups from keys before it).
201*/
202
203bool is_ambiguous_kc(uint16_t kc) {
204 // See the MT() define: https://github.com/qmk/qmk_firmware/blob/master/quantum/quantum_keycodes.h#L642
205 // See the QK_MOD_TAP case: https://github.com/qmk/qmk_firmware/blob/master/quantum/keymap_common.c#L134
206 uint8_t mod = mod_config((kc >> 0x8) & 0x1F);
207 return mod != 0;
208}
209
210bool is_down(pending_key_t *k) {
211 return k->record.event.pressed;
212}
213
214bool is_up(pending_key_t *k) {
215 return !is_down(k);
216}
217
218bool keys_match(pending_key_t *a, pending_key_t *b) {
219 return a->record.event.key.col == b->record.event.key.col
220 && a->record.event.key.row == b->record.event.key.row;
221}
222
223// both the down and corresponding upstroke of a keypress.
224struct _pending_pair_t {
225 pending_key_t *down;
226 pending_key_t *up;
227};
228typedef struct _pending_pair_t pending_pair_t;
229
230// returns true if this keydown event has a corresponding keyup event in the
231// list of buffered keys. also fills out 'p'.
232bool is_downup_pair(pending_key_t *k, pending_pair_t *p) {
233 // first, make sure this event is keydown.
234 if (!is_down(k)) {
235 return false;
236 }
237 // now find its matching keyup.
238 pending_key_t *next = k->next;
239 while (next != NULL) {
240 if (keys_match(k, next) && is_up(next)) {
241 // found it.
242 if (p != NULL) {
243 p->down = k;
244 p->up = next;
245 }
246 return true;
247 }
248 next = next->next;
249 }
250 // didn't find it.
251 return false;
252}
253
254// given a QK_MOD_TAP keycode, return the KC_* version of the modifier keycode.
255uint16_t get_mod_kc(uint16_t keycode) {
256 uint8_t mod = mod_config((keycode >> 0x8) & 0x1F);
257 switch (mod) {
258 case MOD_LCTL:
259 return KC_LCTL;
260 case MOD_RCTL:
261 return KC_RCTL;
262 case MOD_LSFT:
263 return KC_LSFT;
264 case MOD_RSFT:
265 return KC_RSFT;
266 case MOD_LALT:
267 return KC_LALT;
268 case MOD_RALT:
269 return KC_RALT;
270 case MOD_LGUI:
271 return KC_LGUI;
272 case MOD_RGUI:
273 return KC_RGUI;
274 default:
275 // shrug? this shouldn't happen.
276 return keycode;
277 }
278}
279
280bool is_mod_kc(uint16_t keycode) {
281 switch (keycode) {
282 case QK_MODS ... QK_MODS_MAX:
283 return true;
284 default:
285 return false;
286 }
287}
288
289void interpret_as_mod(pending_pair_t *p) {
290 // see https://github.com/qmk/qmk_firmware/issues/1503
291 pending_key_t *k;
292 k = p->down;
293 if (k != NULL) {
294 k->keycode = get_mod_kc(k->keycode);
295 }
296 k = p->up;
297 if (k != NULL) {
298 k->keycode = get_mod_kc(k->keycode);
299 }
300}
301
302void interpret_as_normal(pending_pair_t *p) {
303 pending_key_t *k;
304 k = p->down;
305 if (k != NULL) {
306 k->keycode = k->keycode & 0xFF;
307 }
308 k = p->up;
309 if (k != NULL) {
310 k->keycode = k->keycode & 0xFF;
311 }
312}
313
314void execute_head_and_pop(kring_t *ring) {
315 pending_key_t *head = kring_get(ring, 0);
316 uint16_t kc = head->keycode;
317 if (is_mod_kc(kc)) {
318 if (is_down(head)) {
319 dprintf(" %s: mod down 0x%04X\n", __func__, kc);
320 set_mods(get_mods() | MOD_BIT(kc));
321 } else {
322 dprintf(" %s: mod up 0x%04X\n", __func__, kc);
323 set_mods(get_mods() & ~MOD_BIT(kc));
324 }
325 } else {
326 if (is_down(head)) {
327 dprintf(" %s: key down 0x%04X\n", __func__, kc);
328 register_code16(kc);
329 } else {
330 dprintf(" %s: key up 0x%04X\n", __func__, kc);
331 unregister_code16(kc);
332 }
333 }
334 kring_pop(ring);
335}
336
337// try to figure out what the next pending keypress means.
338bool parse_next(kring_t *pending) {
339 pending_pair_t p;
340 pending_key_t *first = kring_get(pending, 0);
341 if (!is_ambiguous_kc(first->keycode)) {
342 // this pending key isn't ambiguous, so execute it.
343 dprintf(" %s: found unambiguous key\n", __func__);
344 execute_head_and_pop(pending);
345 return true;
346 } else if (is_ambiguous_kc(first->keycode) && is_up(first)) {
347 dprintf(" %s: interpreting keyup as mod\n", __func__);
348 p.down = NULL;
349 p.up = first;
350 interpret_as_mod(&p);
351 execute_head_and_pop(pending);
352 return true;
353 } else if (is_downup_pair(first, &p)) {
354 // 'first' was released before any other pressed key, so treat this as
355 // a rolling series of normal key taps.
356 dprintf(" %s: found down-up pair, interpreting as normal key\n", __func__);
357 interpret_as_normal(&p);
358 execute_head_and_pop(pending);
359 return true;
360 } else {
361 // if another key was pressed and released while 'first' was held, then we
362 // should treat it like a modifier.
363 pending_key_t *next = first->next;
364 while (next != NULL) {
365 if (is_downup_pair(next, NULL)) {
366 dprintf(" %s: found subsequent downup pair, interpreting head as mod\n", __func__);
367 p.down = first;
368 p.up = NULL;
369 interpret_as_mod(&p);
370 execute_head_and_pop(pending);
371 return true;
372 }
373 next = next->next;
374 }
375
376 // we can't disambiguate 'first' yet. wait for another keypress.
377 dprintf(" %s: can't disambiguate (yet)\n", __func__);
378 return false;
379 }
380}
381
382bool process_record_user(uint16_t keycode, keyrecord_t *record) {
383 if (keycode == DEBUG) {
384 return true;
385 }
386
387 if (g_pending.count == 0 && !is_ambiguous_kc(keycode)) {
388 // we have no pending keys and this key isn't ambiguous, so we should
389 // just let QMK take care of it.
390 dprintf("%s: handled by qmk\n", __func__);
391 return true;
392 } else {
393 dprintf("%s: got dual-role key\n", __func__);
394 // append the keypress and then try parsing all pending keypresses.
395 kring_append(&g_pending, keycode, record);
396 while (g_pending.count > 0) {
397 dprintf("%s: looping through %d keys...\n", __func__, g_pending.count);
398 if (!parse_next(&g_pending)) {
399 // one of our keypresses is ambiguous and we can't proceed until
400 // we get further keypresses to disambiguate it.
401 dprintf("%s: %d pending keys are ambiguous\n", __func__, g_pending.count);
402 break;
403 }
404 }
405 return false;
406 }
407}
diff --git a/keyboards/handwired/hexon38/readme.md b/keyboards/handwired/hexon38/readme.md
new file mode 100644
index 000000000..c8ada8e2b
--- /dev/null
+++ b/keyboards/handwired/hexon38/readme.md
@@ -0,0 +1,11 @@
1# hexon38
2
3QMK support for the [hexon38](https://github.com/pepaslabs/hexon38).
4
5## Building
6
7```
8$ cd qmk_firmware
9$ make handwired/hexon38
10```
11
diff --git a/keyboards/handwired/hexon38/rules.mk b/keyboards/handwired/hexon38/rules.mk
new file mode 100644
index 000000000..2b6f17afc
--- /dev/null
+++ b/keyboards/handwired/hexon38/rules.mk
@@ -0,0 +1,64 @@
1# see https://github.com/pepaslabs/hexon38
2
3# MCU name
4MCU = atmega32u4
5
6# Processor frequency.
7# This will define a symbol, F_CPU, in all source code files equal to the
8# processor frequency in Hz. You can then use this symbol in your source code to
9# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
10# automatically to create a 32-bit value in your source code.
11#
12# This will be an integer division of F_USB below, as it is sourced by
13# F_USB after it has run through any CPU prescalers. Note that this value
14# does not *change* the processor frequency - it should merely be updated to
15# reflect the processor speed set externally so that the code can use accurate
16# software delays.
17F_CPU = 16000000
18
19#
20# LUFA specific
21#
22# Target architecture (see library "Board Types" documentation).
23ARCH = AVR8
24
25# Input clock frequency.
26# This will define a symbol, F_USB, in all source code files equal to the
27# input clock frequency (before any prescaling is performed) in Hz. This value may
28# differ from F_CPU if prescaling is used on the latter, and is required as the
29# raw input clock is fed directly to the PLL sections of the AVR for high speed
30# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
31# at the end, this will be done automatically to create a 32-bit value in your
32# source code.
33#
34# If no clock division is performed on the input clock inside the AVR (via the
35# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
36F_USB = $(F_CPU)
37
38# Interrupt driven control endpoint task(+60)
39OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
40
41
42# Bootloader selection
43# Teensy halfkay
44# Pro Micro caterina
45# Atmel DFU atmel-dfu
46# LUFA DFU lufa-dfu
47# QMK DFU qmk-dfu
48# atmega32a bootloadHID
49BOOTLOADER = halfkay
50
51
52# Enabled build options:
53BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration(+1000)
54MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
55EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
56CONSOLE_ENABLE = yes # Console for debug(+400)
57COMMAND_ENABLE = yes # Commands for debug and configuration
58NKRO_ENABLE = yes # USB Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
59
60# Disabled build options:
61SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
62BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
63AUDIO_ENABLE = no
64RGBLIGHT_ENABLE = no