You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

361 lines
10 KiB

/*
* Evil Copyright (c) 2015 Kevin Nygaard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Pinout for Stentura 200 (NOT FOR FLASH)
* Ribbon
* ||||||
* ||||||
* ||||||
* xxxxxx (contacts facing up)
* \\\\\\_ Power +5V
* \\\\\_ 2 Test
* \\\\_ 3 Shift load
* \\\_ 4 Serial out
* \\_ 5 Clock
* \_ Ground
*/
/* IO Definitions STENTURA 200*/
/*
#define CLK 5
#define DATA_IN 4
#define LATCH_EN 3
#define TEST_EN 2
#define BAUD 115200
*/
/*
* Pinout for ProCAT Flash
* IO Board Pads
* (NOTE: THIS IS NOT THE RIBBON CABLE, that is different)
*
* Pads are in two columns, 1,2,3 and 4,5,6. 1 being the square pad (visible from top).
*
* 1 ??? (Must be test. Connects to Resistors)
* 2 PWR
* 3 GND
*
* 4 Serial Out (Pin 9 of first IC)
* 5 CLK (Clk rail)
* 6 SH/LD (Shift Load rail)
*
*/
/* IO Definitions ProCAT Flash*/
#define CLK 5
#define DATA_IN 4
#define LATCH_EN 6
#define TEST_EN 2 //Can't put TEST on 1, which would be UART TX
#define BAUD 9600
#define DEBUG 0 //set to 1 when using Arduino to debug
#define DEBOUNCE_PERIOD 10
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof(*array))
/*
* Setup IO and serial connections
*/
void setup()
{
// Initialize serial
Serial.begin(BAUD);
if(DEBUG){
Serial.println("Hax Steno Begin");
}
// Setup IOs
pinMode(CLK, OUTPUT);
pinMode(LATCH_EN, OUTPUT);
pinMode(DATA_IN, INPUT);
//pinMode(TEST_EN, OUTPUT); //Not needed
// Drive known values to outputs
digitalWrite(CLK, LOW);
digitalWrite(LATCH_EN, LOW);
//digitalWrite(TEST_EN, LOW); //Not needed
}
/*
* Convert raw Stentura byte data into packed Gemini PR format
*
* Data that is seemingly skipped is rendundant, and as far as I can tell,
* unnecessary
*
* Raw matrix mapping of Stentura 200 SRT from serial shift registers
* 0: S-
* 1: T-
* 2: K-
* 3: P-
* 4: W-
* 5: H-
* 6: R-
* 7: A-
* 8: O-
* 9: *
* 10: -E
* 11: -U
* 12: -F
* 13: -R
* 14: -P
* 15: -B
* 16: -L
* 17: -G
* 18: -T
* 19: -S
* 20: -D
* 21: -Z
* 22: #
* 23: N/A
*
* Gemini PR protocol (from plover source ./plover/machine/geminipr.py)
*
* In the Gemini PR protocol, each packet consists of exactly six bytes and the
* most significant bit (MSB) of every byte is used exclusively to indicate
* whether that byte is the first byte of the packet (MSB=1) or one of the
* remaining five bytes of the packet (MSB=0). As such, there are really only
* seven bits of steno data in each packet byte. This is why the STENO_KEY_CHART
* below is visually presented as six rows of seven elements instead of six rows
* of eight elements.
* STENO_KEY_CHART = ("Fn" , "#" , "#" , "#" , "#" , "#" , "#" ,
* "S-" , "S-", "T-", "K-", "P-", "W-" , "H-" ,
* "R-" , "A-", "O-", "*" , "*" , "res", "res",
* "pwr", "*" , "*" , "-E", "-U", "-F" , "-R" ,
* "-P" , "-B", "-L", "-G", "-T", "-S" , "-D" ,
* "#" , "#" , "#" , "#" , "#" , "#" , "-Z")
*
*/
//void construct_data(char raw_data[], char packed_data[])
//{
// packed_data[0] = 0x80;
// packed_data[1] = (raw_data[ 0] << 6) /* S- */
// | (raw_data[ 1] << 1) /* T- */
// | (raw_data[ 2] << 3) /* K- */
// | (raw_data[ 3] << 2) /* P- */
// | (raw_data[ 4] << 0) /* W- */
// | (raw_data[ 5] << 0); /* H- */
//packed_data[2] = (raw_data[ 6] << 6) /* R- */
// | (raw_data[ 7] << 5) /* A- */
// | (raw_data[ 8] << 4) /* O- */
// | (raw_data[ 9] << 3); /* * */
//packed_data[3] = (raw_data[10] << 3) /* -E */
// | (raw_data[11] << 2) /* -U */
// | (raw_data[12] << 1) /* -F */
// | (raw_data[13] << 0); /* -R */
//packed_data[4] = (raw_data[14] << 6) /* -P */
// | (raw_data[15] << 5) /* -B */
// | (raw_data[16] << 4) /* -L */
// | (raw_data[17] << 3) /* -G */
// | (raw_data[18] << 2) /* -T */
// | (raw_data[19] << 1) /* -S */
// | (raw_data[20] << 0); /* -D */
//packed_data[5] = (raw_data[22] << 6) /* # */
// | (raw_data[21] << 0); /* -Z */
//}
/* ProCAT Flash Mapping
*
* The ProCAT Flash is somewhat different. The above Stentura 200 will not work. Instead we have:
*
* * Raw matrix mapping of ProCAT Flash from serial shift registers
* * You can somewhat infer this from the PCB (cheat) by reading letter order, but not all are logical
* 0: H- //
* 1: W- //
* 2: P- //
* 3: K- //
* 4: T- //changes marked with comments
* 5: S- //
* 6: # // //old was R-
*
* 7: A-
* 8: -R //
* 9: F //
* 10: -U //
* 11: -E //
* 12: * //
* 13: -R
*
* 14: -P
* 15: R //was -B
* 16: -Z //
* 17: -D //
* 18: -S //
* 19: -T //
* 20: -G //
*
* 21: -L ??
* 22: #
* 23: ????
*
* need -B
*/
void construct_data(char raw_data[], char packed_data[])
{
packed_data[0] = 0x80;
packed_data[1] = (raw_data[ 5] << 6) /* S- */ //Have to move arrays entries around here, if you are going to
| (raw_data[ 4] << 4) /* T- */ //change values. Need all 24 bits.
| (raw_data[ 3] << 3) /* K- */
| (raw_data[ 2] << 2) /* P- */
| (raw_data[ 1] << 1) /* W- */
| (raw_data[ 0] << 0); /* H- */
packed_data[2] = (raw_data[ 15] << 6) /* R- */
| (raw_data[ 14] << 5) /* A- */
| (raw_data[ 13] << 4) /* O- */
| (raw_data[ 12] << 3); /* * */
packed_data[3] = (raw_data[11] << 3) /* -E */
| (raw_data[10] << 2) /* -U */
| (raw_data[9] << 1) /* -F */
| (raw_data[8] << 0); /* -R */
packed_data[4] = (raw_data[23] << 6) /* -P */ // was 14
| (raw_data[22] << 5) /* -B */ // was dupe 0
| (raw_data[21] << 4) /* -L */
| (raw_data[20] << 3) /* -G */
| (raw_data[19] << 2) /* -T */
| (raw_data[18] << 1) /* -S */
| (raw_data[17] << 0); /* -D */
packed_data[5] = (raw_data[6] << 6) /* # */ //
| (raw_data[16] << 0); /* -Z */
}
/*
* Read keystrokes and return them to the host
*/
void loop(){
// Byte array of depressed keys, following mapping above
// 0 means not pressed, 1 means pressed
static char pressed_keys[24];
// Keys packed in Gemini PR format, ready for transmission over serial
static char packed_keys[6];
// Chord has been started, but is not complete (all keys released)
static char in_progress;
// Send data to host over serial
static char send_data;
// Debounce counter
static int debounce_count;
int i;
char pressed;
char keys_down;
//If keys have already been recorded into pressed_keys array
if (send_data) {
construct_data(pressed_keys, packed_keys);
Serial.write(packed_keys, ARRAY_SIZE(packed_keys));
/*
* Data returned to host, reset stateful variables
* Note that we don't need to do this in the setup, because they are all
* static and guaranteed to be 0 by spec
*/
memset(pressed_keys, 0, sizeof(pressed_keys));
send_data = 0;
in_progress = 0;
// Else, record keys into pressed_keys array
} else {
// Latch current state of all keys
digitalWrite(LATCH_EN, HIGH);
// Read all latched data
keys_down = 0;
for (i = 0; i < ARRAY_SIZE(pressed_keys); i++){
/*
* All inputs are pulled up. Pressing a key shorts the circuit to
* ground.
*
* We invert the logic here to convert to more conventional positive
* logic.
*/
pressed = !digitalRead(DATA_IN);
//At this point, you are reading each individual bit in as a 1 if pressed.
//debug
if(pressed && DEBUG){
Serial.print(pressed, BIN);
Serial.print("Pressed was high for i = ");
Serial.println(i);
}
// Once a key is pressed, it stays pressed until the chord is over
// OR result to array
pressed_keys[i] |= pressed;
if (pressed) {
keys_down = 1;
in_progress = 1;
}
/*
* Toggle clock. Max frequency of shift register MM74HC165 is 30
* MHz, so it can be switched without any delay by Arduino.
*/
digitalWrite(CLK, HIGH);
digitalWrite(CLK, LOW);
}
// Make latch transparent again to capture next set of keystrokes
digitalWrite(LATCH_EN, LOW);
if (keys_down) {
debounce_count = 0;
}
// Return data to host when all keys have been released
if (in_progress && !keys_down && (++debounce_count >= DEBOUNCE_PERIOD)) {
send_data = 1;
}
}
}