TMK for a Hot-Splittable Keyboard? Trying to Understand TMK

User avatar
richfiles

08 May 2017, 01:29

Keyboard76_NP_DZ_Split.jpg
Keyboard76_NP_DZ_Split.jpg (309.82 KiB) Viewed 2631 times
I have a keyboard project that's sat rather... stagnant for almost a year. The main keyboard is complete, and has been for a year, save for firmware. A sin, I know... The issue is that I'm a hardware guy... The last programming I dis was on a TI-89 calculator, and before that, on the Commodore 128. All I ever knew was BASIC, and even that's been a decade and a half since I even looked at it. I picked up this keyboard project, as well as a project to build a Kerbal Space Program instrument/control panel, to make myself learn C... Only I've not made much progress. Things got busy at work, I got on a roll working on the KSP controller, I got injured at work, then my motherboard died... All in all, a full year has passed since I "finished" my keyboard. It still has no firmware, and though I did finally get the plate to build the number pad, I still have not finished it yet.

My goals are:

A: To initially get a functioning firmware on my main keyboard, so I can ACTUALLY USE IT!
B: To figure out a way to be able to poll/not poll a port expander over I2C, based on the logic level of a "sense" signal.

So, here's my progress so far.

First, I've made up a nice layout worksheet that has all the electrical rows and columns identified not he physical layout. It's top down view, so it'll be useful for defining my key layout too.
Keyboard_DZ_75+1_Matrix.png
Keyboard_DZ_75+1_Matrix.png (69.07 KiB) Viewed 2635 times
So, I did start with the TMK firmware. If it can do everything I need it to do, then I'll stick with it, but there's always the option of re-flashing the keyboard with something else in the future if I can't integrate the hot swappable port expander functionality into the code. The code for my Column and Row pin definitions are below:

Code: Select all

