Frank Adams
Published © Apache-2.0

Laptop Touchpad/Trackpoint Conversion to USB

A Teensy converts PS/2, I2C, or ADB from a laptop touchpad or trackpoint into USB mouse commands. Adafruit Trinket M0 code also provided.

IntermediateProtip1 hour30,379
Laptop Touchpad/Trackpoint Conversion to USB

Things used in this project

Hardware components

Adafruit Trinket M0
The other Arduino controllers with hardware USB HID can also be used with the Trinket code.
×1
Teensy 4.0 Development Board
Teensy 4.0 Development Board
The other Teensy controllers like the LC and 3.2 can be used if you can find a source.
×1

Software apps and online services

Arduino IDE
Arduino IDE
Teensyduino
Needed for Teensy loading and code modification.
Adafruit Trinket Library
Needed for Trinket loading and code modification

Story

Read more

Custom parts and enclosures

FPC_8pin1mm Eagle board file

Breakout an FPC cable from the touchpad so it can be jumpered to a Teensy

FPC_10pin0p5mm Eagle Board File

Breakout an FPC cable from the touchpad so it can be jumpered to a Teensy

Schematics

Open Drain Drivers for PS/2 and I2C

Open drain or open collector drivers are used for busses like PS/2 and I2C. These busses need a pull up resistor which may be in the touchpad.

Power filters

Clock and Data Level Translator

If you are using a Teensy LC with a 5 volt touchpad, add this circuit to the board.

Code

Touchpad_3p2

