Rebuilding and Redesigning a Classic Thinkpad Keyboard

User avatar
bearcat

06 Nov 2013, 21:41

Wow, what a great thread -- the prototypes look great!

If i can make some suggestions, you might get a much better deal on the case if you find a way to do it that doesn't involve many acrylic layers. There's a lot of wasted material & excess cutting time, plus some other reasons that GH didn't much care for :roll: But seriously, if you're going to make a beautiful case and get it machined, see about getting it machined from a single piece of alu, rather than machining a bunch of layers.

The other suggestion i have is that matrix vs. stagger vs. symmetric stagger; I found no problems going back and forth between my matrix board and a traditional stagger e.g. on a laptop. It was almost like a separate muscle memory. I don't know if i could go between all three easily, though! ;) I think you'll quickly find that the "p" key (on a traditional qwerty layout) and the period & /? key will be the ones that take the most getting used to on a matrix or symmetric stagger board.

Looks good and can't wait to see what you come out with!

pjmelon

21 Nov 2013, 03:48

Well about to place the order. Got a price from my local machine shop and it was similar (but a little less) to Big Blue Saw. So I am tempted to go with Big Blue Saw. Should I go with Big Blue Saw or go with the local choice?

If I go with Big Blue Saw, they charge a fortune for finishing the product (deburring, etc). Is it worth getting or is it something I can do myself?

pjmelon

16 Dec 2013, 04:15

Well I finally got the keyboard plates.
Everything lines up correctly which is great. Impressed with the quality of the work. First thing is to lightly polish the aluminium and get it ready for the switches. Will start work over the Christmas break.
Attachments
2013-12-15 19.23.17.jpg
2013-12-15 19.23.17.jpg (896.83 KiB) Viewed 10345 times
2013-12-15 19.23.10.jpg
2013-12-15 19.23.10.jpg (949.73 KiB) Viewed 10345 times

User avatar
ماء

16 Dec 2013, 05:40

Really nice,can't wait :P i will make symmetric too :?

pjmelon

17 Dec 2013, 04:51

I am thinking of doing a brushed aluminum look and then clear coat it with wheel clear coat.

User avatar
ماء

17 Dec 2013, 07:29

pjmelon wrote:I am thinking of doing a brushed aluminum look and then clear coat it with wheel clear coat.
I suggest painted also,I'm just scared easily scratched :P but up to you :D

pjmelon

18 Dec 2013, 05:20

Toying with the idea of using a gun coating or even powder coating but wary of how durable the coating is.
I think it may be because I am too hesitant to remove the scratches.
May just bite the bullet and try my technique out on a less visible side first.

pjmelon

22 Dec 2013, 05:29

Ok, I have decided to go with a gun matte black finish.

pjmelon

29 Dec 2013, 04:03

Changed my mind and will skip the gun matte paint.

I will use a separate teensy for the mouse and keyboard as my palm rest has a lot of room.
Is 18 columns and 5 rows too many for my teensy 2?

mtl

29 Dec 2013, 05:21

pjmelon wrote:I will use a separate teensy for the mouse and keyboard as my palm rest has a lot of room.
Is 18 columns and 5 rows too many for my teensy 2?
There's enough room, but barely. You need 23 I/O pins and the Teensy 2 has 25. Of those, one may not be usable (D6) because the Teensy's on-board LED is connected to it. There may be a way to use that pin if needed, though. Anyway, you may also want the Teensy to drive indicator LEDs for caps lock, etc., which would require 1 pin for each LED. So with an 18x5 matrix, you can drive at most two LEDs (e.g., caps- and num- lock), unless you drive an external LED controller.

pjmelon

21 Jan 2014, 05:50

Well I am cranking along with this one. The only hurdle I have hit is with mouse buttons I salvaged from an old thinkpad. Having a lot of trouble getting reliable button presses. I tried attaching the wire using copper tape and now switched to epoxy and will say how that goes.

pjmelon

27 Jan 2014, 04:03

Conductive epoxy is the way to go. It is a little brittle but I managed to get a good connection and then used a hot glue gun to make it stick in place.

Got all the switches wired up. Next step is to join the two halves together and hook it up to the teensy. Super excited cause the end is nigh.