/* Column pin configuration
 * col: 0   1   2   3   4   5   6   7   8   9   10  11  12
 * pin: F0  F1  F4  F5  F6  D4  D5  D2  B7  B3  B2  B1  B0

Code: Select all

/* Row pin configuration
 * row: 0   1   2   3   4   5   6
 * pin: F7  B6  B5  B4  C7  C6  D3
I have a VERY dense matrix of 13x7. It was necessary to keep enough I/O pins open for the Caps Lock LED, Backlight LED PWM, a pair of I2C pins, and the numpad sense pin. The sense pin detects if the number pad is attached, and whether or not it should poll the numpad's port expander over I2C. I figure, software is changeable, so I can re-do code later, once I actually have the number pad built, and have half a clue what the heck I'm actually doing.

Anyway, since I had to keep the matrix small, That super dense layout has almost no gaps, and row 6 is actually a tight cluster of keys in the top right corner. An alteration part way in resulted in the very top right key having a completely nonsensical matrix position, because the change was literally a bodge. Only three intersections in the entire matrix go unused (c0-r6, c1-r6, and c12-r4). This means my matrix does not directly conform to the physical key layout in some areas (all of Row 6 is in the top right corner, and the bottom row is "electrically" compressed to the left, and then includes the keys physically in the bottom right corner well).

Now, I'm stumped at my current place in the code...

What have not figured out yet, is whether the matrix definitions need to be ordered by electrical sequence, or if the values are arbitrary, and allow for physical sequence (as long as the row and column values are correctly defined). The obviously functional way is to lay the matrix out electrically. This seems to be what's assumed with most keyboards, as most keyboards physical layout and electrical layout probably match.

My question then is which (either, or both, or neither, or 42 ;D ) would be correct?

Electrically based layout:

Code: Select all

#define KEYMAP( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, \
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, \
    K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, \
    K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, \
    K40, K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B,     \
    K50, K51, K52, K53, K54, K55, K56, K57, K58, K59, K5A, K5B, K5C, \
              K62, K63, K64, K65, K66, K67, K68, K69, K6A, K6B, K6C  \
) { \
    { KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06, KC_##K07, KC_##K08, KC_##K09, KC_##K0A, KC_##K0B, KC_##K0C }, \
    { KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16, KC_##K17, KC_##K18, KC_##K19, KC_##K1A, KC_##K1B, KC_##K1C }, \
    { KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26, KC_##K27, KC_##K28, KC_##K29, KC_##K2A, KC_##K2B, KC_##K2C }, \
    { KC_##K30, KC_##K31, KC_##K32, KC_##K33, KC_##K34, KC_##K35, KC_##K36, KC_##K37, KC_##K38, KC_##K39, KC_##K3A, KC_##K3B, KC_##K3C }, \
    { KC_##K40, KC_##K41, KC_##K42, KC_##K43, KC_##K44, KC_##K45, KC_##K46, KC_##K47, KC_##K48, KC_##K49, KC_##K4A, KC_##K4B, KC_NO    }, \
    { KC_##K50, KC_##K51, KC_##K52, KC_##K53, KC_##K54, KC_##K55, KC_##K56, KC_##K57, KC_##K58, KC_##K59, KC_##K5A, KC_##K5B, KC_##K5C }, \
    { KC_NO,    KC_NO,    KC_##K62, KC_##K63, KC_##K64, KC_##K65, KC_##K66, KC_##K67, KC_##K68, KC_##K69, KC_##K6A, KC_##K6B, KC_##K6C }  \
}
or...

Physically based layout

Code: Select all

#define KEYMAP( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K62, K6B, K6C, K65, \
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K63,      K69, K6A, \
    K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K64,      K67, K68, \
    K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C,           K5C, K66, \
    K40,      K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B,           K5A, K5B, \
    K50, K51, K52,                K53,                K54, K55, K56,      K57, K58, K59  \
) { \
    { KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06, KC_##K07, KC_##K08, KC_##K09, KC_##K0A, KC_##K0B, KC_##K0C, KC_##K62, KC_##K6B, KC_##K6C, KC_##K65 }, \
    { KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16, KC_##K17, KC_##K18, KC_##K19, KC_##K1A, KC_##K1B, KC_##K1C, KC_##K63, KC_NO,    KC_##K69, KC_##K6A }, \
    { KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26, KC_##K27, KC_##K28, KC_##K29, KC_##K2A, KC_##K2B, KC_##K2C, KC_##K64, KC_NO,    KC_##K67, KC_##K68 }, \
    { KC_##K30, KC_##K31, KC_##K32, KC_##K33, KC_##K34, KC_##K35, KC_##K36, KC_##K37, KC_##K38, KC_##K39, KC_##K3A, KC_##K3B, KC_##K3C, KC_NO,    KC_NO,    KC_##K5C, KC_##K66 }, \
    { KC_##K40, KC_NO,    KC_##K41, KC_##K42, KC_##K43, KC_##K44, KC_##K45, KC_##K46, KC_##K47, KC_##K48, KC_##K49, KC_##K4A, KC_##K4B, KC_NO,    KC_NO,    KC_##K5A, KC_##K5B}, \
    { KC_##K50, KC_##K51, KC_##K52, KC_NO,    KC_NO,    KC_NO,    KC_##K53, KC_NO,    KC_NO,    KC_NO,    KC_##K54, KC_##K55, KC_##K56, KC_NO,    KC_##K57, KC_##K58, KC_##K59 }  \

}
My question is, are the values entered into the table above dependent on position in the list, or just by content. It'd be FAR easier to follow the second layout, but I don't know if it screws with the code to mix and match rows and columns onto different lines in the list like that, or whether it's all good, as long as the numbers match.

My understanding of the code, is that those lists are not "technically" even different lines... That they are basically one long continuous list, with breaks to make it easier to read. What I don't know is whether the code expects to read the list strictly sequentially, the same way it's scanning the matrix, or whether the code is simply defining the physical layout of the key map and defining what matrix intersection applies to the physical layout. I'm confused!

Basically, is the code:

A: expecting all the rows and columns to be in a neat and orderly incrementing layout (all the columns, in order, 0 to the maximum, for row 0, then repeating for row 1, 2, 3... and so on)

or

B: Is this list arbitrarily defining the row/column intersection values for the (later to be entered) keymap, based on the row and column numbers entered. Basically, saying "I want this layout to have r0-c0 through r0-c12, then r6-c3, r6-c11, r6-c12, and finally r6-c5 in the first physical row of keys"... Basically, so later, when I enter the keymap, it knows that ESC is r0-c0, F1-F12 is r0-c1 through r0-c12, and that F13 is r6c3, F14 is r6-c11, F15 is r6-c12, and the media EJECT key is r6-c5 (using my top row as an example).

Does that make sense? :?

I suppose I should also add, I know software is flexible, and I can always integrate new features later on. For now, I just wanna get my keyboard up and running. It's been a YEAR! In the long term though, I want to have the code check whether pin E6 changes states. Depending on whether it is high or low, I want to have it scan a 6x6 matrix on a port expander connected over I2C, or to skip the I2C part of the code altogether and ignore that part of the matrix. I don't yet know how I would go about doing this in the code, and I don't even know if this firmware can even be modded to support it at all. Of course, that's the nice thing about software... There's always room for change. I just don't know how. :roll:

I think, for the transistor driven under-lighting LEDs in each switch housing, I just want to have a default minimum brightness setting at power up, and the option to change the brightness with the keyboard. I already have the max brightness current limited to stay under the USB max current, but I think at power up it'd be wise to keep total draw under 200 mA, and then let the keyboard negotiate 500 mA if you increase brightness... Again, I imagine it's possible, but I don't know if TMK can do it, or how easy it'd be to mod that functionality in. I just don't want the keyboard to require an external power source to power all the LEDs, nor do I want it to trigger "The USB device is drawing too much power" warnings. It'd be nice if it were possible to cap max brightness based on USB power negotiation. I know devices can request up to 500 mA, but initial connections default to 100 mA (if the devices play friendly with he protocol).

User avatar
richfiles

09 May 2017, 18:18

You know... I think for the sakes of getting this thing running, I'm going to select option A, even though layout wise, it's unintuitive. It's intuitive in terms of electrical layout, not physical layout. That's because my electrical matrix kinda jumps around all over the place on the right side of the keyboard, where row 6 and the end of row 5 are all jumbled up into the right two columns.

I guess I KNOW sticking with the electrical layout WOULD work, and I can stick with the electrical layout with the character map as well. I just never got an answer in regards to if a physical layout map would work, or if it'd "confuse' the firmware.

User avatar
mecano

11 May 2017, 00:03

Don't know about TMK but QMK defines keymap as following

Code: Select all

const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS] PROGMEM =
{
    [L_QW] = {
        /* Qwerty
         * .___________________________________________________________________________________________________.
         * |Tab Hyper|   q   |   w   |   e   |   r   |   t   |   y   |   u   |   i   |   o   |   p   |BSP Hyper|
         * |___________________________________________________________________________________________________|
         * |Esc  Ctrl|   a   |   s   |   d   |   f   |   g   |   h   |   j   |   k   |   l   |   ;   |Ent  Ctrl|
         * |___________________________________________________________________________________________________|
         * |(  Shift |   z   |   x   |   c   |   v   |   b   |   n   |   m   |   ,   |   .   |   /   |)  Shift |
         * |___________________________________________________________________________________________________|
         * | Option  | « CMD |   »   |   "   | ' NUM |SPC LS | = CHA | - HRD |   \   |   [   | ] CMD | Option  |
         * '___________________________________________________________________________________________________'
         */
        {KC_ESC,   LG(T),   KC_1,    KC_2,  KC_3,KC_4,KC_5,KC_6,KC_7,KC_8,KC_9,KC_0,KC_MINS,KC_EQL, KC_BSPC},
        {KC_POWER, KC_TAB,  KC_Q,    KC_W,   KC_E,     KC_R,   KC_T,   KC_Y,   KC_U,   KC_I,   KC_O,    KC_P, KC_LBRACKET,KC_RBRACKET,    xxx},
        {LG(W),    KC_CAPS, KC_A,    KC_S,    KC_D,     KC_F,   KC_G,   KC_H,   KC_J,   KC_K,   KC_L,    KC_SCLN, KC_QUOTE, KC_BSLS, KC_ENTER},
        {M(BRIGHT),  KC_LSFT, KC_Z,    KC_X,   KC_C,     KC_V,   KC_B,   KC_N,   KC_M,   KC_COMM,KC_DOT,  KC_SLSH, LSG(LBRACKET),LSG(RBRACKET), KC_RSFT},
        {KC_LCTL, KC_LALT, KC_LGUI, F(_NUM),F(_NAV),KC_SPC,xxx,xxx,xxx,xxx,F(_CHA),KC_RGUI, KC_RALT, KC_RCTL, KC_SLEP}
    },