Arduino
PS2 to USB converter using Remote Mode
/* 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 interfaces the Teensy 3.2 with a PS/2 touchpad from a laptop.
// 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 3.2. Also under Tools, select Keyboard+Mouse+Joystick
//
// Revision History
// 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 
// Rev 1.2 - Feb 2, 2019 - Changed the error routine and added an error LED
// Rev 1.3 - Feb 14, 2019 - Added pinouts for different touchpads
// Rev 1.4 - July 31, 2020 - Added enable command for Elantech touchpads and increased the delay after reset
//
// This code has been tested on the following touchpads:
//
// Dell D630 Touchpad. I used the wires from the touchpad connector as follows:
// Pin 2 = 5V
// Pin 1 = Gnd
// Pin 7 = Clock
// Pin 6 = Data
// This touchpad has resistive pullups so no additional pullups were required. 
//
// HP Pavilion DV9000 Touchpad part number 920-000702-04 Rev A
// The test points on the touchpad were wired as follows:
// T22 = 3.3V  (The touchpad also works with 5V)
// T23 = Gnd   (I soldered to the ground plane)
// T10 = Clock 
// T11 = Data 
// This touchpad has active pullups so no additional pullups were required.
// 
// Dell 1545 touchpad. I soldered wires to the 4 pin connector as follows:
// Pin 1 = 5V
// Pin 4 = Gnd
// Pin 3 = Clock
// Pin 2 = Data
// This touchpad has active pullups so no additional pullups were required. 
//
// The touchpad ps/2 data and clock lines are connected to the following Teensy I/O pins (modify to match your jumper wires)
#define TP_DATA 16
#define TP_CLK 15
// Teensy LED will be lit if the touchpad fails to respond properly during initialization
#define ERROR_LED 13
//
// Declare variable that will be used by functions
boolean touchpad_error = LOW; // sent high when touch pad routine times out
//
// 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 Touchpad***************************
//
// Function to send the touchpad 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_pu(TP_CLK); //
  go_pu(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_pu(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
      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_pu(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
        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
        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_pu(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
      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
      break; // break out of infinite loop
    }
  }
  go_pu(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
      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
  }
  while ((digitalRead(TP_CLK) == LOW) || (digitalRead(TP_DATA) == LOW)) { // loop if clock or data are low
    if (watchdog >= timeout) { //check for infinite loop
      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 touchpad
//
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_pu(TP_CLK); // release the clock
  go_pu(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
      break; // break out of infinite loop
    }
  }
  if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  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
      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
        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
        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
      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
  // bad parity - pass to future error handler
  } 
  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
      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
      break; // break out of infinite loop
    }
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
  // send bad stop bit to future error handler
  }
  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
      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 touchpad_init()
{
  touchpad_error = LOW; // start with no error
  go_pu(TP_CLK); // float the clock and data to touchpad
  go_pu(TP_DATA);
  //  Sending reset command to touchpad
  tp_write(0xff);
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  }
  delay(1000); // wait 1000ms so tp can run its self diagnostic
  //  verify proper response from tp
  if (tp_read() != 0xaa) { // verify basic assurance test passed
    touchpad_error = HIGH;
  } 
  if (tp_read() != 0x00) { // verify basic assurance test passed
    touchpad_error = HIGH;
  }
  // increase resolution from 4 counts/mm to 8 counts/mm
  tp_write(0xe8); //  Sending resolution command
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  } 
  tp_write(0x03); // value of 0x03 = 8 counts/mm resolution (default is 4 counts/mm)
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  }
  //  Sending remote mode code so the touchpad will send data only when polled
  tp_write(0xf0);  // remote mode 
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  } 
  //  Sending touchpad enable code (needed for Elan touchpads)
  tp_write(0xf4);  // tp enable 
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  }
}
// ************************************Begin Routine*********************************************************
void setup()
{
  touchpad_init(); // reset touchpad, then set it's resolution and put it in remote mode 
  pinMode(ERROR_LED, OUTPUT); // define teensy I/O 13 as an output
}

// declare and initialize variables  
  char mstat; // touchpad status reg = Y overflow, X overflow, Y sign bit, X sign bit, Always 1, Middle Btn, Right Btn, Left Btn
  char mx; // touchpad 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 touchpad gives a positive value. 
  char my; // touchpad y movement also 8 bits plus sign. Touchpad 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 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 button_change = 0; // Active high, shows when a touchpad left or right button has changed since last polling cycle
  
// ************************************Main Loop***************************************************************
void loop() {
  if (touchpad_error == LOW) { // check if touchpad is present
    digitalWrite(ERROR_LED, LOW); // turn off LED on Teensy to show touchpad initialized OK
// poll the touchpad for new movement data
    over_flow = 0; // assume no overflow until status is received 
    tp_write(0xeb);  // request data
    if (tp_read() != 0xfa) { // verify correct ack byte
    // bad ack - pass to future error handler
    }
    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 & 0x7f; // mask off 8th bit
    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 & 0x7f; // mask off 8th bit
    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 touchpad_error is set
    if ((over_flow) || (touchpad_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 touchpad left and right button status over usb if no 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;  
    }
// Determine if the left or right touch pad buttons have changed since last polling 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. 
    if (button_change){
      Mouse.set_buttons(left_button, 0, right_button); // send button status
    }
    old_left_button = left_button; // remember new button status for next polling cycle
    old_right_button = right_button;
    }
  else  {
    digitalWrite(ERROR_LED, HIGH);
  }
//
// **************************************End of touchpad routine***********************************
// 
  delay(30);  // wait 30ms before repeating next polling cycle
}

Touchpad I2C

Arduino
Simple Touchpad - I2C to USB Conversion
/* Copyright 2021 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 an I2C Touchpad Controller using a Teensy 3.2 
// It assumes the TP is at I2C address 0x24
// Register 0 function unknown - not used
// Register 1 is the button register. 0x00=no buttons, 0x03=left button, 0x04=right button
// Register 2 function unknown - not used
// Register 3 is the X delta movement 8 bit 2's complement value. Left to right gives positive values
// Register 4 is the Y delta movement 8 bit 2's complement value. Top to bottom gives positive values
// Register 5 function unknown - not used
// Register 6 is the touch sensor. 0x00=no touch, 0x0f=touch - The code below does not use this register
//
// Release Date: August 3, 2021
//
#include <Wire.h> // used for i2c bus to touchpad
//
// define the pins on the Teensy used for the touchpad I2C bus
#define TP_DATA 14 // Touchpad i2c data signal 
#define TP_CLK 15  // Touchpad i2c clock signal 

//
//----------------------------------Setup before main loop-------------------------------------------
//
void setup() {
Wire.begin(); // i2c for touchpad
}
// declare and initialize touchpad variables  
char buttonreg; // touchpad left and right buttons read from register #1. Left button is bit 0 and 1, right button is bit 2
char xreg; // touchpad x movement from register #3. 2's complement, left to right movement gives positive value. 
char yreg; // touchpad y movement from register #4. 2's complement, top to bottom movement gives positive value.
boolean left_button = 0; // Active high, on/off variable for left button 
boolean right_button = 0; // Active high, on/off variable for right button 
boolean old_left_button = 0; // Active high, on/off variable for left button status from the previous polling cycle
boolean old_right_button = 0; // Active high, on/off variable for right button status from the previous polling cycle
boolean button_change = 0; // Active high, shows when a touchpad left or right button has changed since the last polling cycle
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {   
// poll the touchpad for new movement or button data 
//
  Wire.beginTransmission(0x24); // Load the TP register pointer 
  Wire.write(0x01);             // so it points to register 1
  Wire.endTransmission();       // 
  Wire.requestFrom(0x24, 4); // Read 4 bytes from the TP starting at register 1
    
  buttonreg = Wire.read(); // read the button status bits in register 1
  Wire.read(); // read and throw away byte from unused register 2
  xreg = Wire.read(); // read the X value from register 3
  yreg = Wire.read(); // read the Y value from register 4    
// send the x and y data back via usb if either one is non-zero
  if ((xreg != 0x00) || (yreg != 0x00)) {
    Mouse.move(xreg,yreg);
  }
//
// check the left and right bits in the button register from the touchpad
  if (buttonreg == 0x03) {   // the left button is pushed 
    left_button = 1;   // set the left button variable
  }
  else {   // otherwise clear the left button variable
    left_button = 0;   
  }
  if (buttonreg == 0x04) { // the right button is pushed
    right_button = 1;   // set the right button variable
  } 
  else {   // otherwise clear right button variable
    right_button = 0;  
  }
// Determine if the left or right touchpad buttons have changed since last polling cycle using xor
  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. 
  if (button_change){
    Mouse.set_buttons(left_button, 0, right_button); // send left, middle, and right button status
  }
  old_left_button = left_button; // remember button status for next polling cycle
  old_right_button = right_button;
//
  delay(25); // This is the polling rate in msec
// 
}

TPS65-201A-S Azoteq I2C

C/C++
Teensy code to read an Azoteq I2C TPS65-201A-S touchpad and send mouse commands over USB
/* Copyright 2021 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 an Azoteq touchpad Part No: TPS65-201A-S over I2C and sends the results 
// over USB using the Teensyduino mouse.move function. The mouse.set_buttons function is used
// to send a left button push and release based on a finger tap on the touchpad. 
// 
#include <Wire.h>
#define rdy 10 // touchpad ready signal monitored by Teensy, active high
#define rst_n 12 // touchpad reset driven by Teensy, active low
#define tp_address 0x74 // Azoteq touchpad i2c address

char gesture0; // holds gesture events 0
char gesture1; // holds gesture events 1
char sys_info0; // holds system info 0
char sys_info1; // holds system info 1
char finger_count; // number of fingers
char xrel_high; // holds the relative x high 8 bits
char xrel_low; // holds the relative x low 8 bits
char yrel_high; // holds the relative y high 8 bits
char yrel_low; // holds the relative y low 8 bits
char xabs_high; // holds the absolute x high 8 bits
char xabs_low; // holds the absolute x low 8 bits
char yabs_high; // holds the absolute y high 8 bits
char yabs_low; // holds the absolute y low 8 bits
char tch_strength_high; // holds the touch strength high 8 bits
char tch_strength_low; // holds the touch strength low 8 bits
char tch_area; // holds the touch area/size
boolean left_button = 0; // Active high, on/off variable for left button 
boolean old_left_button = 0; // Active high, on/off variable for left button status from the previous polling cycle
boolean button_change = 0; // Active high, shows when a touchpad left button has changed since the last polling cycle
//
// ********************Common Functions**********************************************
//
// 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 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);
}
//
void setup() {
go_z(rdy); // set rdy pin as an input without a pullup
// reset the TP
go_0(rst_n); // drive reset low (active) to TP 
delay (1000); // wait a second
go_1(rst_n); // drive reset high (inactive) to TP  
Wire.begin(); //Initiate the Wire library and join the I2C bus as a master
}

void loop() {
  if (digitalRead(rdy)) { // check rdy signal to see if TP has data
    Wire.beginTransmission(tp_address); //
    Wire.write(0x00); // high btye address of first register to read
    Wire.write(0x0d); // low byte address of first register to read
    Wire.endTransmission(false); // no stop bit (makes a repeated start)
    Wire.requestFrom(tp_address,16); // read 16 bytes starting at 0x000d
    if (Wire.endTransmission(true) == 0) {  // send stop bit and check if TP "acked"
      gesture0 = Wire.read(); // read the gesture 0 byte from register 0x000d
      gesture1 = Wire.read(); // read the gesture 1 byte from register 0x000e
      sys_info0 = Wire.read(); // read the system info 0 byte from register 0x000f
      sys_info1 = Wire.read(); // read the system info 1 byte from register 0x0010
      finger_count = Wire.read(); // read the finger count byte from register 0x0011
      xrel_high = Wire.read(); // read the high relative X byte from register 0x0012
      xrel_low = Wire.read(); // read the low relative X byte from register 0x0013
      yrel_high = Wire.read(); // read the high relative Y byte from register 0x0014
      yrel_low = Wire.read(); // read the low relative Y byte from register 0x0015
      xabs_high = Wire.read(); // read the high absolute X byte from register 0x0016
      xabs_low = Wire.read(); // read the low absolute X byte from register 0x0017
      yabs_high = Wire.read(); // read the high absolute Y byte from register 0x0018
      yabs_low = Wire.read(); // read the low absolute Y byte from register 0x0019
      tch_strength_high = Wire.read(); // read the high touch strength byte from register 0x001a
      tch_strength_low = Wire.read(); // read the low touch strength byte from register 0x001b
      tch_area = Wire.read(); // read the touch area/size byte from register 0x001c
//      
      // send the x and y data back via usb if either one is non-zero
      if ((xrel_low != 0x00) || (yrel_low != 0x00)) {
        Mouse.move(xrel_low,yrel_low);
        xrel_low = 0; // zero out the registers (just in case)
        yrel_low = 0;
      }
      if ((gesture0 & 0x01) == 0x01) { // test bit 0
        left_button = 1;
      }
      else {
        left_button = 0;
      }
      // Determine if the left touchpad button has changed since last polling cycle using xor
      button_change = left_button ^ old_left_button;
      // Don't send button status if there's no change since last time. 
      if (button_change) {
        Mouse.set_buttons(left_button, 0, 0); // send left button value and 0 for middle and right button
      }
      old_left_button = left_button; // remember button status for next polling cycle
    }
    else { 
      // put future no-acknoledge error handler here
    } 
    // Send the End Communication Window Command per para 8.7 of Azoteq data sheet
    Wire.beginTransmission(tp_address); //
    Wire.write(0xee); // high btye of address pointer
    Wire.write(0xee); // low byte of address pointer
    Wire.write(0x00); // data value doesn't matter
    Wire.endTransmission(true); // send stop bit
//    
    delay (30); // overall loop rate in milliseconds 
  } 
}

Azoteq_SAMD21.ino

C/C++
I2C code so the Adafruit Trinket M0 can control an Azoteq touchpad
/* Copyright 2022 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.
*/
// Adafruit Trinket M0 controls an Azoteq touchpad Part No: TPS65-201A-S over I2C and sends the results 
// over USB using the Arduino mouse function. The Mouse.press and release functions are used
// to send a left button push and release based on a finger tap on the touchpad. 
// 
#include <Wire.h>
#include <Mouse.h>
#define led 13
#define rdy 3 // touchpad ready signal monitored by SAMD21, active high
#define rst_n 1 // touchpad reset driven by SAMD21, active low
#define hold_n 4 // input (active low) to hold SAMD21 out of main loop so it can be programmed 
#define tp_address 0x74 // Azoteq touchpad i2c address

