How to build your very own keyboard firmware

User avatar
matt3o
-[°_°]-

02 Jan 2014, 13:08

This is a tutorial about building your own custom firmware with Hasu's keyboard firmware.

It may seem daunting to work with Hasu's wall of code but the files you need to modify are not many and you don't actually need to fully understand the code. I myself don't understand 100% of it, so if I write some bullshits I hope Hasu will chime in.

Development environment
First of all you need to set up the develpment environment. That depends on your system.

Unfortunately Windows is not the best development environment for this kind of things. My suggestion would be to install a minimal linux distribution on a virtual machine (VirtualBox is free) and follow the linux instructions below. Most of issues people are encountering are due to Windows. I you feel lucky and insist on using windows I believe your best option is to install AtmelStudio. I can't help you with that because I haven't personally used it, but it should contain all you need to compile your firmware.

On Mac get CrossPack and from the App Store install XCode.

On Linux you need to install the development tools (eg: on Ubuntu the package should be called "build-essential") and the AVR dev tools. Again, naming varies based on the distro, search for AVR in your package manager and you should find all you need (eg: for Ubuntu should be "gcc-avr gdb-avr binutils-avr avr-libc avrdude").

To actually burn the firmware in the controller you need a programmer. You can use Atmel's FLIP or dfu-programmer, but I find the easiest to use is the Teensy loader.

If you can't actually build the firmware yourself, don't worry, you can still make the modifications needed to the code and I bet someone here on the forum can send you the HEX file you'll be uploading to the keyboard (you still need to install the programmer above).

Get the code
Download and unzip Hasu's code from github. https://github.com/tmk/tmk_keyboard/archive/master.zip

Fortunately you can ignore most of the code there. Go to the keyboard/gh60 directory. We won't never leave that directory, I've told you it was easy.

Remember to check Hasu's repository once in a while, it is very active and it often gets updated.

Main config
Open Makefile file and check the following globals:

Code: Select all

TARGET = gh60_lufa
...
...
MCU = atmega32u4
...
...
#NKRO_ENABLE = yes
if you are using the Teensy++ change the MCU to at90usb1287. Also comment out (place a # at the beginning of the line) the NKRO_ENABLE feature. You don't need it, and it is not supported by LUFA. More about this later.

Now open the config.h file.

You can give custom values to MANUFACTURER, PRODUCT and DESCRIPTION, but that's completely optional. What's important is:

Code: Select all

#define MATRIX_ROWS 5
#define MATRIX_COLS 14
Change the values to respectively the number of rows and columns of your keyboard.

Setting up rows and columns
Now back to the teensy. Write down how you connected the rows/cols (and eventually the capslock LED) to the teensy's pins. Remember to avoid pin D6 that is dedicated to the teensy's internal LED.

Just for the sake of this tutorial let's say you have 15 cols and 5 rows. In this example the columns are connected like this:

Code: Select all

col:  0   1   2   3   4   5   6   7   8   9   10  11  12  13  14
pin: F7  B6  B5  B4  D7  C7  C6  D3  D2  D1   D0  B7  B3  B2  B1
Rows look like this instead:

Code: Select all

row:  0   1   2   3   4
pin: F0  F1  F4  F5  F6
Remember, this is just an example, your pin configuration might be different.

You can check the name of the pins from PJRC pinout page or reading them directly from the Teensy.

Open the matrix.c file. Search for the init_cols function and change it according to your pinout

Code: Select all

static void  init_cols(void)
{
    // Input with pull-up(DDR:0, PORT:1)
    DDRF  &= ~(1<<7);
    PORTF |=  (1<<7);
    DDRB  &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1);
    PORTB |=  (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1);
    DDRD  &= ~(1<<7 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    PORTD |=  (1<<7 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    DDRC  &= ~(1<<7 | 1<<6);
    PORTC |=  (1<<7 | 1<<6);
}
It's easier than it looks. The name of the pins are composed by one letter and one number from 0 to 7. If you look at the list of columns, I have one pin starting with F and the number for that pin is 7. So to activate that pin I have to add the following to the list:

Code: Select all

    DDRF  &= ~(1<<7);
    PORTF |=  (1<<7);
DDRF and PORTF are the hardware registers that control the port F pins. 1<<7 is a bitwise operator that picks the 8th bit of that port (remember we start counting from 0, so the 8th pin is actually number 7).

Let's proceed with the second port.

Code: Select all

    DDRB  &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1);
    PORTB |=  (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1);
In this case we are on port B (DDRB and PORTB) and if you look at our cols schema you'll see that we are using the pins number B7, B6, B5, B4, B3, B2 and B1 (even if they are not consecutive on the matrix, it doesn't matter). We use | (or) to smartly react to any of the selected pins. 1<<7 means B7, 1<<6 means B6, and so on.

