How to build your very own keyboard firmware

kapish

23 May 2014, 19:06

Hi, I've been following matt30 build and based on that I manage to build one myself. Thank you matt30 :lol:
I've followed his guide and everything seems to works fine except for caps lock LED. The LED positive side is connected to to 330ohm resistor and port B2 on teensy, while the negative sign is grounded to ground beside port B0.

I've change the code in hasu's led.c but without any luck with led, though caps lock is registering.

Code: Select all

void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK)) {
        // output low
        DDRB |= (1<<2);
        PORTB &= ~(1<<2);
    } else {
        // Hi-Z
        DDRB &= ~(1<<2);
        PORTB &= ~(1<<2);
    }
}
Anyone know what could be the mistake?

User avatar
hasu

23 May 2014, 23:51

If you connected LED anode to PB2 pin via resistor, you need to output high voltage to light up.

kapish

24 May 2014, 10:22

Found a solution for caps lock led, courtesy of Wraul from another forum. Incase someone else having same issue.

Code: Select all

void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK))
    {
        // Output high.
        DDRB |= (1<<2);
        PORTB |= (1<<2);
    }
    else
    {
        // Output low.
        DDRB &= ~(1<<2);
        PORTB &= ~(1<<2);
    }

}
Last edited by kapish on 27 May 2014, 22:28, edited 7 times in total.

smnanthny

24 May 2014, 20:05

I wonder if someone could take a look at this error for me. It's probably a silly mistake somewhere but I've looked for a while and can't spot it.

When compiling with avr-gcc I get..

keymap_poker.c:8:64: error: macro "KEYMAP" requires 40 arguments, but only 39 given
keymap_poker.c:5: error: 'KEYMAP' undeclared here (not in a function)
make: *** [obj_gh60_lufa/keyboard_poker.o] Error 1

Here are the files..

Config.h

Code: Select all

/*
Copyright 2012 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef CONFIG_H
#define CONFIG_H


/* USB Device descriptor parameter */
#define VENDOR_ID       0xFEED
#define PRODUCT_ID      0x6060
#define DEVICE_VER      0x0001
#define MANUFACTURER    smnanthny
#define PRODUCT         40board
#define DESCRIPTION     40% custom keyboard by Simon Anthony

/* key matrix size */
#define MATRIX_ROWS 4
#define MATRIX_COLS 12

/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST

/* Set 0 if debouncing isn't needed */
#define DEBOUNCE    5

/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
#define LOCKING_SUPPORT_ENABLE
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE

/* key combination for command */
#define IS_COMMAND() ( \
    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
)



/*
 * Feature disable options
 *  These options are also useful to firmware size reduction.
 */

/* disable debug print */
//#define NO_DEBUG

/* disable print */
//#define NO_PRINT

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
//#define NO_ACTION_MACRO
//#define NO_ACTION_FUNCTION

#endif
Matrix.c

Code: Select all

/*
Copyright 2012 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * scan matrix
 */
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <util/delay.h>
#include "print.h"
#include "debug.h"
#include "util.h"
#include "matrix.h"


#ifndef DEBOUNCE
#   define DEBOUNCE	5
#endif
static uint8_t debouncing = DEBOUNCE;

/* matrix state(1:on, 0:off) */
static matrix_row_t matrix[MATRIX_ROWS];
static matrix_row_t matrix_debouncing[MATRIX_ROWS];

static matrix_row_t read_cols(void);
static void init_cols(void);
static void unselect_rows(void);
static void select_row(uint8_t row);


inline
uint8_t matrix_rows(void)
{
    return MATRIX_ROWS;
}

inline
uint8_t matrix_cols(void)
{
    return MATRIX_COLS;
}

void matrix_init(void)
{
    // initialize row and col
    unselect_rows();
    init_cols();

    // initialize matrix state: all keys off
    for (uint8_t i=0; i < MATRIX_ROWS; i++) {
        matrix[i] = 0;
        matrix_debouncing[i] = 0;
    }
}

uint8_t matrix_scan(void)
{
    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
        select_row(i);
        _delay_us(30);  // without this wait read unstable value.
        matrix_row_t cols = read_cols();
        if (matrix_debouncing[i] != cols) {
            matrix_debouncing[i] = cols;
            if (debouncing) {
                debug("bounce!: "); debug_hex(debouncing); debug("\n");
            }
            debouncing = DEBOUNCE;
        }
        unselect_rows();
    }

    if (debouncing) {
        if (--debouncing) {
            _delay_ms(1);
        } else {
            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
                matrix[i] = matrix_debouncing[i];
            }
        }
    }

    return 1;
}

bool matrix_is_modified(void)
{
    if (debouncing) return false;
    return true;
}

inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
    return (matrix[row] & ((matrix_row_t)1<<col));
}

inline
matrix_row_t matrix_get_row(uint8_t row)
{
    return matrix[row];
}

void matrix_print(void)
{
    print("\nr/c 0123456789ABCDEF\n");
    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
        phex(row); print(": ");
        pbin_reverse16(matrix_get_row(row));
        print("\n");
    }
}