char gesture0; // holds gesture events 0
char gesture1; // holds gesture events 1
char sys_info0; // holds system info 0
char sys_info1; // holds system info 1
char finger_count; // number of fingers
char xrel_high; // holds the relative x high 8 bits
char xrel_low; // holds the relative x low 8 bits
char yrel_high; // holds the relative y high 8 bits
char yrel_low; // holds the relative y low 8 bits
char xabs_high; // holds the absolute x high 8 bits
char xabs_low; // holds the absolute x low 8 bits
char yabs_high; // holds the absolute y high 8 bits
char yabs_low; // holds the absolute y low 8 bits
char tch_strength_high; // holds the touch strength high 8 bits
char tch_strength_low; // holds the touch strength low 8 bits
char tch_area; // holds the touch area/size
boolean left_button = 0; // Active high, on/off variable for left button 
boolean old_left_button = 0; // Active high, on/off variable for left button status from the previous polling cycle
//
// ********************Common Functions**********************************************
//
// 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);
}
//
void setup() {
  go_0(led); // turn off the led
  go_pu(hold_n); // set the hold_n pin as an input with a pullup
  go_z(rdy); // set rdy pin as an input without a pullup
// reset the TP
  go_0(rst_n); // drive reset low (active) to TP 
  delay (1000); // wait a second
  go_1(rst_n); // drive reset high (inactive) to TP
  while (digitalRead(!hold_n)) {
    // stay in while loop until hold_n goes high to hold SAMD21 from being a mouse so it can be programmed
  }
  Mouse.begin(); // Initiate USB mouse
  Wire.begin(); // Initiate the Wire library and join the I2C bus as a master
}