So it follows how the keyboard is physically wired.

User avatar
richfiles

11 May 2017, 03:43

Truth be told... Life, work, etc got busy when I was trying to learn this stuff. This is the first chance I've really had to get back into it. I have a day off this weekend, so it's tempting to just move forward and try. What's the worst that happens... The keyboard remains exactly as it has been for a year?

Quite honestly... A physical map makes keeping track of layouts WAY easier. Another comment elsewhere seems to have also suggested non-sequential matrix positions seemed to work in the lists, also suggesting that physical layouts are possible, not just only electrically accurate layouts. Nice!

It's time to get this thing working. For now, the main keyboard. Later the numberpad.

Currently, I'm only controlling a Caps Lock LED and a single unified backlight PWM. I'm going to have to control the numberpad backlight LEDs separately from the main keyboard's PWM pin. I'd have to control it over I2C. I have some small I2C DAC boards that I think would be ideal to control the numberpad LEDs by just commanding them to a particular level. There will be unused matrix positions on the number pad, so I could potentially use those for control of individual LEDs if I wanted to make indicators on the number pad someday. I have three spare matrix positions on the main keyboard. As of this moment, I'm not planning on using any of those spare matrix positions for anything at all though.

Being able to hot swap the I2C is going to be absolutely necessary for the numberpad to be able to detach and attach. What I really need in the future, I think, is to have the ability to "skip" part of the matrix scan, based on whether the sense signal is high or low. No idea if TMK or QMK or any other firmware has any chance of being able to be modified to make checking a port expander over I2C an optional check, based on a conditional test.

User avatar
mecano

14 May 2017, 16:44

Check the split keyboard code in QMK. It uses two ATMegas so you will have plenty of pins for your leds then.

User avatar
Ray

14 May 2017, 17:47

As for the physical vs. electrical layout thing: That's a main point that you can do with the KEYMAP-macro. You can use it as an abstraction layer between the physical layout and electrical connections. As a side effect it lets you save the "KC_" of the beginning of the keycode defines. Let me try to explain it bottom up:

Basically you need to have as many "KC_NO"s in the second half of the macro as there are gaps in your elecrical wiring matrix. The second half needs to represent your actual electrical wiring matrix.
In the first half, you can kind of "remap" that matrix to what you like to work with, physical layout is certainly a good option.
The KEYMAP-macro translates an easy to comprehend list into an uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS], which is basically an array of (electrical) matrices.

Post Reply

Return to “Keyboards”