diff --git a/Example_Keyboards/Lenovo_ThinkPad_T61/Teensy 3p2/Lenovo_T61_KBandTP.ino b/Example_Keyboards/Lenovo_ThinkPad_T61/Teensy 3p2/Lenovo_T61_KBandTP.ino index 1c014a9..a8ec3e6 100644 --- a/Example_Keyboards/Lenovo_ThinkPad_T61/Teensy 3p2/Lenovo_T61_KBandTP.ino +++ b/Example_Keyboards/Lenovo_ThinkPad_T61/Teensy 3p2/Lenovo_T61_KBandTP.ino @@ -1,4 +1,4 @@ -/* +/* 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 @@ -9,23 +9,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -// It controls a Lenovo ThinkPad T61 Laptop Keyboard and PS/2 Trackpoint using a Teensy 3.2 on +// This software controls a Lenovo ThinkPad T61 Laptop Keyboard and PS/2 Trackpoint using a Teensy LC on // a daughterboard with a 44 pin FPC connector. The keyboard part number is 42T3177. // 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 for the Trackpoint was originally from https://playground.arduino.cc/uploads/ComponentLib/mouse.txt -// but the interface to the host was changed from RS232 serial to USB using the PJRC Mouse functions. -// A watchdog timer was also added to the "while loops" so the code can't hang if a clock edge is missed. -// In the Arduino IDE, select Tools, Teensy 3.2. Also under Tools, select Keyboard+Mouse+Joystick +// 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 -// Initial Release Nov 23, 2018 +// Rev 1.0 - Nov 23, 2018 - Original Release +// Rev 1.1 - Dec 2, 2018 - Replaced ps/2 trackpoint code from playground arduino with my own code // // Trackpoint signals -#define MDATA 18 // ps/2 data to trackpoint -#define MCLK 19 // ps/2 clock to trackpoint -#define MRESET 0 // active high trackpoint reset at power up +#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 @@ -151,191 +151,265 @@ int mod_alt_l = 0; int mod_alt_r = 0; int mod_gui = 0; // -// *****************Functions for Trackpoint*************************** -// Function to send the trackpoint a command -void trackpoint_write(char data) +// **************Functions common to keyboard and trackpoint************************** +// +// Function to set a pin to high impedance (acts like open drain output) +void go_z(int pin) { - char i; - char parity = 1; - // put pins in output mode - go_z(MDATA); - go_z(MCLK); - elapsedMillis watchdog; // set watchdog to zero - delayMicroseconds(300); - go_0(MCLK); - delayMicroseconds(300); - go_0(MDATA); - delayMicroseconds(10); - // start bit - go_z(MCLK); - // wait for trackpoint to take control of clock) - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - // clock is low, and we are clear to send data - for (i=0; i < 8; i++) { - if (data & 0x01) { - go_z(MDATA); - } - else { - go_0(MDATA); - } - // wait for clock cycle - while (digitalRead(MCLK) == LOW) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - parity = parity ^ (data & 0x01); - data = data >> 1; - } - // parity - if (parity) { - go_z(MDATA); - } - else { - go_0(MDATA); - } - // wait for clock cycle - while (digitalRead(MCLK) == LOW) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - // stop bit - go_z(MDATA); - delayMicroseconds(50); - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - // wait for trackpoint to switch modes - while ((digitalRead(MCLK) == LOW) || (digitalRead(MDATA) == LOW)) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - // put a hold on the incoming data. - go_0(MCLK); + 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 trackpoint_read(void) +char tp_read(void) { - char data = 0x00; - int i; - char bity = 0x01; - // start the clock - elapsedMillis watchdog; // set watchdog to zero - go_z(MCLK); - go_z(MDATA); - delayMicroseconds(50); - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop + 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; // break out of infinite loop } } - delayMicroseconds(5); // wait for clock ring to settle - while (digitalRead(MCLK) == LOW) { // eat start bit - if (watchdog >= 200) { //check for 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; // break out of infinite loop } } - for (i=0; i < 8; i++) { - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for 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; // break out of infinite loop } } - if (digitalRead(MDATA) == HIGH) { - data = data | bity; - } - while (digitalRead(MCLK) == LOW) { - if (watchdog >= 200) { //check for 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; // break out of infinite loop } } - bity = bity << 1; - } - // ignore parity bit - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for 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; // break out of infinite loop } } - while (digitalRead(MCLK) == LOW) { - if (watchdog >= 200) { //check for 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; // break out of infinite loop } } - // eat stop bit - while (digitalRead(MCLK) == HIGH) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - while (digitalRead(MCLK) == LOW) { - if (watchdog >= 200) { //check for infinite loop - trackpoint_error = HIGH; // set error flag - break; - } - } - // put a hold on the incoming data. - go_0(MCLK); - return data; +// 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(MCLK); // float the clock and data to trackpoint - go_z(MDATA); + 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(MRESET); // drive low + go_0(TP_RESET); // drive low delay(1000); // wait 1 second - go_1(MRESET); // drive High to activate Reset signal to trackpoint + 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(MRESET); // drive Reset back to the inactive (low) state + 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 - trackpoint_write(0xff); - trackpoint_read(); // ack byte - // Read ack byte - trackpoint_read(); // blank - trackpoint_read(); // blank + 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 - trackpoint_write(0xf0); // remote mode - trackpoint_read(); // Read ack byte - delayMicroseconds(100); + 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***************************** @@ -456,35 +530,6 @@ void send_normals() { Keyboard.set_key6(slot6); Keyboard.send_now(); } -// **************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); -} // //************************************Setup******************************************* void setup() { @@ -617,11 +662,11 @@ void loop() { // poll the trackpoint for new movement data over_flow = 0; // assume no overflow until status is received trackpoint_error = LOW; // start with no error - trackpoint_write(0xeb); // request data - trackpoint_read(); // ignore ack - mstat = trackpoint_read(); // save into status variable - mx = trackpoint_read(); // save into x variable - my = trackpoint_read(); // save into y variable + tp_write(0xeb); // request data + tp_read(); // ignore ack + 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 }