void loop() {
  if (digitalRead(rdy)) { // check rdy signal to see if TP has data
    go_1(led); // turn on the led when there is activity on the TP
    Wire.beginTransmission(tp_address); //
    Wire.write(0x00); // high btye address of first register to read
    Wire.write(0x0d); // low byte address of first register to read
    Wire.endTransmission(false); // no stop bit (makes a repeated start)
    Wire.requestFrom(tp_address,16); // read 16 bytes starting at 0x000d
    if (Wire.endTransmission(true) == 0) {  // send stop bit and check if TP "acked"
      gesture0 = Wire.read(); // read the gesture 0 byte from register 0x000d
      gesture1 = Wire.read(); // read the gesture 1 byte from register 0x000e
      sys_info0 = Wire.read(); // read the system info 0 byte from register 0x000f
      sys_info1 = Wire.read(); // read the system info 1 byte from register 0x0010
      finger_count = Wire.read(); // read the finger count byte from register 0x0011
      xrel_high = Wire.read(); // read the high relative X byte from register 0x0012
      xrel_low = Wire.read(); // read the low relative X byte from register 0x0013
      yrel_high = Wire.read(); // read the high relative Y byte from register 0x0014
      yrel_low = Wire.read(); // read the low relative Y byte from register 0x0015
      xabs_high = Wire.read(); // read the high absolute X byte from register 0x0016
      xabs_low = Wire.read(); // read the low absolute X byte from register 0x0017
      yabs_high = Wire.read(); // read the high absolute Y byte from register 0x0018
      yabs_low = Wire.read(); // read the low absolute Y byte from register 0x0019
      tch_strength_high = Wire.read(); // read the high touch strength byte from register 0x001a
      tch_strength_low = Wire.read(); // read the low touch strength byte from register 0x001b
      tch_area = Wire.read(); // read the touch area/size byte from register 0x001c
//      
      // send the x and y data back via usb if either one is non-zero
      if ((xrel_low != 0x00) || (yrel_low != 0x00)) {
        Mouse.move(xrel_low,yrel_low);
        xrel_low = 0; // zero out the registers (just in case the TP doesn't)
        yrel_low = 0;
      }
      if ((gesture0 & 0x01) == 0x01) {  // test bit 0
      Mouse.click(MOUSE_LEFT);
//        left_button = 1;
      }
//      else {
//        left_button = 0;
//      }
      // Determine if the left touchpad button has changed since last polling cycle 
//      if ((left_button == 1) && (old_left_button == 0)) {
//        Mouse.press(MOUSE_LEFT); // push left button 
//      }
//      else if ((left_button == 0) && (old_left_button == 1)) {
//        Mouse.release(MOUSE_LEFT); // release left button
//      }
//      old_left_button = left_button; // remember button status for next polling cycle
    }
    else { 
      // put future no-acknoledge error handler here
    } 
    // Send the End Communication Window Command per para 8.7 of Azoteq data sheet
    Wire.beginTransmission(tp_address); //
    Wire.write(0xee); // high btye of address pointer
    Wire.write(0xee); // low byte of address pointer
    Wire.write(0x00); // data value doesn't matter
    Wire.endTransmission(true); // send stop bit
//    
    delay (30); // overall loop rate in milliseconds 
  }
  else {
    go_0(led); // turn off the led when there is no tp activity 
  }
}

SAMD21_PS2.ino

