[Tradução/adaptação] Como fazer o firmware para seu teclado – parte 1

By | 18/11/2017

Autor: matt3o
Artigo original: https://deskthority.net/viewtopic.php?f=7&t=7177

Este é um tutorial sobre fazer o seu próprio firmware personalizado com o firmware para teclado do Hasu.

Pode parecer assustador trabalhar na montanha de código do Hasu mas são poucos os arquivos que você precisa modificar e você não precisa entender completamente o código. Eu mesmo não entendo 100% dele, então se eu escrever alguma besteira espero que o Hasu me corrija.

Ambiente de desenvolvimento

Antes de começar você precisa preparar o seu ambiente de desenvolvimento. Isso depende do seu sistema.

Infelizmente o Windows não é o melhor ambiente de desenvolvimento para este tipo de coisa. Minha sugestão seria instalar uma distribuição Linux mínima numa máquina virtual (o VirtualBox é gratuito) e seguir as instruções para o Linux abaixo. Muitos dos problemas que as pessoas têm devem-se ao Windows. Se estiver mesmo disposto a insistir no Windows, acredito que a melhor opção seria instalar o Atmel Studio. Não sou de muita ajuda, já que nunca usei esta ferramenta, mas ela deve conter todo o necessário para compilar seu firmware.

[Nota do Tradutor: este tutorial foi escrito originalmente em janeiro de 2014. Em julho de 2015 foi lançado o Windows 10; em seu update de aniversário, divulgado em agosto de 2016, foi introduzido o Subsistema Linux para Windows 10. Com isso, basta seguir as instruções referentes ao Linux para compilar o seu firmware. Instruções sobre como instalar o Subsistema Linux neste link (apenas em inglês). Outro link útil é este, sobre como usar portas seriais dentro do Subsistema Linux. Isso será necessário para fazer o upload do firmware para o microcontrolador de seu teclado através da porta USB.]

No Mac pegue o CrossPack e instale o XCode da App Store.

No Linux você precisa instalar as ferramentas de desenvolvimento (por exemplo, no Ubuntu o pacote deve chamar-se “build-essential“) e as ferramentas de desenvolvimento AVR. Ressalto que o nome exato depende da distribuição, procure por AVR no seu gerenciador de pacotes e você deve encontrar tudo o que precisa (no Ubuntu os pacotes são “gcc-avr gdb-avr binutils-avr avr-libc avrdude“, instale usando o Synaptic ou através da linha de comando sudo apt-get install gcc-avr gdb-avr binutils-avr avr-libc avrdude).

Para transferir o firmware para o microcontrolador você precisa de um programador. Você pode usar o FLIP, da Atmel (o link é da Microchip porque esta adquiriu aquela em 2016), ou o dfu-programmer, mas eu acho mais fácil usar o Teensy Loader.

Se você não tem como fazer o firmware por si mesmo não se preocupe, você ainda pode fazer as modificações necessárias no código e eu aposto que alguém no fórum pode te mandar o arquivo HEX que você gravará no teclado (ainda assim você instalar um dos programadores listados acima).

Pegue o código

Faça o download do código do Hasu e descompacte-o: https://github.com/tmk/tmk_keyboard/archive/master.zip

Felizmente você pode ignorar a maior parte do código. Vá para o diretório keyboard/gh60. Nós não sairemos deste diretório, eu falei que era fácil.

Lembre-se de conferir o repositório do Hasu de vez em quando, ele é bem ativo e volta e meia recebe atualizações.

Configuração principal

Abra o arquivo Makefile e verifique as seguintes variáveis globais:

TARGET = gh60_lufa
...
...
MCU = atmega32u4
...
...
#NKRO_ENABLE = yes

