Keyboard: Palm serial keyboard USB converter (#4485)

* Initial palm_usb support

* removing left over sun .c file

* fixing licenses

* actually adding updated files

* fixing build error

* more include cleanup
master
milestogo 2018-12-06 17:13:15 -08:00 committed by Drashna Jaelre
parent 2fb14845d5
commit 3f96ba0113
7 changed files with 772 additions and 0 deletions

View File

@ -0,0 +1,120 @@
/*
Copyright 2012 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This code makes use of cy384's Arduino USB HID adapter for the Palm Portable
Keyboard, released under the BSD licence */
#pragma once
#define CUSTOM_MATRIX 2
#define VENDOR_ID 0xFEED
#define PRODUCT_ID 0x0001
#define DEVICE_VER 0x0100
#define MANUFACTURER QMK
#define PRODUCT Stowaway converter
#define DESCRIPTION USB converter for Stowaway keyboard
// IO pins to serial
// https://deskthority.net/wiki/Arduino_Pro_Micro for pin lookup
#define VCC_PIN D1 // pro micro 2
#define RX_PIN D0 //pro micro 3 , was 8 on cy384
#define RTS_PIN C6 // 5 //[ was D4 // 4 on the cy384
#define DCD_PIN E6 //7
// if using the particular arduino pinout of CY384
#ifdef CY384
#define GND_PIN D7 //6
#define PULLDOWN_PIN B1 // 15
#endif
#ifndef HANDSPRING
// Set to 1 for Handspring or to disable RTS/DCD based handshake.
#define HANDSPRING 0
#endif
#define MAXDROP 10 // check if keyboard is connected every X polling cycles
#define SLEEP_TIMEOUT 500000 // check keyboard/reset this many millis
#define MATRIX_ROWS 12
#define MATRIX_COLS 8
/* key combination for command */
#define IS_COMMAND() ( \
keyboard_report->mods == (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) || \
keyboard_report->mods == (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI)) || \
keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
)
/* Serial(USART) configuration
* asynchronous, negative logic, 9600baud, no flow control
* 1-start bit, 8-data bit, non parity, 1-stop bit
*/
#define SERIAL_SOFT_BAUD 9600
#define SERIAL_SOFT_PARITY_NONE
#define SERIAL_SOFT_BIT_ORDER_LSB
#if (HANDSPRING == 0)
#define SERIAL_SOFT_LOGIC_NEGATIVE //RS232 logic
#endif
/* RXD Port */
#define SERIAL_SOFT_RXD_ENABLE
// we are using Pro micro pin 3 / D0 as serial
#define SERIAL_SOFT_RXD_DDR DDRD
#define SERIAL_SOFT_RXD_PORT PORTD
#define SERIAL_SOFT_RXD_PIN PIND
#define SERIAL_SOFT_RXD_BIT 0
#define SERIAL_SOFT_RXD_VECT INT0_vect
/* RXD Interupt */
#define SERIAL_SOFT_RXD_INIT() do { \
/* pin configuration: input with pull-up */ \
SERIAL_SOFT_RXD_DDR &= ~(1<<SERIAL_SOFT_RXD_BIT); \
SERIAL_SOFT_RXD_PORT |= (1<<SERIAL_SOFT_RXD_BIT); \
/* enable interrupt: INT0(rising edge) */ \
EICRA |= ((1<<ISC01)|(1<<ISC00)); \
EIMSK |= (1<<INT0); \
sei(); \
} while (0)
#define SERIAL_SOFT_RXD_INT_ENTER()
#define SERIAL_SOFT_RXD_INT_EXIT() do { \
/* clear interrupt flag */ \
EIFR = (1<<INTF0); \
} while (0)
#define SERIAL_SOFT_RXD_READ() (SERIAL_SOFT_RXD_PIN&(1<<SERIAL_SOFT_RXD_BIT))
/* TXD Port */
#define SERIAL_SOFT_TXD_ENABLE
#define SERIAL_SOFT_TXD_DDR DDRD
#define SERIAL_SOFT_TXD_PORT PORTD
#define SERIAL_SOFT_TXD_PIN PIND
#define SERIAL_SOFT_TXD_BIT 3
#define SERIAL_SOFT_TXD_HI() do { SERIAL_SOFT_TXD_PORT |= (1<<SERIAL_SOFT_TXD_BIT); } while (0)
#define SERIAL_SOFT_TXD_LO() do { SERIAL_SOFT_TXD_PORT &= ~(1<<SERIAL_SOFT_TXD_BIT); } while (0)
#define SERIAL_SOFT_TXD_INIT() do { \
/* pin configuration: output */ \
SERIAL_SOFT_TXD_DDR |= (1<<SERIAL_SOFT_TXD_BIT); \
/* idle */ \
SERIAL_SOFT_TXD_ON(); \
} while (0)

View File

@ -0,0 +1,398 @@
/*
Copyright 2018 milestogo
with elements Copyright 2014 cy384 under a modified BSD license
building on qmk structure Copyright 2012 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "protocol/serial.h"
#include "timer.h"
#include "pincontrol.h"
/*
* Matrix Array usage:
*
* ROW: 12(4bits)
* COL: 8(3bits)
*
* +---------+
* 0|00 ... 07|
* 1|00 ... 07|
* :| ... |
* :| ... |
* A| |
* B| |
* +---------+
*/
static uint8_t matrix[MATRIX_ROWS];
// we're going to need a sleep timer
static uint16_t last_activity ;
// and a byte to track duplicate up events signalling all keys up.
static uint16_t last_upKey ;
// serial device can disconnect. Check every MAXDROP characters.
static uint16_t disconnect_counter = 0;
// bitmath masks.
#define KEY_MASK 0b10000000
#define COL_MASK 0b00000111
#define ROW_MASK 0b01111000
#define ROW(code) (( code & ROW_MASK ) >>3)
#define COL(code) ((code & COL_MASK) )
#define KEYUP(code) ((code & KEY_MASK) >>7 )
static bool is_modified = false;
__attribute__ ((weak))
void matrix_init_kb(void) {
matrix_init_user();
}
__attribute__ ((weak))
void matrix_scan_kb(void) {
matrix_scan_user();
}
__attribute__ ((weak))
void matrix_init_user(void) {
}
__attribute__ ((weak))
void matrix_scan_user(void) {
}
inline
uint8_t matrix_rows(void)
{
return MATRIX_ROWS;
}
inline
uint8_t matrix_cols(void)
{
return MATRIX_COLS;
}
void pins_init(void) {
// set pins for pullups, Rts , power &etc.
//print ("pins setup\n");
pinMode(VCC_PIN, PinDirectionOutput);
digitalWrite(VCC_PIN, PinLevelLow);
#if ( HANDSPRING == 0)
#ifdef CY835
pinMode(GND_PIN, PinDirectionOutput);
digitalWrite(GND_PIN, PinLevelLow);
pinMode(PULLDOWN_PIN, PinDirectionOutput);
digitalWrite(PULLDOWN_PIN, PinLevelLow);
#endif
pinMode(DCD_PIN, PinDirectionInput);
pinMode(RTS_PIN, PinDirectionInput);
#endif
/* check that the other side isn't powered up.
test=digitalRead(DCD_PIN);
xprintf("b%02X:", test);
test=digitalRead(RTS_PIN);
xprintf("%02X\n", test);
*/
}
uint8_t rts_reset(void) {
static uint8_t firstread ;
/* bounce RTS so device knows it is rebooted */
// On boot, we keep rts as input, then switch roles here
// on leaving sleep, we toggle the same way
firstread=digitalRead(RTS_PIN);
// printf("r%02X:", firstread);
pinMode(RTS_PIN, PinDirectionOutput);
if (firstread == PinLevelHigh) {
digitalWrite(RTS_PIN, PinLevelLow);
}
_delay_ms(10);
digitalWrite(RTS_PIN, PinLevelHigh);
/* the future is Arm
if (palReadPad(RTS_PIN_IOPRT) == PinLevelLow)
{
_delay_ms(10);
palSetPadMode(RTS_PINn_IOPORT, PinDirectionOutput_PUSHPULL);
palSetPad(RTS_PORT, RTS_PIN);
}
else
{
palSetPadMode(RTS_PIN_RTS_PORT, PinDirectionOutput_PUSHPULL);
palSetPad(RTS_PORT, RTS_PIN);
palClearPad(RTS_PORT, RTS_PIN);
_delay_ms(10);
palSetPad(RTS_PORT, RTS_PIN);
}
*/
_delay_ms(5);
//print("rts\n");
return 1;
}
uint8_t get_serial_byte(void) {
static uint8_t code;
while(1) {
code = serial_recv();
if (code) {
debug_hex(code); debug(" ");
return code;
}
}
}
uint8_t palm_handshake(void) {
// assumes something has seen DCD go high, we've toggled RTS
// and we now need to verify handshake.
// listen for up to 4 packets before giving up.
// usually I get the sequence FF FA FD
static uint8_t codeA=0;
for (uint8_t i=0; i < 5; i++) {
codeA=get_serial_byte();
if ( 0xFA == codeA) {
if( 0xFD == get_serial_byte()) {
return 1;
}
}
}
return 0;
}
uint8_t palm_reset(void) {
print("@");
rts_reset(); // shouldn't need to power cycle.
if ( palm_handshake() ) {
last_activity = timer_read();
return 1;
} else {
print("failed reset");
return 0;
}
}
uint8_t handspring_handshake(void) {
// should be sent 15 ms after power up.
// listen for up to 4 packets before giving up.
static uint8_t codeA=0;
for (uint8_t i=0; i < 5; i++) {
codeA=get_serial_byte();
if ( 0xF9 == codeA) {
if( 0xFB == get_serial_byte()) {
return 1;
}
}
}
return 0;
}
uint8_t handspring_reset(void) {
digitalWrite(VCC_PIN, PinLevelLow);
_delay_ms(5);
digitalWrite(VCC_PIN, PinLevelHigh);
if ( handspring_handshake() ) {
last_activity = timer_read();
disconnect_counter=0;
return 1;
} else {
print("-HSreset");
return 0;
}
}
void matrix_init(void)
{
debug_enable = true;
//debug_matrix =true;
serial_init(); // arguments all #defined
#if (HANDSPRING == 0)
pins_init(); // set all inputs and outputs.
#endif
print("power up\n");
digitalWrite(VCC_PIN, PinLevelHigh);
// wait for DCD strobe from keyboard - it will do this
// up to 3 times, then the board needs the RTS toggled to try again
#if ( HANDSPRING == 1)
if ( handspring_handshake() ) {
last_activity = timer_read();
} else {
print("failed handshake");
_delay_ms(1000);
//BUG /should/ power cycle or toggle RTS & reset, but this usually works.
}
#else /// Palm / HP device with DCD
while( digitalRead(DCD_PIN) != PinLevelHigh ) {;}
print("dcd\n");
rts_reset(); // at this point the keyboard should think all is well.
if ( palm_handshake() ) {
last_activity = timer_read();
} else {
print("failed handshake");
_delay_ms(1000);
//BUG /should/ power cycle or toggle RTS & reset, but this usually works.
}
#endif
// initialize matrix state: all keys off
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
matrix_init_quantum();
return;
}
uint8_t matrix_scan(void)
{
uint8_t code;
code = serial_recv();
if (!code) {
/*
disconnect_counter ++;
if (disconnect_counter > MAXDROP) {
// set all keys off
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
}
*/
// check if the keyboard is asleep.
if (timer_elapsed(last_activity) > SLEEP_TIMEOUT) {
#if(HANDSPRING ==0 )
palm_reset();
#else
handspring_reset();
#endif
return 0;
}
}
last_activity = timer_read();
disconnect_counter=0; // if we are getting serial data, we're connected.
debug_hex(code); debug(" ");
switch (code) {
case 0xFD: // unexpected reset byte 2
print("rstD ");
return 0;
case 0xFA: // unexpected reset
print("rstA ");
return 0;
}
if (KEYUP(code)) {
if (code == last_upKey) {
// all keys are not pressed.
// Manual says to disable all modifiers left open now.
// but that could defeat sticky keys.
// BUG? dropping this byte.
last_upKey=0;
return 0;
}
// release
if (matrix_is_on(ROW(code), COL(code))) {
matrix[ROW(code)] &= ~(1<<COL(code));
last_upKey=code;
}
} else {
// press
if (!matrix_is_on(ROW(code), COL(code))) {
matrix[ROW(code)] |= (1<<COL(code));
}
}
matrix_scan_quantum();
return code;
}
bool matrix_is_modified(void)
{
return is_modified;
}
inline
bool matrix_has_ghost(void)
{
return false;
}
inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
return (matrix[row] & (1<<col));
}
inline
uint8_t matrix_get_row(uint8_t row)
{
return matrix[row];
}
void matrix_print(void)
{
print("\nr/c 01234567\n");
for (uint8_t row = 0; row < matrix_rows(); row++) {
phex(row); print(": ");
pbin_reverse(matrix_get_row(row));
print("\n");
}
}
uint8_t matrix_key_count(void)
{
uint8_t count = 0;
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
count += bitpop(matrix[i]);
}
return count;
}

View File

@ -0,0 +1,96 @@
# Stowaway Serial keyboard to USB protocol converter
A converter for Palm Pilot era Stowaway serial keyboards.
Makes extensive use of the code from [cy384](https://github.com/cy384/ppk_usb). Ported to QMK by [milestogo](https://github.com/milestogo).
Hardware Supported: See hardware section below
Hardware Availability: self-built
Make example for this keyboard (after setting up your build environment):
make converter/palm_usb/stowaway:default
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Hardware
Target MCU is ATMega32u4 but other USB capable AVRs should also work.
cy843 has a very specific way of wiring in order to fit all pins in sequence. It breaks
qmk because the Arduino softserial library uses different pins from QMK.
I've wired the pro micro hardware as follows.
Label| TX0,RX1,GND,GND,2 ,3 ,4 ,5 ,6 ,7
Palm | , , * ,GND,VCC,RX ,NC ,RTS,nc ,DCD
MCU | ,D1 ,D0 , ,C6 , ,E6
\* The RX line from the keyboard should be conected to a ~10K ohm pull down resistor to ground.
RX --|--3
10K
|
GND
Power management is not implemented yet, this just reboots the keyboard frequently.
### Keyboards:
Think Outside Stowaway Keyboards
There are at least 5 different versions of these keyboards out there.
Group 1: Palm 3, Palm 5 & HP Journada 540, and Compaq iPaq keyboards. These share
the same RTS protocol, but with different pinouts for each device.
Group 2: Handspring keyboards. These don't do handshaking protocol, and use TTL signal.
Set HANDSPRING to 1 in config.h
Group 3: IRDA models. Untested but theoretically serial.
### Connectors
See https://github.com/cy384/ppk_usb for wiring & sample 3d printable sockets.
Only Palm3 wiring has been tested.
RXD pin is output from keyboard to MCU's RX.
Viewed from left to right with the keyboard in typing position.
Palm3: [NC, VCC, RXD, RTS, NC, NC, DCD, NC, NC, GND]
Palm5: [NC, VCC, RXD, RTS, NC, NC, DCD, NC, NC, GND] (same order, different connector)
Handspring: [VCC/TXD, NC, NC, NC, GND, NC, NC, RXD]
Journada: [NC, NC, NC, GND, NC, RTS, NC, DTR/VCC, RXD, DCD, NC] [GND-IN, VCC-IN]
Ipaq: [NC, NC, DTR/VCC, NC, NC, RTS, NC, RXD, DCD, GND, NC, NC]
### Protocol
Signal: Asynchronous, Negative logic, 9600baud, No Flow control
Frame format: 1-Start bit, 8-Data bits, No-Parity, 1-Stop bit
AVR USART engine expects positive logic while stowaway keyboard signal is negative.
To use AVR UART engine you need external inverter in front of RX and TX pin.
Otherwise you can software serial routine to communicate the keyboard.
This converter uses software method, you doesn't need any inverter part.
Commands From System To Keyboard
none
Commands From Keyboard To System
0xFA Reset/Ready Response(followed by 0xFD)
References
* http://www.splorp.com/pdf/stowawayhwref.pdf
### Todo
- Test on anything but a palm 3 model keyboard.
- Change all of the soft serial to match the new Helix based code so that it is easier
to switch pins.
- The driver should check for a keyboard that pressed the delete key then disconnected.
Check every MAXDROP scans that the keyboard is there, and if not, clear the matrix.
Not implemented yet, since matrix scan is so much faster than serial.

View File

@ -0,0 +1,46 @@
MCU = atmega32u4 # Teensy 2.0
F_CPU = 16000000
ARCH = AVR8
F_USB = $(F_CPU)
# Interrupt driven control endpoint task
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
BOOTLOADER = caterina
# Build Options
# change to "no" to disable the options, or define them in the Makefile in
# the appropriate keymap folder that will get included automatically
#
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = no # Mouse keys(+4700)
EXTRAKEY_ENABLE = no # Audio control and System control(+450)
CONSOLE_ENABLE = yes # Console for debug(+400)
COMMAND_ENABLE = yes # Commands for debug and configuration
NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
MIDI_ENABLE = no # MIDI controls
AUDIO_ENABLE = no # Audio output on port C6
UNICODE_ENABLE = no # Unicode
UNICODEMAP_ENABLE = no
BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight.
CUSTOM_MATRIX = yes
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
#HARDWARE_SERIAL = yes
SRC += matrix.c
ifdef HARDWARE_SERIAL
# untested with palm_usb
SRC += protocol/serial_uart.c
OPT_DEFS += -DHARDWARE_SERIAL
else
SRC += protocol/serial_soft.c
endif
DEFAULT_FOLDER = converter/palm_usb/stowaway

View File

@ -0,0 +1,59 @@
/*
Copyright 2018 milestogo
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum layers {
_QWERTY=0,
_CDH,
_FN
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/*
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS, EQL, BACK, APP0,
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC, RBRC, BSLS, APP1,
CAPS, A, S, D, F, G, H, J, K, L, SCLN, QUOT, ENT, APP2,
LSFT, Z, X, C, V, B, N, M, COMM, DOT, SLSH, RSFT, UP, APP3,
LCTL, FN, LALT, CMD, SPACE,SPACE,GRAVE,DONE, DEL, LEFT, DOWN, RIGHT
*/
[_QWERTY] = LAYOUT( /* Base */
KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_ESC,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, TG(_CDH),
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGUP,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_PGDN,
KC_LCTL, MO(_FN), KC_LALT, KC_LGUI, KC_SPACE,KC_SPACE,KC_GRAVE,KC_RGUI, KC_DEL, KC_LEFT,KC_DOWN, KC_RIGHT
),
[_CDH] = LAYOUT( /* Base */
KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_ESC,
KC_TAB, KC_Q, KC_W, KC_F, KC_P, KC_B, KC_J, KC_L, KC_U, KC_Y, KC_SCLN, KC_LBRC, KC_RBRC, KC_BSLS, _______,
KC_CAPS, KC_A, KC_R, KC_S, KC_T, KC_G, KC_M, KC_N, KC_E, KC_I, KC_O, KC_QUOT, KC_ENT, KC_PGUP,
KC_LSFT, KC_Z, KC_X, KC_C, KC_D, KC_V, KC_K, KC_H, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_PGDN,
KC_LCTL, MO(_FN), KC_LALT, KC_LGUI, KC_SPACE,KC_SPACE,KC_GRAVE,KC_RGUI, KC_DEL, KC_LEFT,KC_DOWN, KC_RIGHT
),
[_FN] = LAYOUT( // FN Key
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, KC_ENT, _______, _______, _______, _______, _______, _______, _______
),
};