C/C++
Teensy PS2 touchpad code converted to run on a Adafruit Trinket M0.
/* Copyright 2022 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.
*/
// Adafruit Trinket M0 controls a PS/2 touchpad and sends releative movement and button pushes over USB.
//
#include <Mouse.h>
//
#define TP_DATA 0
#define TP_CLK 2
// Trinket LED will light if the touchpad fails to respond properly during initialization
#define ERROR_LED 13
//
// Declare variable that will be used by functions
boolean touchpad_error = LOW; // sent high when touch pad routine times out
//
// 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 Touchpad***************************
//
// Function to send the touchpad a byte of data (command)
//
void tp_write(char send_data)  
{
  char odd_parity = 0; // clear parity bit count
// Enable the bus by floating the clock and data
  go_pu(TP_CLK); //
  go_pu(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_pu(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
// put watchdog timer here to breakout of 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_pu(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
// put watchdog timer here to breakout of 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
//        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_pu(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
// put watchdog timer here to breakout of loop
  }
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
// put watchdog timer here to breakout of loop
  }
  go_pu(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
// put watchdog timer here to breakout of loop
  }
  delayMicroseconds(1); // wait to let the data settle
  if (digitalRead(TP_DATA)) { // Ack bit s/b low if good transfer
  }
  while ((digitalRead(TP_CLK) == LOW) || (digitalRead(TP_DATA) == LOW)) { // loop if clock or data are low
// put watchdog timer here to breakout of 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 touchpad
//
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_pu(TP_CLK); // release the clock
  go_pu(TP_DATA); // release the data
  delayMicroseconds(5); // delay to let clock go high
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
// put watchdog timer here to breakout of loop
  }
  if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
// put watchdog timer here to breakout of 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
// put watchdog timer here to breakout of 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
// put watchdog timer here to breakout of loop
    }
  }
// receive parity
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
// put watchdog timer here to breakout of 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
  // bad parity - pass to future error handler
  } 
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
// put watchdog timer here to breakout of loop
  }
// stop bit
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
// put watchdog timer here to breakout of loop
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
  // send bad stop bit to future error handler
  }
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
// put watchdog timer here to breakout of 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 touchpad_init()
{
  touchpad_error = LOW; // start with no error
  go_pu(TP_CLK); // float the clock and data to touchpad
  go_pu(TP_DATA);
  //  Sending reset command to touchpad
  tp_write(0xff);
  if (tp_read() != 0xfa) { // verify correct ack byte
    touchpad_error = HIGH;
  }
  delay(1000); // wait 1000ms so tp can run its self diagnostic
  //  verify proper response from tp
  if (tp_read() != 0xaa) { // verify basic assurance test passed
    touchpad_error = HIGH;
  } 
  if (tp_read() != 0x00) { // verify basic assurance test passed
    touchpad_error = HIGH;
  }
  //  Send touchpad disable code 
  tp_write(0xf5);  // tp disable 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }
  // Load Mode Byte with 00 using the following special sequence from page 38.
  // Send set resolution to 0 four times followed by a set sample rate to 0x14
// #1 set resolution
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  tp_write(0x01);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }
// #2 set resolution
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }
// #3 set resolution
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }
// #4 set resolution
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }
// Set sample rate
  tp_write(0xf3);  // set sample rate 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  tp_write(0x14);  // to 14 hex 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  } 
  // set the resolution 
  tp_write(0xe8); //  Sending resolution command
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  } 
  tp_write(0x03); // value of 0x03 = 8 counts/mm resolution (default is 4 counts/mm)
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  } 
  // set the sample rate 
  tp_write(0xf3); //  Sending sample rate command
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  } 
  tp_write(0x28); // 0x28 = 40 samples per second, the default value used for Synaptics TP
  if (tp_read() != 0xfa) { // verify correct ack byte
//    init_error = HIGH;
  }  
  //  Sending remote mode code so the touchpad will send data only when polled
  tp_write(0xf0);  // remote mode 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    touchpad_error = HIGH;
  } 
  //  Sending touchpad enable code (needed for Elan touchpads)
  tp_write(0xf4);  // tp enable 
  if (tp_read() != 0xfa) { // verify correct ack byte
//    touchpad_error = HIGH;
  }
}
// ************************************Begin Routine*********************************************************
void setup()
{
  touchpad_init(); // reset touchpad, then set it's resolution and put it in remote mode 
  pinMode(ERROR_LED, OUTPUT); // define Trinket I/O 13 as an output
  Mouse.begin(); // Initiate USB mouse
}

// declare and initialize variables  
  char mstat; // touchpad status reg = Y overflow, X overflow, Y sign bit, X sign bit, Always 1, Middle Btn, Right Btn, Left Btn
  char mx; // touchpad 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 touchpad gives a positive value. 
  char my; // touchpad y movement also 8 bits plus sign. Touchpad 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 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