Hopefully the firmware doesnt give me grief.

User avatar
scottc

27 Jan 2014, 04:09

pjmelon wrote:Well I am cranking along with this one. The only hurdle I have hit is with mouse buttons I salvaged from an old thinkpad. Having a lot of trouble getting reliable button presses. I tried attaching the wire using copper tape and now switched to epoxy and will say how that goes.
For what it's worth, I believe that hasu's TMK firmware supports mouse presses too. So if the Thinkpad buttons prove too irritating, you could always have Cherry MX trackpoint buttons. ;)

By the way, this is a very cool project. Please post more pictures when it's done! :mrgreen:

User avatar
ماء

27 Jan 2014, 04:26

btw pjmelon, your id in GH driekus? :P

mtl

27 Jan 2014, 05:39

Sounds like it's coming together pretty good, pjmelon! If you're using the TMK firmware, I don't think it'll give you issues TrackPoint-wise. It works pretty well after you get config.h set up and get it to build. Not sure how it handles the split halves, but I imagine you have something figured out for that. :)

BTW, if you want additional features, I've put a fair amount of effort into extending the TP support in TMK. For instance, configuring the TP sensitivity, press-to-select, hardware diagnostics, and saving/loading the configuration into EEPROM are all pretty straightforward. The fork is here if you are interested.

pjmelon

27 Jan 2014, 15:09

ماء wrote:btw pjmelon, your id in GH driekus? :P
Yup the one and the same. :)

pjmelon

27 Jan 2014, 15:10

mtl wrote:Sounds like it's coming together pretty good, pjmelon! If you're using the TMK firmware, I don't think it'll give you issues TrackPoint-wise. It works pretty well after you get config.h set up and get it to build. Not sure how it handles the split halves, but I imagine you have something figured out for that. :)

BTW, if you want additional features, I've put a fair amount of effort into extending the TP support in TMK. For instance, configuring the TP sensitivity, press-to-select, hardware diagnostics, and saving/loading the configuration into EEPROM are all pretty straightforward. The fork is here if you are interested.
Thanks mtl, I will definitely check out your fork. Initially was looking to get functional firmware and I am at that stage now. Next to look at full features including sensitivity adjustment.

pjmelon

03 Feb 2014, 04:49

Well the end is nigh. Spent the last two days working solid on it. The trackpoint is wired and works well although will play around with mtl's fork when I get some time. Wired in the keyboard and working on firmware now. Got most of it figured out with only a few errors in my make. Ill sleep on it and recheck my keymaps as that is where the error is.
Attachments
2014-02-02 16.34.47.jpg
2014-02-02 16.34.47.jpg (96.53 KiB) Viewed 10094 times
2014-02-02 16.09.24.jpg
2014-02-02 16.09.24.jpg (92.47 KiB) Viewed 10094 times

pjmelon

04 Feb 2014, 02:18

Thought I would post here rather than hijack matt3o's thread.
Used the TMK gh60 guide to put my firmware together.
When I make the firmware I get the following error:

matrix.c: In function 'read_cols':
matrix.c:172: warning: left shift count >= width of type
matrix.c:173: warning: left shift count >= width of type
matrix.c:176: error: expected expression before ';' token
make: *** [obj_gh60_lufa/matrix.o] Error 1

This refers to the following lines in my matrix.c

Code: Select all

		   (PIND&(1<<1) ? 0 : (1<<15))|
		   (PIND&(1<<0) ? 0 : (1<<16))|
		   (PINB&(1<<7) ? 0 : (1<<17))|
I think that I am missing a reference to how many columns there are in the keyboard.
Here is 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  17
 * pin: D7  B4  B5  B6  F7  F6  F5  F4  F1  F0  D4  D5  C7  C6  D3  D1  D0  B7 
 */
static void  init_cols(void)
{
    // Input with pull-up(DDR:0, PORT:1)
    DDRF  &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
    PORTF |=  (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
    DDRD  &= ~(1<<7 | 1<<5 | 1<<4 | 1<<3 | 1<<1 | 1<<0);
    PORTD |=  (1<<7 | 1<<5 | 1<<4 | 1<<3 | 1<<1 | 1<<0);
    DDRC  &= ~(1<<7 | 1<<6);
    PORTC |=  (1<<7 | 1<<6);
    DDRB  &= ~(1<<7 | 1<<6 | 1<< 5 | 1<<4);
    PORTB |=  (1<<7 | 1<<6 | 1<< 5 | 1<<4);
}

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

/* Row pin configuration
 * row: 0   1   2   3   4
 * pin: E6  B3  B1  B2  B0
 */
static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRE  &= ~0b00000010;
    PORTE &= ~0b00000010;
	DDRD  &= ~0b11110000;
    PORTD &= ~0b11110000;
}

static void select_row(uint8_t row)
{
    // Output low(DDR:1, PORT:0) to select
    switch (row) {
        case 0:
            DDRE  |= (1<<6);
            PORTE &= ~(1<<6);
            break;
        case 1:
            DDRB  |= (1<<3);
            PORTB &= ~(1<<3);
            break;
        case 2:
            DDRB  |= (1<<1);
            PORTB &= ~(1<<1);
            break;
        case 3:
            DDRB  |= (1<<2);
            PORTB &= ~(1<<2);
            break;
        case 4:
            DDRB  |= (1<<0);
            PORTB &= ~(1<<0);
            break;
    }
}
Config.h

Code: Select all

#ifndef CONFIG_H
#define CONFIG_H


/* USB Device descriptor parameter */
#define VENDOR_ID       0xFEED
#define PRODUCT_ID      0x6060
#define DEVICE_VER      0x0001
#define MANUFACTURER    geekhack
#define PRODUCT         GH60
#define DESCRIPTION     t.m.k. keyboard firmware for GH60

/* key matrix size */
#define MATRIX_ROWS 5
#define MATRIX_COLS 18

