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 F7
– PINF&(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 B6
– PINB&(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!