// 
// ************************************Main Loop***************************************************************
void loop() {
  if (touchpad_error == LOW) { // check if touchpad is present
    digitalWrite(ERROR_LED, LOW); // turn off LED on Trinket to show touchpad initialized OK
// poll the touchpad for new movement data
    over_flow = 0; // assume no overflow until status is received 
    tp_write(0xeb);  // request data
    if (tp_read() != 0xfa) { // verify correct ack byte
    // bad ack - pass to future error handler
    }
    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 & 0x7f; // mask off 8th bit
    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 & 0x7f; // mask off 8th bit
    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 touchpad_error is set
    if ((over_flow) || (touchpad_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 touchpad left and right button status over usb if no error
    if ((0x01 & mstat) == 0x01) {   // if left button set 
      left_button = 1;   
    }
    else {   // clear left button
      left_button = 0;   
    }
    if (left_button && !old_left_button) { // true if left button was just pushed
      Mouse.press(MOUSE_LEFT);
    }
    else if (!left_button && old_left_button) { // true if left button was just released
      Mouse.release(MOUSE_LEFT);
    }
    if ((0x02 & mstat) == 0x02) {   // if right button set 
      right_button = 1;   
    } 
    else {   // clear right button
      right_button = 0;  
    }
    if (right_button && !old_right_button) { // true if right button was just pushed
      Mouse.press(MOUSE_RIGHT);
    }
    else if (!right_button && old_right_button) { // true if right button was just released
      Mouse.release(MOUSE_RIGHT);
    }
//
    old_left_button = left_button; // remember new button status for next polling cycle
    old_right_button = right_button;
    }
  else  {
    digitalWrite(ERROR_LED, HIGH);
  }
//
// **************************************End of touchpad routine***********************************
// 
  delay(30);  // wait 30ms before repeating next polling cycle
}

Touchpad_Stream_mode

Arduino
PS2 to USB Converter using Stream Mode
//
// This software interfaces a Teensy 3.2 with a PS/2 laptop touchpad.
// The touchpad is commanded to be in Stream Mode (not remote mode).
// This causes the touchpad to send data when ever it detects finger movement.
// 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 3.2. Also under Tools, select Keyboard+Mouse+Joystick
//
// Revision History
// Rev 1.0 - Aug 1, 2020 - Original Release
//
// The touchpad ps/2 data and clock lines are connected to the following Teensy I/O pins
#define TP_DATA 0
#define TP_CLK 1
// Teensy LED will be turned ON to show if errors are detected during initialization or during normal use
#define ERROR_LED 13
//
// Declare variables that will be used by functions
boolean init_error = LOW; // set high if initialization detects an error
boolean bit_error = LOW; // set high if main loop detects an error
char mstat; // touchpad status 8 bit register = Y overflow, X overflow, Y sign bit, X sign bit, Always 1, Middle Btn, Right Btn, Left Btn
char mx; // touchpad 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 touchpad gives a positive value. 
char my; // touchpad y movement also 8 bits plus sign bit in status reg. Touchpad movement away from the user gives a positive value.
//
// 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 set a pin to a logic low
void go_0(int pin)
{
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
}
//
// Function to set a pin to a logic high
void go_1(int pin)
{
  pinMode(pin, OUTPUT);
  digitalWrite(pin, HIGH);
}
//
// *****************Functions for Touchpad***************************
//
// Function to send the touchpad a byte of data (command)
//
void tp_write(char send_data)  
{
  unsigned int timeout = 200; // breakout of loop if watchdog over this value in msec
  elapsedMillis watchdog; // zero the watchdog timer clock
  char odd_parity = 0; // clear parity bit count
// Enable the PS/2 bus by floating the clock and data
  go_pu(TP_CLK); //
  go_pu(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_pu(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 because tp did not respond
      init_error = HIGH;
      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_pu(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
        init_error = HIGH;
        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
        init_error = HIGH;
        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_pu(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
      init_error = HIGH;
      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
      init_error = HIGH;
      break; // break out of infinite loop
    }
  }
  go_pu(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
      init_error = HIGH;
      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
  }
  while ((digitalRead(TP_CLK) == LOW) || (digitalRead(TP_DATA) == LOW)) { // loop if clock or data are low
    if (watchdog >= timeout) { //check for infinite loop
      init_error = HIGH;
      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 touchpad
//
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_pu(TP_CLK); // release the clock
  go_pu(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
      init_error = HIGH;
      break; // break out of infinite loop
    } 
  }
  if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  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
      init_error = HIGH;
      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
        init_error = HIGH;
        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
        init_error = HIGH;
        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
      init_error = HIGH;
      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
    init_error = HIGH;
  } 
  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
      init_error = HIGH;
      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
      init_error = HIGH;
      break; // break out of infinite loop
    }
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
  // send bad stop bit to future error handler
  }
  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
      init_error = HIGH;
      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
}
//
// Function to decode 3 bytes of data from the touchpad = status, X Delta, Y Delta
//
void tp_packet(void)           
{
// ******************************Receive Byte 0 = Status***********************
  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_pu(TP_CLK); // release the clock
  go_pu(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 (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  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 (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
    }
  }
// receive parity
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  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
    bit_error = HIGH;
  } 
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
// stop bit
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
    bit_error = HIGH;
  }
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  mstat = rcv_data; // save data result in status byte
// ******************************Receive Byte 1 = Delta X**************************  
  rcv_data = 0; // initialize to zero
  mask = 1; // shift a 1 across the 8 bits to select where to load the data
  rcv_parity = 0; // count the ones received
  delayMicroseconds(5); // delay to let the clock stop ringing
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  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 (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
    }
  }
// receive parity
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  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
    bit_error = HIGH;
  } 
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
// stop bit
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
    bit_error = HIGH;
  }
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  mx = rcv_data; // save data result in delta x byte
// Receive Byte 1 = Delta Y  
// ******************************Receive Byte 2 = Delta Y**************************  
  rcv_data = 0; // initialize to zero
  mask = 1; // shift a 1 across the 8 bits to select where to load the data
  rcv_parity = 0; // count the ones received
  delayMicroseconds(5); // delay to let the clock stop ringing
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  if (digitalRead(TP_DATA)) { // Start bit s/b low from tp
  // start bit not correct - put error handler here if desired
  }  
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  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 (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
    }
  }