/* 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
And finally keymap_poker.c

Code: Select all

#include "keymap_common.h"

const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
KEYMAP(
        GRV, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,  LBRC,RBRC, DEL, BSPC, HOME, PGUP, \
        ESC, TAB, QUOT,COMM,DOT, P,   Y,   F,   G,   C,   R,   L,   SLSH,EQL, BSLS, END, DEL, PGDN, \
        CAPS,A,   O,   E,   U,   I,   D,   H,   T,   N,   S,   MINS,     ENT,  \
        LSFT,SCLN,Q,   J,   K,   X,   B,   M,   W,   V,   Z,             RSFT, UP, \
        LCTL,LGUI,LALT,          SPC, SPC,                     RALT, RGUI, RCTL, LEFT, DOWN, RIGHT),

};

const uint16_t PROGMEM fn_actions[] = {

};
Any help would be appreciated

User avatar
hasu

04 Feb 2014, 02:40

Your column size is 18 beyond int size(16bit) and 'matrix_row_t' is defined as uint32_t(unsigned long) in actual fact in matrix.h.
That error message says you can't rotate int value with 16 or 17 because it is beyond int size. Note that you wirte '1' in C it means 1 of int value, namely 16bit in this case. So you must use long or unsigned long value here. You can learn C number literals quickly here. http://c.comsci.us/etymology/literals.html

TLDR; Use 1L or 1UL. :)

EDIT:
Metal case looks nice!

pjmelon

04 Feb 2014, 03:40

Thanks hasu, I am trying to grasp what you are saying but my brain is just not there yet.
So I would replace matrix.c column 16 and 17 with 1UL and 2UL?
Sorry this is my first voyage into anything coding related and have had to teach myself (with help here) everything I know so far.

mtl

04 Feb 2014, 03:48

You need to cast the intermediate bit-shift expression to a type larger than int. I think hasu's example does that, and I think what he is saying is that you'd want to write 16L instead of 16, and similarly 17L instead of 17. Here's an example from my read_cols() that does it another way, which maybe you can adapt:

Code: Select all

static matrix_row_t read_cols(void)
{
    matrix_row_t cols = 0;

    uint8_t pin_a = PINA;
    uint8_t pin_c = PINC;
    uint8_t pin_d = PIND;
    uint8_t pin_e = PINE;
    uint8_t pin_f = PINF;

    if (!( pin_c & (1<<3) )) cols |= ((matrix_row_t) 1<<0);
    if (!( pin_c & (1<<2) )) cols |= ((matrix_row_t) 1<<1);
    if (!( pin_c & (1<<1) )) cols |= ((matrix_row_t) 1<<2);
    if (!( pin_c & (1<<0) )) cols |= ((matrix_row_t) 1<<3);
    if (!( pin_f & (1<<6) )) cols |= ((matrix_row_t) 1<<4);
    if (!( pin_f & (1<<5) )) cols |= ((matrix_row_t) 1<<5);
    if (!( pin_f & (1<<4) )) cols |= ((matrix_row_t) 1<<6);
    if (!( pin_f & (1<<3) )) cols |= ((matrix_row_t) 1<<7);
    if (!( pin_f & (1<<2) )) cols |= ((matrix_row_t) 1<<8);
    if (!( pin_f & (1<<1) )) cols |= ((matrix_row_t) 1<<9);
    if (!( pin_e & (1<<1) )) cols |= ((matrix_row_t) 1<<10);
    if (!( pin_e & (1<<0) )) cols |= ((matrix_row_t) 1<<11);
    if (!( pin_d & (1<<7) )) cols |= ((matrix_row_t) 1<<12);
    if (!( pin_e & (1<<7) )) cols |= ((matrix_row_t) 1<<13);
    if (!( pin_f & (1<<0) )) cols |= ((matrix_row_t) 1<<14);
    if (!( pin_a & (1<<5) )) cols |= ((matrix_row_t) 1<<15);
    if (!( pin_d & (1<<4) )) cols |= ((matrix_row_t) 1<<16);
    if (!( pin_d & (1<<3) )) cols |= ((matrix_row_t) 1<<17);
    if (!( pin_e & (1<<6) )) cols |= ((matrix_row_t) 1<<18);

    return cols;
}
So, without being able to test it on my end, maybe this would work for you?

Code: Select all

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

User avatar
hasu

04 Feb 2014, 04:08

Yes, I think mtl's code works.

One solution is mtl's.
(matrix_row_t)1<<16
this is equivalent to
(uint32_t)1<<16

Another is
1UL<<16
This is what I mentionted. I didn't compile and test this code, though, I think this also works.

pjmelon

04 Feb 2014, 04:27

Excellent, got it to make using these instructions.
Plugged the keyboard in have a problem that every time I hit a button all of the keys in that column are typed. It seems consistent on every single row.

pjmelon

04 Feb 2014, 04:45

Actually the more I look at it the more random it feels.

User avatar
ماء

04 Feb 2014, 08:31

pjmelon wrote:Actually the more I look at it the more random it feels.
pjmelon,how to feel you type on DSA than DCS on staggering :)
though i think DSA would good on Columnar sgg

BTW:Hasu uses ava,i never see before :P

pjmelon

04 Feb 2014, 17:15

The random feel that I was referring to was the output of the keys when I press the buttons. When I first got the keyboard made I put all the switches and keys on and found it quite nice to type on.

I am going to check tonight for short circuits to make sure that I have everything wired correctly. I think my problem may be in the code as well so will have another look tonight at that as well. Always frustrating trying to troubleshoot. Given that it has taken 6 months to get this far I am a little impatient and excited to finish up the project.

mtl

04 Feb 2014, 19:12

Good luck! Getting the matrix working is a big milestone and is when a hunk of metal and wires and plastic bits first becomes a keyboard. :-)

User avatar
hasu

04 Feb 2014, 20:37

Looks like unselect_rows is not correct or inconsistent with comment. Check again.
Are you using Teensy? If not you will need a trick to use Port F.

mtl

04 Feb 2014, 20:53

Good catch, hasu. The unselect_rows() comment is consistent with the implementation of select_row(), but the unselect_rows() code is inconsistent with both.

I think this is the Port F trick hasu is referring to (goes in matrix_init()):

Code: Select all

    // To use PORTF disable JTAG with writing JTD bit twice within four cycles.
    MCUCR |= (1<<JTD);
    MCUCR |= (1<<JTD);

Post Reply

Return to “Workshop”