[WORKLOG] Acid Shine 1.0. Teclado mecánico desde 0

AcidNos

Bueno, pues estoy otra vez por aquí, despues de un tiempo sin dejaros algun trabajito, con otro mas. Esta vez se trata de un TeensyBoard, es decir, un teclado 100% custom realizado con un Teensy, 2.0++ en este caso, y los restos del Ducky Shine 3 que me murio de la noche a la mañana. Ahora mismo lo he bautizado como Acid Shine.

Comenzando el proyecto

Como bien sabeis algunos, hace tiempo que murio el controlador del Ducky y, a pesar de mis intentos por resucitarlo, no fui capaz de devolverlo a la vida. Asi pues, me arme de valor y me plantee devolverlo a la vida de la mejor forma posible, personalizandolo al 100%.

No me quedaba otra que desmontar un bien preciado y que tanto tiempo me costo conseguir. Lo primero fue desmontar los leds y los switches, arduo trabajo si no tienes una punta ancha con la que calentar las dos soldaduras de los leds. En mi caso la tenia, asi que no me costo demasiado, pero si el desmontar los switches. Como ya os he enseñado como desmontarlos en el hilo del taller, no me paro mucho aquí, solo un par de fotos del proceso:


Como tenia ganas de probar los Gateron Clear, vi aquí una oportunidad perfecta para ello, asi que me pedi en eBay un pack de 120 para montarlos en el proyecto. El coste de estos fue de unos 30€.


Una vez recibidos, lo primero de todo fue montar los switchs en el plate del Ducky. Decir que estos ajustaban a la perfeccion, asi que no tuve que hacer inventos para colocarlos ni quitar holguras. Junto con los switchs, deje ya montados los estabilizadores aprovechados del mismo Ducky, que eran Plate Mounted. En un vistazo rapido, quedaron asi:


Y despues de montar los switchs, le tocaba el turno a los leds. Decir que aquí, tenia que doblar las patillas de estos para impedir que se saliesen del sitio. Una vez dobladas las patillas, ajustaban a la perfeccion sin necesidad de usar ningun tipo de fijacion adicional.



Para la barra espaciadora, y para mantener la estetica original del Ducky, me las ingenie para incluir dos trozos de una placa virgen partida por la mitad. Aprovechando el hueco entre el switch de la espaciadora y el estabilizador de la misma. Esto me permitiria instalar dos leds adicionales para poder iluminar toda la barra sin problema.


Y antes de seguir, hice una prueba rapida para ver como quedaba. La prueba la hice con leds azules, puesto que no tenia los pink que queria usar y estaban en camino. Para probarlos use un par de cables soldados directamente y la fuente de alimentacion que me hice en su dia con una de PC (algunos habeis visto el worklog que publique en el foro en su dia). El resultado fue mas que satisfactorio.

Y esto fue todo respecto al montaje en el plate. Ahora pasamos a la parte importante.

Preparando la matriz y el cableado interno

Teniendo todo en su sitio, toca empezar a cablear. Para cablear los leds, elegi un tipo de cable que se usa normalmente en bobinados. Esto me permitiria poder tener el cable semidesnudo sin que haya posibilidad de contacto con otros cables, ya que este tipo de cable al, estar barnizado, solo es conductivo si se lija previamente. Al ser rigido, me permitiria tambien un cableado mas limpio, cosa que me iba a hacer falta teniendo en cuenta que iba a instalar leds.

El cable en cuestion es este:

Asi pues, solo quedaba ponerse manos a la obra. Este cable era un poco engorroso por el hecho de que habia que “pelarlo” para poder soldarlo, ya que en caso contrario, el barniz no permite hacer soldaduras en el cable.


Una prueba despues de cablear la primera fila de leds:

Y cableado completo de los leds y una prueba para ver que todo iba como debia:


Aqui me emocione y me aventure a colocar las keycaps para ver como se veria el teclado con la instalacion de los leds:

La idea original era aprovechar un regulador de 5V a 3,3V que me venia con el Teensy, pero tuve un pequeño problema de consumos con el y al final tuve que cambiar de idea, pero mas adelante os explicare el por que.

cableando la matriz

Paso ahora para la matriz. En primar lugar los diodos. Estos diodos son del tipo 1N4148.

Su funcion es evitar que haya corrientes de retorno y que haya pulsaciones erroneas en el teclado por la matriz del mismo. Ademas, aprovechando la largura de las patillas de estos, se pueden soldar de forma que unidos nos queden las filas de la matriz unidas. En algunas zonas es necesario el uso de “alargadores”, que en mi caso se trata de patillas cortadas previamente de los diodos como se ve en la union de la tecla ESC y el F1.

Si a alguien le interesa saber como funciona la matriz, aquí puede verlo perfectamente, pero en ingles:

http://pcbheaven.com/wikipages/How_Key_Matrices_Works/

Para impedir las corrientes de retorno, la instalacion de los diodos debe de ser con el positivo al switch y el negativo a la matriz (el negativo es la rallita negra que se ve en uno de los extremos del diodo). Asi que manos a la obra con los diodos. Aqui quise hacer una instalacion diferente a lo que se ve normalmente, pero al final tuve que optar por lo mismo que hay hecho por la construccion del teclado. Lo unico que debia tener en cuenta era el ahorrar el maximo espacio posible, asi que al final me decidi por soldarlos de esta manera:

Una vez soldados los diodos, le tocaba el turno a las columnas. Para las columnas compre una cinta de cable plano. Aunque para cablear los switches tuve que separarlos, luego este tipo de cable me permitiria hacer un “envio” limpio de los switches al alojamiento del Teensy como vereis mas adelante.

La cinta de cable que compre es esta:

Y una vez separados los cables, solo quedaba empezar uniendo las columnas.

Para ello no corte el cable, sino que lo pele de forma que pudiese tener el cable de una sola pieza para facilitarme la soldadura. En algunos momentos tuve que cortar el cable y soldar, pero fue lo de menos. Y asi con todos los cables hasta terminar las columnas:

Una vez hecha la matriz, tocaba dar alojamiento al Teensy. En mi caso, al tratarse del 2.0++, que es mas largo que el 2.0 por el hecho de tener mas pines, tuve que buscar un alojamiento fuera del plate. Para ello aproveche el mismo hueco que tenia la carcasa del Ducky. Esto me permitiria tambien tener el Teensy cerca del conector USB del propio Ducky y, con otra placa virgen perforada, hacer las conexiones del UBS y del regulador.

Una vez sabido el alojamiento del Teensy, de la placa del regulador y del conector USB, quedaba mandar las filas y las columnas a los pines del propio Teensy. Aqui es donde vereis como el la cinta de cable plano me permitio dejar una instalacion mas o menos limpia.

Este es el conector USB original del Ducky:

Y asi me quedo todo una vez soldado y en su sitio.

Tambien aproveche para hacer un agujero a la parte inferior de la carcasa y poner un pulsador. Este pulsador, tiene la misma funcion que el propio del Teensy, ponerlo en modo DFU, o lo que es lo mismo, ponerlo en modo programacion:

Y esto es lo que me permitia el conector USB original del Ducky, mantener la conexión original:

Y esto es todo, de momento, ya que tuve que hacer cambios posteriormente en la instalacion de la matriz y de los leds.

programando el Teensy 2.0 ++

Llegamos a la parte interesante del proyecto, la programacion. La eleccion fue facil, la mas versatil, y 100% modificable, el Hasu's TMK Firmware. Para aprender un poco sobre este firmware, me ayude de un post de Matt3o en DeskThority, en el que explica como hacer la programacion de un 60%, del wiki del TMK Keymap editor y de otro post de Geekhack referente tambien a este firmware.

Os dejo los links de las paginas que use para ello:

https://deskthority.net/workshop-f7/how-to-build-your-very-own-keyboard-firmware-t7177.html
http://www.tmk-kbd.com/tmk_keyboard/editor/index.html
https://geekhack.org/index.php?topic=41989.0

Para empezar vamos a necesitar varias cosas:

Lo primero de todo, y despues de haber instalado lo mencionado y con el Firmware en C:/ (recomiendo descomprimirlo aquí por un mejor acceso a posteriori desde la consola de comandos), debemos de tener bien claro como tenemos el cableado de nuestro teclado. Para ello me prepare una pequeña chuleta de los pines del Teensy y de la matriz del teclado:

Antes de seguir, comentar que esta imagen hay dos cosas a tener en cuenta. Me confundi a la hora de poner los pines de las columnas en el Teensy. Realmente la “Fila 1” empieza en el Pin PD0 y no en el PD1 como se ve en la foto. Y por otro lado, la numeracion de la matriz, en el codigo empieza por el 0, por lo que habra que restar 1 a cada posicion de la matriz.

Dicho esto comenzamos con la programacion.