// receive parity
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  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
    bit_error = HIGH;
  } 
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
// stop bit
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == HIGH) { // loop until the clock goes low
  }
  if (digitalRead(TP_DATA) == LOW) { // check if stop bit is bad (low)
    bit_error = HIGH;
  }
  delayMicroseconds(1); // delay to let the clock settle out
  while (digitalRead(TP_CLK) == LOW) { // loop until the clock goes high
  }
  my = rcv_data; // save data result in delta y byte
//
// Inhibit the bus so the tp only talks when we're listening
  go_0(TP_CLK);
}
//
void touchpad_init()
{
  init_error = LOW; // start with no error
  go_pu(TP_CLK); // float the clock and data to touchpad
  go_pu(TP_DATA);
  //  Sending reset command to touchpad
  tp_write(0xff);
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
  delay(1000); // wait 1 second so tp can run its self diagnostic
  //  verify proper response from tp
  if (tp_read() != 0xaa) { // verify basic assurance test passed
    init_error = HIGH;
  } 
  if (tp_read() != 0x00) { // verify basic assurance test passed
    init_error = HIGH;
  }
  //  Send touchpad disable code so that the mode byte can be loaded next
  tp_write(0xf5);  // tp disable 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
  // Load Mode Byte with 00 using the following special sequence from page 38 of Synaptics Interfaceing Guide.
  // Send set resolution to 0 four times followed by a set sample rate to 0x14
  // The resolution and sample rate are not actually changed but instead it loads the mode byte.
// #1 set resolution
  tp_write(0xe8);  // set resolution (actually part of setting mode byte)
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
// #2 set resolution (actually part of setting mode byte)
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
// #3 set resolution
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
// #4 set resolution (actually part of setting mode byte)
  tp_write(0xe8);  // set resolution 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  tp_write(0x00);  // to zero 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
// Set sample rate (actually part of setting mode byte)
  tp_write(0xf3);  // set sample rate 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  tp_write(0x14);  // to 14 hex 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  } 
// This completes the mode byte load
  // set the resolution for real
  tp_write(0xe8); //  Sending resolution command
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  } 
  tp_write(0x03); // value of 0x03 = 8 counts/mm resolution (default is 4 counts/mm)
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  } 
  // set the sample rate for real
  tp_write(0xf3); //  Sending sample rate command
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  } 
  tp_write(0x28); // 0x28 = 40 samples per second, the default value used for Synaptics TP
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }  
  //  Load stream mode command so tp will send data whenever it's available
  tp_write(0xea);  // stream mode 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  } 
  //  Send touchpad enable code 
  tp_write(0xf4);  // tp enable 
  if (tp_read() != 0xfa) { // verify correct ack byte
    init_error = HIGH;
  }
}
//
// ************************************Begin Routine*********************************************************
void setup() {
  pinMode(ERROR_LED, OUTPUT); // define teensy I/O 13 as an output
  touchpad_init(); // reset tp and check that self diagnostic passed. Put tp in stream mode and enable it 
  digitalWrite(ERROR_LED, init_error); //LED off means touchpad passed its power up test, ACKed all communications, and never caused a watchdog timeout
  delay(5000);  // wait 5 seconds to show init_error LED status
  digitalWrite(ERROR_LED, LOW); // Turn off LED for 1/2 second 
  delay(500);  // 1/2 second delay
  digitalWrite(ERROR_LED, HIGH); // Turn on LED momentarily to indicate code is in main loop 
  delay(500); // 1/2 second delay
  digitalWrite(ERROR_LED, bit_error); // Turn off LED and switch its function to show bit errors in main loop
}
//
// declare and initialize variables  
  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 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 button_change = 0; // Active high, shows when a touchpad left or right button has changed since last polling cycle
//  
// ************************************Main Loop***************************************************************
void loop() {
// Touchpad is enabled and in stream mode
  tp_packet(); // this function loops until there is bus activity and then saves data into mstat, mx, and my bytes for USB transmission below.
  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 by throwing away lsb
  mx = mx & 0x7f; // mask off 8th bit
  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 by throwing away lsb 
  my = my & 0x7f; // mask off 8th bit
  if ((0x20 & mstat) == 0x20) {   // move the sign into 
    my = 0x80 | my;              // the 8th bit position
  } 
// Invert the 8 bit value because y movement in ps/2 format is the opposite direction in touchpad.move function
  my = (~my + 0x01); // invert the y data by taking the 2's complement (invert and add 1)
// zero out mx and my if over_flow is detected because the data is garbage
  if (over_flow) { 
    mx = 0x00;       
    my = 0x00;
    bit_error = HIGH; // flag an error
  } 
// Send the x and y data back via usb if either one is non-zero
  if ((mx != 0x00) || (my != 0x00)) {
    Mouse.move(mx,my);
  }
//
// pull out the left and right button status bits from the mstat byte 
  if ((0x01 & mstat) == 0x01) {   // is bit 1 set 
    left_button = 1;   // set the left_button status
  }
  else {   // clear the left_button status
    left_button = 0;   
  }
  if ((0x02 & mstat) == 0x02) {   // is bit 2 set 
    right_button = 1;   // set the right_button status
  } 
  else {   // clear the right_button status
    right_button = 0;  
  }
// Determine if the left or right touch pad buttons have changed
  button_change = (left_button ^ old_left_button) | (right_button ^ old_right_button);
// Don't send button status if there's no change since last usb button transmission. 
  if (button_change){
    Mouse.set_buttons(left_button, 0, right_button); // send button status
    old_left_button = left_button; // remember new button status for next polling cycle
    old_right_button = right_button;
  }
//
  digitalWrite(ERROR_LED, bit_error); // LED is used to show NACK, bad parity, overflow, or bad stop bits. Any error is latched "on"
}