I believe at this point it should be clear how it works. Proceed with all the other columns.

Cool. We just initiated the columns. Now we have to read them. Search for the read_cols function:

Code: Select all

static matrix_row_t read_cols(void)
{
    return (PINF&(1<<7) ? 0 : (1<<0)) |
           (PINB&(1<<6) ? 0 : (1<<1)) |
           (PINB&(1<<5) ? 0 : (1<<2)) |
           (PINB&(1<<4) ? 0 : (1<<3)) |
           (PIND&(1<<7) ? 0 : (1<<4)) |
           (PINC&(1<<7) ? 0 : (1<<5)) |
           (PINC&(1<<6) ? 0 : (1<<6)) |
           (PIND&(1<<3) ? 0 : (1<<7)) |
           (PIND&(1<<2) ? 0 : (1<<8)) |
           (PIND&(1<<1) ? 0 : (1<<9)) |
           (PIND&(1<<0) ? 0 : (1<<10)) |
           (PINB&(1<<7) ? 0 : (1<<11)) |
           (PINB&(1<<3) ? 0 : (1<<12)) |
           (PINB&(1<<2) ? 0 : (1<<13)) |
           (PINB&(1<<1) ? 0 : (1<<14));
}
This function associates each pin to its col. So

Code: Select all

(PINF&(1<<7) ? 0 : (1<<0))
Tells the controller that if pin F7 (PINF...7) is active then we are on the first column (again zero based, 1<<0 means first column).

Code: Select all

(PINB&(1<<6) ? 0 : (1<<1))
If pin B6 (PINB...6) is high then we are on the second column (1<<1). And so on. A piece of cake.

Now to the rows. Search the unselect_rows function.

Code: Select all

static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRF  &= ~0b01110011;
    PORTF &= ~0b01110011;
}
Hasu chose a different syntax here. We are talking binary this time, but it's still pretty easy. If you look at the rows schema you'll see that we are using pins number 0, 1, 4, 5, 6 of the F port.

So we write to the F port with DDRF and PORTF like we did for the columns but to choose the port number we set it to 1. (actually we are unselecting them, but this is meant for newbie and I'm not going deeper into this).

0b just tells the compiler that we are talking binary. Then you have 8 zeros and ones. If you look closely you'll see that the 1s are --reading from right to left-- in the 1st, 2nd, 5th, 6th, 7th positions, or pin number 0, 1, 4, 5, 6 that are the columns that we are using for our keyboard.

Start counting from the right. The first bit is PIN0, the last is PIN7. Just set the pins you use to 1. Peachy, isn't it?

Now to selecting the rows. Search for the select_row function.

Code: Select all

static void select_row(uint8_t row)
{
    // Output low(DDR:1, PORT:0) to select
    switch (row) {
        case 0:
            DDRF  |= (1<<0);
            PORTF &= ~(1<<0);
            break;
        case 1:
            DDRF  |= (1<<1);
            PORTF &= ~(1<<1);
            break;
        case 2:
            DDRF  |= (1<<4);
            PORTF &= ~(1<<4);
            break;
        case 3:
            DDRF  |= (1<<5);
            PORTF &= ~(1<<5);
            break;
        case 4:
            DDRF  |= (1<<6);
            PORTF &= ~(1<<6);
            break;
    }
}
At this point it should pretty clear what's going on here.

case 0: means: "if we are on the first row".

Code: Select all

DDRF  |= (1<<0);
PORTF &= ~(1<<0);
Means: "select port F, bit 0".

Now modify your ports/pins according to your schema.

Lastly open the led.c file and change the port to the one you connected the LED to.

Code: Select all

void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK)) {
        // output low
        DDRD |= (1<<4);
        PORTD &= ~(1<<4);
    } else {
        // Hi-Z
        DDRD &= ~(1<<4);
        PORTD &= ~(1<<4);
    }
}
In this case we have the LED on pin D4. Let's say it were on pin F0 it would have been:

Code: Select all

DDRF |= (1<<0);
PORTF &= ~(1<<0);
Okay, a lot to digest. That's enough for the first lesson. In the next session we will be customizing the actual key matrix and the function layers. If you have questions or you spotted some errors, just let me know.

Part 2 is just around the bend.
Last edited by matt3o on 03 Jan 2014, 18:33, edited 2 times in total.

User avatar
Muirium
µ

02 Jan 2014, 13:51

Thanks for this, Matt. It certainly looks more complex than configuring Soarer's Controller, but I suspect it may be worth it if there's layer locks and indicators later in the tutorial. I've gotten very used to Soarer's system, but I might as well give this a shot once I've finished the 16x8 diode matrix on my Tipro. This can handle that, right?

User avatar
matt3o
-[°_°]-

02 Jan 2014, 13:53

Muirium wrote:I might as well give this a shot once I've finished the 16x8 diode matrix on my Tipro. This can handle that, right?
sure, why not? :)

User avatar
Broadmonkey
Fancy Rank

02 Jan 2014, 14:10

Great write! Hasu's code is pretty easy to configure once you have figured out how the code works. I know I used a fair few hours learning it, so this should really help newcomers a lot!

I used it to build a numpad and just used the GH60 code as a basis and the only part that has given me problems is the LED (num lock).

User avatar
matt3o
-[°_°]-

02 Jan 2014, 14:18

yeah once you get used to it, it's simpler than it seems.

For a numpad I believe you don't even need a matrix, although it is more elegant of course. You could connect each switch to a pin (you should have enough).

User avatar
ماء

02 Jan 2014, 14:24

Guide make Own(DIY) Keyboard by matt3o
Step 1 :?: :P
Step 2 :idea: :?
Step 3(Done) 8-) :geek:
Play :roll:
Last edited by ماء on 03 Jan 2014, 10:09, edited 2 times in total.

User avatar
Kurk

02 Jan 2014, 18:34

Thanks a lot, matt3o. I have absolutely no clue about programming but your explanations helped me understanding some basics of the TMK code. :)

User avatar
scottc

02 Jan 2014, 18:43

Oh wow, thank you very much matt3o! You're basically making my custom keyboard for me with all of these tutorials! :D

User avatar
Vierax

02 Jan 2014, 22:03

Nice work Matt, I'll link this thread to enlighten some lost new DIYer :)

User avatar
matt3o
-[°_°]-

03 Jan 2014, 00:13

thanks guys. tomorrow I'll post the second part. It's actually easier to do than explain. I might do a quick screencast as well.

User avatar
matt3o
-[°_°]-

03 Jan 2014, 18:32

Welcome to part 2 of "Build your own keyboard firmware" tutorial.

It may seem hard at first but if you follow all the steps of this tutorial you'll see that it's harder to explain than to do.

We are still on keyboard/gh60 directory. All we need resides there. This time we are going to customize the actual keyboard matrix and FN layers.

Defining the matrix
First of all we need to map the matrix in the keymap_common.h file.

The bit we need is:

Code: Select all