uint8_t matrix_key_count(void)
{
    uint8_t count = 0;
    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
        count += bitpop16(matrix[i]);
    }
    return count;
}

/* Column pin configuration
 * col: 0   1   2   3   4   5   6   7   8   9   10  11
 * pin: D0  D1  D2  D3  D4  D5  D6  D7  B4  B5  B6  B7  (Rev.A)
 */
static void  init_cols(void)
{
    // Input with pull-up(DDR:0, PORT:1)
    DDRD  &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7);
    PORTD |=  (1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7);
    DDRB  &= ~(1<<4 | 1<<5 | 1<<6 | 1<<7);
    PORTB |=  (1<<4 | 1<<5 | 1<<6 | 1<<7);
}

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

/* Row pin configuration
 * row: 0   1   2   3
 * pin: F0  F1  F2  F3
 */
static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRF  &= ~0b00001111;
    PORTF &= ~0b00001111;
}

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<<2);
            PORTF &= ~(1<<2);
            break;
        case 3:
            DDRF  |= (1<<3);
            PORTF &= ~(1<<3);
            break;
    }
}
Keymap_common.h

Code: Select all

/*
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEYMAP_COMMON_H
#define KEYMAP_COMMON_H

#include <stdint.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include "keycode.h"
#include "action.h"
#include "action_macro.h"
#include "report.h"
#include "host.h"
#include "print.h"
#include "debug.h"
#include "keymap.h"


extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
extern const uint16_t fn_actions[];


/* GH60 keymap definition macro
 * K2C, K31 and  K3C are extra keys for ISO
 */
#define KEYMAP( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, \
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, 	   K1B, \
    K20, K21, 	   K23, K24, K25, K26, K27, K28, K29, K2A, K2B, \
    K30, K31, K32,           K35,                	  K3A, K3B  \
) { \
    { 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_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16, KC_##K17, KC_##K18, KC_##K19, KC_NO, 	KC_##K1B }, \
    { KC_##K20, KC_##K21, KC_NO, 	KC_##K23, KC_##K24, KC_##K25, KC_##K26, KC_##K27, KC_##K28, KC_##K29, KC_##K2A, KC_##K2B }, \
    { KC_##K30, KC_##K31, KC_##K32, KC_NO,    KC_NO,    KC_NO, 	  KC_##K36, KC_NO,    KC_NO,    KC_NO, 	  KC_##K3A, KC_##K3B }  \
}

/* ANSI valiant. No extra keys for ISO */
#define KEYMAP_ANSI( \
    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,      K2D, \
    K30, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B,           K3D, \
    K40, K41, K42,           K45,                     K4A, K4B, K4C, K4D  \
) 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, NO,  K2D, \
    K30, NO,  K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, NO,  K3D, \
    K40, K41, K42,           K45,                NO,  K4A, K4B, K4C, K4D  \
)


#define KEYMAP_HHKB( \
    K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K49,\
    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,      K2D, \
    K30, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B,      K3D, K3C, \
    K40, K41, K42,           K45,                     K4A, K4B, K4C, K4D  \
) 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, NO,  K2D, \
    K30, NO,  K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, \
    K40, K41, K42,           K45,                K49, K4A, K4B, K4C, K4D  \
)

#endif
Keymap_poker.c

Code: Select all

#include "keymap_common.h"

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: qwerty */
	KEYMAP(ESC,  Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   BSPC,	\
		    TAB,  A,   S,   D,   F,   G,   H,   J,   K,   L,        ENT,	\
		    LSFT, Z,        X,   C,   V,   B,   N,   M,   COMM,DOT, FN1,	\
		    LCTL, LGUI,FN0         	     SPC,                RALT,RCTL),
};
const uint16_t PROGMEM fn_actions[] = {};
and finally this is the layout I'm using..

http://ne0.cc/laygenV2#KVwMo

User avatar
Laser
emacs -nw

24 May 2014, 20:10

I think you need a comma between FN0 and SPC, in your keymaps[]... assignation.
That's why the compiler sees 39 arguments instead of 40.

smnanthny

24 May 2014, 20:14

Oh god xD see I knew it was a simple mistake.. thank you friend. I've been staring at the screen too long :)

User avatar
Laser
emacs -nw

24 May 2014, 20:20

Two pairs of eyes are always better ;)

I was searching for a "geekandpoke" cartoon, but can't seem to find it - one developer was saying to his boss that he was doing "pair programming" alone: he looked at his code .. twice :)

elmin

07 Jun 2014, 08:15

Has anyone tried to use this with a KVM switch? My keyboard works great when plugged into my laptop, but with the KVM (a C2G two-port model) it doesn't seem to be recognized. I followed this tutorial to set it up.

User avatar
hasu

07 Jun 2014, 09:55

I have no experience with KVM siwtches but I can guess some functions may harm them.

Try getting rid of build opitons like NKRO, Mousekey, Extrakey and Console from Makefile. Note that **comment out** the options to remove, NOT giving "no" instead of "yes".

elmin

07 Jun 2014, 10:23

Good idea, I'll try that next time I bring the keyboard home.

User avatar
Eszett

17 Jun 2014, 04:36

Question #1: Anyone with experience building a firmware for Majestouch 2 TKL?