Apple Data Bus Trackball

Arduino
ADB to USB Conversion
/* 
   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 an Apple ADB interface for a trackball. It uses a  
// Teensy 3.2 and is based on the code from this Imgur post https://imgur.com/a/BQjt9le
// 
// Revision History
// Initial Release Aug 3, 2021
// 
#define ADB_DATA_PIN 5 // TB bidirectional data pin
//
// TB Constants
int ADB_TIMEOUT = 10000;
// TB Functions
static void send_0bit() {
 digitalWrite(ADB_DATA_PIN, LOW);
 delayMicroseconds(65);
 digitalWrite(ADB_DATA_PIN, HIGH);
 delayMicroseconds(35);
}

static void send_1bit() {
 digitalWrite(ADB_DATA_PIN, LOW);
 delayMicroseconds(35);
 digitalWrite(ADB_DATA_PIN, HIGH);
 delayMicroseconds(65);
}

static void attn() {
  //delay(1);
  digitalWrite(ADB_DATA_PIN, LOW);
  delayMicroseconds(800-35);
  send_0bit();
}

void send_talk_command() {
 //attn
 pinMode(ADB_DATA_PIN, OUTPUT);
 attn();
 //address (3) 0011
 //0
  send_0bit();
  send_0bit();
  send_1bit();
  send_1bit();
 //talk 11
  send_1bit();
  send_1bit();
 //register 00
  send_0bit();
  send_0bit();
 //stop bit
  send_0bit();
 //start to stop time
 delayMicroseconds(140);

}
int receive_data_packet() {
 pinMode(ADB_DATA_PIN, INPUT);
 int data_time = 0;
 bool command_stop = false;
 int adb_data = 0;
 //begin reading ADB datapin
 
 while(digitalRead(ADB_DATA_PIN)!=LOW && data_time < ADB_TIMEOUT){
  //wait for beginning of start bit
  data_time+=1;
  delayMicroseconds(1);
 }

  while(digitalRead(ADB_DATA_PIN)!=HIGH && data_time < ADB_TIMEOUT){
  //wait for end of start bit
  data_time+=1;
  delayMicroseconds(1);
 }

 while(digitalRead(ADB_DATA_PIN)!=LOW && data_time < ADB_TIMEOUT){
  //wait for beginning of first data bit
  data_time+=1;
  delayMicroseconds(1);
 }

 //data packet
 data_time = 0;
 while(!command_stop && data_time < ADB_TIMEOUT) {
  int low = 0;
  int high = 0;
  bool bit_stop = false;
  while(!bit_stop && data_time < ADB_TIMEOUT) {
    
    int adb_stream = digitalRead(ADB_DATA_PIN);
    if(adb_stream == LOW) low+=1;
    if(adb_stream == HIGH) high+=1;
    if(adb_stream == LOW && high > 0) bit_stop = true;
    if(high > 240){
      bit_stop = true;
      break;
    }
    data_time+=1;
    delayMicroseconds(1);
  }
  if(high > 240){
    command_stop = true;
  }
  //append the new data bit to adb_data
  if(high > low && !command_stop) adb_data = (adb_data << 1) + 1;
  if(low > high && !command_stop) adb_data = adb_data << 1;
  
 }
 //stop bit 
 return adb_data;
}

void parse_data(int data) {
      if(data !=0) {
       int mousex = data & 0b0000000001111111;
       int mousey = (data >> 8) & 0b01111111;
       //the mouse value is
        //7 bit two's complement
       if((mousey & 0b1000000) != 0) {
          mousey =  ((~mousey + 1) & 0b01111111) * -1;
       }
       if((mousex & 0b1000000) != 0) {
          mousex =  ((~mousex + 1) & 0b01111111) * -1;
       }
       Mouse.move(mousex, mousey);
       Mouse.move(mousex, mousey);

      int mouseClick = data & 0b1000000000000000;
      
      if(mouseClick == 0) {
        Mouse.set_buttons(1,0,0);
      }
      else if(mouseClick != 0) {
        Mouse.set_buttons(0,0,0);
      }
     }
}

//
//----------------------------------Setup-------------------------------------------
void setup() {
  
}
//
//---------------------------------Main Loop---------------------------------------------
//
void loop() {   
   send_talk_command();
   parse_data(receive_data_packet());
//
  delay(20); // 
}

Credits

Frank Adams

Frank Adams

4 projects • 11 followers
I am a retired Boeing engineer that enjoys experimenting with Pi, Arduino, and Teensy projects.

Comments