#define KEYMAP( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, \
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, \
    K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, \
    K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, \
    K40, K41, K42,           K45,                K49, K4A, K4B, K4C, K4D  \
) { \
    { 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_##K0D }, \
    { 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_##K1D }, \
    { 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_##K2D }, \
    { 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_##K3D }, \
    { KC_##K40, KC_##K41, KC_##K42, KC_NO,    KC_NO,    KC_##K45, KC_NO,    KC_NO,    KC_NO,    KC_##K49, KC_##K4A, KC_##K4B, KC_##K4C, KC_##K4D }  \
}
The above schema should reflect your keyboard matrix, or how you wired the switches together.

Take the first row:

Code: Select all

    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, \
It means that all switches in the first row are straight connected together. If you need more keys you can keep adding them like so:

Code: Select all

    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, K0F, \
Here I've added two new keys in the first row (K0E and K0F). Remember to add ", \" (coma+backslash) at the end of each line.

You have to repeat the same key configuration in block of code below.

Code: Select all

    { 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_##K0D }, \
As you can see it's the same thing we did above with a slightly different syntax.

Now look at the last row:

Code: Select all

    K40, K41, K42,           K45,                K49, K4A, K4B, K4C, K4D  \
This time we have a jump from K42 to K45. In fact the lower row usually has fewer keys and there's a gap between the ALT key and the SPACEBAR switches. Be careful checking which column each switch is connected to.

Code: Select all

    { KC_##K40, KC_##K41, KC_##K42, KC_NO,    KC_NO,    KC_##K45, KC_NO,    KC_NO,    KC_NO,    KC_##K49, KC_##K4A, KC_##K4B, KC_##K4C, KC_##K4D }  \
In the second block we have to identify empty spaces with the

Code: Select all

KC_NO
code. So while in the first block we just jump from one column to another if no physical connection is present, in the second block we need to fill the void with KC_NO.

Have a look at the matrix of the HHFox (you better copy the code below and paste into a text editor).

Code: Select all

#define KEYMAP( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, \
    K10,      K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E, \
    K20,      K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C,      K2E, \
    K30,           K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, K3E, \
         K41,      K43,                     K48,           K4B, K4C            \
) { \
    { 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_##K0D, KC_##K0E }, \
    { KC_##K10, KC_NO   , KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16, KC_##K17, KC_##K18, KC_##K19, KC_##K1A, KC_##K1B, KC_##K1C, KC_##K1D, KC_##K1E }, \
    { KC_##K20, KC_NO   , KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26, KC_##K27, KC_##K28, KC_##K29, KC_##K2A, KC_##K2B, KC_##K2C, KC_NO   , KC_##K2E }, \
    { KC_##K30, KC_NO   , KC_NO   , KC_##K33, KC_##K34, KC_##K35, KC_##K36, KC_##K37, KC_##K38, KC_##K39, KC_##K3A, KC_##K3B, KC_##K3C, KC_##K3D, KC_##K3E }, \
    { KC_NO   , KC_##K41, KC_NO   , KC_##K43, KC_NO   , KC_NO   , KC_NO   , KC_NO   , KC_##K48, KC_NO,    KC_NO   , KC_##K4B, KC_##K4C, KC_NO   , KC_NO    }  \
}
That exactly resembles the hand wired matrix. It's a bit boring to write up but nothing too complex. Just be careful at writing down the letters and numbers correctly.

You can safely ignore the KEYMAP_ANSI thing.

Keymap
And finally the fun part! By default the makefile is building the keymap for the Poker keyboard, so for the sake of simplicity we are going to modify that file. Open the keymap_poker.c.

Clean everything up and replace it with:

Code: Select all

#include "keymap_common.h"

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {

};

const uint16_t PROGMEM fn_actions[] = {

};
The first is the array of keymaps. The first element in the array will be our main qwerty (or whatever) keymap. You can copy/paste an already made keymap or start over. The base Poker keymap is as follow:

Code: Select all

    KEYMAP(ESC,  1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, GRV, BSLS, \
           TAB,  Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC,      \
           LCTL, A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,ENT,            \
           LSFT, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,RSFT,  FN0,          \
           LALT, FN1,           SPC,                     PAUS,RALT),
Please note that this is just the list of characters from left to right as you see them on the keyboard. You don't need to add spaces or special codes. Here we are adding some spaces and new lines just to make it more readable, but technically this could be all in one line.

Basically just read the characters on your keyboard from left to right, from top to bottom.

The keymap for the HHFox would look like so:

Code: Select all

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: qwerty */
    KEYMAP(ESC,  1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, GRV, BSLS, \
           TAB,  Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC,      \
           LCTL, A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,ENT,            \
           LSFT, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,RSFT,FN0,            \
           LALT, FN1,           SPC,                     PAUS,RALT),
};
If you want to be able to burn your firmware without accessing the teensy reset switch you better add the PAUS key, and it must be on the first layer (not an FN layer).

Function layers
Function keys are defined with the FNx syntax, where x is a number from 0 to 5.

To add a function layer we just add a new keymap to the array. The following is the first FN layer of the HHFox.

Code: Select all

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: qwerty */
    KEYMAP(ESC,  1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, GRV, BSLS, \
           TAB,  Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC,      \
           LCTL, A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,ENT,            \
           LSFT, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,RSFT,FN0,            \
           LALT, FN1,           SPC,                     PAUS,FN2),

    /* 1: FN 1 */
    KEYMAP(MUTE, F1,   F2,   F3,   F4,   F5,   F6,   F7,   F8,   F9,   F10,  F11,  F12,  VOLU, VOLD, \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, PGUP, UP,   TRNS, DEL,  \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, MYCM, TRNS, TRNS, PGDN, LEFT, RGHT, TRNS,  \
           TRNS, TRNS, TRNS, CALC, TRNS, TRNS, TRNS, TRNS, HOME, END,  DOWN, TRNS, TRNS, \
           LGUI, TRNS,             TRNS,                   PSCR, RCTL),
};
Same shit as above, the only difference is that we use the keyword TRNS (transparent) for all the keys that do not change in behavior when the function key is pressed.

So far we just defined the function layer, to actually activate it we need to add something in the fn_action array

Code: Select all

const uint16_t PROGMEM fn_actions[] = {
  [0] = ACTION_LAYER_MOMENTARY(1),
};
This means that the FN[0] key will trigger the layer (1) with a ACTION_LAYER_MOMENTARY action. You can define many actions, the most common/useful of which are:
  • ACTION_LAYER_MOMENTARY(layer), activate layer when holding down the FN key
  • ACTION_LAYER_TOGGLE(layer), activate on first press, deactivate on second press
  • ACTION_LAYER_TAP_KEY(layer, key), works like ACTION_LAYER_MOMENTARY when holding but executes the specified key on tap (quick press/release phase)
Let's add a tap action to the HHFox.

Code: Select all

const uint16_t PROGMEM fn_actions[] = {
  [0] = ACTION_LAYER_MOMENTARY(1),
  [1] = ACTION_LAYER_TAP_KEY(1, KC_ESC),
};
Holding the FN1 key we activate the first function layer (same as FN0), but by quickly tapping the same key we get the ESC key instead. Pretty cool, huh? Thank Hasu for that.

The following would be the final keymap for the HHFox (please note this is not my actual key config). I've taken the liberty of adding a second function layer that is toggled by pressing the FN2 key.

Code: Select all

#include "keymap_common.h"

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: qwerty */
    KEYMAP(ESC,  1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, GRV, BSLS, \
           TAB,  Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC,      \
           LCTL, A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,ENT,            \
           LSFT, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,RSFT,  FN0,          \
           LALT, FN1,           SPC,                     PAUS,RALT),

    /* 1: FN 1 */
    KEYMAP(MUTE, F1,   F2,   F3,   F4,   F5,   F6,   F7,   F8,   F9,   F10,  F11,  F12,  VOLU, VOLD, \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, PGUP, UP,   TRNS, DEL,  \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, MYCM, TRNS, TRNS, PGDN, LEFT, RGHT, TRNS,  \
           TRNS, TRNS, TRNS, CALC, TRNS, TRNS, TRNS, TRNS, HOME, END,  DOWN, TRNS, TRNS, \
           LGUI, TRNS,             TRNS,                   PSCR, RCTL),

    /* 2: FN 2 */
    KEYMAP(MUTE, F1,   F2,   F3,   F4,   F5,   F6,   F7,   F8,   F9,   F10,  F11,  F12,  VOLU, VOLD, \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, PGUP, UP,   TRNS, DEL,  \
           TRNS, TRNS, TRNS, TRNS, TRNS, TRNS, MYCM, TRNS, TRNS, PGDN, LEFT, RGHT, TRNS,  \
           TRNS, TRNS, TRNS, CALC, TRNS, TRNS, TRNS, TRNS, HOME, END,  DOWN, TRNS, TRNS, \
           LGUI, TRNS,             TRNS,                   PSCR, SLEP),
};
const uint16_t PROGMEM fn_actions[] = {
  [0] = ACTION_LAYER_MOMENTARY(1),
  [1] = ACTION_LAYER_TAP_KEY(1, KC_ESC),
  [2] = ACTION_LAYER_TOGGLE(2)
};
Oh the joy! We are done!

To the compile!
Exit. Open the terminal go to the gh60 directory and run:

Code: Select all

make -f Makefile
If you did everything fine you'll end up with a file called gh60_lufa.hex.

Connect your keyboard to the USB. On linux you need to do nothing, everything is already working. On Mac and Windows you'll probably have to follow a quick installation.

When done, run the teensy loader, activate the "Auto" button, load the firmware (the hex file) and press the reset button on the teensy. The firmware will be uploaded and the keyboard will start working right away!

Troubleshooting
Now one of the following scenarios might happen:

Everything's fine and your keyboard will be working right away
Cool! You're a keyboard master!

The keyboard starts firing random characters on screen and the computer starts beeping
Disconnect the keyboard, fast! This is most likely a software problem. Double check your code. When you are ready to re-flash, load the new firmware in the teensy loader, reconnect the keyboard and quickly press the reset button.

The keyboard works but some keys are faulty
This is likely a hardware problem. Check your wiring and double check for shorts.

I closed my keyboard and I don't have access to the hardware reset button
Assuming your keyboard is correctly working, software reset is it accomplished with LSHIFT + RSHIFT + PAUSE.

Everything seems to work but some mods return the wrong key
Disconnect the keyboard and reconnect holding down the BACKSPACE.

That's all for now. Thanks for watching. If you have any trouble don't hesitate to ask.
Last edited by matt3o on 08 Feb 2014, 09:16, edited 1 time in total.

pjmelon

05 Jan 2014, 03:40

Your timing is perfect Matt3o. I am just starting to look at the firmware for my keyboard and this will make my life a lot easier.

Greystoke

05 Jan 2014, 03:44

This is a huge help, thank you! I've been glumly staring at my Televideo project, trying to figure out how to fix it (half the keys work, the others return random characters...), this guide could not have come at a more apropos time!

Greystoke

22 Jan 2014, 16:12

So I could use some help. I've modified all the files listed above to reflect the layout for my hardwired Televideo keyboard (and appropriate controller for the Teensy++2.0), but when I got to use "make" it just says; "make: nothing to be done for "Makefile".

Any idea what I could check? Failing that, can someone compile the hex file for me, from the files I've modified?

User avatar
matt3o
-[°_°]-

22 Jan 2014, 16:20

what OS are you on?

Greystoke

22 Jan 2014, 16:38

matt3o wrote:what OS are you on?
Should have specified that! Both the machines I tried makefile on are running Windows 7-64 bit.

User avatar
matt3o
-[°_°]-

22 Jan 2014, 16:48

1) have you installed winavr? http://sourceforge.net/projects/winavr/ ... /20100110/
2) try just "make" without (-f Makefile)
3) send the zip I'll compile it for you

Greystoke

22 Jan 2014, 16:56

matt3o wrote:1) have you installed winavr? http://sourceforge.net/projects/winavr/ ... /20100110/
2) try just "make" without (-f Makefile)
3) send the zip I'll compile it for you
Yep, WinAVR is installed and in the machine path, and I've tried every variant of Make/makefile I could think of. It's frustrating, as I've created other hex files in the past, just can't figure out what I'm missing. :cry:

I'll send the zip file along, maybe there's an error in one of the source files. I'm just trying to create one layer, to get the keyboard functional, so I stripped out or left a number of sections blank.

Thanks!

User avatar
regack

08 Feb 2014, 01:58

Thank you, Matt3o, for putting this together. I muddled around with it before, but didn't quite understand what was going on, but you've explained it all very well, and I've got some good firmware now for my keypad.

User avatar
fifted

08 Feb 2014, 04:50

Amen; I hadn't seen this until regack bumped it. Great step-by-step guide, should be of service to 'doxers, too.

User avatar
clickclack123

08 Feb 2014, 15:39

Great tutorial matt3o.

jgrade

03 Mar 2014, 02:32

hi,

i just followed your steps and i found a problem. :oops:
i have built a ten keyless keyboard and the last row is not working. i double checked the connections and everything is connected the right way.
i have 6 col and 16 row (btw i have a portuguese layout)

this is a sample of my matrix.c

Code: Select all

/* Column pin configuration
    * col:  0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
    * pin: B0  B1  B2  B3  B7  D0  D1  D2  D3   C6  C7  D5  D4  D7  B4  B5  B6
 */

static void  init_cols(void)
{
    // Input with pull-up(DDR:0, PORT:1)
    DDRB  &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    PORTB |=  (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    DDRD  &= ~(1<<7 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    PORTD |=  (1<<7 | 1<<5 | 1<<4 | 1<<3 | 1<<2 | 1<<1 | 1<<0 );
    DDRC  &= ~(1<<7 | 1<<6 );
    PORTC |=  (1<<7 | 1<<6 );

}

static matrix_row_t read_cols(void) 
{
    return (PINB&(1<<0) ? 0 : (1<<0)) |
           (PINB&(1<<1) ? 0 : (1<<1)) |
           (PINB&(1<<2) ? 0 : (1<<2)) |
           (PINB&(1<<3) ? 0 : (1<<3)) |
           (PINB&(1<<7) ? 0 : (1<<4)) |
           (PIND&(1<<0) ? 0 : (1<<5)) |
           (PIND&(1<<1) ? 0 : (1<<6)) |
           (PIND&(1<<2) ? 0 : (1<<7)) |
           (PIND&(1<<3) ? 0 : (1<<8)) |     
           (PINC&(1<<6) ? 0 : (1<<9)) |
           (PINC&(1<<7) ? 0 : (1<<10)) |
           (PIND&(1<<5) ? 0 : (1<<11)) |
           (PIND&(1<<4) ? 0 : (1<<12)) |
           (PIND&(1<<7) ? 0 : (1<<13)) |
           (PINB&(1<<4) ? 0 : (1<<14)) |
           (PINB&(1<<5) ? 0 : (1<<15)) |
           (PINB&(1<<6) ? 0 : (1<<16));
}

/* Row pin configuration
 * row: 0   1   2   3   4   5
 * pin: F0  F1  F4  F5  F6  F7
 */
static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRF  &= ~0b11110011;
    PORTF &= ~0b11110011;
}

static void select_row(uint8_t row)
{
    // Output low(DDR:1, PORT:0) to select
    switch (row) {
        case 0:
            DDRF  |= (1<<0);
            PORTF &= ~(1<<0);
            break;
        case 1:
            DDRF  |= (1<<1);
            PORTF &= ~(1<<1);
            break;
        case 2:
            DDRF  |= (1<<4);
            PORTF &= ~(1<<4);
            break;
        case 3:
            DDRF  |= (1<<5);
            PORTF &= ~(1<<5);
            break;
        case 4:
            DDRF  |= (1<<6);
            PORTF &= ~(1<<6);
            break;
        case 5:
            DDRF  |= (1<<7);
            PORTF &= ~(1<<7);
            break;
    }
}
and here you have the keymap_common.h

Code: Select all

#define KEYMAP( \
    K00,      K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D,       K0E, K0F, K0G, \
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D,       K1E, K1F, K1G, \
    K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C,            K2E, K2F, K2G, \
    K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D,                     \
    K40, K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B,      K4D,            K4F,      \
    K50, K51, K52,                K56,                K5A, K5B,      K5D,       K5E, K5F, K5G \
) { \
    { KC_##K00, KC_NO,    KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06, KC_##K07, KC_##K08, KC_##K09, KC_##K0A, KC_##K0B, KC_##K0C, KC_##K0D, KC_##K0E, KC_##K0F, KC_##K0G }, \
    { 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_##K1D, KC_##K1E, KC_##K1F, KC_##K1G }, \
    { 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_NO,    KC_##K2E, KC_##K2F, KC_##K2G }, \
    { 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_##K3D, KC_NO,    KC_NO,    KC_NO }, \
    { 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_##K4D, KC_NO,    KC_##K4F, KC_NO }, \
    { KC_##K50, KC_##K51, KC_##K52, KC_NO,    KC_NO,    KC_NO,    KC_##K56, KC_NO,    KC_NO,    KC_NO,    KC_##K5A, KC_##K5B, KC_NO,    KC_##K5D, KC_##K5E, KC_##K5F, KC_##K5G } \
}
btw i've tried the Soarer_Controller_v1.20_beta4 and all is working but i think my code is fine :cry:

User avatar
hasu

04 Mar 2014, 02:00

It looks to me like you have 6*rows* and *17* *cols* :)
So not clear which keys don't work for you, but I guess K*G keys in keymap doesn't work.

You will be puzzled with C lang spec when number of column goes beyond 16. See this.
http://deskthority.net/workshop-f7/rebu ... ml#p146279

boogerlad

06 Apr 2014, 01:59

The issue with nkro on lufa seems to be resolved. https://github.com/tmk/tmk_keyboard/issues/47 Secondly, what if there's no led? Is it possible to use teensy's built in led on d6 or just do nothing?

bktlr

16 Apr 2014, 16:15

Me and a friend of mine have a problem with our keyboard, we're making a small one with only 28 keys (for counterstrike). The problem is not in the hardware or the soldering which we've double checked, it's somewhere in the code. The problem as is seems now is that we can't assign the keys to the buttons and get "random" results, except for three keys which we can change however we please.
Is there some kind of default key mapping it's getting the information from? (Some keys gives is responding with letters we don't have (i.e. 8, we don't want 8, we haven't written 8 anywhere in the program) which make me think we're collecting the information from some kind of "standard" mapping).
We are getting quite frustrated and don't know what to do. We also don't know where to find it in the code so we wont post everything, although we will show it if you think you know where the problem is.
Thanks for answering and sorry for the bad English.

User avatar
hasu

17 Apr 2014, 02:27

Hmm, infomation given is not enough, in particular when you don't know the cuase.
1) Post pics of your keyboard; all over appearance, upside and downside of internal(PCB or hand wiring) at least.(we never can see your *custom* keyboard without pics)
2) Show your schematic of circuit if possible(handwriting is no problem). Or your key matrix table at least.
3) Show all of your code used to build your firmware; set up your repository in github.com(preferable) or other service you like.
4) Describe your problem more deail. Can you describe procedure to get undesired '8'? Can you reporduce the problem? Or it seems to be completely "random" without rules?

EDIT: Your English is no problem if you can read my Engrish :D

bktlr

17 Apr 2014, 03:32

hasu wrote:Hmm, infomation given is not enough, in particular when you don't know the cuase.
1) Post pics of your keyboard; all over appearance, upside and downside of internal(PCB or hand wiring) at least.(we never can see your *custom* keyboard without pics)
2) Show your schematic of circuit if possible(handwriting is no problem). Or your key matrix table at least.
3) Show all of your code used to build your firmware; set up your repository in github.com(preferable) or other service you like.
4) Describe your problem more deail. Can you describe procedure to get undesired '8'? Can you reporduce the problem? Or it seems to be completely "random" without rules?

