Add files via upload

This commit is contained in:
Frank Adams 2022-03-10 16:36:25 -08:00 committed by GitHub
parent b52c865cdf
commit 5852cb436e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 6866 additions and 0 deletions

Binary file not shown.

View file

@ -0,0 +1,572 @@
/* Copyright 2019 Olga, based on work of Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software implements old Compaq keyboard controller on Teensy 3.5 connected directly with keyboard
// IMPORTANT ! DO NOT CONNECT PIN 13 AS IT DRIVES LED AND GIVES RANDOM READING !
// This routine uses the Teensyduino "Micro-Manager Method" to send Normal and Modifier
// keys over USB.
// Description of Teensyduino keyboard functions is at www.pjrc.com/teensy/td_keyboard.html
//
// Revision History
// Initial Release Feb 14, 2021
//
//
// comment this line to have debug code included
#define NODEBUG
// keyboard to teensy connection definitions
#define KBD_X 32
#define KBD_FN 34
#define KBD_LSH 35
#define KBD_LCT 36
#define KBD_RSH 37
#define KBD_RCT 38
#define KBD_RAL 39
#define KBD_LAL 14
#define KBD_A 15
#define KBD_B 16
#define KBD_C 17
#define KBD_D 18
#define KBD_E 19
#define KBD_F 20
#define KBD_G 21
#define KBD_H 22
#define KBD_1 30
#define KBD_2 29
#define KBD_3 28
#define KBD_4 27
#define KBD_5 26
#define KBD_6 25
#define KBD_7 24
#define KBD_8 12
#define KBD_9 11
#define KBD_10 10
#define KBD_11 8
#define KBD_CAPS 2
#define KBD_NUM 3
#define KBD_SCR 4
#define USB_LED_NUM_LOCK 0
#define USB_LED_CAPS_LOCK 1
#define USB_LED_SCROLL_LOCK 2
struct KeyDesc {
const char* name;
boolean isModifier;
int pin1;
int pin2;
int keyCode;
int fnKeyCode;
int numKeyCode;
int scrKeyCode;
boolean scanned;
};
struct KeySent {
int rawKey;
int sendKey;
};
struct KeyDesc Keys[] =
{
{ "LCTRL", true, KBD_X, KBD_LCT, MODIFIERKEY_LEFT_CTRL, 0, 0, 0 },
{ "RCTRL", true, KBD_X, KBD_RCT, MODIFIERKEY_RIGHT_CTRL, 0, 0, 0 },
{ "LSHIFT", true, KBD_X, KBD_LSH, MODIFIERKEY_LEFT_SHIFT, 0, 0, 0 },
{ "RSHIFT", true, KBD_X, KBD_RSH, MODIFIERKEY_RIGHT_SHIFT, 0, 0, 0 },
{ "LALT", true, KBD_X, KBD_LAL, MODIFIERKEY_LEFT_ALT, 0, 0, 0 },
{ "RALT", true, KBD_X, KBD_RAL, MODIFIERKEY_RIGHT_ALT, 0, 0, 0 },
{ "FN", true, KBD_X, KBD_FN, MODIFIERKEY_GUI, 0, 0, 0 },
{ "ESC", false, KBD_A, KBD_3, KEY_ESC, 0, 0, 0 },
{ "F1", false, KBD_A, KBD_2, KEY_F1, 0, 0, 0 },
{ "F2", false, KBD_A, KBD_4, KEY_F2, 0, 0, 0 },
{ "F3", false, KBD_A, KBD_1, KEY_F3, 0, 0, 0 },
{ "F4", false, KBD_A, KBD_8, KEY_F4, 0, 0, 0 },
{ "F5", false, KBD_A, KBD_11, KEY_F5, 0, 0, 0 },
{ "F6", false, KBD_A, KBD_5, KEY_F6, 0, 0, 0 },
{ "F7", false, KBD_A, KBD_7, KEY_F7, 0, 0, 0 },
{ "F8", false, KBD_A, KBD_6, KEY_F8, 0, 0, 0 },
{ "F9", false, KBD_A, KBD_10, KEY_F9, 0, 0, 0 },
{ "F10", false, KBD_A, KBD_9, KEY_F10, 0, 0, 0 },
{ "F11", false, KBD_G, KBD_9, KEY_F11, 0, 0, 0 },
{ "F12", false, KBD_G, KBD_10, KEY_F12, 0, 0, 0 },
{ "NUMLCK", false, KBD_G, KBD_6, KEY_NUM_LOCK, 0, 0, 0 },
{ "PRTSCR", false, KBD_G, KBD_7, KEY_PRINTSCREEN, 0, 0, 0 },
{ "~", false, KBD_B, KBD_3, KEY_TILDE, 0, 0, 0 },
{ "1", false, KBD_B, KBD_2, KEY_1, 0, 0, 0 },
{ "2", false, KBD_B, KBD_4, KEY_2, 0, 0, 0 },
{ "3", false, KBD_B, KBD_1, KEY_3, 0, 0, 0 },
{ "4", false, KBD_B, KBD_8, KEY_4, 0, 0, 0 },
{ "5", false, KBD_B, KBD_11, KEY_5, 0, 0, 0 },
{ "6", false, KBD_B, KBD_5, KEY_6, 0, 0, 0 },
{ "7", false, KBD_B, KBD_7, KEY_7, KEY_HOME, KEYPAD_7, 0 },
{ "8", false, KBD_B, KBD_6, KEY_8, KEY_UP, KEYPAD_8, 0 },
{ "9", false, KBD_B, KBD_10, KEY_9, KEY_PAGE_UP, KEYPAD_9, 0 },
{ "0", false, KBD_B, KBD_9, KEY_0, 0, KEYPAD_ASTERIX, 0 },
{ "-(B)", false, KBD_F, KBD_9, KEY_MINUS, 0, 0, 0 },
{ "=(Apo)", false, KBD_F, KBD_10, KEY_EQUAL, 0, 0, 0 },
{ "BCKSPC", false, KBD_F, KBD_6, KEY_BACKSPACE, 0, 0, 0 },
{ "SCRLCK", false, KBD_F, KBD_7, KEY_SCROLL_LOCK, 0, 0, 0 },
{ "TAB", false, KBD_C, KBD_3, KEY_TAB, 0, 0, 0 },
{ "Q", false, KBD_C, KBD_2, KEY_Q, 0, 0, 0 },
{ "W", false, KBD_C, KBD_4, KEY_W, 0, 0, 0 },
{ "E", false, KBD_C, KBD_1, KEY_E, 0, 0, 0 },
{ "R", false, KBD_C, KBD_8, KEY_R, 0, 0, 0 },
{ "T", false, KBD_C, KBD_11, KEY_T, 0, 0, 0 },
{ "Y", false, KBD_C, KBD_5, KEY_Y, 0, 0, 0 },
{ "U", false, KBD_C, KBD_7, KEY_U, KEY_LEFT, KEYPAD_4, 0 },
{ "I", false, KBD_C, KBD_6, KEY_I, 0, KEYPAD_5, 0 },
{ "O", false, KBD_C, KBD_10, KEY_O, KEY_RIGHT, KEYPAD_6, 0 },
{ "P", false, KBD_C, KBD_9, KEY_P, 0, KEYPAD_MINUS, 0 },
{ "[(Ue)", false, KBD_H, KBD_9, KEY_LEFT_BRACE, 0, 0, 0 },
{ "](+~)", false, KBD_H, KBD_10, KEY_RIGHT_BRACE, 0, 0, 0 },
{ "ENTER", false, KBD_H, KBD_8, KEY_ENTER, 0, KEYPAD_ENTER, 0 },
{ "PAUSE", false, KBD_H, KBD_7, KEY_PAUSE, 0, 0, 0 },
{ "CAPSLCK", false, KBD_D, KBD_2, KEY_CAPS_LOCK, 0, 0, 0 },
{ "A", false, KBD_D, KBD_4, KEY_A, 0, 0, 0 },
{ "S", false, KBD_D, KBD_1, KEY_S, 0, 0, 0 },
{ "D", false, KBD_D, KBD_8, KEY_D, 0, 0, 0 },
{ "F", false, KBD_D, KBD_11, KEY_F, 0, 0, 0 },
{ "G", false, KBD_D, KBD_5, KEY_G, 0, 0, 0 },
{ "H", false, KBD_D, KBD_3, KEY_H, 0, 0, 0 },
{ "J", false, KBD_D, KBD_7, KEY_J, KEY_END, KEYPAD_1, 0 },
{ "K", false, KBD_D, KBD_6, KEY_K, KEY_DOWN, KEYPAD_2, 0 },
{ "L", false, KBD_D, KBD_10, KEY_L, KEY_PAGE_DOWN, KEYPAD_3, 0 },
{ ";(Oe)", false, KBD_D, KBD_9, KEY_SEMICOLON, 0, KEYPAD_PLUS, 0 },
{ "'(Ae)", false, KBD_H, KBD_5, KEY_QUOTE, 0, 0, 0 },
{ "\\(#)", false, KBD_H, KBD_11, KEY_BACKSLASH, 0, 0, 0 },
{ "INS", false, KBD_H, KBD_1, KEY_INSERT, 0, 0, 0 },
{ "<>", false, KBD_E, KBD_4, KEY_MENU, 0, 0, 0 },
{ "Z", false, KBD_E, KBD_1, KEY_Z, 0, 0, 0 },
{ "X", false, KBD_E, KBD_8, KEY_X, 0, 0, 0 },
{ "C", false, KBD_E, KBD_11, KEY_C, 0, 0, 0 },
{ "V", false, KBD_E, KBD_5, KEY_V, 0, 0, 0 },
{ "B", false, KBD_E, KBD_3, KEY_B, 0, 0, 0 },
{ "N", false, KBD_E, KBD_7, KEY_N, 0, 0, 0 },
{ "M", false, KBD_E, KBD_6, KEY_M, 0, 0, 0 },
{ ",", false, KBD_E, KBD_10, KEY_COMMA, KEY_INSERT, KEYPAD_0, 0 },
{ ".", false, KBD_E, KBD_9, KEY_PERIOD, KEY_DELETE, KEYPAD_PERIOD, 0 },
{ "/", false, KBD_F, KBD_5, KEY_SLASH, 0, KEYPAD_SLASH, 0 },
{ "DEL", false, KBD_F, KBD_1, KEY_DELETE, 0, 0, 0 },
{ "SPACE", false, KBD_G, KBD_3, KEY_SPACE, 0, 0, 0 },
{ "UP", false, KBD_F, KBD_8, KEY_UP, KEY_PAGE_UP, 0, KEY_PAGE_UP },
{ "LEFT", false, KBD_G, KBD_11, KEY_LEFT, KEY_HOME, 0, KEY_HOME },
{ "DOWN", false, KBD_G, KBD_8, KEY_DOWN, KEY_PAGE_DOWN, 0, KEY_PAGE_DOWN },
{ "RIGHT", false, KBD_G, KBD_1, KEY_RIGHT, KEY_END, 0, KEY_END },
{ "none", false, 0, 0, 0, 0 } // last key - no key
};
// second keycode accessible via modifier
#define SECOND_CODE MODIFIERKEY_GUI
// number of teensy pins
#define PIN_MAX 39
// slots hold the normal key values to be sent over USB.
// WARNING - this must be set between 1 and 6
#define SLOT_NORMAL_COUNT 6
struct KeySent slots[SLOT_NORMAL_COUNT];
// modifiers table holds - surprise - the modifier keys states
#define SLOT_MOD_COUNT 6
struct KeySent mods[SLOT_MOD_COUNT];
// general check for button pressed - uses raw key
boolean is_pressed(int rawKey, const struct KeySent* slot, int slotCount) {
for(int i = 0; i < slotCount ; i++) {
if (slot[i].rawKey == rawKey) {
return true;
}
}
return false;
}
// general slot setup function, returns true on change
boolean set_slot(int rawKey, int sendKey, boolean isPressed, struct KeySent* slot, int slotCount) {
boolean currentlyPressed = is_pressed(rawKey, slot, slotCount);
if (currentlyPressed == isPressed) {
return false;
}
for(int i = 0; i < slotCount ; i++) {
if ((isPressed && slot[i].rawKey == 0) || (!isPressed && slot[i].rawKey == rawKey)) {
slot[i].rawKey = isPressed ? rawKey : 0;
slot[i].sendKey = isPressed ? sendKey : 0;
return true;
}
}
return false;
}
// check if normal key pressed
boolean is_key_pressed(int rawKey) {
return is_pressed(rawKey, slots, SLOT_NORMAL_COUNT);
}
// check if mod key pressed
boolean is_mod_pressed(int rawKey) {
return is_pressed(rawKey, mods, SLOT_MOD_COUNT);
}
// Function to load the key name into the first available slot
boolean load_slot(int rawKey, int sendKey) {
return set_slot(rawKey, sendKey, true, slots, SLOT_NORMAL_COUNT);
}
// Function to clear the slot that contains the key name
boolean clear_slot(int rawKey) {
return set_slot(rawKey, 0, false, slots, SLOT_NORMAL_COUNT);
}
// Function to load the modifier key name into the appropriate mod variable
boolean load_mod(int m_key) {
return set_slot(m_key, m_key, true, mods, SLOT_MOD_COUNT);
}
// Function to load 0 into the appropriate mod variable
boolean clear_mod(int m_key) {
return set_slot(m_key, 0, false, mods, SLOT_MOD_COUNT);
}
//
// Function to send the modifier keys over usb
void send_mod() {
int totalMod = 0;
for(int i = 0; i < SLOT_MOD_COUNT ; i++) {
totalMod |= mods[i].sendKey;
}
Keyboard.set_modifier(totalMod);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
for(int i = 0 ; i < SLOT_NORMAL_COUNT ; i++) {
int value = slots[i].sendKey;
switch(i) {
case 0:
Keyboard.set_key1(value);
break;
case 1:
Keyboard.set_key2(value);
break;
case 2:
Keyboard.set_key3(value);
break;
case 3:
Keyboard.set_key4(value);
break;
case 4:
Keyboard.set_key5(value);
break;
case 5:
Keyboard.set_key6(value);
break;
default:
// no send here
break;
}
}
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
////// DEBUG CODE
void d_start() {
#ifndef NODEBUG
while( !Serial );
Serial.begin(9600);
#endif
}
void d_print(const char* format, ...) {
#ifndef NODEBUG
va_list(aptr);
int ret;
char buffer[200];
va_start(aptr, format);
ret = vsprintf(buffer, format, aptr);
va_end(aptr);
if (ret >= 0) {
Serial.println(buffer);
} else {
Serial.println("--- error constructing debug log ---");
}
#endif
}
////// END DEBUG CODE
int KeyCount = 0;
int readPins = 0;
int readPIN[PIN_MAX];
int writePins = 0;
int writePIN[PIN_MAX];
boolean is_in_array(int val, int* arr, int max) {
for(int i = 0 ; i < max ; i++) {
if (arr[i] == val) {
return true;
}
}
return false;
}
void blink() {
go_0(KBD_CAPS);
go_0(KBD_NUM);
go_0(KBD_SCR);
delay(200);
go_1(KBD_CAPS);
go_1(KBD_NUM);
go_1(KBD_SCR);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
d_start();
d_print("Setup Start: LED");
Keyboard.begin();
go_1(KBD_CAPS);
go_1(KBD_NUM);
go_1(KBD_SCR);
d_print("Setup LED complete, setup tables start.");
// setup keyboard tables
for(int i = 0 ; i < SLOT_NORMAL_COUNT ; i++) {
slots[i].rawKey = 0;
slots[i].sendKey = 0;
}
for(int i = 0 ; i < SLOT_MOD_COUNT ; i++) {
mods[i].rawKey = 0;
mods[i].sendKey = 0;
}
// initialize readers and writers
for(int i = 0 ; i < PIN_MAX ; i++) {
readPIN[i] = -1;
writePIN[i] = -1;
}
for(int i = 0 ; ; i++) {
struct KeyDesc& keyDesc = Keys[i];
if (keyDesc.keyCode == 0) {
KeyCount = i;
break;
}
int pin1 = keyDesc.pin1;
boolean pin1InRead = is_in_array(pin1, readPIN, readPins);
boolean pin1InWrite = is_in_array(pin1, writePIN, writePins);
int pin2 = keyDesc.pin2;
boolean pin2InRead = is_in_array(pin2, readPIN, readPins);
boolean pin2InWrite = is_in_array(pin2, writePIN, writePins);
if (pin1InRead || pin2InWrite) {
// means changed places of pins
pin1 = keyDesc.pin2;
pin2 = keyDesc.pin1;
keyDesc.pin1 = pin1;
keyDesc.pin2 = pin2;
boolean pinInRead = pin1InRead;
boolean pinInWrite = pin1InWrite;
pin1InRead = pin2InRead;
pin1InWrite = pin2InWrite;
pin2InRead = pinInRead;
pin2InWrite = pinInWrite;
#ifndef NODEBUG
char text[100];
sprintf(text, "Swapped pins for key %s, pins now are %d %d", keyDesc.name, keyDesc.pin1, keyDesc.pin2);
d_print(text);
#endif
}
if ((pin1InRead && pin1InWrite) || (pin2InRead && pin2InWrite) || (pin1InRead && pin2InRead) || (pin1InWrite && pin2InWrite)) {
#ifndef NODEBUG
char text[100];
sprintf(text, "Error in keyboard configuration for key %s pins %d %d", keyDesc.name, keyDesc.pin1, keyDesc.pin2);
d_print(text);
#endif
continue;
}
if (!pin1InWrite) {
writePIN[writePins++] = pin1;
}
if (!pin2InRead) {
readPIN[readPins++] = pin2;
}
}
for(int i = 0 ; i < writePins ; i++) {
go_z(readPIN[i]);
}
for(int i = 0 ; i < writePins ; i++) {
go_z(writePIN[i]);
}
d_print("Setup OK");
blink();
}
//
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// read LED state
boolean isCapsLock = keyboard_leds & (1<<USB_LED_CAPS_LOCK);
boolean isNumLock = keyboard_leds & (1<<USB_LED_NUM_LOCK);
boolean isScrollLock = keyboard_leds & (1<<USB_LED_SCROLL_LOCK);
// prepare table of scanned keys
for(int i = 0 ; i < KeyCount ; i++) {
Keys[i].scanned = false;
}
for(int j = 0 ; j < writePins ; j++) {
// find maximum pin count in prepared table
int pinAsOutput = writePIN[j];
// set this pin as output, find corresponding keys and set their other pins as input will pullup
// input on other pins
boolean anyKey = false;
boolean hasUnscanned = false;
for(int i = 0 ; i < KeyCount; i++) {
KeyDesc& keyDesc = Keys[i];
hasUnscanned |= !keyDesc.scanned;
if (keyDesc.pin1 == pinAsOutput && !keyDesc.scanned) {
anyKey = true;
go_pu(keyDesc.pin2);
}
}
// if no unscanned keys found, break
if (!hasUnscanned) {
break;
}
// if no keys for this pin found, next loop
if (!anyKey) {
continue;
}
// logic LOW will set this to active
go_0(pinAsOutput);
// delay to settle
delayMicroseconds(10);
// read state
for(int i = 0 ; i < KeyCount ; i++) {
KeyDesc& keyDesc = Keys[i];
if (keyDesc.pin1 == pinAsOutput && !keyDesc.scanned) {
keyDesc.scanned = true;
int keyCode = keyDesc.keyCode;
int sendCode = keyCode;
int fnKeyCode = keyDesc.fnKeyCode;
boolean useFnCode = fnKeyCode != 0 && is_mod_pressed(SECOND_CODE);
if (!useFnCode) {
if (isScrollLock && keyDesc.scrKeyCode != 0) {
sendCode = keyDesc.scrKeyCode;
}
if (isNumLock && keyDesc.numKeyCode != 0) {
sendCode = keyDesc.numKeyCode;
}
}
int currentValue = digitalRead(keyDesc.pin2);
go_z(keyDesc.pin2);
if (!currentValue) {
// new key pressed
if (keyDesc.isModifier) {
if (load_mod(keyCode)) {
d_print("Mod pressed: %s",keyDesc.name);
send_mod();
}
} else {
// if must use code2, then release keyCode and substitute keyCode with second code
// then release SECOND_CODE, then press code2, then re-press SECOND_CODE
if (useFnCode) {
if (load_slot(keyCode, fnKeyCode)) {
d_print("Fn pressed: %s",keyDesc.name);
clear_mod(SECOND_CODE);
send_mod();
send_normals();
load_mod(SECOND_CODE);
send_mod();
}
} else {
// normal way
if (load_slot(keyCode, sendCode)) {
d_print("Normal pressed: %s",keyDesc.name);
send_normals();
}
}
}
} else { // key released
if (keyDesc.isModifier) {
if (clear_mod(keyCode)) {
d_print("Mod released: %s",keyDesc.name);
send_mod();
}
} else {
if (clear_slot(keyCode)) {
d_print("Released: %s",keyDesc.name);
send_normals();
}
}
}
}
}
// disable pin
go_z(pinAsOutput);
}
//
// **********keyboard scan complete
//
// Turn on the LEDs
//
if (isCapsLock) {
go_0(KBD_CAPS);
} else {
go_1(KBD_CAPS);
}
if (isNumLock) {
go_0(KBD_NUM);
} else {
go_1(KBD_NUM);
}
if (isScrollLock) {
go_0(KBD_SCR);
} else {
go_1(KBD_SCR);
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

Binary file not shown.

View file

@ -0,0 +1,500 @@
/* Copyright 2019 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software implements a GRID 1550 Laptop Keyboard Controller using a Teensy LC
// for use with a Windows PC.
//
// Revision History
// Initial Release Dec 12, 2019 - Created from Linux version, uses alt codes for \ and | which are not
// compatible with Linux
//
#define MODIFIERKEY_FN 0x8f // give Fn key a fake HID code
#define CAPS_LED 13 // Teensy LED on IO#13 shows Caps-Lock
#define NUM_LED 0 // Teensy IO#0 shows Num-Lock (wire to anode of new LED)
//
const byte rows_max = 11; // sets the number of rows in the matrix
const byte cols_max = 13; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names
// described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
int normal[rows_max][cols_max] = {
{0,0,0,0,0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,0,KEY_F9,0},
{0,0,0,0,KEY_SPACE,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_ESC,KEY_1,0,KEY_F6,KEY_F7,KEY_F8,KEY_F10,0},
{0,0,0,0,0,KEY_TAB,KEY_Q,KEY_W,KEY_2,KEY_3,0,KEY_6,0},
{0,0,0,0,0,KEY_CAPS_LOCK,KEY_A,KEY_S,KEY_E,KEY_4,KEY_5,KEY_7,0},
{0,0,0,KEY_LEFT,0,0,0,KEY_Z,KEY_F,KEY_D,KEY_8,KEY_9,0},
{0,0,0,KEY_DOWN,0,0,KEY_X,KEY_C,KEY_G,KEY_R,KEY_T,KEY_0,0},
{0,0,0,KEY_NUM_LOCK,0,KEY_SCROLL_LOCK,KEY_V,KEY_B,KEY_H,KEY_Y,KEY_U,KEY_MINUS,0},
{0,0,0,KEY_TILDE,0,KEY_UP,KEY_N,KEY_M,KEY_J,KEY_K,KEY_I,KEY_INSERT,0},
{0,0,0,KEY_BACKSLASH,0,KEY_RIGHT_BRACE,KEY_COMMA,KEY_BACKSPACE,KEY_L,KEY_DELETE,KEY_O,KEY_EQUAL,0},
{0,0,0,KEY_RIGHT,0,KEY_ENTER,KEY_SLASH,KEY_PERIOD,KEY_QUOTE,KEY_SEMICOLON,KEY_LEFT_BRACE,KEY_P,0}
};
// Load the numlock key matrix with key names at the correct row-column location.
// This matrix is the same as the normal matrix except for the number pad keys
// A zero indicates no numlock key at that location.
int numlock[rows_max][cols_max] = {
{0,0,0,0,0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,0,KEY_F9,0},
{0,0,0,0,KEY_SPACE,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_ESC,KEY_1,0,KEY_F6,KEY_F7,KEY_F8,KEY_F10,0},
{0,0,0,0,0,KEY_TAB,KEY_Q,KEY_W,KEY_2,KEY_3,0,KEY_6,0},
{0,0,0,0,0,KEY_CAPS_LOCK,KEY_A,KEY_S,KEY_E,KEY_4,KEY_5,KEYPAD_7,0},
{0,0,0,KEY_LEFT,0,0,0,KEY_Z,KEY_F,KEY_D,KEYPAD_8,KEYPAD_9,0},
{0,0,0,KEY_DOWN,0,0,KEY_X,KEY_C,KEY_G,KEY_R,KEY_T,KEYPAD_ASTERIX,0},
{0,0,0,KEY_NUM_LOCK,0,KEY_SCROLL_LOCK,KEY_V,KEY_B,KEY_H,KEY_Y,KEYPAD_4,KEY_MINUS,0},
{0,0,0,KEY_TILDE,0,KEY_UP,KEY_N,KEY_M,KEYPAD_1,KEYPAD_2,KEYPAD_5,KEY_INSERT,0},
{0,0,0,KEY_BACKSLASH,0,KEY_RIGHT_BRACE,KEYPAD_0,KEY_BACKSPACE,KEYPAD_3,KEY_DELETE,KEYPAD_6,KEY_EQUAL,0},
{0,0,0,KEY_RIGHT,0,KEY_ENTER,KEYPAD_SLASH,KEYPAD_PERIOD,KEY_QUOTE,KEYPAD_PLUS,KEY_LEFT_BRACE,KEYPAD_MINUS,0}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{MODIFIERKEY_LEFT_SHIFT,MODIFIERKEY_FN,MODIFIERKEY_RIGHT_SHIFT,0,0,0,0,0,0,0,0,0,MODIFIERKEY_LEFT_ALT},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,MODIFIERKEY_LEFT_CTRL,0,0,0,0,0,0,0},
{0,0,0,0,0,MODIFIERKEY_RIGHT_ALT,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0}
};
// Load the media key matrix with Fn key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,KEY_HOME,0,0,0,0,0,0,0,0,0},
{0,0,0,KEY_PAGE_DOWN,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_PAUSE,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_PAGE_UP,0,0,0,0,0,KEY_F11,0},
{0,0,0,0,0,0,0,0,0,KEY_F12,0,0,0},
{0,0,0,KEY_END,0,0,0,0,0,0,0,0,0}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//
// Define the Teensy LC I/O numbers (translated from the FPC pin #)
// Row FPC pin # 04,06,08,10,12,14,16,18,20,22,24
// Teensy I/O # 01,02,03,04,05,06,07,08,09,10,11
int Row_IO[rows_max] = {1,2,3,4,5,6,7,8,9,10,11}; // Teensy LC I/O numbers for rows
//
// Column FPC pin # 01,03,05,07,09,11,13,15,17,19,21,23,26
// Teensy I/O # 23,22,24,21,25,20,19,18,17,16,15,14,12
int Col_IO[cols_max] = {23,22,24,21,25,20,19,18,17,16,15,14,12}; // Teensy LC I/O numbers for columns
// Declare variables that will be used by functions
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
}
//
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Pi to Teensy that give keyboard LED status. Caps lock is bit D1.
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys including the Fn special case
if (modifier[x][y] != 0) { // check if modifier key exists
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
if (Fn_pressed) { // only send modifier keys if Fn key is not currently held down
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else { // Fn key is already held down so check if right alt key is pressed and send print screen if yes
if (modifier[x][y] == MODIFIERKEY_RIGHT_ALT) {
Keyboard.press(KEY_PRINTSCREEN);
delay(5);
Keyboard.release(KEY_PRINTSCREEN);
delay(5);
}
}
}
else {
Fn_pressed = LOW; // Fn status variable is active low
old_key[x][y] = LOW; // old_key state is "pressed" (active low)
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
else {
Fn_pressed = HIGH; // Fn is no longer active
old_key[x][y] = HIGH; // old_key state is "not pressed"
}
}
}
// ***********end of modifier section
//
// ***********Normal keys and media keys in this section
else if ((normal[x][y] != 0) || (media[x][y] != 0)) { // check if normal or media key exists at this location in the array
if (!digitalRead(Col_IO[y]) && (old_key[x][y]) && (!slots_full)) { // check if key pressed and not previously pressed and slots not full
old_key[x][y] = LOW; // Save state of key as "pressed"
if (Fn_pressed) { // Fn_pressed is active low so it is not pressed and normal key needs to be sent
if (keyboard_leds & 1) { // test if Num Lock is turned on
load_slot(numlock[x][y]); //update first available slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got pressed
}
else {
load_slot(normal[x][y]); //update first available slot with key name from normal matrix
send_normals(); // send all slots over USB including the key that just got pressed
}
}
else if (media[x][y] != 0) { // Fn is pressed so send media if a key exists in the matrix
Keyboard.press(media[x][y]); // media key is sent using keyboard press function per PJRC
delay(5); // delay 5 milliseconds before releasing to make sure it gets sent over USB
Keyboard.release(media[x][y]); // send media key release
}
else if (normal[x][y] == KEY_Z) { // Fn is pressed but no media key so check if KEY_Z is pressed
// if using this keyboard on a Raspberry Pi, comment out the following Alt code section and uncomment the unicode section
// *******Windows PC Alt Code sequence to give a backslash is Alt 92*******************************
Keyboard.press(MODIFIERKEY_LEFT_ALT); // Push and hold Left Alt
delay(5);
Keyboard.press(KEYPAD_9); // Push 9
delay(5);
Keyboard.release(KEYPAD_9); // Release 9
delay(5);
Keyboard.press(KEYPAD_2); // Push 2
delay(5);
Keyboard.release(KEYPAD_2); // Release 2
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_ALT); // Release Left Alt
delay(5);
// *******Raspberry Pi Unicode sequence to give a backslash is Control+Shift+u 5c Enter*****************************
/* Keyboard.press(MODIFIERKEY_LEFT_CTRL); // Push and hold Left Control
delay(5);
Keyboard.press(MODIFIERKEY_LEFT_SHIFT); // Push and hold Left Shift
delay(5);
Keyboard.press(KEY_U); // Push u
delay(5);
Keyboard.release(KEY_U); // Release u
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_CTRL); // Release Left Control
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_SHIFT); // Release Left Shift
delay(5);
Keyboard.press(KEY_5); // Push 5
delay(5);
Keyboard.release(KEY_5); // Release 5
delay(5);
Keyboard.press(KEY_C); // Push c
delay(5);
Keyboard.release(KEY_C); // Release c
delay(5);
Keyboard.press(KEY_ENTER); // Push Enter
delay(5);
Keyboard.release(KEY_ENTER); // Release Enter
delay(5);
*/
}
else if (normal[x][y] == KEY_X) { // Fn is pressed but no media key so check if KEY_X is pressed
// if using this keyboard on a Raspberry Pi, comment out the following Alt code section and uncomment the unicode section
// *******Windows PC Alt Code sequence to give a | is Alt 124*******************************
Keyboard.press(MODIFIERKEY_LEFT_ALT); // Push and hold Left Alt
delay(5);
Keyboard.press(KEYPAD_1); // Push 1
delay(5);
Keyboard.release(KEYPAD_1); // Release 1
delay(5);
Keyboard.press(KEYPAD_2); // Push 2
delay(5);
Keyboard.release(KEYPAD_2); // Release 2
delay(5);
Keyboard.press(KEYPAD_4); // Push 4
delay(5);
Keyboard.release(KEYPAD_4); // Release 4
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_ALT); // Release Left Alt
delay(5);
// *******Raspberry Pi Unicode sequence to give a | is Control+Shift+u 7c Enter*****************************
/* Keyboard.press(MODIFIERKEY_LEFT_CTRL); // Push and hold Left Control
delay(5);
Keyboard.press(MODIFIERKEY_LEFT_SHIFT); // Push and hold Left Shift
delay(5);
Keyboard.press(KEY_U); // Push u
delay(5);
Keyboard.release(KEY_U); // Release u
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_CTRL); // Release Left Control
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_SHIFT); // Release Left Shift
delay(5);
Keyboard.press(KEY_7); // Push 7
delay(5);
Keyboard.release(KEY_7); // Release 7
delay(5);
Keyboard.press(KEY_C); // Push c
delay(5);
Keyboard.release(KEY_C); // Release c
delay(5);
Keyboard.press(KEY_ENTER); // Push Enter
delay(5);
Keyboard.release(KEY_ENTER); // Release Enter
delay(5);
*/
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if (Fn_pressed) { // Fn is not pressed
if (keyboard_leds & 1) { // test if Num Lock is turned on
clear_slot(numlock[x][y]); //clear slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got released
}
else {
clear_slot(normal[x][y]); //clear slot with key name from normal matrix
send_normals(); // send all slots over USB including the key that just got released
}
}
}
}
// **************end of normal and media key section
//
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// Turn on the LED on the Teensy for Caps Lock based on bit 1 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the LED
}
else {
go_0(CAPS_LED); // turn off the LED
}
//
// Turn on the LED on the Teensy for Num Lock based on bit 0 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1) { // mask off all bits but D0 and test if set
go_1(NUM_LED); // turn on the Num Lock LED
}
else {
go_0(NUM_LED); // turn off the Num Lock LED
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

View file

@ -0,0 +1,501 @@
/* Copyright 2019 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software implements a GRID 1550 Laptop Keyboard Controller using a Teensy LC
// for use with a Raspberry Pi or similar Linux computer.
//
// Revision History
// Initial Release Sept 15, 2019
// Revision Dec 8, 2019 - Increased delays from 5ms to 25ms for the Alt 92 key codes to give backslash
// Revision Dec 11, 2019 - Added Unicode method to give backslash and pipe since Alt codes don't work for Linux
//
#define MODIFIERKEY_FN 0x8f // give Fn key a fake HID code
#define CAPS_LED 13 // Teensy LED on IO#13 shows Caps-Lock
#define NUM_LED 0 // Teensy IO#0 shows Num-Lock (wire to anode of new LED)
//
const byte rows_max = 11; // sets the number of rows in the matrix
const byte cols_max = 13; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names
// described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
int normal[rows_max][cols_max] = {
{0,0,0,0,0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,0,KEY_F9,0},
{0,0,0,0,KEY_SPACE,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_ESC,KEY_1,0,KEY_F6,KEY_F7,KEY_F8,KEY_F10,0},
{0,0,0,0,0,KEY_TAB,KEY_Q,KEY_W,KEY_2,KEY_3,0,KEY_6,0},
{0,0,0,0,0,KEY_CAPS_LOCK,KEY_A,KEY_S,KEY_E,KEY_4,KEY_5,KEY_7,0},
{0,0,0,KEY_LEFT,0,0,0,KEY_Z,KEY_F,KEY_D,KEY_8,KEY_9,0},
{0,0,0,KEY_DOWN,0,0,KEY_X,KEY_C,KEY_G,KEY_R,KEY_T,KEY_0,0},
{0,0,0,KEY_NUM_LOCK,0,KEY_SCROLL_LOCK,KEY_V,KEY_B,KEY_H,KEY_Y,KEY_U,KEY_MINUS,0},
{0,0,0,KEY_TILDE,0,KEY_UP,KEY_N,KEY_M,KEY_J,KEY_K,KEY_I,KEY_INSERT,0},
{0,0,0,KEY_BACKSLASH,0,KEY_RIGHT_BRACE,KEY_COMMA,KEY_BACKSPACE,KEY_L,KEY_DELETE,KEY_O,KEY_EQUAL,0},
{0,0,0,KEY_RIGHT,0,KEY_ENTER,KEY_SLASH,KEY_PERIOD,KEY_QUOTE,KEY_SEMICOLON,KEY_LEFT_BRACE,KEY_P,0}
};
// Load the numlock key matrix with key names at the correct row-column location.
// This matrix is the same as the normal matrix except for the number pad keys
// A zero indicates no numlock key at that location.
int numlock[rows_max][cols_max] = {
{0,0,0,0,0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,0,KEY_F9,0},
{0,0,0,0,KEY_SPACE,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_ESC,KEY_1,0,KEY_F6,KEY_F7,KEY_F8,KEY_F10,0},
{0,0,0,0,0,KEY_TAB,KEY_Q,KEY_W,KEY_2,KEY_3,0,KEY_6,0},
{0,0,0,0,0,KEY_CAPS_LOCK,KEY_A,KEY_S,KEY_E,KEY_4,KEY_5,KEYPAD_7,0},
{0,0,0,KEY_LEFT,0,0,0,KEY_Z,KEY_F,KEY_D,KEYPAD_8,KEYPAD_9,0},
{0,0,0,KEY_DOWN,0,0,KEY_X,KEY_C,KEY_G,KEY_R,KEY_T,KEYPAD_ASTERIX,0},
{0,0,0,KEY_NUM_LOCK,0,KEY_SCROLL_LOCK,KEY_V,KEY_B,KEY_H,KEY_Y,KEYPAD_4,KEY_MINUS,0},
{0,0,0,KEY_TILDE,0,KEY_UP,KEY_N,KEY_M,KEYPAD_1,KEYPAD_2,KEYPAD_5,KEY_INSERT,0},
{0,0,0,KEY_BACKSLASH,0,KEY_RIGHT_BRACE,KEYPAD_0,KEY_BACKSPACE,KEYPAD_3,KEY_DELETE,KEYPAD_6,KEY_EQUAL,0},
{0,0,0,KEY_RIGHT,0,KEY_ENTER,KEYPAD_SLASH,KEYPAD_PERIOD,KEY_QUOTE,KEYPAD_PLUS,KEY_LEFT_BRACE,KEYPAD_MINUS,0}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{MODIFIERKEY_LEFT_SHIFT,MODIFIERKEY_FN,MODIFIERKEY_RIGHT_SHIFT,0,0,0,0,0,0,0,0,0,MODIFIERKEY_LEFT_ALT},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,MODIFIERKEY_LEFT_CTRL,0,0,0,0,0,0,0},
{0,0,0,0,0,MODIFIERKEY_RIGHT_ALT,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0}
};
// Load the media key matrix with Fn key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,KEY_HOME,0,0,0,0,0,0,0,0,0},
{0,0,0,KEY_PAGE_DOWN,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_PAUSE,0,0,0,0,0,0,0},
{0,0,0,0,0,KEY_PAGE_UP,0,0,0,0,0,KEY_F11,0},
{0,0,0,0,0,0,0,0,0,KEY_F12,0,0,0},
{0,0,0,KEY_END,0,0,0,0,0,0,0,0,0}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//
// Define the Teensy LC I/O numbers (translated from the FPC pin #)
// Row FPC pin # 04,06,08,10,12,14,16,18,20,22,24
// Teensy I/O # 01,02,03,04,05,06,07,08,09,10,11
int Row_IO[rows_max] = {1,2,3,4,5,6,7,8,9,10,11}; // Teensy LC I/O numbers for rows
//
// Column FPC pin # 01,03,05,07,09,11,13,15,17,19,21,23,26
// Teensy I/O # 23,22,24,21,25,20,19,18,17,16,15,14,12
int Col_IO[cols_max] = {23,22,24,21,25,20,19,18,17,16,15,14,12}; // Teensy LC I/O numbers for columns
// Declare variables that will be used by functions
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
}
//
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Pi to Teensy that give keyboard LED status. Caps lock is bit D1.
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys including the Fn special case
if (modifier[x][y] != 0) { // check if modifier key exists
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
if (Fn_pressed) { // only send modifier keys if Fn key is not currently held down
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else { // Fn key is already held down so check if right alt key is pressed and send print screen if yes
if (modifier[x][y] == MODIFIERKEY_RIGHT_ALT) {
Keyboard.press(KEY_PRINTSCREEN);
delay(5);
Keyboard.release(KEY_PRINTSCREEN);
delay(5);
}
}
}
else {
Fn_pressed = LOW; // Fn status variable is active low
old_key[x][y] = LOW; // old_key state is "pressed" (active low)
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
else {
Fn_pressed = HIGH; // Fn is no longer active
old_key[x][y] = HIGH; // old_key state is "not pressed"
}
}
}
// ***********end of modifier section
//
// ***********Normal keys and media keys in this section
else if ((normal[x][y] != 0) || (media[x][y] != 0)) { // check if normal or media key exists at this location in the array
if (!digitalRead(Col_IO[y]) && (old_key[x][y]) && (!slots_full)) { // check if key pressed and not previously pressed and slots not full
old_key[x][y] = LOW; // Save state of key as "pressed"
if (Fn_pressed) { // Fn_pressed is active low so it is not pressed and normal key needs to be sent
if (keyboard_leds & 1) { // test if Num Lock is turned on
load_slot(numlock[x][y]); //update first available slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got pressed
}
else {
load_slot(normal[x][y]); //update first available slot with key name from normal matrix
send_normals(); // send all slots over USB including the key that just got pressed
}
}
else if (media[x][y] != 0) { // Fn is pressed so send media if a key exists in the matrix
Keyboard.press(media[x][y]); // media key is sent using keyboard press function per PJRC
delay(5); // delay 5 milliseconds before releasing to make sure it gets sent over USB
Keyboard.release(media[x][y]); // send media key release
}
else if (normal[x][y] == KEY_Z) { // Fn is pressed but no media key so check if KEY_Z is pressed
// if using this keyboard on a Windows PC, uncomment the following Alt code section and comment out the unicode section
// *******Windows PC Alt Code sequence to give a backslash is Alt 92*******************************
/* Keyboard.press(MODIFIERKEY_LEFT_ALT); // Push and hold Left Alt
delay(5);
Keyboard.press(KEYPAD_9); // Push 9
delay(5);
Keyboard.release(KEYPAD_9); // Release 9
delay(5);
Keyboard.press(KEYPAD_2); // Push 2
delay(5);
Keyboard.release(KEYPAD_2); // Release 2
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_ALT); Release Left Alt
delay(5);
*/
// *******Raspberry Pi Unicode sequence to give a backslash is Control+Shift+u 5c Enter*****************************
Keyboard.press(MODIFIERKEY_LEFT_CTRL); // Push and hold Left Control
delay(5);
Keyboard.press(MODIFIERKEY_LEFT_SHIFT); // Push and hold Left Shift
delay(5);
Keyboard.press(KEY_U); // Push u
delay(5);
Keyboard.release(KEY_U); // Release u
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_CTRL); // Release Left Control
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_SHIFT); // Release Left Shift
delay(5);
Keyboard.press(KEY_5); // Push 5
delay(5);
Keyboard.release(KEY_5); // Release 5
delay(5);
Keyboard.press(KEY_C); // Push c
delay(5);
Keyboard.release(KEY_C); // Release c
delay(5);
Keyboard.press(KEY_ENTER); // Push Enter
delay(5);
Keyboard.release(KEY_ENTER); // Release Enter
delay(5);
}
else if (normal[x][y] == KEY_X) { // Fn is pressed but no media key so check if KEY_X is pressed
// if using this keyboard on a Windows PC, uncomment the following Alt code section and comment out the unicode section
// *******Windows PC Alt Code sequence to give a | is Alt 124*******************************
/* Keyboard.press(MODIFIERKEY_LEFT_ALT); // Push and hold Left Alt
delay(5);
Keyboard.press(KEYPAD_1); // Push 1
delay(5);
Keyboard.release(KEYPAD_1); // Release 1
delay(5);
Keyboard.press(KEYPAD_2); // Push 2
delay(5);
Keyboard.release(KEYPAD_2); // Release 2
delay(5);
Keyboard.press(KEYPAD_4); // Push 4
delay(5);
Keyboard.release(KEYPAD_4); // Release 4
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_ALT); Release Left Alt
delay(5);
*/
// *******Raspberry Pi Unicode sequence to give a | is Control+Shift+u 7c Enter*****************************
Keyboard.press(MODIFIERKEY_LEFT_CTRL); // Push and hold Left Control
delay(5);
Keyboard.press(MODIFIERKEY_LEFT_SHIFT); // Push and hold Left Shift
delay(5);
Keyboard.press(KEY_U); // Push u
delay(5);
Keyboard.release(KEY_U); // Release u
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_CTRL); // Release Left Control
delay(5);
Keyboard.release(MODIFIERKEY_LEFT_SHIFT); // Release Left Shift
delay(5);
Keyboard.press(KEY_7); // Push 7
delay(5);
Keyboard.release(KEY_7); // Release 7
delay(5);
Keyboard.press(KEY_C); // Push c
delay(5);
Keyboard.release(KEY_C); // Release c
delay(5);
Keyboard.press(KEY_ENTER); // Push Enter
delay(5);
Keyboard.release(KEY_ENTER); // Release Enter
delay(5);
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if (Fn_pressed) { // Fn is not pressed
if (keyboard_leds & 1) { // test if Num Lock is turned on
clear_slot(numlock[x][y]); //clear slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got released
}
else {
clear_slot(normal[x][y]); //clear slot with key name from normal matrix
send_normals(); // send all slots over USB including the key that just got released
}
}
}
}
// **************end of normal and media key section
//
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// Turn on the LED on the Teensy for Caps Lock based on bit 1 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the LED
}
else {
go_0(CAPS_LED); // turn off the LED
}
//
// Turn on the LED on the Teensy for Num Lock based on bit 0 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1) { // mask off all bits but D0 and test if set
go_1(NUM_LED); // turn on the Num Lock LED
}
else {
go_0(NUM_LED); // turn off the Num Lock LED
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

View file

@ -0,0 +1,156 @@
/* Copyright 2019 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software reads the 6 logic signals from a Grid 1550 laptop "mouse" and converts it to USB.
// The Grid 1550 laptop has a shaft in place of a touchpad. The shaft is rolled forward or backwards
// which moves the cursor up or down. Likewise the entire shaft can be slid left or right to move the cursor left or right.
// There are two buttons on the left of the shaft and two buttons on the right of the shaft.
// The two buttons closest to the shaft act like normal left mouse buttons.
// The two buttons furthest from the shaft are right mouse buttons.
// Pins 3 and 4 are active for up/down movement. Pins 5 and 6 are active for left/right movement.
// The pin pairs use a rotational encoding scheme that is described in the following wiki page:
// https://en.wikipedia.org/wiki/Rotary_encoder
//
// I/O pins 0 & 1 are used to enable/disable the mouse and to adjust its speed
// I/O 1 I/O 0 Result
// High High Count by 1
// High Low Count by 2
// Low High Count by 3
// Low Low Mouse disabled
//
// Revision History
// Rev 1.0 - Nov 3, 2019 - Original Release
// Rev 1.1 - Nov 4, 2019 - Fixed swapped mouse buttons
// Rev 1.2 - Nov 4, 2019 - Added speed controls
//
#include <TimerOne.h> // used for making a 24msec timer
//
#define SPEED_PIN0 0 // Input with pullup - Can be driven by the Teensy controlling the keyboard
#define SPEED_PIN1 1 // Input with pullup - Can be driven by the Teensy controlling the keyboard
// Connect the Teensy I/O's 3 thru 8 to the Grid mouse connector pins 3 thru 8
#define MOUSE_PIN3 3 // Left/Right Encoder signal A
#define MOUSE_PIN4 4 // Left/Right Encoder signal B
#define MOUSE_PIN5 5 // Up/Down Encoder signal A
#define MOUSE_PIN6 6 // Up/Down Encoder signal B
#define MOUSE_PIN7 7 // Right Mouse Button
#define MOUSE_PIN8 8 // Left Mouse Button
//
// Declare and initialize variables
boolean left_button = 0; // on/off variable for left button, 1 = pushed
boolean right_button = 0; // on/off variable for right button, 1 = pushed
boolean old_left_button = 0; // on/off variable for left button from the previous cycle
boolean old_right_button = 0; // on/off variable for right button from the previous cycle
boolean button_change = 0; // Shows when the left or right buttons have changed, 1 = change
volatile int8_t x_count = 0; // left/right movement. 8 bit signed. Volatile because used in ISR
volatile int8_t y_count = 0; // up or down movement. 8 bit signed. Volatile because used in ISR
boolean x_A; // holds the state of pin 3
boolean x_A_Last; // holds the previous state of pin 3
boolean y_A; // holds the state of pin 5
boolean y_A_Last; // holds the previous state of pin 5
boolean mouse_on = 1; // high when mouse is turned on, low when turned off
boolean speed0 = 1; // result of reading pin 0
boolean speed1 = 1; // result of reading pin 1
int8_t count_by = 1; // value to add or substract from x & y counters (1, 2, or 3 depending on SPEED_PIN0 & 1)
//
// USB Interrupt Service Routine (ISR) Activates every 24msec when timer1 ticks
void sendUSB(void) {
// send the x and y data back to the host via usb if either counter is non-zero.
// if the mouse is turned off, the counters can't increment so they will both be zero.
if ((x_count != 0x00) || (y_count != 0x00)) {
Mouse.move(x_count,y_count);
x_count = 0; // clear the x and y counters
y_count = 0;
}
// read the touchpad left and right buttons
if (!digitalRead(MOUSE_PIN7)) { // check if right button is low (low = pushed)
right_button = 1; // save state of button
}
else { // clear right button
right_button = 0; // save state of button
}
if (!digitalRead(MOUSE_PIN8)) { // check if left button is low (low = pushed)
left_button = 1; // save state of button
}
else { // clear left button
left_button = 0; // save state of button
}
// Determine if the left or right mouse buttons have changed (using XOR) since the last cycle
button_change = (left_button ^ old_left_button) | (right_button ^ old_right_button);
// Don't send button status if there's no change since last time or if the mouse is turned off.
if (button_change && mouse_on){
Mouse.set_buttons(left_button, 0, right_button); // send button status over USB
}
old_left_button = left_button; // remember button status for the next cycle
old_right_button = right_button;
}
// *****************Setup the timer and input pins***********************************************
void setup() {
Timer1.initialize(24000); // 24msec timer
Timer1.attachInterrupt(sendUSB); // sendUSB function will run every 24 milliseconds
pinMode(SPEED_PIN0, INPUT_PULLUP); // both speed controls are inputs with pullups
pinMode(SPEED_PIN1, INPUT_PULLUP); // if left floating, both will be 1's to give count_by 1
pinMode(MOUSE_PIN3, INPUT); // teensy I/O 3 is an input for left/right signal A
pinMode(MOUSE_PIN4, INPUT); // teensy I/O 4 is an input for left/right signal B
pinMode(MOUSE_PIN5, INPUT); // teensy I/O 5 is an input for up/down signal A
pinMode(MOUSE_PIN6, INPUT); // teensy I/O 6 is an input for up/down signal B
pinMode(MOUSE_PIN7, INPUT); // teensy I/O 7 is an input for right mouse button
pinMode(MOUSE_PIN8, INPUT); // teensy I/O 8 is an input for left mouse button
x_A_Last = digitalRead(MOUSE_PIN3); // save logic state of pin 3
y_A_Last = digitalRead(MOUSE_PIN5); // save logic state of pin 5
}
// *********Main Loop constantly watches for activity on pins 3 and 5 plus status on pins 0 & 1*********
void loop() {
// adjust the x counter if pin 3 changes (i.e. has an up or down edge)
x_A = digitalRead(MOUSE_PIN3); // Read the current state of pin 3
if (x_A != x_A_Last){ // If pin 3 is different than the previous pin 3, an edge has occured.
noInterrupts(); // disable the timer interrupt while modifying the x counter
if (digitalRead(MOUSE_PIN4) != x_A) { // see if pin 4 equals pin 3
x_count = x_count + count_by; // pin 3 is ahead of pin 4 so move the cursor to the right by increasing the counter
}
else {
x_count = x_count - count_by; // pin 3 is behind pin 4 so move the cursor to the left by decreasing the counter
}
interrupts(); // enable the timer interrupt
}
x_A_Last = x_A; // save the state of pin 3 for the next loop
// adjust the y counter if pin 5 has an up or down edge
y_A = digitalRead(MOUSE_PIN5); // Reads the current state of pin 5
if (y_A != y_A_Last){ // If pin 5 is different than the previous pin 5, an edge has occured.
noInterrupts(); // disable the timer interrupt while modifying the y counter
if (digitalRead(MOUSE_PIN6) != y_A) { // see if pin 6 equals pin 5
y_count = y_count + count_by; // pin 5 is ahead of pin 6 so move the cursor down by increasing the counter
}
else {
y_count = y_count - count_by; // pin 5 is behind pin 6 so move the cursor up by decreasing the counter
}
interrupts(); // enable the timer interrupt
}
y_A_Last = y_A; // save the state of pin 5 for the next loop
// Read speed pins 0 & 1 to adjust count and on/off control
speed0 = digitalRead(SPEED_PIN0);
speed1 = digitalRead(SPEED_PIN1);
if (speed0 && speed1) { // this is the high & high case
count_by = 1; // slow speed movement
mouse_on = 1; // turn mouse on
}
else if (!speed0 && speed1) { // this is the low & high case
count_by = 2; // middle speed movement
mouse_on = 1; // turn mouse on
}
else if (speed0 && !speed1){ // this is the high & low case
count_by = 3; // high speed movement
mouse_on = 1; // turn mouse on
}
else { // this is the low & low case
count_by = 0; // no movement
mouse_on = 0; // turn mouse off
}
}

Binary file not shown.

View file

@ -0,0 +1,809 @@
/* Copyright 2020 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software controls a IBM ThinkPad T43 Laptop Keyboard and PS/2 Trackpoint using a Teensy 3.2 on
// a daughterboard with a 40 pin connector.
// This routine uses the Teensyduino "Micro-Manager Method" to send Normal and Modifier
// keys over USB. Only the volume control multi-media keys are supported by this routine.
// Description of Teensyduino keyboard functions is at www.pjrc.com/teensy/td_keyboard.html
// The ps/2 code uses the USB PJRC Mouse functions at www.pjrc.com/teensy/td_mouse.html
// The ps/2 code has a watchdog timer so the code can't hang if a clock edge is missed.
// In the Arduino IDE, select Tools, Teensy LC. Also under Tools, select Keyboard+Mouse+Joystick
//
// Revision History
// Rev 1.0 - March 23, 2020 - Original Release
// Rev 1.1 - March 24, 2020 - Added Num pad keys
//
// Trackpoint signals
#define TP_DATA 18 // ps/2 data to trackpoint
#define TP_CLK 19 // ps/2 clock to trackpoint
#define TP_RESET 0 // active high trackpoint reset at power up
// Keyboard LEDs
#define CAPS_LED 28 // Wire these 3 I/O's to the anode side of LED's
#define NUM_LED 29 // Wire the cathode side thru a dropping resistor
#define SCRL_LED 30 // to ground.
#define BLINK_LED 13 // The LED on the Teensy is programmed to blink
// Keyboard Fn key (aka HOTKEY)
#define HOTKEY 8 // Fn key plus side
#define HOTKEY_RTN 23 // Fn key minus side (always driven low in this routine)
// sync signal for measuring scan frequency
#define SYNC 27
// Set the keyboard row & column size
const byte rows_max = 16; // sets the number of rows in the matrix
const byte cols_max = 8; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
int normal[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0},
{0,0,KEY_PRINTSCREEN,KEY_SCROLL_LOCK,0,0,0,0},
{0,0,0,0,0,0,0,0},
{KEY_LEFT,KEY_PAUSE,KEY_END,0,0,KEY_HOME,KEY_UP,0},
{0,KEY_Z,KEY_1,KEY_Q,KEY_TAB,KEY_TILDE,KEY_ESC,KEY_A},
{0,0,KEY_PAGE_DOWN,0,0,KEY_PAGE_UP,0,0},
{KEY_RIGHT,0,KEY_F12,0,0,KEY_INSERT,0,0},
{KEY_DOWN,0,KEY_F11,0,0,KEY_DELETE,0,0},
{KEY_SLASH,0,KEY_0,KEY_P,KEY_LEFT_BRACE,KEY_MINUS,KEY_QUOTE,KEY_SEMICOLON},
{0,KEY_C,KEY_3,KEY_E,KEY_F3,KEY_F2,KEY_F4,KEY_D},
{0,KEY_PERIOD,KEY_9,KEY_O,KEY_F7,KEY_F8,0,KEY_L},
{KEY_B,KEY_V,KEY_4,KEY_R,KEY_T,KEY_5,KEY_G,KEY_F},
{0,KEY_X,KEY_2,KEY_W,KEY_CAPS_LOCK,KEY_F1,0,KEY_S},
{KEY_SPACE,KEY_ENTER,KEY_F10,0,KEY_BACKSPACE,KEY_F9,KEY_F5,KEY_BACKSLASH},
{0,KEY_COMMA,KEY_8,KEY_I,KEY_RIGHT_BRACE,KEY_EQUAL,KEY_F6,KEY_K},
{KEY_N,KEY_M,KEY_7,KEY_U,KEY_Y,KEY_6,KEY_H,KEY_J}
};
// Load the num lock key matrix which is the same as the normal matrix except for the number pad keys
// A zero indicates no key at that location.
int numlock[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0},
{0,0,KEY_PRINTSCREEN,KEY_SCROLL_LOCK,0,0,0,0},
{0,0,0,0,0,0,0,0},
{KEY_LEFT,KEY_PAUSE,KEY_END,0,0,KEY_HOME,KEY_UP,0},
{0,KEY_Z,KEY_1,KEY_Q,KEY_TAB,KEY_TILDE,KEY_ESC,KEY_A},
{0,0,KEY_PAGE_DOWN,0,0,KEY_PAGE_UP,0,0},
{KEY_RIGHT,0,KEY_F12,0,0,KEY_INSERT,0,0},
{KEY_DOWN,0,KEY_F11,0,0,KEY_DELETE,KEY_MENU,0},
{KEYPAD_PLUS,0,KEYPAD_SLASH,KEYPAD_ASTERIX,KEY_LEFT_BRACE,KEY_MINUS,KEY_QUOTE,KEYPAD_MINUS},
{0,KEY_C,KEY_3,KEY_E,KEY_F3,KEY_F2,KEY_F4,KEY_D},
{0,KEYPAD_PERIOD,KEYPAD_9,KEYPAD_6,KEY_F7,KEY_F8,0,KEYPAD_3},
{KEY_B,KEY_V,KEY_4,KEY_R,KEY_T,KEY_5,KEY_G,KEY_F},
{0,KEY_X,KEY_2,KEY_W,KEY_CAPS_LOCK,KEY_F1,0,KEY_S},
{KEY_SPACE,KEY_ENTER,KEY_F10,0,KEY_BACKSPACE,KEY_F9,KEY_F5,KEY_BACKSLASH},
{0,KEY_COMMA,KEYPAD_8,KEYPAD_5,KEY_RIGHT_BRACE,KEY_EQUAL,KEY_F6,KEYPAD_2},
{KEY_N,KEYPAD_0,KEYPAD_7,KEYPAD_4,KEY_Y,KEY_6,KEY_H,KEYPAD_1}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,MODIFIERKEY_RIGHT_CTRL,0,0,0,MODIFIERKEY_LEFT_CTRL,0,0},
{MODIFIERKEY_RIGHT_ALT,0,0,0,0,0,MODIFIERKEY_LEFT_ALT,0},
{0,MODIFIERKEY_RIGHT_SHIFT,0,0,MODIFIERKEY_LEFT_SHIFT,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,MODIFIERKEY_GUI,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
// Load the media key matrix with key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,KEY_MEDIA_VOLUME_INC,KEY_MEDIA_VOLUME_DEC,0,0,KEY_MEDIA_MUTE},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1}
};
//
// Define the Teensy 3.2 I/O numbers
//
// Row FPC pin #
// Teensy I/O # 22,01,21,02,20,03,04,05,06,24,07,25,33,26,32,31
int Row_IO[rows_max] = {22,1,21,2,20,3,4,5,6,24,7,25,33,26,32,31}; // Teensy 3.2 I/O numbers for rows
//
// Column FPC pin #
// Teensy I/O # 14,15,16,12,11,10,09,17
int Col_IO[cols_max] = {14,15,16,12,11,10,9,17}; // Teensy 3.2 I/O numbers for columns
//
// Declare variables that will be used by functions
boolean trackpoint_error = LOW; // sent high when track point routine times out
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// **************Functions common to keyboard and trackpoint**************************
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
// *****************Functions for Trackpoint***************************
//
// Function to send the trackpoint a byte of data (command)
//
void tp_write(char send_data)
{
unsigned int timeout = 200; // breakout of loop if over this value in msec
elapsedMillis watchdog; // zero the watchdog timer clock
char odd_parity = 0; // clear parity bit count
// Enable the bus by floating the clock and data
go_z(TP_CLK); //
go_z(TP_DATA); //
delayMicroseconds(250); // wait before requesting the bus
go_0(TP_CLK); // Send the Clock line low to request to transmit data
delayMicroseconds(100); // wait for 100 microseconds per bus spec
go_0(TP_DATA); // Send the Data line low (the start bit)
delayMicroseconds(1); //
go_z(TP_CLK); // Release the Clock line so it is pulled high
delayMicroseconds(1); // give some time to let the clock line go high
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
// send the 8 bits of send_data
for (int j=0; j<8; j++) {
if (send_data & 1) { //check if lsb is set
go_z(TP_DATA); // send a 1 to TP
odd_parity = odd_parity + 1; // keep running total of 1's sent
}
else {
go_0(TP_DATA); // send a 0 to TP
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
send_data = send_data >> 1; // shift data right by 1 to prepare for next loop
}
// send the parity bit
if (odd_parity & 1) { //check if lsb of parity is set
go_0(TP_DATA); // already odd so send a 0 to TP
}
else {
go_z(TP_DATA); // send a 1 to TP to make parity odd
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
go_z(TP_DATA); // Release the Data line so it goes high as the stop bit
delayMicroseconds(80); // testing shows delay at least 40us
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
delayMicroseconds(1); // wait to let the data settle
if (digitalRead(TP_DATA)) { // Ack bit s/b low if good transfer
trackpoint_error = HIGH; //bad ack bit so set the error flag
}
while ((digitalRead(TP_CLK) == LOW) || (digitalRead(TP_DATA) == LOW)) { // loop if clock or data are low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
// Inhibit the bus so the tp only talks when we're listening
go_0(TP_CLK);
}
//
// Function to get a byte of data from the trackpoint
//
char tp_read(void)
{
unsigned int timeout = 200; // breakout of loop if over this value in msec
elapsedMillis watchdog; // zero the watchdog timer clock
char rcv_data = 0; // initialize to zero
char mask = 1; // shift a 1 across the 8 bits to select where to load the data
char rcv_parity = 0; // count the ones received
go_z(TP_CLK); // release the clock
go_z(TP_DATA); // release the data
delayMicroseconds(5); // delay to let clock go high
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
trackpoint_error = HIGH; // No start bit so set the error flag
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
for (int k=0; k<8; k++) {
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
if (digitalRead(TP_DATA)) { // check if data is high
rcv_data = rcv_data | mask; // set the appropriate bit in the rcv data
rcv_parity++; // increment the parity bit counter
}
mask = mask << 1;
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
}
// receive parity
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
if (digitalRead(TP_DATA)) { // check if received parity is high
rcv_parity++; // increment the parity bit counter
}
rcv_parity = rcv_parity & 1; // mask off all bits except the lsb
if (rcv_parity == 0) { // check for bad (even) parity
trackpoint_error = HIGH; //bad parity so set the error flag
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
// stop bit
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
trackpoint_error = HIGH; //bad stop bit so set the error flag
}
delayMicroseconds(1); // delay to let the clock settle out
while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
if (watchdog >= timeout) { //check for infinite loop
trackpoint_error = HIGH; // set error flag
break; // break out of infinite loop
}
}
// Inhibit the bus so the tp only talks when we're listening
go_0(TP_CLK);
return rcv_data; // pass the received data back
}
//
void trackpoint_init()
{
trackpoint_error = LOW; // start with no error
go_z(TP_CLK); // float the clock and data to trackpoint
go_z(TP_DATA);
// Trackpoint Reset signal is active high. Start it off low to let power stabilize
go_0(TP_RESET); // drive low
delay(1000); // wait 1 second
go_1(TP_RESET); // drive High to activate Reset signal to trackpoint
delay(1000); // wait 1 second to give it a good long reset
go_0(TP_RESET); // drive Reset back to the inactive (low) state
delay(1000); // wait 1 second before proceeding so trackpoint is ready
// Sending reset command to trackpoint
tp_write(0xff);
if (tp_read() != 0xfa) { // verify correct ack byte
trackpoint_error = HIGH;
}
delayMicroseconds(100); // give the tp time to run its self diagnostic
// verify proper response from tp
if (tp_read() != 0xaa) { // verify basic assurance test passed
trackpoint_error = HIGH;
}
if (tp_read() != 0x00) { // verify correct device id
trackpoint_error = HIGH;
}
// Sending remote mode code so the trackpoint will send data only when polled
tp_write(0xf0); // remote mode
if (tp_read() != 0xfa) { // verify correct ack byte
trackpoint_error = HIGH;
}
if (trackpoint_error == HIGH) { // check for any errors from tp
delayMicroseconds(300); // wait before trying to initialize tp one last time
tp_write(0xff); // send tp reset code
tp_read(); // read but don't look at response from tp
// Read ack byte
tp_read(); // read but don't look at response from tp
tp_read(); // read but don't look at response from tp
// Sending remote mode code so the trackpoint will send data only when polled
tp_write(0xf0); // remote mode
tp_read(); // read but don't look at response from tp
delayMicroseconds(100);
}
}
//
// *****************Functions for Keyboard*****************************
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
//************************************Setup*******************************************
void setup() {
// ************trackpoint setup
trackpoint_init(); // reset trackpoint, then set it's resolution and put it in remote mode
if (trackpoint_error) {
trackpoint_init(); // try one more time to initialize the trackpoint
}
// ************keyboard setup
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
//
go_0(HOTKEY_RTN); // Always drive the Hotkey return side low
go_pu(HOTKEY); // Pull up the Hotkey plus side for reading
//
pinMode(BLINK_LED, OUTPUT); // I/O 13 drives the LED on the Teensy
pinMode(SYNC, OUTPUT); // I/O 27 drives a scope for frequency measurement
}
//
// *******declare and initialize trackpoint variables
char mstat; // trackpoint status reg = Y overflow, X overflow, Y sign bit, X sign bit, Always 1, Middle Btn, Right Btn, Left Btn
char mx; // trackpoint x movement = 8 data bits. The sign bit is in the status register to
// make a 9 bit 2's complement value. Left to right on the trackpoint gives a positive value.
char my; // trackpoint y movement also 8 bits plus sign. trackpoint movement away from the user gives a positive value.
boolean over_flow; // set if x or y movement values are bad due to overflow
boolean left_button = 0; // on/off variable for left button = bit 0 of mstat
boolean right_button = 0; // on/off variable for right button = bit 1 of mstat
boolean middle_button = 0; // on/off variable for middle button = bit 2 of mstat
boolean old_left_button = 0; // on/off variable for left button status the previous polling cycle
boolean old_right_button = 0; // on/off variable for right button status the previous polling cycle
boolean old_middle_button = 0; // on/off variable for middle button status the previous polling cycle
boolean button_change = 0; // Active high, shows when any trackpoint button has changed since last polling cycle
// **********declare and initialize keyboard variables
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Host to Teensy that give keyboard LED status.
char blink_count = 0; // Blink loop counter
boolean blinky = LOW; // Blink LED state
boolean sync_sig = LOW; // sync pulse to measure scan frequency
//
//*********************************Main Loop*******************************************
//
void loop() {
// *************Keyboard Main**************
// Read the Fn key (aka Hotkey) which is not part of the key matrix
if (!digitalRead(HOTKEY)) {
Fn_pressed = LOW; // Fn key is pressed (active low)
}
else {
Fn_pressed = HIGH; // Fn key is not pressed
}
//
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys
if (modifier[x][y] != 0) { // check if modifier key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
}
// ***********end of modifier section
//
// ***********Normal keys section
else if (normal[x][y] != 0) { // check if normal key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y]) && (!slots_full)) { // check if key pressed and not previously pressed and slots not full
old_key[x][y] = LOW; // Save state of key as "pressed"
if ((normal[x][y] == KEY_SCROLL_LOCK) && (!Fn_pressed)) { // check for special case of Num Lock Key
load_slot(KEY_NUM_LOCK); // update first available slot with Num Lock instead of Scroll Lock
send_normals(); // send all slots over USB including the Num Lock Key that just got pressed
}
else if (keyboard_leds & 1) { // test if Num Lock is turned on
load_slot(numlock[x][y]); //update first available slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got pressed
}
else {
load_slot(normal[x][y]); //update first available slot with normal key name
send_normals(); // send all slots over USB including the key that just got pressed
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if ((normal[x][y] == KEY_SCROLL_LOCK) && (!Fn_pressed)) { // check for special case of Num Lock Key
clear_slot(KEY_NUM_LOCK); // clear the slot that contains Num Lock
send_normals(); // send all slots over USB including the Num Lock key
}
else if (keyboard_leds & 1) { // test if Num Lock is turned on
clear_slot(numlock[x][y]); //clear slot with key name from numlock matrix
send_normals(); // send all slots over USB including the key that just got released
}
else {
clear_slot(normal[x][y]); //clear the slot that contains the normal key name
send_normals(); // send all slots over USB including the key that was just released
}
}
}
// **************end of normal section
//
// *************Volume key section. Note PJRC states that volume up, down, & mute should be sent with Keyboard.press function.
else if (media[x][y] != 0) { // check if any volume control key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // check if key is pressed and was not previously pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
Keyboard.press(media[x][y]); // send volume key press
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
Keyboard.release(media[x][y]); // send volume key release
}
}
// ***************end of volume section
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// ****************************Trackpoint Routine*********************************
//
// poll the trackpoint for new movement data
over_flow = 0; // assume no overflow until status is received
trackpoint_error = LOW; // start with no error
tp_write(0xeb); // request data
if (tp_read() != 0xfa) { // verify correct ack byte
trackpoint_error = HIGH;
}
mstat = tp_read(); // save into status variable
mx = tp_read(); // save into x variable
my = tp_read(); // save into y variable
if (((0x80 & mstat) == 0x80) || ((0x40 & mstat) == 0x40)) { // x or y overflow bits set?
over_flow = 1; // set the overflow flag
}
// change the x data from 9 bit to 8 bit 2's complement
mx = mx >> 1; // convert to 7 bits of data by dividing by 2
mx = mx & 0x7f; // don't allow sign extension
if ((0x10 & mstat) == 0x10) { // move the sign into
mx = 0x80 | mx; // the 8th bit position
}
// change the y data from 9 bit to 8 bit 2's complement and then take the 2's complement
// because y movement on ps/2 format is opposite of touchpad.move function
my = my >> 1; // convert to 7 bits of data by dividing by 2
my = my & 0x7f; // don't allow sign extension
if ((0x20 & mstat) == 0x20) { // move the sign into
my = 0x80 | my; // the 8th bit position
}
my = (~my + 0x01); // change the sign of y data by taking the 2's complement (invert and add 1)
// zero out mx and my if over_flow or trackpoint_error is set
if ((over_flow) || (trackpoint_error)) {
mx = 0x00; // data is garbage so zero it out
my = 0x00;
}
// send the x and y data back via usb if either one is non-zero
if ((mx != 0x00) || (my != 0x00)) {
Mouse.move(mx,my);
}
//
// send the trackpoint left and right button status over usb if no error
if (!trackpoint_error) {
if ((0x01 & mstat) == 0x01) { // if left button set
left_button = 1;
}
else { // clear left button
left_button = 0;
}
if ((0x02 & mstat) == 0x02) { // if right button set
right_button = 1;
}
else { // clear right button
right_button = 0;
}
if ((0x04 & mstat) == 0x04) { // if middle button set
middle_button = 1;
}
else { // clear middle button
middle_button = 0;
}
// Determine if any buttons have changed since last polling cycle
button_change = ((left_button ^ old_left_button) | (right_button ^ old_right_button) | (middle_button ^ old_middle_button));
// Don't send button status if there's no change since last time.
if (button_change) {
Mouse.set_buttons(left_button, middle_button, right_button); // send button status
}
old_left_button = left_button; // remember new button status for next polling cycle
old_right_button = right_button;
old_middle_button = middle_button;
}
// **************************************End of trackpoint routine***********************************
//
// *******keyboard LEDs
// Turn on or off the LEDs for Num Lock, Caps Lock, and Scroll Lock based on bit 0, 1, and 2 from the keyboard_leds
// variable controlled by the USB host computer
//
if (keyboard_leds & 1) { // mask off all bits but D0 and test if set
go_1(NUM_LED); // turn on the Num Lock LED
}
else {
go_0(NUM_LED); // turn off the Num Lock LED
}
//
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the Caps Lock LED
}
else {
go_0(CAPS_LED); // turn off the Caps Lock LED
}
//
//
if (keyboard_leds & 1<<2) { // mask off all bits but D2 and test if set
go_1(SCRL_LED); // turn on the Scroll Lock LED
}
else {
go_0(SCRL_LED); // turn off the Scroll Lock LED
}
//
// Blink LED on Teensy to show a heart beat
//
if (blink_count == 0x17) {
digitalWrite(BLINK_LED, blinky);
blinky = !blinky;
blink_count = 0;
}
else {
blink_count = blink_count + 1;
}
//
// Provide a sync pulse to measure the scan frequency
//
sync_sig = !sync_sig; // toggle the sync signal
digitalWrite(SYNC, sync_sig);
//
// ****************End of main loop
//
delay(24); // The overall keyboard/trackpoint scanning rate was measured at 30ms
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
MODIFIERKEY_LEFT_CTRL 6 14
MODIFIERKEY_RIGHT_CTRL
MODIFIERKEY_LEFT_SHIFT 5 13
MODIFIERKEY_RIGHT_SHIFT 4 12
MODIFIERKEY_LEFT_ALT 7 15
MODIFIERKEY_RIGHT_ALT
MODIFIERKEY_GUI 8 16
MODIFIERKEY_FN 2 10
KEY_A 1 10
KEY_B 1 14
KEY_C 1 16
KEY_D 8 11
KEY_E 6 13
KEY_F 3 16
KEY_G 4 13
KEY_H 3 15
KEY_I 5 15
KEY_J 3 14
KEY_K 3 9
KEY_L 4 9
KEY_M 1 12
KEY_N 1 13
KEY_O 7 12
KEY_P 4 15
KEY_Q 7 14
KEY_R 6 12
KEY_S 4 16
KEY_T 6 11
KEY_U 13 7
KEY_V 1 15
KEY_W 6 15
KEY_X 2 9
KEY_Y 6 9
KEY_Z 2 15
KEY_TILDE
KEY_1 8 14
KEY_2 8 13
KEY_3 8 12
KEY_4 4 14
KEY_5 10 8
KEY_6 8 9
KEY_7 7 11
KEY_8 3 10
KEY_9 7 9
KEY_0 5 16
KEY_MINUS 5 14
KEY_EQUAL 2 11
KEY_BACKSPACE 7 10
KEY_ESC 1 9
KEY_F1 FN 8 14
KEY_F2 FN 8 13
KEY_F3 FN 8 12
KEY_F4 FN 4 14
KEY_F5 FN 10 8
KEY_F6 FN 8 9
KEY_F7 FN 7 11
KEY_F8 FN 3 10
KEY_F9 FN 7 9
KEY_F10 FN 5 16
KEY_F11
KEY_F12
KEY_INSERT
KEY_DELETE 7 16
KEY_RIGHT 3 12
KEY_LEFT 3 13
KEY_UP 5 11
KEY_DOWN 4 11
KEY_MENU
KEY_SLASH 1 11
KEY_PERIOD 2 12
KEY_COMMA 4 10
KEY_SEMICOLON 2 13
KEY_QUOTE
KEY_ENTER 6 10
KEY_LEFT_BRACE 5 12
KEY_RIGHT_BRACE 5 9
KEY_BACKSLASH 2 16
KEY_CAPS_LOCK 3 11
KEY_TAB 6 16
KEY_SPACE 2 14
KEY_HOME FN 3 13
KEY_END FN 3 12
KEY_PAGE_UP FN 5 11
KEY_PAGE_DOWN FN 4 11
KEY_NUM_LOCK

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View file

@ -0,0 +1,349 @@
/* Copyright 2018 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software implements a Sony Vaio PCG-K25 Laptop Keyboard Controller using a Teensy LC on
// a daughterboard with a 24 pin FPC connector. The keyboard part number is KFRMBA151B.
// This routine uses the Teensyduino "Micro-Manager Method" to send Normal and Modifier
// keys over USB. Multi-media keys are are sent with keyboard press and release functions.
// Description of Teensyduino keyboard functions is at www.pjrc.com/teensy/td_keyboard.html
//
// Revision History
// Initial Release Nov 15, 2018
// Rev 1 July 16, 2019 - check if slots are full when detecting a key press
//
#define MODIFIERKEY_FN 0x8f // give Fn key a HID code
#define CAPS_LED 13 // Teensy LED shows Caps-Lock
//
const byte rows_max = 8; // sets the number of rows in the matrix
const byte cols_max = 8; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
int normal[rows_max][cols_max] = {
{KEY_ESC,KEY_A,KEY_SLASH,KEY_M,KEY_N,KEY_B,KEY_V,KEY_C},
{KEY_X,0,KEY_EQUAL,KEY_PERIOD,KEY_SEMICOLON,KEY_SPACE,KEY_Z,KEY_BACKSLASH},
{KEY_K,KEY_8,KEY_CAPS_LOCK,KEY_RIGHT,KEY_LEFT,KEY_J,KEY_H,KEY_F},
{KEY_L,KEY_COMMA,KEY_DOWN,0,KEY_G,KEY_4,KEY_P,KEY_S},
{KEY_RIGHT_BRACE,0,KEY_UP,KEY_LEFT_BRACE,0,KEY_MINUS,KEY_I,KEY_0},
{KEY_Y,KEY_ENTER,KEY_T,KEY_R,KEY_E,0,KEY_W,KEY_TAB},
{KEY_9,KEY_BACKSPACE,KEY_7,KEY_O,KEY_U,KEY_Q,0,KEY_DELETE},
{KEY_6,KEY_5,KEY_D,KEY_3,KEY_2,KEY_1,0,0}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0},
{0,MODIFIERKEY_FN,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,MODIFIERKEY_RIGHT_SHIFT,0,0,0,0},
{0,0,0,0,MODIFIERKEY_LEFT_SHIFT,0,0,0},
{0,0,0,0,0,MODIFIERKEY_LEFT_CTRL,0,0},
{0,0,0,0,0,0,MODIFIERKEY_LEFT_ALT,0},
{0,0,0,0,0,0,0,MODIFIERKEY_GUI}
};
// Load the media key matrix with key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1}
};
//
// Define the Teensy LC I/O numbers (translated from the FPC pin #)
// Row FPC pin # 01,02,03,04,05,08,09,10,15,16,19,20,21,22,23,24
// Teensy I/O # 23,00,22,01,24,03,25,04,18,07,16,09,15,10,14,11
int Row_IO[rows_max] = {23, 0, 22, 1, 24, 2, 21, 3}; // Teensy LC I/O numbers for rows
//
// Column FPC pin # 06,07,11,12,13,14,17,18
// Teensy I/O # 02,21,20,05,19,06,17,08
int Col_IO[cols_max] = {25, 4, 20, 5, 19, 6, 18, 7}; // Teensy LC I/O numbers for columns
// Declare variables that will be used by functions
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
}
//
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Pi to Teensy that give keyboard LED status. Caps lock is bit D1.
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys including the Fn special case
if (modifier[x][y] != 0) { // check if modifier key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else {
Fn_pressed = LOW; // Fn status variable is active low
old_key[x][y] = LOW; // old_key state is "pressed" (active low)
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
else {
Fn_pressed = HIGH; // Fn is no longer active
old_key[x][y] = HIGH; // old_key state is "not pressed"
}
}
}
// ***********end of modifier section
//
// ***********Normal keys and media keys in this section
else if ((normal[x][y] != 0) || (media[x][y] != 0)) { // check if normal or media key exists at this location in the array
if (!digitalRead(Col_IO[y]) && (old_key[x][y]) && (!slots_full)) { // check if key pressed and not previously pressed and slots not full
old_key[x][y] = LOW; // Save state of key as "pressed"
if (Fn_pressed) { // Fn_pressed is active low so it is not pressed and normal key needs to be sent
load_slot(normal[x][y]); //update first available slot with normal key name
send_normals(); // send all slots over USB including the key that just got pressed
}
else if (media[x][y] != 0) { // Fn is pressed so send media if a key exists in the matrix
Keyboard.press(media[x][y]); // media key is sent using keyboard press function per PJRC
delay(5); // delay 5 milliseconds before releasing to make sure it gets sent over USB
Keyboard.release(media[x][y]); // send media key release
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if (Fn_pressed) { // Fn is not pressed
clear_slot(normal[x][y]); //clear the slot that contains the normal key name
send_normals(); // send all slots over USB including the key that was just released
}
}
}
// **************end of normal and media key section
//
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// Turn on the LED on the Teensy for Caps Lock based on bit 1 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the LED
}
else {
go_0(CAPS_LED); // turn off the LED
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

View file

@ -0,0 +1,375 @@
/* Copyright 2018 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This software implements a Dell Inspiron 1525 Laptop Keyboard Controller using a Teensy LC on
// a daughterboard with a 25 pin FPC connector. The keyboard part number is D9K01.
// This routine uses the Teensyduino "Micro-Manager Method" to send Normal and Modifier
// keys over USB. Multi-media keys are sent with keyboard press and release functions.
// Description of Teensyduino keyboard functions is at www.pjrc.com/teensy/td_keyboard.html
//
// Revision History
// Initial Release Nov 15, 2018
//
//
#define MODIFIERKEY_FN 0x8f // give Fn key a HID code
#define KEY_CURLEY_L 0xb8 // define Curley Braces HID codes, don't work
#define KEY_CURLEY_R 0xb9
#define KEY_TILDENEW 0x7e
#define KEY_BPND 0x9c //doesn't work
#define KEY_EURO 0x20ac //doesn't work
#define CAPS_LED 13 // Teensy LED shows Caps-Lock
//
const byte rows_max = 14; // sets the number of rows in the matrix
const byte cols_max = 9; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
//
int normal[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0},
{0,KEY_EQUAL,0,KEY_ENTER,0,KEY_F11,0,0,KEY_BACKSPACE},
{0,KEY_MINUS,0,KEY_QUOTE,KEY_RIGHT,KEY_F10,0,0,KEY_BACKSLASH},
{0,KEY_0,0,0,KEY_DOWN,KEY_F9,KEY_SEMICOLON,KEY_UP,KEY_P},
{0,0,0,0,0,0,0,0,0},
{0,KEY_9,KEY_DELETE,KEY_PERIOD,KEY_LEFT,KEY_F8,KEY_L,0,KEY_O},
{0,KEY_8,KEY_SLASH,KEY_COMMA,0,KEY_F7,KEY_K,0,KEY_I},
{0,KEY_7,0,KEY_M,0,KEY_F6,KEY_J,0,KEY_U},
{0,KEY_6,0,KEY_N,0,KEY_F5,KEY_H,0,KEY_Y},
{0,KEY_5,0,KEY_B,0,KEY_F4,KEY_G,0,KEY_T},
{0,KEY_4,KEY_SPACE,KEY_V,0,KEY_F3,KEY_F,0,KEY_R},
{0,KEY_3,0,KEY_C,0,KEY_F2,KEY_D,0,KEY_E},
{0,KEY_2,0,KEY_X,0,KEY_F1,KEY_S,0,KEY_W},
{0,KEY_1,0,KEY_Z,0,KEY_ESC,KEY_A,KEY_TAB,KEY_Q}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,MODIFIERKEY_RIGHT_SHIFT,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,MODIFIERKEY_FN,0,0,0,0},
{0,0,0,0,MODIFIERKEY_LEFT_ALT,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,MODIFIERKEY_LEFT_SHIFT,0},
{0,0,MODIFIERKEY_CTRL,0,0,0,0,0,0},
{0,0,MODIFIERKEY_GUI,0,0,0,0,0,0}
};
// Load the media key matrix with Fn key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{KEY_SYSTEM_POWER_DOWN,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,KEY_RIGHT_BRACE,KEY_END,0,0,0,0},
{0,0,0,0,KEY_PAGE_DOWN,0,KEY_LEFT_BRACE,KEY_PAGE_UP,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,KEY_HOME,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,KEY_EURO,0,0,0,0,0,0,0},
{0,KEY_BPND,0,0,0,0,0,0,0},
{0,KEY_TILDE,0,0,0,0,0,0,0},
{0,KEY_TILDENEW,0,0,0,0,0,KEY_CAPS_LOCK,0}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1}
};
//
// Define the Teensy LC I/O numbers (translated from the FPC pin #)
// Row FPC pin # 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17
// Teensy I/O # 23,00,22,01,24,02,21,03,25,04,20,05,19,06,18,07,17
int Row_IO[rows_max] = {20,5,19,6,18,7,17,8,16,9,15,10,14,11}; // Teensy LC I/O numbers for rows
//
// Column FPC pin # 18,19,20,21,22,23,24,25
// Teensy I/O # 08,16,09,15,10,14,11,26
int Col_IO[cols_max] = {23,0,22,1,24,2,21,3,25}; // Teensy LC I/O numbers for columns
// Declare variables that will be used by functions
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
slots_full = LOW;
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
}
//
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Pi to Teensy that give keyboard LED status. Caps lock is bit D1.
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys including the Fn special case
if (modifier[x][y] != 0) { // check if modifier key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else {
Fn_pressed = LOW; // Fn status variable is active low
old_key[x][y] = LOW; // old_key state is "pressed" (active low)
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
else {
Fn_pressed = HIGH; // Fn is no longer active
old_key[x][y] = HIGH; // old_key state is "not pressed"
}
}
}
// ***********end of modifier section
//
// ***********Normal keys and media keys in this section
else if ((normal[x][y] != 0) || (media[x][y] != 0)) { // check if normal or media key exists at this location in the array
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // check if key is pressed and was not previously pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
if (Fn_pressed) { // Fn_pressed is active low so it is not pressed and normal key needs to be sent
load_slot(normal[x][y]); //update first available slot with normal key name
send_normals(); // send all slots over USB including the key that just got pressed
}
else if (media[x][y] != 0) { // Fn is pressed so send media if a key exists in the matrix
Keyboard.press(media[x][y]); // media key is sent using keyboard press function per PJRC
delay(5); // delay 5 milliseconds before releasing to make sure it gets sent over USB
Keyboard.release(media[x][y]); // send media key release
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if (Fn_pressed) { // Fn is not pressed
clear_slot(normal[x][y]); //clear the slot that contains the normal key name
send_normals(); // send all slots over USB including the key that was just released
}
}
}
// **************end of normal and media key section
//
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// Turn on the LED on the Teensy for Caps Lock based on bit 1 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the LED
}
else {
go_0(CAPS_LED); // turn off the LED
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View file

@ -0,0 +1,16 @@
Connector for Q10 keyboard is a Hirose BM14B(0.8)-24DS-0.4V(53) Digikey PN H122178CT-ND
The custom breakout board is from reply #20 of this forum post:
https://www.eevblog.com/forum/beginners/how-to-connect-to-a-very-very-challanging-blackberry-q10-keyboard-connector/
Columns and rows associated with their pin on custom breakout board in the keyboard matrix of the Q10 keyboard:
COL1 COL2 COL3 COL4 COL5
PIN7 PIN9 PIN10 PIN17 PIN19
ROW1 PIN6 Q E R U O
ROW2 PIN8 W S G H L
ROW3 PIN18 sym D T Y I
ROW4 PIN20 A P sh2 ret bck
ROW5 PIN21 alt Z V B $
ROW6 PIN22 spc X C N M
ROW7 PIN27 mike sh1 F J K

View file

@ -0,0 +1,432 @@
/* Copyright 2018 Frank Adams
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// The PCB is documented in reply#20 of this post:
// https://www.eevblog.com/forum/beginners/how-to-connect-to-a-very-very-challanging-blackberry-q10-keyboard-connector/
#define MODIFIERKEY_FN 0x8f // give Fn key a HID code
#define CAPS_LED 13 // Teensy LED shows Caps-Lock
#define KEY_HASHTILDE 0x32
#define KEY_KPLEFTPAREN 0xb6 // Keypad {
#define KEY_KPRIGHTPAREN 0xb7 // Keypad )
#define KEY_AT 0xce // Keypad @
#define KEY_EXCL 0xcf // Keypad !
#define KEY_COLON 0xcb // Keypad :
#define KEY_APOSTROPHE 0x34
#define KEY_QQUOTE 0x34
#define KEY_UNDER 0x01 // _
#define KEY_QUEST 0x38 // _
//
const byte rows_max = 7; // sets the number of rows in the matrix
const byte cols_max = 5; // sets the number of columns in the matrix
//
// Load the normal key matrix with the Teensyduino key names described at www.pjrc.com/teensy/td_keyboard.html
// A zero indicates no normal key at that location.
//
int normal[rows_max][cols_max] = {
{KEY_Q,KEY_E,KEY_R,KEY_U,KEY_O},
{KEY_W,KEY_S,KEY_G,KEY_H,KEY_L},
{0,KEY_D,KEY_T,KEY_Y,KEY_I},
{KEY_A,KEY_P,0,KEY_ENTER,KEY_BACKSPACE},
{0,KEY_X,KEY_V,KEY_B,0},
{KEY_SPACE,KEY_Z,KEY_C,KEY_N,KEY_M},
{KEY_ESC,0,KEY_F,KEY_J,KEY_K}
};
// Load the modifier key matrix with key names at the correct row-column location.
// A zero indicates no modifier key at that location.
int modifier[rows_max][cols_max] = {
{0,0,0,0,0},
{0,0,0,0,0},
{MODIFIERKEY_FN,0,0,0,0},
{0,0,MODIFIERKEY_RIGHT_SHIFT,0,0},
{MODIFIERKEY_LEFT_ALT,0,0,0,MODIFIERKEY_RIGHT_CTRL},
{0,0,0,0,0},
{0,MODIFIERKEY_LEFT_SHIFT,0,0,0}
};
// Load the media key matrix with Fn key names at the correct row-column location.
// A zero indicates no media key at that location.
int media[rows_max][cols_max] = {
{KEY_HASHTILDE,KEY_2,KEY_3,KEY_UNDER,KEYPAD_PLUS},
{KEY_1,KEY_4,KEY_SLASH,KEY_COLON,KEY_QQUOTE},
{0,KEY_5,KEY_KPLEFTPAREN,KEY_KPRIGHTPAREN,KEY_MINUS},
{KEYPAD_ASTERIX,KEY_AT,0,0,KEY_DELETE},
{0,KEY_8,KEY_QUEST,KEY_EXCL,0},
{0,KEY_7,KEY_9,KEY_COMMA,KEY_PERIOD},
{KEY_0,0,KEY_6,KEY_SEMICOLON,KEY_QUOTE}
};
// Initialize the old_key matrix with one's.
// 1 = key not pressed, 0 = key is pressed
boolean old_key[rows_max][cols_max] = {
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1}
};
//
// Define the Teensy LC I/O numbers (translated from the FPC pin #)
int Row_IO[rows_max] = {4,6,18,20,21,22,23}; // Teensy LC I/O numbers for rows
//
int Col_IO[cols_max] = {5,7,8,17,19}; // Teensy LC I/O numbers for columns
// Declare variables that will be used by functions
boolean slots_full = LOW; // Goes high when slots 1 thru 6 contain normal keys
// slot 1 thru slot 6 hold the normal key values to be sent over USB.
int slot1 = 0; //value of 0 means the slot is empty and can be used.
int slot2 = 0;
int slot3 = 0;
int slot4 = 0;
int slot5 = 0;
int slot6 = 0;
//
int mod_shift_l = 0; // These variables are sent over USB as modifier keys.
int mod_shift_r = 0; // Each is either set to 0 or MODIFIER_ ...
int mod_ctrl_l = 0;
int mod_ctrl_r = 0;
int mod_alt_l = 0;
int mod_alt_r = 0;
int mod_gui = 0;
//
// Function to load the key name into the first available slot
void load_slot(int key) {
if (!slot1) {
slot1 = key;
}
else if (!slot2) {
slot2 = key;
}
else if (!slot3) {
slot3 = key;
}
else if (!slot4) {
slot4 = key;
}
else if (!slot5) {
slot5 = key;
}
else if (!slot6) {
slot6 = key;
}
if (!slot1 || !slot2 || !slot3 || !slot4 || !slot5 || !slot6) {
slots_full = LOW; // slots are not full
}
else {
slots_full = HIGH; // slots are full
}
}
//
// Function to clear the slot that contains the key name
void clear_slot(int key) {
if (slot1 == key) {
slot1 = 0;
}
else if (slot2 == key) {
slot2 = 0;
}
else if (slot3 == key) {
slot3 = 0;
}
else if (slot4 == key) {
slot4 = 0;
}
else if (slot5 == key) {
slot5 = 0;
}
else if (slot6 == key) {
slot6 = 0;
}
slots_full = LOW;
}
//
// Function to load the modifier key name into the appropriate mod variable
void load_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = m_key;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = m_key;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = m_key;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = m_key;
}
}
//
// Function to load 0 into the appropriate mod variable
void clear_mod(int m_key) {
if (m_key == MODIFIERKEY_LEFT_SHIFT) {
mod_shift_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_SHIFT) {
mod_shift_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_CTRL) {
mod_ctrl_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_CTRL) {
mod_ctrl_r = 0;
}
else if (m_key == MODIFIERKEY_LEFT_ALT) {
mod_alt_l = 0;
}
else if (m_key == MODIFIERKEY_RIGHT_ALT) {
mod_alt_r = 0;
}
else if (m_key == MODIFIERKEY_GUI) {
mod_gui = 0;
}
}
//
// Function to send the modifier keys over usb
void send_mod() {
Keyboard.set_modifier(mod_shift_l | mod_shift_r | mod_ctrl_l | mod_ctrl_r | mod_alt_l | mod_alt_r | mod_gui);
Keyboard.send_now();
}
//
// Function to send the normal keys in the 6 slots over usb
void send_normals() {
Keyboard.set_key1(slot1);
Keyboard.set_key2(slot2);
Keyboard.set_key3(slot3);
Keyboard.set_key4(slot4);
Keyboard.set_key5(slot5);
Keyboard.set_key6(slot6);
Keyboard.send_now();
}
//
// Function to set a pin to high impedance (acts like open drain output)
void go_z(int pin)
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
//
// Function to set a pin as an input with a pullup
void go_pu(int pin)
{
pinMode(pin, INPUT_PULLUP);
digitalWrite(pin, HIGH);
}
//
// Function to send a pin to a logic low
void go_0(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
//
// Function to send a pin to a logic high
void go_1(int pin)
{
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//
//----------------------------------Setup-------------------------------------------
void setup() {
for (int a = 0; a < cols_max; a++) { // loop thru all column pins
go_pu(Col_IO[a]); // set each column pin as an input with a pullup
}
//
for (int b = 0; b < rows_max; b++) { // loop thru all row pins
go_z(Row_IO[b]); // set each row pin as a floating output
}
}
//
boolean Fn_pressed = HIGH; // Initialize Fn key to HIGH = "not pressed"
extern volatile uint8_t keyboard_leds; // 8 bits sent from Pi to Teensy that give keyboard LED status. Caps lock is bit D1.
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {
// Scan keyboard matrix with an outer loop that drives each row low and an inner loop that reads every column (with pull ups).
// The routine looks at each key's present state (by reading the column input pin) and also the previous state from the last scan
// that was 30msec ago. The status of a key that was just pressed or just released is sent over USB and the state is saved in the old_key matrix.
// The keyboard keys will read as logic low if they are pressed (negative logic).
// The old_key matrix also uses negative logic (low=pressed).
//
for (int x = 0; x < rows_max; x++) { // loop thru the rows
go_0(Row_IO[x]); // Activate Row (send it low)
delayMicroseconds(10); // give the row time to go low and settle out
for (int y = 0; y < cols_max; y++) { // loop thru the columns
// **********Modifier keys including the Fn special case
if (modifier[x][y] != 0) { // check if modifier key exists at this location in the array (a non-zero value)
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // Read column to see if key is low (pressed) and was previously not pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
load_mod(modifier[x][y]); // function reads which modifier key is pressed and loads it into the appropriate mod_... variable
send_mod(); // function sends the state of all modifier keys over usb including the one that just got pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
}
else {
Fn_pressed = LOW; // Fn status variable is active low
old_key[x][y] = LOW; // old_key state is "pressed" (active low)
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed and was previously pressed
if (modifier[x][y] != MODIFIERKEY_FN) { // Exclude Fn modifier key
clear_mod(modifier[x][y]); // function reads which modifier key was released and loads 0 into the appropriate mod_... variable
send_mod(); // function sends all mod's over usb including the one that just released
old_key[x][y] = HIGH; // Save state of key as "not pressed"
}
else {
Fn_pressed = HIGH; // Fn is no longer active
old_key[x][y] = HIGH; // old_key state is "not pressed"
}
}
}
// ***********end of modifier section
//
// ***********Normal keys and media keys in this section
else if ((normal[x][y] != 0) || (media[x][y] != 0)) { // check if normal or media key exists at this location in the array
if (!digitalRead(Col_IO[y]) && (old_key[x][y])) { // check if key is pressed and was not previously pressed
old_key[x][y] = LOW; // Save state of key as "pressed"
if (Fn_pressed) { // Fn_pressed is active low so it is not pressed and normal key needs to be sent
load_slot(normal[x][y]); //update first available slot with normal key name
send_normals(); // send all slots over USB including the key that just got pressed
}
else if (media[x][y] != 0) { // Fn is pressed so send media if a key exists in the matrix
if (media[x][y] == KEY_AT) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_2);
delay(5);
Keyboard.release(KEY_2);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_HASHTILDE) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_3);
delay(5);
Keyboard.release(KEY_3);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_UNDER) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_MINUS);
delay(5);
Keyboard.release(KEY_MINUS);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_COLON) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_SEMICOLON);
delay(5);
Keyboard.release(KEY_SEMICOLON);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_KPLEFTPAREN) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_9);
delay(5);
Keyboard.release(KEY_9);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_KPRIGHTPAREN) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_0);
delay(5);
Keyboard.release(KEY_0);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_QUEST) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_SLASH);
delay(5);
Keyboard.release(KEY_SLASH);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_EXCL) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_1);
delay(5);
Keyboard.release(KEY_1);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else if (media[x][y] == KEY_QQUOTE) {
Keyboard.press(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
Keyboard.press(KEY_QUOTE);
delay(5);
Keyboard.release(KEY_QUOTE);
delay(5);
Keyboard.release(MODIFIERKEY_RIGHT_SHIFT);
delay(5);
}
else {
Keyboard.press(media[x][y]); // media key is sent using keyboard press function per PJRC
delay(5); // delay 5 milliseconds before releasing to make sure it gets sent over USB
Keyboard.release(media[x][y]); // send media key release
}
}
}
else if (digitalRead(Col_IO[y]) && (!old_key[x][y])) { //check if key is not pressed, but was previously pressed
old_key[x][y] = HIGH; // Save state of key as "not pressed"
if (Fn_pressed) { // Fn is not pressed
clear_slot(normal[x][y]); //clear the slot that contains the normal key name
send_normals(); // send all slots over USB including the key that was just released
}
}
}
// **************end of normal and media key section
//
}
go_z(Row_IO[x]); // De-activate Row (send it to hi-z)
}
//
// **********keyboard scan complete
//
// Turn on the LED on the Teensy for Caps Lock based on bit 1 in the keyboard_leds variable controlled by the USB host computer
//
if (keyboard_leds & 1<<1) { // mask off all bits but D1 and test if set
go_1(CAPS_LED); // turn on the LED
}
else {
go_0(CAPS_LED); // turn off the LED
}
//
delay(25); // The overall keyboard scanning rate is about 30ms
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB