Использование Тачпада от ноутбука
- Войдите на сайт для отправки комментариев
Всем привет! Имеется несколько тачпадов от ноутбуков, хотелось бы их использовать в своих проектах. Все тачпады используют контроллеры Synaptics: T1006, T1007, SS2202A, и подобные. Интерфейс подключения - PS/2. В сети нашел несколько библиотек и примеров подключения. Но все дают только один и тот же функционал: передается направление перемещения мыши по "x" и "y" с определенным ускорением. Как это работает можно посмотреть в этом видео: https://www.youtube.com/watch?v=drMe4CBBxds
То есть не зависимо от положения мыши, не зависимо от положения пальца на тачпаде, передается только направление движения пальца-мыши, и с какой скоростью идет это движение.
В то же время тачпады имеют разметку функциональных кнопок и скролов. На фото ниже видно как тачпад от Тошибы имеет такую разметку.
Это подразумевает что контроллеры Synaptics имеют возможность отслеживания не только направление и скорость перемещения, но и точное место прикосновения пальца к тачпаду. То есть, можно на тачпад нанести разметку собственных кнопок и сделать сенсорную клавиатуру... Но ни одна библиотека или пример не дают такой возможности. Только направление и скорость перемещения. Вот ссылка на архив с несколькими библиотеками:
https://drive.google.com/file/d/1SO5TpSx55HcjLBe08xqkSR_SRMjc2RuO/view?usp=sharing
Логическим анализатором я снял дамп работы с тачпадом во время "покоя", то есть пальцем его не трогать. Отчетливо видно что передается 5 пакетов, но во всех примерах считывание идет только 3 раза. Для тестов а подключал большой тачпад (правый верхний), он так же имеет кнопку. Кнопка подключена к контроллеру но ее нажатие никак не передается....
Скетч использовал этот:
/* * an arduino sketch to interface with a ps/2 mouse. * Also uses serial protocol to talk back to the host * and report what it finds. */ /* * Pin 5 is the mouse data pin, pin 6 is the clock pin * Feel free to use whatever pins are convenient. */ #define MDATA 5 #define MCLK 6 /* * according to some code I saw, these functions will * correctly set the mouse clock and data pins for * various conditions. */ void gohi(int pin) { pinMode(pin, INPUT); digitalWrite(pin, HIGH); } void golo(int pin) { pinMode(pin, OUTPUT); digitalWrite(pin, LOW); } void mouse_write(char data) { char i; char parity = 1; // Serial.print("Sending "); // Serial.print(data, HEX); // Serial.print(" to mouse\n"); // Serial.print("RTS"); /* put pins in output mode */ gohi(MDATA); gohi(MCLK); delayMicroseconds(300); golo(MCLK); delayMicroseconds(300); golo(MDATA); delayMicroseconds(10); gohi(MCLK); /* start bit */ while (digitalRead(MCLK) == HIGH); /* wait for mouse to take control of clock); */ for (i=0; i < 8; i++) { /* clock is low, and we are clear to send data */ if (data & 0x01) { gohi(MDATA); }else{ golo(MDATA); } while (digitalRead(MCLK) == LOW); /* wait for clock cycle */ while (digitalRead(MCLK) == HIGH); parity = parity ^ (data & 0x01); data = data >> 1; } if (parity) { /* parity */ gohi(MDATA); }else{ golo(MDATA); } while (digitalRead(MCLK) == LOW); while (digitalRead(MCLK) == HIGH); gohi(MDATA); /* stop bit */ delayMicroseconds(50); while (digitalRead(MCLK) == HIGH); while ((digitalRead(MCLK) == LOW) || (digitalRead(MDATA) == LOW)); /* wait for mouse to switch modes */ golo(MCLK); /* put a hold on the incoming data. */ // Serial.print("done.\n"); } /* * Get a byte of data from the mouse */ char mouse_read(void){ char data = 0x00; int i; char bit = 0x01; // Serial.print("reading byte from mouse\n"); /* start the clock */ gohi(MCLK); gohi(MDATA); delayMicroseconds(50); while (digitalRead(MCLK) == HIGH); delayMicroseconds(5); /* not sure why */ while (digitalRead(MCLK) == LOW) /* eat start bit */; for (i=0; i < 8; i++) { while (digitalRead(MCLK) == HIGH); if (digitalRead(MDATA) == HIGH){ data = data | bit; } while (digitalRead(MCLK) == LOW); bit = bit << 1; } /* eat parity bit, which we ignore */ while (digitalRead(MCLK) == HIGH); while (digitalRead(MCLK) == LOW); /* eat stop bit */ while (digitalRead(MCLK) == HIGH); while (digitalRead(MCLK) == LOW); /* put a hold on the incoming data. */ golo(MCLK); // Serial.print("Recvd data "); // Serial.print(data, HEX); // Serial.print(" from mouse\n"); return data; } void mouse_init() { gohi(MCLK); gohi(MDATA); // Serial.print("Sending reset to mouse\n"); mouse_write(0xff); mouse_read(); /* ack byte */ // Serial.print("Read ack byte1\n"); mouse_read(); /* blank */ mouse_read(); /* blank */ //mouse_read(); /* blank */ //mouse_read(); /* blank */ // Serial.print("Sending remote mode code\n"); mouse_write(0xf0); /* remote mode */ mouse_read(); /* ack */ // Serial.print("Read ack byte2\n"); delayMicroseconds(100); } void setup() { Serial.begin(9600); mouse_init(); } /* * get a reading from the mouse and report it back to the * host via the serial line. */ void loop(){ char mstat; char mx; char my; /* get a reading from the mouse */ mouse_write(0xeb); /* give me data! */ mouse_read(); /* ignore ack */ mstat = mouse_read(); mx = mouse_read(); my = mouse_read(); /* send the data back up */ Serial.print(mstat, BIN); Serial.print("\tX="); Serial.print(mx, DEC); Serial.print("\tY="); Serial.print(my, DEC); Serial.println(); delay(20); /* twiddle */ }
В сети нашел пример работы с тачпадом и считывания "зоны скролла", скетч практически идентичный, за исключением того что в функции инициализации татча чуть больше команд, а в основном цикле программы чтение с тачпада идет 4 раза.
/* Modified mouse initialization to accomondate for * Intellimouse vertical wheel (scroll) * by DGar 02/05/2014 */ /* * an arduino sketch to interface with a ps/2 mouse. * Also uses serial protocol to talk back to the host * and report what it finds. */ /* * Pin 5 is the mouse data pin, pin 6 is the clock pin * Feel free to use whatever pins are convenient. */ #define MDATA 5 #define MCLK 6 /* * according to some code I saw, these functions will * correctly set the mouse clock and data pins for * various conditions. */ void gohi(int pin) { pinMode(pin, INPUT); digitalWrite(pin, HIGH); } void golo(int pin) { pinMode(pin, OUTPUT); digitalWrite(pin, LOW); } void mouse_write(char data) { char i; char parity = 1; // Serial.print("Sending "); // Serial.print(data, HEX); // Serial.print(" to mouse\n"); // Serial.print("RTS"); gohi(MDATA); /* put pins in output mode */ gohi(MCLK); delayMicroseconds(300); golo(MCLK); delayMicroseconds(300); golo(MDATA); delayMicroseconds(10); gohi(MCLK); /* start bit */ while (digitalRead(MCLK) == HIGH); /* wait for mouse to take control of clock); */ for (i=0; i < 8; i++) { /* clock is low, and we are clear to send data */ if (data & 0x01) { gohi(MDATA); }else{ golo(MDATA); } while (digitalRead(MCLK) == LOW); /* wait for clock cycle */ while (digitalRead(MCLK) == HIGH); parity = parity ^ (data & 0x01); data = data >> 1; } if (parity) { /* parity */ gohi(MDATA); }else{ golo(MDATA); } while (digitalRead(MCLK) == LOW); while (digitalRead(MCLK) == HIGH); gohi(MDATA); /* stop bit */ delayMicroseconds(50); while (digitalRead(MCLK) == HIGH); while ((digitalRead(MCLK) == LOW) || (digitalRead(MDATA) == LOW)); /* wait for mouse to switch modes */ golo(MCLK); /* put a hold on the incoming data. */ // Serial.print("done.\n"); } /* * Get a byte of data from the mouse */ char mouse_read(void) { char data = 0x00; int i; char bit = 0x01; // Serial.print("reading byte from mouse\n"); gohi(MCLK); /* start the clock */ gohi(MDATA); delayMicroseconds(50); while (digitalRead(MCLK) == HIGH); delayMicroseconds(5); /* not sure why */ while (digitalRead(MCLK) == LOW); /* eat start bit */ for (i=0; i < 8; i++) { while (digitalRead(MCLK) == HIGH); if (digitalRead(MDATA) == HIGH) { data = data | bit; } while (digitalRead(MCLK) == LOW); bit = bit << 1; } while (digitalRead(MCLK) == HIGH); /* eat parity bit, which we ignore */ while (digitalRead(MCLK) == LOW); while (digitalRead(MCLK) == HIGH); /* eat stop bit */ while (digitalRead(MCLK) == LOW); golo(MCLK); /* put a hold on the incoming data. */ // Serial.print("Recvd data "); // Serial.print(data, HEX); // Serial.print(" from mouse\n"); return data; } void mouse_init(){ char mouseId; gohi(MCLK); gohi(MDATA); // Serial.print("Sending reset to mouse\n"); mouse_write(0xff); mouse_read(); /* ack byte */ // Serial.print("Read ack byte1\n"); mouse_read(); /* blank */ mouse_read(); /* blank */ // Serial.print("Setting sample rate 200\n"); mouse_write(0xf3); /* Set rate command */ mouse_read(); /* ack */ mouse_write(0xC8); /* Set rate command */ mouse_read(); /* ack */ // Serial.print("Setting sample rate 100\n"); mouse_write(0xf3); /* Set rate command */ mouse_read(); /* ack */ mouse_write(0x64); /* Set rate command */ mouse_read(); /* ack */ // Serial.print("Setting sample rate 80\n"); mouse_write(0xf3); /* Set rate command */ mouse_read(); /* ack */ mouse_write(0x50); /* Set rate command */ mouse_read(); /* ack */ // Serial.print("Read device type\n"); mouse_write(0xf2); /* Set rate command */ mouse_read(); /* ack */ mouse_read(); /* mouse id, if this value is 0x00 mouse is standard, if it is 0x03 mouse is Intellimouse */ // Serial.print("Setting wheel\n"); mouse_write(0xe8); /* Set wheel resolution */ mouse_read(); /* ack */ mouse_write(0x03); /* 8 counts per mm */ mouse_read(); /* ack */ mouse_write(0xe6); /* scaling 1:1 */ mouse_read(); /* ack */ mouse_write(0xf3); /* Set sample rate */ mouse_read(); /* ack */ mouse_write(0x28); /* Set sample rate */ mouse_read(); /* ack */ mouse_write(0xf4); /* Enable device */ mouse_read(); /* ack */ // Serial.print("Sending remote mode code\n"); mouse_write(0xf0); /* remote mode */ mouse_read(); /* ack */ // Serial.print("Read ack byte2\n"); delayMicroseconds(100); } void setup() { Serial.begin(9600); mouse_init(); } /* * get a reading from the mouse and report it back to the * host via the serial line. */ void loop() { char mstat; char mx; char my; char mz; /* get a reading from the mouse */ mouse_write(0xeb); /* give me data! */ mouse_read(); /* ignore ack */ mstat = mouse_read(); mx = mouse_read(); my = mouse_read(); mz = mouse_read(); /* send the data back up */ Serial.print(mstat, BIN); Serial.print("\tX="); Serial.print(mx, DEC); Serial.print("\tY="); Serial.print(my, DEC); Serial.print("\tZ="); Serial.print(mz, DEC); Serial.println(); delay(20); /* twiddle */ }
Но не работает! Если в основном цикле программы чтение данных идет более 3-х раз то Ардуино зависает. Если убрать строчку
mz = mouse_read();
То код работает, но считывает только перемещения.
Может вы можете что-то посоветовать? Может есть готовое решение или библиотеки, которые реализуют получение не только перемещения но и места прикосновения?
Отчетливо видно что передается 5 пакетов, но во всех примерах считывание идет только 3 раза.
...
То код работает, но считывает только перемещения.
Может вы можете что-то посоветовать? Может есть готовое решение или библиотеки, которые реализуют получение не только перемещения но и места прикосновения?
Готового решения не видел. Собственно, сегодняшние тачпады способны работать с несколькими одновременными касаниями, поэтому, естественно, должны передавать больше информации.
Ну а посоветовать: раз передается 5 байтов, то их все и нужно считывать и выводить в порт для последующего анализа.
Для катча фингеров следует переключить девайс в Absolute mode. И пилить свой дривер под синаптик , если готового на горизонте не наблюдается.
http://blog.amigas.ru/wp-content/uploads/2014/03/touchpad_RevB.pdf
3.2. Absolute mode Synaptics TouchPads also support an Absolute mode of operation, where the TouchPad transmits an extended packet that reports the absolute finger position on the pad (X, Y), the finger pressure or contact area (Z), and other information, including the state of the buttons. The Synaptics Windows drivers operate the pad in Absolute mode. They use advanced algorithms to transform the absolute (X, Y, Z) data into smooth relative cursor motion, plus a wide variety of tapping and scrolling gestures and other features such as EdgeMotion
Спасибо! а то что-то не мог найти вменяемого даташита. Буду изучать что это такое "Absolute mode" и с чем его едят
Спасибо за помощь! Вот рабочий пример: