Crossfade Bicolor LED with PS/2 Touchpad

 Posted by:   Posted on:   Updated on:  2018-02-25T12:52:06Z

How to interface a Hantick PS/2 touchpad with Arduino. Use it to crossfade a bicolor LED.

Some while ago, I found a Processing example that would crossfade an Arduino connected bicolor LED depending on mouse position over a window with a gradient. Since I had a touchpad from an old netbook, I decided to use a hardware approach: interface this to Arduino and use it to change LED color by swiping left or right. Horizontal swiping will change the duty factor of PWM signals that light the LED.

This is a simple project that can be built with other kind of input devices like potentiometer or joystick. But, my purpose was to get that touchpad working. I had to use a logic analyzer to determine its pinout. Luckily, since it uses PS/2 protocol, it sends some bytes without connection to a host device. The PS/2 protocol is well documented and pretty easy. ATmega microcontrollers used by Arduino boards don't have hardware support for this protocol, therefore it must be implemented in software. Some searching revealed a lot of libraries for PS/2 devices, but not all worked for me. This may be because the touchpad I used is pretty old and may not support all protocol features.

Crossfade Bicolor LED with PS/2 Touchpad
The touchpad I used is Hantick HTX5330M. I could not find a datasheet for it or for the IC it uses. It connects to motherboard by a 6 wire ribbon cable. Identifying power and ground lines is easy (most PCBs use GND as a plane and between this and VCC there are large capacitors). A closer look near the motherboard connector, shows two pull-up resistors of 7.5k.

Touchpad connector on motherboard
Touchpad connector on motherboard
The red and black dots represent the power lines. And it seems there are only tow signal lines, the ones that are pulled up. The manufacturer provided two test points (TP9 and TP10) for these lines. That's where I connected the logic analyzer. It looks like TP9 is PS/2 DATA and TP10 is PS/2 CLOCK. The device uses 5V levels. In the top photo, you see I cut this part of the motherboard because I wanted to keep the connector and the rather unusual pull-ups of 7.5k.

Hantick HTX5330M touchpad
Hantick HTX5330M touchpad
I managed to find a specification page for this device. This confirmed 5V usage and PS/2 compatibility of the 48 by 32.5mm device. I can proceed and connect this to an Arduino compatible board. Since many PS/2 projects I could find used Arduino pins 5 and 6 I did the same. Pin D6 goes to TP10 (clock) and pin D5 to TP9 (data). 5V are also supplied by Arduino (the touchpad draws about 3mA).

Connect the touchpad and LED to Arduino
Connect the touchpad and LED to Arduino
Let's get to the software. PS/2 is a bidirectional synchronous serial protocol (see The PS/2 Mouse/Keyboard Protocol by Adam Chapweske). When the host device keeps bus idle, the touchpad generates clock and send data to the host. Clock is always generated by PS/2 device, not the host. When the host needs to send data, it generates a request state and waits the clock from the PS/2 device. Mouse and touchpad have specific commands and three working modes. The host can tell the touchpad to send data as soon as it receives (stream mode) or it can poll the device frequently for data (remote mode). The third mode is wrap mode, where the device echoes data received from the host. Computers equipped with PS/2 input devices prefer to use stream mode (read more about The PS/2 Mouse Interface by Adam Chapweske).

The Arduino library that worked best for me is written by Jacek Kunicki and can be donwloaded from GitHub. It works in remote mode (it polls mouse/touchpad for new data every 20ms). Using this library, the code becomes very simple. Here is the Arduino sketch.
#include "PS2Mouse.h"
#define DATA_PIN 5
#define CLOCK_PIN 6

PS2Mouse mouse(CLOCK_PIN, DATA_PIN);

int red, green;

void setup() {
  Serial.begin(9600);
  Serial.println("LED Crossfade with PS/2 Touchpad (or mouse)");
  Serial.println("Using PS/2 librray by Jacek Kunicki (https://github.com/rucek)");
  
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  red = 127; green = 127;

  analogWrite(9, red);
  analogWrite(10, green);
  
  mouse.initialize();
}

void loop() {
  MouseData data = mouse.readData();
  red += data.position.x;
  green -= data.position.x;

  if (red > 255) red = 255; if (red < 0) red = 0;
  if (green > 255) green = 255; if (green < 0) green = 0;
  
  analogWrite(9, red);
  analogWrite(10, green);

  delay(20);
}
The LED is connected to PWM pins. The start-up duty cycle is 50% (127 is nearly half of 255). Only X axis is read and used to modify PWM duty cycle. The movement on X axis generates a relative value, which is positive for right swiping and negative for left swiping. This value is added to one of the LEDs and subtracted from the other. If it's positive, it will increase duty cycle of PWM signal that controls the LED to which is added and decrease the duty where it is subtracted. If it's negative, the opposite happens.

3 comments :

  1. Hi! First off I wanna thank you so much for doing this! You got me a lot further than I could have gotten by myself :) My question to you though is if you can determine the pinout of the test points on the actual PCB? I lost the original motherboard connector so I need to tap into the test points directly, but there are more than the number of wires being used and I am not sure which is which. If you can't find them yourself, could you point me in the right direction to the information I will need to test them myself?

    ReplyDelete
    Replies
    1. Rotate my photo of the touchpad 180 degrees. The pins are then in the order you see in the breadboard diagram photo. Anyway, the thicker track on the touchpad PCB which comes from a side pin of the connector and goes to some capacitors is supply pin.

      Delete
  2. /*please help me I downloaded the test code as such I get no station:*/
    #include
    #include
    LiquidCrystal_I2C lcd(0x27, 16, 2);
    #define SN761672A_ADDR (0xC2 >> 1)
    // tuner registers
    byte DB1, DB2, CB, BB;
    uint32_t freq_kHz;
    void setup()
    {
    Wire.begin();
    lcd.backlight();
    lcd.begin(16, 2);
    //Tuner_init();
    SN761672A_setTVFrequency(985000);
    lcd.setCursor(0,0);
    lcd.print("Station:");

    }

    void loop()
    {
    lcd.setCursor(8,0);
    lcd.print(freq_kHz);
    void SN761672A_setTVFrequency(uint32_t freq_kHz);
    }

    void SN761672A_setTVFrequency(uint32_t freq_kHz)
    {
    // band bits

    if (freq_kHz < 168000) BB = 0x00; // band A, VHF-Lo
    else if (freq_kHz < 448000) BB = 0x01; // band B, VHF-Hi
    else BB = 0x08; // band C, UHF

    // control
    CB = 0xCA;//11001010

    freq_kHz = freq_kHz / 31.25 + 1245;
    DB1 = (freq_kHz >> 8) & 0x1F;
    DB2 = freq_kHz & 0xFF;

    Wire.beginTransmission(SN761672A_ADDR);
    Wire.write(DB1);
    Wire.write(DB2);
    Wire.write(CB);
    Wire.write(BB);
    Wire.endTransmission();
    }

    ReplyDelete

Please read the comments policy before publishing your comment.