Se você estiver usando um Teensy++ 2.0 modifique o valor da linha MCU para at90usb1287. Também comente (coloque um # no início da linha) a função NKRO_ENABLE. Você não precisa dela, e ela não é suportada pelo LUFA. Falarei mais sobre isso depois.

Agora abra o arquivo config.h.

Você pode colocar valores personalizados para MANUFACTURER (fabricante), PRODUCT (produto) e DESCRIPTION (descrição), mas isso é completamente opcional. O importante é:

#define MATRIX_ROWS 5
#define MATRIX_COLS 14

Mude os valores para o número de linhas e colunas, respectivamente, que o seu teclado possui.

Configurando as linhas e colunas

Agora de volta para o Teensy (ou outro microcontrolador que esteja sendo usado). Anote como você conectou as linhas/colunas (e possivelmente o LED do Caps Lock) aos pinos do Teensy. Lembre-se de evitar o pino D6, que é dedicado ao LED interno do Teensy.

Como exemplo digamos que você tenha 15 colunas e 5 linhas. Neste exemplo as colunas foram conectadas desta maneira:

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

E as linhas foram conectadas desta maneira:

row:  0   1   2   3   4
pin: F0  F1  F4  F5  F6

Lembre-se, este é apenas um exemplo, a sua configuração de pinos pode ser diferente. Você pode conferir o nome dos pinos nesta página da PJRC ou olhando diretamente no Teensy.

Abra o arquivo matrix.c, procure a função init_cols e mude-a de acordo com a sua configuração de pinos:

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

É mais fácil do que parece. Os nomes dos pinos é composto de uma letra e um número de 0 a 7. Se você olhar para a lista de colunas, eu tenho um pino começando com F e o número deste pino é 7. Então para ativar este pino tenho que adicionar as seguintes linhas à lista:

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

DDRF e PORTF são registradores de hardware que controlam os pinos da porta F. 1<<7 é um operador binário que seleciona o 8° bit desta porta (lembre-se que nós começamos a contar a partir de 0, então o 8° pino é na verdade o pino 7).

Vamos prosseguir para a segunda porta:

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

Neste caso estamos na porta B (DDRB e PORTB) e se você prestar atenção no nosso esquema de colunas verá que estou usando os pinos B7, B6, B5, B4, B3, B2 e B1 (mesmo que eles não estejam ordenados na matriz, isso não importa neste momento). Nós usamos o símbolo | (operador lógico OU) para, de maneira esperta, ativar todos os pinos escolhidos ao mesmo tempo. Desta forma, 1<<7 significa B7, 1<<6 significa B6 e assim por diante.

Acredito que agora você já deva ter percebido o padrão do código e como ele funciona. Faça o mesmo para o restante das colunas.

Ótimo! Nós acabamos de inicializar as colunas, agora nós precisamos lê-las. Procure a função read_cols:

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

Esta função associa cada pino à sua respectiva coluna. Desta forma:

(PINF&(1<<7) ? 0 : (1<<0))

diz ao microcontrolador que se o pino F7PINF&(1<<7) – estiver ativo, então nós estamos na primeira coluna (lembrando que começamos a contar a partir do 0, 1<<0 significa primeira coluna).

(PINB&(1<<6) ? 0 : (1<<1))

Se o pino B6PINB&(1<<6) – estiver em ativado, então estamos na segunda coluna – (1<<1) -, e assim por diante. Moleza.

Agora vamos para as linhas. Procure a função unselect_rows.

static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRF  &= ~0b01110011;
    PORTF &= ~0b01110011;
}

Aqui o Hasu escolheu usar uma sintaxe diferente. Nós estamos falando em binário agora, mas ainda assim é bem fácil. Se você olhar para nosso esquema de linhas, verá que estamos usando os pinos 0, 1, 4, 5 e 6 da porta F.

Então nós escrevemos na porta F usando DDRF e PORTF, assim como fizemos para as colunas, mas para escolher o número da porta nós a configuramos em 1. (Na verdade nós estamos desligando-as, mas este é um guia para iniciantes e não vamos nos aprofundar nestes detalhes).

0b diz ao compilador que nós estamos falando em binário. Então você tem 8 zeros e uns. Se você prestar atenção, verá que os 1s estão nas (lendo da direita para a esquerda) 1ª, 2ª, 5ª, 6ª e 7ª posições, ou nos pinos números 0, 1, 4, 5 e 6, que são as linhas que estamos usando em nosso teclado.

Comece a contar da direita. O primeiro bit é o pino 0, o último é o pino 7. Apenas ative os pinos que estiver usando mudando seu valor de 0 para 1. Bacana, não é mesmo?

Agora para selecionarmos as linhas. Procure a função select_row.

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

A esta altura do campeonato tudo já deve estar claro para você.

case 0: significa “se nós estivermos na primeiro linha”.

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

significa “selecione a porta F, bit 0”.

Agora modifique suas portas e pinos de acordo com o seu esquema.

Por último, abra o arquivo led.c e mude a porta para aquela em que o LED estiver conectado.

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

Neste caso nosso LED está no pino D4. Digamos que o queiramos no pino F0, nós mudaríamos o código desta maneira:

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

e

DDRF &= ~(1<<4);
PORTF &= ~(1<<0);

Ok, vamos parar por aqui, isso é o suficiente para a primeira lição. No próximo post nós iremos personalizar a matriz do teclado propriamente dita e suas camadas de funções. Se você tiver perguntas ou encontrar algum erro, fale comigo no fórum.

Atualização: a parte 2 já está no ar!