It may seem daunting to work with Hasu's wall of code but the files you need to modify are not many and you don't actually need to fully understand the code. I myself don't understand 100% of it, so if I write some bullshits I hope Hasu will chime in.
Development environment
First of all you need to set up the develpment environment. That depends on your system.
Unfortunately Windows is not the best development environment for this kind of things. My suggestion would be to install a minimal linux distribution on a virtual machine (VirtualBox is free) and follow the linux instructions below. Most of issues people are encountering are due to Windows. I you feel lucky and insist on using windows I believe your best option is to install AtmelStudio. I can't help you with that because I haven't personally used it, but it should contain all you need to compile your firmware.
On Mac get CrossPack and from the App Store install XCode.
On Linux you need to install the development tools (eg: on Ubuntu the package should be called "build-essential") and the AVR dev tools. Again, naming varies based on the distro, search for AVR in your package manager and you should find all you need (eg: for Ubuntu should be "gcc-avr gdb-avr binutils-avr avr-libc avrdude").
To actually burn the firmware in the controller you need a programmer. You can use Atmel's FLIP or dfu-programmer, but I find the easiest to use is the Teensy loader.
If you can't actually build the firmware yourself, don't worry, you can still make the modifications needed to the code and I bet someone here on the forum can send you the HEX file you'll be uploading to the keyboard (you still need to install the programmer above).
Get the code
Download and unzip Hasu's code from github. https://github.com/tmk/tmk_keyboard/archive/master.zip
Fortunately you can ignore most of the code there. Go to the keyboard/gh60 directory. We won't never leave that directory, I've told you it was easy.
Remember to check Hasu's repository once in a while, it is very active and it often gets updated.
Main config
Open Makefile file and check the following globals:
Code: Select all
TARGET = gh60_lufa
...
...
MCU = atmega32u4
...
...
#NKRO_ENABLE = yes
Now open the config.h file.
You can give custom values to MANUFACTURER, PRODUCT and DESCRIPTION, but that's completely optional. What's important is:
Code: Select all
#define MATRIX_ROWS 5
#define MATRIX_COLS 14
Setting up rows and columns
Now back to the teensy. Write down how you connected the rows/cols (and eventually the capslock LED) to the teensy's pins. Remember to avoid pin D6 that is dedicated to the teensy's internal LED.
Just for the sake of this tutorial let's say you have 15 cols and 5 rows. In this example the columns are connected like this:
Code: Select all
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
Code: Select all
row: 0 1 2 3 4
pin: F0 F1 F4 F5 F6
You can check the name of the pins from PJRC pinout page or reading them directly from the Teensy.
Open the matrix.c file. Search for the init_cols function and change it according to your pinout
Code: Select all
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);
}
Code: Select all
DDRF &= ~(1<<7);
PORTF |= (1<<7);
Let's proceed with the second port.
Code: Select all
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);
I believe at this point it should be clear how it works. Proceed with all the other columns.
Cool. We just initiated the columns. Now we have to read them. Search for the read_cols function:
Code: Select all
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));
}
Code: Select all
(PINF&(1<<7) ? 0 : (1<<0))
Code: Select all
(PINB&(1<<6) ? 0 : (1<<1))
Now to the rows. Search the unselect_rows function.
Code: Select all
static void unselect_rows(void)
{
// Hi-Z(DDR:0, PORT:0) to unselect
DDRF &= ~0b01110011;
PORTF &= ~0b01110011;
}
So we write to the F port with DDRF and PORTF like we did for the columns but to choose the port number we set it to 1. (actually we are unselecting them, but this is meant for newbie and I'm not going deeper into this).
0b just tells the compiler that we are talking binary. Then you have 8 zeros and ones. If you look closely you'll see that the 1s are --reading from right to left-- in the 1st, 2nd, 5th, 6th, 7th positions, or pin number 0, 1, 4, 5, 6 that are the columns that we are using for our keyboard.
Start counting from the right. The first bit is PIN0, the last is PIN7. Just set the pins you use to 1. Peachy, isn't it?
Now to selecting the rows. Search for the select_row function.
Code: Select all
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;
}
}
case 0: means: "if we are on the first row".
Code: Select all
DDRF |= (1<<0);
PORTF &= ~(1<<0);
Now modify your ports/pins according to your schema.
Lastly open the led.c file and change the port to the one you connected the LED to.
Code: Select all
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);
}
}
Code: Select all
DDRF |= (1<<0);
PORTF &= ~(1<<0);
Part 2 is just around the bend.