
Time for a quick tutorial about running TMK firmware on the WhiteFox.
Let me thank Hasu (AGAIN) and flabbergast. I'm just a reporter here, they are the real heroes.
This is just a quick introduction, you'd need to be somewhat familiar with TMK firmware already.
First of all please bear in mind that TMK on WhiteFox is still an experimental project but we need your help testing it. Without a wider user-base we can't fix the bugs.
Setting up the environment
The best way to work with the firmware is from linux. If you are on windows I strongly suggest to install a small linux distro on a virtual machine (virtualbox is free). You don't even need a desktop manager or a GUI, a barebone debian testing would be more than enough. I'm not sure but maybe cygwin might also work.
On a Mac it's easier, but you'd have to install 4Gig or developer tools anyway (X Code), so --unless you already have Xcode installed-- it might be faster/easier to just go with a virtual machine.
Of course you need to install the dev tools, on Debian and Ubuntu I believe they are called "build-essential". You also need to install the cross compiler for ARM Cortex-A/R/M processors. By searching "arm-none-eabi" in your package manager you should be able to find it. On Arch linux I had to install "arm-none-eabi-gcc", "arm-none-eabi-newlib" and "arm-none-eabi-binutils".
Also install "dfu-util" to burn the firmware.
Important: it is always better to flash the firmware from the host OS. So if you are running linux inside a virtual machine, you should flash the firmware from windows (or mac) not directly from inside the virtual machine.
If you insist on using Windows you can download the windows installer here https://launchpad.net/gcc-arm-embedded/+download on a Mac you can compile from source or I'm sure you can find the package on homebrew.
Now get the TMK code from https://github.com/tmk/whitefox . The easiest is to use git
Code: Select all
$ git clone https://github.com/tmk/whitefox.git
$ cd whitefox
$ git submodule update --init --recursive
If everything went smooth try to compile the default firmware. just
Code: Select all
$ make
Try to burn the firmware onto the WhiteFox with dfu-utils. Press the flash button on the bottom of the keyboard and issue the following command (of course you'll need two keyboards connected)
Code: Select all
$ dfu-utils -D build/ch.bin
The keyboard should be working, you don't (shouldn't) need to unplug it.
Customizing the matrix
Layout customization is pretty easy if you are familiar with TMK firmware. Open the keymap_plain.c file with your text editor of choice.
You'll see:
Code: Select all
const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/* Layer 0: Default Layer
* ,---------------------------------------------------------------.
* |Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =| \| `|Ins|
* |---------------------------------------------------------------|
* |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]|Backs|Del|
* |---------------------------------------------------------------|
* |CapsLo| A| S| D| F| G| H| J| K| L| ;| '|Enter |PgU|
* |---------------------------------------------------------------|
* |Shif| | Z| X| C| V| B| N| M| ,| .| /|Shift |Up |PgD|
* |---------------------------------------------------------------|
* |Ctrl|Gui |Alt | Space |Fn0 |Alt |Gui | |Lef|Dow|Rig|
* `---------------------------------------------------------------'
*/
[0] = KEYMAP( \
ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSLS,GRV, INS, \
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSPC, DEL, \
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT,NUHS,ENT, PGUP,\
LSFT,NUBS,Z, X, C, V, B, N, M, COMM,DOT, SLSH,RSFT, UP, PGDN,\
LCTL,LGUI,LALT, SPC, RALT,RGUI,RCTL, LEFT,DOWN,RGHT \
),
};
const uint16_t fn_actions[] = {
};
First of all we need to add an FN key. Just for the sake of this tutorial, let's put it in place of RGUI, but you can put it wherever you want.
Code: Select all
...
LCTL,LGUI,LALT, SPC, RALT,FN0,RCTL, LEFT,DOWN,RGHT \
...
Then we add a keymap for the new layer. Something like this should work for now.
Code: Select all
[0] = KEYMAP( \
ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSLS,GRV, INS, \
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSPC, DEL, \
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT,NUHS,ENT, PGUP,\
LSFT,NUBS,Z, X, C, V, B, N, M, COMM,DOT, SLSH,RSFT, UP, PGDN,\
LCTL,LGUI,LALT, SPC, RALT,RGUI,RCTL, LEFT,DOWN,RGHT \
),
[1] = KEYMAP( \
GRV ,F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, TRNS,TRNS,MUTE,\
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PSCR,TRNS,TRNS,TRNS, TRNS,\
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PENT, VOLD,\
TRNS,TRNS,FN1 ,FN2 ,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, PGUP,VOLU,\
TRNS,TRNS,TRNS, TRNS, TRNS,TRNS,TRNS, HOME,PGDN,END \
),
Finally we need to add an action to the layer, it is a momentary layer so all we have to do is add ACTION_LAYER_MOMENTARY action to the fn_actions array.
Code: Select all
const uint16_t fn_actions[] = {
[0] = ACTION_LAYER_MOMENTARY(1),
};
Now save and compile it again. Burn and enjoy your first layer.
As you can see it's same old TMK thing. Get used to it and proceed to the next chapter.
Backlight
This is where things get complicated. Follow me closely.
The WhiteFox is able to light each LED separately. Super cool. You can light any number of them at any brightness you desire. This is relatively easy when you want to toggle a single LED but it gets a bit complicated if you need more sophisticated setups.
But let's start from the single LED.
FIrst of all include the "led_controller.h" header file at the top of the file
Code: Select all
#include "keymap_common.h"
#include "led_controller.h"
#define ACTION_LEDS_ENTER 2
..
Now we are telling the controller to light the ENTER LED when FN1 is pressed.
Add the FN1 action to the fn_actions array:
Code: Select all
const uint16_t fn_actions[] = {
[0] = ACTION_LAYER_MOMENTARY(1),
[1] = ACTION_FUNCTION(ACTION_LEDS_ENTER),
};
Code: Select all
void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) {
(void)opt;
switch(id) {
case ACTION_LEDS_ENTER:
// toggle enter LED on press
if(record->event.pressed) {
// signal the LED controller thread
chMBPost(&led_mailbox, LED_MSG_ENTER_TOGGLE, TIME_IMMEDIATE);
}
break;
}
}
Open the led_controller.c file and search for "0x78". That would be the address of the LED under the enter key. The address matrix is as follow:
Code: Select all
11 12 13 14 15 16 17 18 21 22 23 24 25 26 27 28
31 32 33 34 35 36 37 38 41 42 43 44 45 46 47
48 51 52 53 54 55 56 57 58 61 62 63 64 65 66
67 68 71 72 73 74 75 76 77 78 81 82 83 84 85
86 87 88 91 92 93 (94) 95 96 97
Code: Select all
the Enter key is 65 where A = 6 and B = 5.
0x24 + (A-1)*0x10 + (B-1)
therefore
0x24 + (6-1)*0x10 + (5-1) = 0x78
so 0x78 is the enter led
Code: Select all
printf '%x\n' $((0x24 + (6-1)*0x10 + (5-1)))
Let's have a quick look at the incriminating code.
Code: Select all
case LED_MSG_ENTER_TOGGLE:
is31_read_register(0, 0x78, &temp);
chThdSleepMilliseconds(1);
if(temp) {
// turn off on pages 1 and 2
is31_write_register(0, 0x78, 0);
is31_write_register(1, 0x78, 0);
} else {
// turn on on pages 1 and 2
is31_write_register(0, 0x78, 0xFF);
is31_write_register(1, 0x78, 0xFF);
}
break;
Now. Why are we doing this twice (for page 1 and 2)? Very good question, thanks for asking.
Backlight pages
As far as I understand communication between the main controller and the LED controller is quite slow, so we have to initialize the backlight on the init phase. To do so we have save an "image" of the backlight setup in one of the 8 available pages.
Say you want to light up WASD and the arrow cluster as shown the picture above, you do so by creating a picture of the LED you want lighted up with the brightness you want. Think of the keyboard backlight as a very low resolution monitor. Each LED is a pixel, each pixel may have 256 levels of brightness.
At startup we store the image we want to draw on the keyboard LED matrix in one of the available pages. We will be later able to switch from one page to another (with a nice fade out/in effect).
There are certain LEDs though that need to keep their state no matter what page we are showing. Think of the CAPS LOCK for example, or the ENTER LED we did earlier. The state of those keys must be remembered no matter what page we are in. That is the reason why we are writing the state of the ENTER LED two times. The first time on page 0 the second time on page 1. Currently we have nothing on "page 1" (we haven't set any effect for it yet), but if we had 3 images (eg: full off, full on and WASD) we would have to print the ENTER LED value on all three of our pages.
Hope it all makes sense. It took a while to get used to it but it will be easier to understand with some examples.
For now try to absorb what we've discussed so far, the next time I'll show you how to design a backlight image. If you want to get a glimpse of it you could have a look at the kyemap_flabber.c and the led_controller.c files.
The image for WASD+ESC+Arrows is
Code: Select all
const uint8_t led_game[83] = {
0x24,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x34,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x44,
0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x54,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x74,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x84,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x94,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xA4,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
};