Add files via upload

This commit is contained in:
Frank Adams 2022-01-25 15:15:25 -08:00 committed by GitHub
parent 23686d5a7b
commit 5c2df74e0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 536 additions and 0 deletions

View file

@ -0,0 +1,151 @@
/* 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
}
}

View file

@ -0,0 +1,385 @@
/* 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
}