Nos vamos a la carpeta donde hayamos descomprimido el firmware TMK. Una vez en dicha carpeta, vamos a “tmk_keyboard-master/keyboard/GH60”. Esto quiere decir que estamos dentro del firmware preparado para una placa GH60 ya preprogramado, que es el que vamos a usar para el teclado.

Una vez aquí, abrimos con el Notepad++ el fichero llamado “Makefile”, y buscamos los siguientes codigos:

MCU = atmega32u4
...
...
#NKRO_ENABLE = yes 

Aqui, donde pone MCU = atmega32u4, hay que poner el referente al chip del controlador que estemos usando. En este caso, al usar el Teensy 2.0 ++ , he de poner el at90usb1286, que es el que monta.

Despues buscamos en el mismo fichero “NKRO_ENABLE = yes” y lo comentamos poniendo una # delante, puesto que vamos a programar el Teensy con LUFA y no se soporta en este modo de programacion. Guardamos y cerramos.

Ahora nos vamos al fichero “config.h” y lo abrimos con el Notepad ++. Aqui tenemos que encontrar el codigo que va a definir nuestra matriz:

#define MATRIX_ROWS 6
#define MATRIX_COLS 21

Aqui ponemos cuantas filas y columnas tiene el teclado, que en este caso son 6 filas y 21 columnas y despues guardamos y cerramos.

Ahora hay que definir en los pines del Teensy las filas y las columnas. Aqui, despues de haber programado el teclado en los pines de la foto que puse antes, tuve ciertos problemas en 4 columnas, asi que al final la posicion de cada columna y fila dentro del Teensy tuve que modificarlos. Yo os recomiendo que os hagais una chuleta con la conexión de los mismos para no perderos y os la pongais dentro del fichero “matrix.c” en forma de comentario.

Dicho esto, una vez abierto el fichero “matrix.c” con el Notepad++, escribimos nuestros pines como en este ejemplo que no tiene por que ser igual en todos los casos:

// col: 0   1   2   3   4   5   6   7   8   9   10  11  12  13
// pin: F0  F1  E6  C7  C6  B6  D4  B1  B0  B5  B4  D7  D6  B3

Y lo mismo para las columnas:

// row: 0   1   2   3   4
// pin: D0  D1  D2  D3  D5

Recordad que esto es un simple ejemplo y puede variar según la configuracion del teclado.

Una vez hecho esto, dentro del mismo fichero, hay que buscar la funcion que inicia los pines del Teensy donde hemos conectado las columnas:

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);
} 

Bien, aunque parezca algo complicado, no lo es en absoluto. Este codigo lo que hace es decirle al Teensy, que pines van a funcionar en nuestro teclado. Como ejemplo, vemos que tenemos el codigo siguiente:

    DDRF  &= ~(1<<7);
    PORTF |=  (1<<7);

DDRF y PORTF son los registros de hardware que controlan los puertos F del Teensy. 1<<7 es el operador que asigna la posicion o el bit 8 (recordad que aquí las numeraciones empiezan desde el 0) de dicho puerto.

En castellano viene a decir que DDRF y PORTF asignan el puerto F del Teensy, y 1<<7 asigna el numero de dicho pin. En este caso el pin PF7. Esto hay que hacerlo para todos los pines que vayamos a usar en nuestro teclado.

En mi caso, como ejemplo, tengo los pines PC2, PC3, PC4, PC5, PC6 y PC7 ocupados, por lo que el codigo para dichos pines seria el siguiente:

DDRC  &= ~(1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7);
PORTC |=  (1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7);

Fijaos que en DDRC y PORTC, lo que ha cambiado es la letra C, que corresponde al pin que queremos usar y al que le asignamos los numeros de los pines dentro del mismo codigo usando el simbolo “|” para separarlos entre si. En resumen, en esas dos filas, hemos dicho que se inicien los pines PC2, PC3, PC4, CP5, PC6 y PC7.

Una vez teniendo todos los pines asignados, me queda algo como esto:

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

Ahora pasemos a definir las columnas de nuestra matriz. Buscamos dentro del mismo fichero la funcion que asigna a las columnas que viene a ser algo asi:

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));
} 

Bien, basicamente el funcionamiento es parecido al de la asignacion de las columnas, solo que con una pequeña variacion. Aqui tambien, debido a que estoy programando un teclado Full, el codigo hay que variarlo un poquito para que nos funcione a la perfeccion. Gracias al compañero Cortes, no tuve que estrujarme mucho la cabeza en esta parte.

El codigo para mi teclado seria el siguiente:

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

Si tomamos la primera linea:

(PINB&(1<<0) ? 0 : ((matrix_row_t) 1<<0)) |

reconocemos ya un codigo que nos resulta algo familiar. Esta linea quiere decir que, nuestra columna, que en tiene el pin PB0 en el Teensy esta asignada mediante el codigo“PINB0&(1<<0)”, y que corresponde a la primera columna de la matriz “matrix_row_t) 1<<0”, y asi sucesivamente.

Pues si, parecia algo mas complicado, pero aun no hemos acabado con el codigo. Siguiendo en el mismo fichero, nos queda definir las filas de nuestra matriz, asi que nos toca buscar este codigo:

static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRD  &= ~0b00111111;
    PORTD &= ~0b00111111;
}

Bien, es un poco dificil de explicar, pero intentare hacerlo lo mejor posible. Aqui lo que estamos queriendo decir, es donde estan nuestras filas en el Teensy, solo que esta vez de proma binaria. Mirando despues del simbolo “=”, tenemos lo siguiente:

0b quiere decir que nuestro codigo va a ir en binario.
Los siguientes 8 digitos, corresponden a las filas que vamos a tener o no activas, siendo 0 para las columnas no activas del puerto en cuestion y 1 para las columnas activas del puerto en cuestion, en este caso el puerto D (DDRD y PORTD).

Aqui la asignacion de los puertos va de derecha a izquierda, asi que si queremos iniciar los puertos 3 y 5 del pin B, por poner un ejemplo, el codigo resultante nos quedaria asi:

static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRB  &= ~0b00010100;
    PORTB &= ~0b00010100;
}

Facil verdad?? Pues escribimos lo correspondiente al teclado, que en mi caso queda asi:

static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRD  &= ~0b00111111;
    PORTD &= ~0b00111111;
}

Y por ultimo solo nos queda saber cuando estamos en una columna u otra, asi que buscamos el codigo correspondiente:

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;
    }
} 

Aqui vemos claramente que cada sentencia case, correspone a una fila, siendo para la primera fila (case 0) la asignacion del pin PF0, por lo que solo tendremos que adaptar el codigo a nuestro teclado y quedara algo asi:

static void select_row(uint8_t row)
{
    // Output low(DDR:1, PORT:0) to select
    switch (row) {
        case 0:
            DDRD  |= (1<<0);
            PORTD &= ~(1<<0);
            break;
        case 1:
            DDRD  |= (1<<1);
            PORTD &= ~(1<<1);
            break;
        case 2:
            DDRD  |= (1<<2);
            PORTD &= ~(1<<2);
            break;
        case 3:
            DDRD  |= (1<<3);
            PORTD &= ~(1<<3);
            break;
        case 4:
            DDRD  |= (1<<4);
            PORTD &= ~(1<<4);
            break;
		case 5:
            DDRD  |= (1<<5);
            PORTD &= ~(1<<5);
            break;
    }
}

Una vez terminado con esto, ya hemos definido la posicion de los pines de la matriz de nuestro teclado. Ya solo quedaria definir el mapa de teclado y la posicion de cada tecla dentro de la matriz (digo solo aunque esto tiene su trabajo :P)

Esta parte es un poco engorrosa y tediosa y se entiende un poco mejor viendo el codigo in situ, ya que aquí en MV, dada la anchura de los foros, puede dar lugar a confusiones. Aun asi tratare de explicarla lo mejor posible.

Siguiendo con la programacion, tenemos que hemos definido las filas y las columnas de nuestro teclado y hemos establecido los pines del Teensy. Ahora solo queda definir la matriz en funcion del layout que hemos elegido.

Para ello nos vamos al fichero “keymap_comon.h”. Recordad que seguimos en el mismo directorio que para el resto de ficheros que ya hemos programado. Una vez dentro de este fichero, vamos a buscar este codigo:

#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 }  \
} 

En este punto se trata de hacer lo siguiente. Como vereis, y si os fijais un poco, la disposicion del codigo tiene mas o menos la misma forma y posicion que las teclas de un teclado 60% un poco ordenado para diferenciar bien las filas y las columnas.

Aqui en el foro no se vera muy bien, pero si copiais y pegais en el Notepad++ vereis como se asemeja al layout del teclado.

Podeis pegar el codigo de mi teclado para verlo mucho mejor:

#define KEYMAP( \
    K00,      K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D,       K0E, K0F, K0G,     K0H, K0I, K0J, K0K,\
    K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D,       K1E, K1F, K1G,     K1H, K1I, K1J, K1K,\
    K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C,            K2E, K2F, K2G,     K2H, K2I, K2J,     \
    K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D,                          K3H, K3I, K3J, K3K,\
    K40, K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B, K4C,                 K4F,          K4H, K4I, K4J,     \
	K50, K51, K52,                K56,                K5A, K5B, K5C, K5D,       K5E, K5F, K5G,          K5I, K5J, K5K \
) { \
    { 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_##K0H, KC_##K0I, KC_##K0J, KC_##K0K }, \
    { 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_##K1H, KC_##K1I, KC_##K1J, KC_##K1K }, \
    { 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_##K2H, KC_##K2I, KC_##K2J, KC_NO    }, \
    { 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_##K3H, KC_##K3I, KC_##K3J, KC_##K3K }, \
    { 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_##K4C, KC_NO,             KC_NO,    KC_##K4F, KC_NO,            KC_##K4H, KC_##K4I, KC_##K4J, 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_##K5C, KC_##K5D,          KC_##K5E, KC_##K5F, KC_##K5G,         KC_NO,    KC_##K5I, KC_##K5J, KC_##K5K }  \
}

#endif

Ya se que el codigo parece un caos y que parece dificil de entender, pero creedme, es mas dificil explicarlo. Si os fijais en la primera fila, vamos a tener el mismo numero de teclas que en nuestra matriz. Se pueden añadir o quitar según se necesite, solo que siempre hay que poner el codigo tal cual y como lo tenemos en nuestra matriz.

Cogiendo el ejemplo de la primera fila del Acid Shine:

K00,      K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D,       K0E, K0F, K0G,     K0H, K0I, K0J, K0K,\

Vemos que directamen paso de K00 a k02. La razon es que en mi matriz, en la segunda columna, no existe k01. En su lugar hay un espacio entre el ESC y el F1. ¿Entendido hasta aquí? Pues bien, con este codigo, tenemos que tratar de “dibujar” nuestra matriz tal y como la hemos cableado.

Para que veais como queda el codigo de mi teclado en el Notepad++ os dejo esta imagen que seguro que os ayudara a entenderlo un poco mejor:

En mi caso he añadido la parte que corresponde a las teclas numericas y TK como se puede ver en la imagen. En la imagen, tambien se ve que el codigo tiene, aproximadamente, el mismo dibujo que la matriz del teclado.

La diferencia entre la parte superior del codigo y la parte inferior es muy simple. En la parte superior, los sitios donde no hay teclas de nuestra matriz, se quedan en blanco, y cada tecla la llamamos por un numero que vas desde “K00” hasta el numero “K0Z” según necesitemos.

En la parte inferior, tenemos que poner delante de la asignacion de cada tecla el codigo “KC_##”, y a su vez los espacios en los que no tengamos teclas en nuestra matriz, denominarlos como “KC_NO” .

Esto se ve mas claro en la ultima linea, donde esta ubicada la barra espaciadora. Si os fijais hay a cada lado del switch de la barra espaciadora, 3 huecos en blanco que en nuestra matriz no existen (se salta de la tecla K52 a la tecla K56). Tenemos que decirle al Teensy esto de alguna manera, y la manera de decirselo es simplemente la que ya os he comentado, con el codigo KC_NO.

Esto tiene que quedar exactamente tal y como tenemos nuestra matriz soldada (de ahi que sea necesaria la chuleta que imprimi al principio). Si por un casual no esta bien esta parte, podemos tener problemas de que haya teclas que no hagan bien su trabajo, o teclas que multiplican la pulsacion.

Una vez tenemos nuestra matriz metida en este fichero, guardamos y cerramos.

Ya estamos muy cerca de tener nuestro teclado terminado. Solo nos queda definir el “trabajo” de cada tecla dentro de lo que hemos programado antes. Para ello abrimos el fichero “keymap_poker.c”.

En este fichero, se programa el uso de cada tecla y de las capas del teclado, es decir, que es aquí donde tenemos todo nuestro poder y capadidad creativos para nuestro teclado :P.

Lo primero fijaros en mi codigo (una vez mas recomiendo abrirlo en el Notepad++):

#include "keymap_common.h"
const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: qwerty */
    KEYMAP(
        ESC,       F1,  F2,  F3,  F4,  F5,  F6,  F7,  F8,  F9, F10, F11, F12,      PSCR,SLCK,PAUS,          CALC, APP,MAIL, HOME,  \
        GRV,   1,   2,   3,   4,   5,   6,   7,   8,   9,   0,MINS, EQL,BSPC,      INS, HOME,PGUP,          NLCK,PSLS,PAST, PMNS,  \
        TAB,   Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,LBRC,RBRC,           DEL, END, PGDN,            P7,  P8,  P9,        \
       CAPS,   A,   S,   D,   F,   G,   H,   J,   K,   L,SCLN,QUOT,NUHS, ENT,                                 P4,  P5,  P6, PPLS,  \
       LSFT,NUBS,   Z,   X,   C,   V,   B,   N,   M,COMM, DOT,SLSH,     RSFT,            UP,                  P1,  P2,  P3,        \
       LCTL,LGUI,LALT,                SPC,               RALT, RGUI, FN1,RCTL,      LEFT,DOWN,RGHT,                 P0,PDOT, PENT)
};

Os suena algo de lo que veis?? Exacto, la misma matriz que previamente hemos metido a la hora de asignar las teclas, solo que esta vez estamos diciendo lo que tiene que hacer cada una.

Antes de nada, os dejo un link con los codigos necesarios para cada tecla del teclado:

https://github.com/tmk/tmk_keyboard/blob/master/tmk_core/doc/keymap.md

En este link podeis ver la designacion de cada tecla en su codigo. Para que me entendais os dejo unos ejemplos claros de como seria el codigo en nuestro fichero y la tecla a la que corresponderia:

KC_LCTL y KC_RCTL para Control izquierdo y derecho respectivamente.
KC_LGUI y KC_RGUI para teclas de Windows izquierda y derecha respectivamente.
KC_BSPC para el Backspace
KC_ENT para el Enter.

Como se puede ver, esto es bastante intuitivo, asi que no hay mucho mas que explicar. Simplemente tenemos que, siguiendo la matriz de nuestro teclado, asignar una funcion a cada tecla.

Y por ultimo queda la programacion de las capas de nuestro teclado. Sigue siendo igual de facil. Solo es necesario añadir un nuevo keymap (otro array hablando en codigo) al fichero en el que estamos trabajando.

Yo aun no he añadido ninguna capa porque estoy un poco vago. Voy a tomar como ejemplo el codigo original para el GH60 para poder explicaros todo al completo, pero es tan simple como parece:

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),
}; 

Recordad que por cada capa hay que añadir un nuevo keymap, pudiendo añadir solamente hasta un total de 5 capas por la programacion que estamos usando. El codigo TRNS que se ve en este caso, no quiere decir nada mas que esa tecla no tiene uso, es decir, que es transparente.

Ya solo nos queda definir como queremos que trabajen nuestras capas dentro del Array. Para ello deberemos añadir al final del fichero el codigo siguiente:

const uint16_t PROGMEM fn_actions[] = {
  [0] = ACTION_LAYER_MOMENTARY(1),
  [1] = ACTION_LAYER_TAP_KEY(1, KC_ESC),
}; 

Esto quiere decir que el FN[0], que es la tecla FN con la primera capa (FN1 en el teclado) esta ejecutando la funcion ACTION_LAYER_MOMENTARY, que en castellano significa, que la capa permanece activa mientras este pulsada la tecla FN.

Las funciones mas comunes para las capas son las siguiente:

  • ACTION_LAYER_MOMENTARY(layer), activa la capa cuando la tecla FN esta presionada-
  • ACTION_LAYER_TOGGLE(layer), Activa la capa cuando se presiona una vez, la desactiva cuando se presiona por seguna vez (como Caps Lock).
  • ACTION_LAYER_TAP_KEY(layer, key), trabaja igual que ACTION_LAYER_MOMENTARY cuando se mantiene pero ejecuta una tecla especifica al pulsarla (exactamente no se como trabaja esta funcion hasta que no la pruebe, asi que no puedo especificar mucho mas sobre ella).

Este seria un ejemplo de codigo terminado para un GH60 con dos capas en su programacion:

#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)
}; 

Y ya esta, guardamos el fichero y si todo ha ido bien, cosa rara en la primera vez, solo nos quedaria compilar el codigo y meterlo en el Teensy para poder disfrutar del teclado como dios manda.

Ya solo nos queda abrir Cygwin con el fichero Cygwin.bat que hay en el directorio donde se ha instalado. Se nos abrira una consola de comandos y, dentro de esta, tendremos que dirigirnos al directorio donde tenemos los ficheros del codigo de nuestro teclado.

Si os acordais, os recomende tenerlo todo en C:\ y la razon era esta precisamente. La forma de llegar hasta los directorios es la misma que en el terminal DOS de Windows.

Cd C:\tmk_keyboard-master\keyboard\gh60

Una vez aquí, solo tenemos que escribir lo siguiente:

make -f Makefile

Esperamos a que termine y si nos fijamos en el directorio de los ficheros del codigo, se nos habran creado unos cuantos archivos y entre ellos uno llamado “gh60_lufa.hex”. Este sera el fichero que tendremos que subir al Teensy.

Es el momento de abrir el Teensy loader. Al abrirlo tendremos que conectar el Teensy al ordenador y ponerlo en modo DFU, o lo que es lo mismo, en modo programacion. Esto se hace pulsando el boton del Teensy teniendolo enchufado al PC. El mismo programa nos dice como tenemos que hacerlo:

Al pulsar el boton, se pondra el Teensy Loader disponible para trabajar con el, es decir, reconocera el modo DFU de nuestro Teensy y la misma aplicación se pondra de la siguiente manera:

Ya solo nos queda buscar el fichero .hex que hemos creado con nuestro codigo y subirlo al Teensy. Por lo que vamos a File/Open HEX File y buscamos nuestro fichero .hex ubicado en el directorio GH60.

Una vez abierto, el Teensy Loader nos dejara pulsar los botones, los cuales antes no nos dejaba pulsar. Asi que le damos al boton que apunta hacia abajo para programar el Teensy con nuestro Firmware y cuando termine le damos al boton de la flecha derecha para reiniciarlo y salir del modo DFU.

Esto se hace en un momento. Asi que si todo ha salido bien, ya tenemos nuestro teclado listo para usar como si de un teclado profesional se tratase.

Si por un casual falla el “make”, ten por seguro que parte de tu codigo esta mal. Es muy importante si el compilador falla, repasar absolutamente todo el codigo. Un simple punto y coma, o un simple parentesis, puede arruinarte un codigo y volverte loco buscando fallos. Hay que respasarlo todo con lupa.

Tambien puede darse el caso, como me ocurrio a mi, que los pines del Teensy no trabajen como debieran y tengas que cambiar algunos pines del mismo (en mi caso me ocurrio con 4 pines que se conoce que compartian cierta informacion.

Y esto es todo. Parecia mas dificil de lo que realmente es, pero al final ya tengo mi propio teclado funcionando. Espero ver alguno mas despues de publicar esto en un idioma que ya nos es mas familiar.

Problemas y soluciones despues de programar

Problemas con el funcionamiento del teclado:

Aqui pueden surgir varias cuestiones. En mi caso tuve problemas con 5 columnas del teclado. 4 de ellas no enviaban ninguna señal. 1 de ellas me pulsaba todas las teclas de una columna y las teclas que estaban a su izquierda y derecha. Despues de repasar la matriz y ver que todo estaba perfectamente soldado, descubri que el problema era del Teensy. La solucion fue cambiar el cableado de las columnas que me fallaban a otro pin del Teensy y arreglar el codigo en consecuencia.

Problemas con los leds

Aqui el problema fue simple de solucionar. Si os acordais, en un principio, mi intencion era usar un regulador que me traia el Teensy consigo y que baja la tension de 5V a 3.3V, que es la que utilizan los leds. El problema fue que, al poco rato de uso, el regulador se calentaba demasiado debido al consumo excesivo que tenia que soportar ya que, si mal no creo, este regulador solo soporta consumos no superiores a 250mA.

La solucion fue quitar el regulador e instalar una resistencia a cada led, de forma que el consumo fuese acorde a la capacidad de corriente que deja pasar el Teensy. Ahora tengo los leds conectados directamente a un solo pin del Teensy y aguanta perfectamente sin calentarse.

Aqui podeis ver como hice la instalacion de las resistencias de 100Ohm y ¼W:


Y asi luce ahora mismo el teclado con la carcasa presentada en su sitio (sin cerrar del todo por si tengo que modificar algun problema a largo plazo)

Por el momento no me han surgido mas problemas y, a falta de crear las capas, decir que va el teclado a la perfeccion. Me encanta el tacto de los Gateron clear y la robustez con la que ha quedado todo instalado.

Agradecimientos

Y ya solo queda la parte mas facil, o mas dificil según se mire. Como muchos sabeis, me encanta la electronica, me encanta cacharrear y me encanta el hardware. Pero tambien es cierto que lo que mas anima a hacer estas cosas, es el apoyo, agradecimiento y por que no decirlo, los elogios de todos aquellos que ven tu trabajo y agradecen que compartas tus conocimientos que tanto tiempo y esfuerzo cuesta adquirir.

Pues para todas esas personas es este apartado, ya que gracias a esas personas, te vienes arriba y te propones metas. Te propones proyectos que igual en un principio te vienen grandes o que veas lejanos y que, cuando terminas, ves que los has conseguido gracias a un apoyo que otros te brindan.

Por supuesto gracias tambien a aquellos que han realizado su trabajo y han compartido sus conocimientos con el resto del mundo:

Matt3o de DH, por sus conocimientos y su ayuda desinteresada
Usuarios de GH y DH
Hasu por la realizacion del Firmware que hace posible que nuestros teclados funcionen y por resolver las dudas a usuarios de forma desinteresada.
Cortes de Media Vida por su ayuda cuando la he necesitado
A todos aquellos que han aportado su granito de arena sin saberlo
Y por supuesto y como no, gracias a MV y a su gran comunidad, sobre todo a la del foro de Hard & Soft, por dejarnos enseñar nuestros trabajos y por hacer de esta una comunidad cada vez mas grande.

Y no me enrollo mas. Esto es todo chicos. Aunque tarde, he cumplido con la promesa de enseñaros a programar vuestro propio teclado. Espero ver pronto mas proyectos por esta comunidad y hacer que la gente se anime a coger el estañador, que como siempre digo, es mas facil de lo que parece :)

14
hda

Vaya curro de hilo. Me ha gustado. ¡Y vaya trabajo!

Gracias por compartir :D

1 respuesta
Doest

Menudo trabajazo te has pegado :) . A la espera estoy para ver como terminas el 40% de este proyecto que te has propuesto :muac:

2 respuestas
AcidNos

#2 #3 Gracias!!!!

#3 El proyecto esta terminado ya :P, solo que llevaba escrito un buen cacho y se me estaba haciendo un poco pesado ya. Mañana seguramente termine con el resto :)

2 1 respuesta
P

Increible curro, a favoritos

B

Grandísimo trabajo, que makina

M3nDi

#4

Cuando dices que los montas en serie? xd

1 respuesta
AcidNos

Gracias a todos!!!!

#7 Echale un ojo a esto :) http://www.mediavida.com/foro/hard-soft/encuesta-posible-gb-teclados-mecanicos-60-561779

1 respuesta
M3nDi

#8

Lo vi, pero eso lo veo mas como teclado portatil, a mi me interesaba uno full, con ñ, asi que tendre que acabar comprandome un k70, xq el model m que tengo me marea el sonido al pulsar ( aun que es muy comodo )

AcidNos

Editado #1 con toda la programacion necesaria para el teclado. A disfrutarlo!!!!

#3 ya lo tienes todo :)

1
mansaia

#1 muy buen trabajo acidnos. Has vuelto a darle vida a tu amado teclado blanco

AcidNos

Cambios a vista. Conecte los leds directamente al pin de 5V del Teensy por probar que todo funcionase perfectamente, aunque creo que no lo comente en #1. El Caps Lock, lo deje conectado por separado pero no conseguia hacerlo funcionar aparte, por lo que me he tenido que poner a solucionar esto y ahora estoy ojeando como jugar con la activacion y desactivacion de los leds y como controlar la intensidad de los mismos. Buscando info sobre el backlighting he dado con algo que desconocia y que era la causa de que mi Caps Lock no funcionase en condiciones que deberemos modificar dentro del fichero "led.c" antes de compilar.

En los teclados con leds, sabeis que hay dos formas de conectar estos:

  • PIN Teensy -> LED -> Resistencia -> USB GND

O bien:

  • PIN Teensy -> LED -> Resistencia -> USB 5V

Esto quiere decir que de la primera forma controlamos el led mediante positivo y de la segunda mediante negativo. Teniendo en cuenta esto, el codigo en segun que situacion, varia. Por ejemplo, imaginando que el Caps Lock esta en el pin B6 del Teensy, tenemos dos opciones.

Control mediante positivo:

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

Contro mediante negativo:

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

Si os fijais, en la linea numero 5, el codigo varia brevemente entre "|= " y "&= ". Esto quiere decir que segun tengamos conectados los leds habra que escribir el codigo de una manera u otra. Esto le "dice" al Teensy la forma en la que tenemos conectados los leds, enviando la señal de forma negativa o positiva segun nos convenga.

Ahora estoy investigando como jugar con el backlighting del teclado. En cuanto descubra como hacerlo, lo posteo por aqui.

txepox

No había visto esto!

Enhorabuena. Menudo curro!

Usuarios habituales