Question #2: What’s the keycode to get into bootloader mode? This is the most important keycode when messing around for the first time ...

User avatar
HzFaq

17 Jun 2014, 11:28

I'm running Hasu's TMK firmware on my Filco, I might be able to help.

To get to bootloader mode, it's magic key (default is both shifts) and Pause...It's in the TMK readme.

User avatar
Eszett

18 Jun 2014, 04:21

@HzFaq Thank you. There are tons of things I need to learn, and I’ll probably learn ’em by trial and error. But beforehand I have to make sure that I’ve got a fallback solution, that is, a working firmware as .hex file and to know how to get to the bootloader. Then, I can mess around with compiling my botch, and when it rains errors I can rely on my fallback.

Question: There are 6 rows on my Filco (obviously), why are there 7 rows mentioned in the schematics? (look in pink circle)
sdfsdf.jpg
sdfsdf.jpg (26.03 KiB) Viewed 9303 times

User avatar
HzFaq

18 Jun 2014, 10:39

I think that has something to do with how the Filco matrix is laid out...there's actually 8 rows (0-7), see the below from the wiki for the full matrix.

http://deskthority.net/wiki/Controller_ ... stouch_TKL

User avatar
Eszett

19 Jun 2014, 00:03

Alright, the Filco Majestouch 2 TKL matrix is 18 cols and 8 rows. Even though it seems illogical to me I have to accept the fact.

User avatar
scottc

20 Jun 2014, 02:23

Just went through this and made the firmware for my board in advance of assembling my prototype. All of the parts are waiting for me at home, and I'm really excited to get it together. These two guides have been so useful, thank you very much matt3o! Thanks to you, it took me just a couple of hours to get from a fresh git clone to customised firmware that compiles.

I suspect that real-life wiring might not be quite as simple as my sloppy mtpaint example, though :D

Image

But that will only need a small amount of adjustment in the matrix layout if turns out that it's not the optimal physical layout.

User avatar
MrMen

26 Jun 2014, 23:20

I just want to thank you ! Reading this post and you BrownFox, I have been able to make my «very own» keypad. Thank you for all this !

User avatar
matt3o
-[°_°]-

26 Jun 2014, 23:43

glad to help guys.

and beware of shorts :P

EnigmaSA

10 Jul 2014, 15:51

matt3o and Hasu,

Thank you so much for the guide, and for the firmware. I recently found an old Chicony KB-5191 that wasn't working (Fried controller I guess) and I wanted to try make a custom keyboard from it.

I don't really have access to a place that can cut a plate for me so I just used my dremel to get rid of the tracks between keys on the PCB and handwired my own matrix.

I didn't expect applying the firmware to the teensy to be so easy, but you guys made it a breeze. Any questions I may have had were answered in the comments already. (I was seriously struggling with having more than 16 columns!)

I'm typing this on my new keyboard right now and I just wanted to thank you two for making it possible. I'll try and post some pictures when I get the wood for my case cut nicely but as of now she's a bit naked.

-- Jean

User avatar
matt3o
-[°_°]-

10 Jul 2014, 15:57

there you go, another happy customer! :)

congrats Jean, send some photos!

EnigmaSA

12 Jul 2014, 17:14

http://imgur.com/R5jJnlx,o2nyo0D,GPzZLB ... ,lEGuTLU#0

There's a little album. I'm no carpenter but I'm really happy with how it came out and the kind of rugged look that it has.

User avatar
Muirium
µ

12 Jul 2014, 17:19

I like the old exposed electronics:

Image

Those staples in the frame are structural, right?

EnigmaSA

12 Jul 2014, 17:56

I wasn't sure whether to leave the old electronics but when I decided to go for a more rugged look I thought it would be better. Glad you like it!

The staples were originally there to hold it while the glue set, but I actually grew quite fond of how they looked. I'm pretty sure that I could take them out without any consequences, but a part of me doesn't want to risk it. My cuts weren't perfect and it took some effort to get the joints to fit nicely.

User avatar
Muirium
µ

12 Jul 2014, 18:04

Together with the original electrics, they have a nice little bit of the old Mad Max kind of post apocalyptic reuse thing going on. Although you'll have to mount weapons somewhere to really get that going.

EnigmaSA

12 Jul 2014, 18:09

I'm sure I could make a plan for that somehow, I think I have a whopping one spare pin on my Teensy. Bring on the explosions.

nisilhum

04 Oct 2014, 15:42

This is include dual role keys?

User avatar
elRetto

06 Oct 2014, 20:22

hey there,
when I try to compile I get this error message and I have no idea why.
Spoiler:
Image
is that my fault and if what can i do?

User avatar
scottc

06 Oct 2014, 20:28

It'd help if you could show the source of the files that you modified. Otherwise it's difficult to figure out what's going on.

User avatar
elRetto

06 Oct 2014, 20:30

this happens even when its completely unalterd, freshly unzipped

User avatar
scottc

06 Oct 2014, 21:40

I don't have any idea then, sorry! Maybe someone who has done this on Windows could help. I've only ever done it on Debian.

Post Reply

Return to “Workshop”