View File

@ -0,0 +1,53 @@
/*
Copyright 2018 milestogo
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
/* Stowaway Keyboard
based on matrix from http://www.splorp.com/pdf/stowawayhwref.pdf
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS, EQL, BACK APP0,
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC, RBRC, BSLS, APP1,
CAPS, A, S, D, F, G, H, J, K, L, SCLN, QUOT, ENT, APP2,
LSFT, Z, X, C, V, B, N, M, COMM, DOT, SLSH, RSFT, UP, APP3,
LCTL, FN, LALT, CMD, SPACE,SPACE,GRAVE,DONE, DEL, LEFT, DOWN, RIGHT
*/
#define LAYOUT( \
K000, K001, K002, K004, K005, K006, K007, K064, K065, K066, K060, K061, K062, K063,\
K031, K011, K012, K013, K014, K015, K016, K074, K075, K076, K077, K070, K071, K072, K073,\
K030, K021, K022, K023, K024, K025, K026, K084, K085, K086, K087, K080, K081, K082,\
K110, K003, K020, K054, K055, K056, K057, K094, K095, K096, K090, K111, K091, K092,\
K032, K042, K043, K010, K027, K067, K017, K097, K100, K101, K102, K103 \
) { \
{ K000, K001, K002 , K003, K004, K005, K006, K007 }, \
{ K010, K011, K012 , K013, K014, K015, K016, K017 }, \
{ K020, K021, K022 , K023, K024, K025, K026, K027 }, \
{ K030, K031, K032 , KC_NO,KC_NO, KC_NO,KC_NO, KC_NO }, \
{ KC_NO, KC_NO, K042 , K043, KC_NO, KC_NO,KC_NO, KC_NO }, \
{ KC_NO, KC_NO, KC_NO, KC_NO,K054, K055, K056, K057 }, \
{ K060, K061, K062 , K063, K064, K065, K066, K067 }, \
{ K070, K071, K072 , K073, K074, K075, K076, K077 }, \
{ K080, K081, K082 , KC_NO,K084, K085, K086, K087 }, \
{ K090, K091, K092 , KC_NO,K094, K095, K096, K097 }, \
{ K100, K101, K102 , K103, KC_NO, KC_NO,KC_NO, KC_NO }, \
{ K110, K111, KC_NO, KC_NO,KC_NO, KC_NO,KC_NO, KC_NO } \
}