EDIT: Your English is no problem if you can read my Engrish :D
Here's some more information:
pictures: http://imgur.com/a/Wattc
layout: http://www.keyboard-layout-editor.com/# ... 5a0015ac55
code: (couldn't figure github out...): https://www.dropbox.com/s/4i88hq7h1ih2y ... d_code.zip

pinouts used:
* col: 0 1 2 3 4 5
* pin: D0 D5 D2 D3 C6 C7

* row: 0 1 2 3 4
* pin: D4 D7 B4 B5 B6

The problem:
Some keys(1, 5, E, F1) works.
Some other keys return the wrong character that exists on the layout(i.e R>B, T>D, S>ESC)
Some keys returns characters that the keyboard doesnt have at all(ESC>V, Caps>V, Shift>8)
Some keys doesn't do anything.

What the keys do doesn't change, they always do the same error. I'm starting to think the Teensy is faulty, I have a couple more of them so we might resolder onto a new one.

Thanks a lot for your awesome work guys, it would be impossible to complete this project without your help! :)

User avatar
hasu

17 Apr 2014, 05:14

According to diode direction of your keyboard columns and rows are reverse in software.
Just exchange those in hardware or software.(I guess you guys prefer software method :D)
I didn't find other faults than this, good work so far!
You may need to learn how matrix works and primer of AVR I/O operation.

Not far to complete your project. good luck!

bktlr

18 Apr 2014, 14:50

hasu wrote:According to diode direction of your keyboard columns and rows are reverse in software.
Just exchange those in hardware or software.(I guess you guys prefer software method :D)
I didn't find other faults than this, good work so far!
You may need to learn how matrix works and primer of AVR I/O operation.

Not far to complete your project. good luck!
I don't understand what you mean by that, I have wired it the same way as matt3o did on his brownfox. Could you try to explain a little bit more what the problem is and how I can fix it in the software? Thanks! :)

User avatar
hasu

19 Apr 2014, 00:14

Oops, my bad. Your diode direction is OK. Ignore my previous post.
Looks like it should work to me.

Post Reply

Return to “Workshop”