Что можно сделать со сканером из дешёвого многофункционального устройства? Я решил, что превращу его в сенсорный экран с мультитачем, добавив всего несколько дополнительных компонентов и немного программирования.

Как же я пришёл к такой безумной идее? Источником вдохновения может быть что угодно. Ко мне оно пришло, когда я убирал стол, который использую для хакинга. Как обычно, на нём валялась куча деталей и поломанных устройств, и среди прочего — многофункциональный принтер Epson Stylus SX125. Я купил его, чтобы задействовать детали принтера в каком-то другом эксперименте, и хотя оборудование сканера было в порядке, плата контроллера, управлявшая и принтером, и сканером, вероятно, уже ни на что не была способна.

Сканер слишком большой, чтобы хранить его где-нибудь, но в нём всё равно оставались полезные детали наподобие шагового двигателя, поэтому я решил его разобрать. В процессе разборки обнаружилась головка сканера. Это сканер наподобие LIDE (Led InDirect Exposure — техника, разработанная Canon), обладающий некоторыми преимуществами. В старых сканерах использовались маленькие цветные линейные ПЗС-датчики и куча тяжёлой оптики для сканирования, а также подсветка сканируемого документа на флуоресцентных лампах с холодным катодом (CCFL), из-за чего сканеры были тяжёлыми и объёмными. В этом же сканере используется чёрно-белый линейный ПЗС-датчик той же ширины, что и документ, немного крошечной оптики и многоцветный светодиод для освещения документа.

Недавно я узнал, что считывать данные с линейных ПЗС-датчиков не так уж сложно. По сути, они работают, как огромный аналоговый сдвиговый регистр: подаём на контакт высокий сигнал, чтобы приказать ПЗС-датчик «сделать снимок», а затем на другой контакт подаём тактовый сигнал. Каждый раз, когда поступает следующий тактовый импульс, ПЗС-датчик отправляет аналоговое значение, соответствующее уровню света, падающего на следующий пиксель. Так как ПЗС-датчик — это динамический элемент, нельзя слишком понижать тактовый сигнал, а если перестать считывать из него кадры, то для повторного «запуска» устройства понадобится какое-то время.

Как только ко мне в руки попала головка сканера, я понял, что должен её хакнуть. Раньше я никогда не взаимодействовал с микроконтроллером, имеющим столько датчиков света, поэтому решил, что это будет подходящим оправданием, чтобы отложить уборку комнаты на потом.

Запускаем ПЗС-датчик

Итак, как нам убедить ПЗС-датчик выполнять наши желания и сообщать мне, что он видит? Первым делом я подумал, что, вероятно, смогу что-нибудь найти, разобрав сканирующий элемент. Поработав ножом, я достал длинную печатную плату, находившуюся в основе элемента. Я был очень рад, что при его снятии был аккуратен: в верхней части платы обнаружились SMD-конденсаторы, а вся линия состояла из восьми длинных маленьких линейных датчиков. У этих датчиков не было корпуса или какой-либо защиты: можно было увидеть идущие к ним золотые проводники и даже оторвать их, если не быть аккуратным. Однако разборка платы оказалась полезной: я заметил, что из двенадцати идущих к ней соединений четыре шли напрямую к RGB-светодиоду, два были подключены к слою заземления, а на одной стоял такой большой конденсатор, что это не мог не быть источник питания для интегральной схемы.

Поэтому когда я снова собрал устройство, мне оставалось разобраться в предназначении всего пяти соединений. Как я говорил выше, у меня было примерное представление о том, как должны выглядеть три формы сигнала на линиях. И мне повезло: хоть печатная плата контроллера МФУ была поломана, её сил оказалось достаточно, чтобы попробовать откалибровать принтер, в процессе этого выполняя считывание с печатной платы. Достаточно было подключить осциллограф, чтобы понять, где находятся нужные мне сигналы; на двух других линиях присутствовало стабильное напряжение постоянного тока.

Немного поколдовав над кодом и макетной платой, я научил ATMega88 выдавать те же сигналы, что и оригинальный контроллер. И это сработало: на осциллографе была линия, которая поднималась и опускалась, когда я махал рукой над датчиком, отбрасывая тень на его части. Можно даже различать отдельные пиксели: похоже, у линейного ПЗС-датчика их примерно 2500. ATMega ещё не считывал аналоговый сигнал, но эта система подтвердила, что можно успешно считывать данные с датчика. Также мне удалось выяснить смысл оставшихся двух контактов: один из них, цифровой, выбирает выходное напряжение, а второй, аналоговый входной контакт, изменяет чувствительность датчика. Я присоединил к нему переменный резистор 2K5, чтобы настраивать чувствительность вручную.

Итак, теперь у нас есть работающий линейный ПЗС-датчик. Что можно с ним сделать?

Возможность отбрасывать тень, координаты которой можно сразу измерить, напомнила мне о ещё одной моей давней идее. Что, если поместить датчик под монитор компьютера, а наверху установить несколько инфракрасных светодиодов? Его можно использовать в качестве тачскрина: когда прижимаешь к дисплею палец, он отбрасывает тень от IR-светодиодов на датчик, который может её обнаружить. Если использовать несколько светодиодов с программным управлением, то достаточно будет простых расчётов для воссоздания координат касания из данных тени.

Значит, с идеей мы определились: передаём данные датчика в PC, выполняем вычисления, а затем воспроизводим координаты экрана, которых коснулся пользователь.

Оборудование

Первым делом нужно как-то передать аналоговые данные в микроконтроллер. Это довольно непросто: данные передаются из датчика с частотой 2 МГц всё ещё в аналоговом виде. В оборудовании устройства эта проблема решалась специальным 8-битным ЦАП, который вроде бы находился в чипе основного контроллера. Я же планировал использовать только ATMega88. Сигнал, который я пытался получить, был аналоговым, но в моём случае его можно было рассматривать, как цифровой: мне достаточно знать, падает ли тень на считываемый пиксель.

На самом деле, скорости ATMega88 должно быть вполне достаточно для считывания однобитных цифровых сигналов исключительно программным способом: микроконтроллеру можно выставить тактовую частоту 20 МГц, что, учитывая частоту датчика 2 МГц, позволяет обеспечить 10 команд на пиксель для его считывания и сохранения в памяти. Однако для этого понадобится писать довольно плотный ассемблерный код. Есть и другой способ считывания пикселей: при помощи порта SPI. Это интегральное периферийное устройство может считывать по 8 бит данных за раз и передавать их в виде байта программному обеспечению, работающему в микроконтроллере. Благодаря этому у меня будет 80 тактов команд для обработки одного байта. Этого достаточно даже для неоптимизированного кода на C. С аппаратной точки зрения это тоже не потребует особо много компонентов: аналоговый вывод с датчика можно подать напрямую на контакт данных SPI в AVR; контакт будет считывать «1» при достаточно высоком аналоговом уровне, а в противном случае «0». Затем при помощи ввода яркости линейного ПЗС-датчика можно установить подходящее пороговое напряжение.

Но есть один аспект, о котором нужно позаботиться: так как датчик работает некорректно, если на него не подан постоянный тактовый сигнал, а тактовый сигнал SPI прекращается, когда программа не запрашивает байт, мне нужно генерировать тактовый сигнал где-то ещё. Для этого я использовал контакт таймера: он генерирует постоянный сигнал 2 МГц. Когда прошивка хочет считать линию, она подаёт импульс за ввод-«защёлку» датчика, который затем начинает выводить уровни пикселей. Они считываются блоком SPI, сконфигурированным, как slave, и хранятся в памяти для дальнейшей отправки на компьютер.

Исходя из всего этого, я составил такую схему:

Большой чип посередине — это ATMega88p. Он работает на 3,3 В, как и линейный датчик. Теоретически, я не мог бы разогнать его до 20 МГц (при напряжении 3,3 В это выходит за пределы его характеристик), но на практике у меня никогда не было с этим проблем. AVR управляет пятью IR-светодиодами (стандартные инфракрасные светодиоды на 850 нм). Так как я решил выполнять основную часть обработки на PC, а это был всего лишь proof-of-concept, передавать местоположения теней на PC я стал через UART и завалявшуюся у меня плату FT232. Микроконтроллер передаёт местоположение тени, её размер и вызвавший её светодиод, поэтому всё равно нужно отправлять мало данных.

Собираем устройство

Так как это экспериментальное устройство, я собрал его на небольшой макетной плате. Линейный ПЗС-датчик я подсоединил к ней при помощи жил Ethernet-кабеля.

Перед тем, как приступать к работе над остальной частью оборудования, мне нужно было кое-что исправить. В процессе экспериментов я заметил, что датчик быстро насыщается светом, падающим на него от ламп и окон. Мне нужно было сделать так, чтобы он смотрел только на инфракрасные светодиоды, находящиеся прямо над ним: я взял два куска L-образного металлического профиля и закрасил две половины чёрным.

Затем я приклеил две детали к верхней части датчика, оставив между ними узкую щель. Благодаря этому на датчик должно гораздо меньше влиять внешнее освещение. Ещё больше помог бы инфракрасный фильтр, но у меня его не было, и на практике моего решения оказалось достаточно.

Вот и готовая система. Обратите внимание на инфракрасные светодиоды над экраном и довольно небрежно закреплённый под ним линейный ПЗС-датчик.

Также на этой фотографии можно увидеть программное обеспечение для PC. Я написал тестовое приложение для калибровки сенсорного экрана и преобразования показаний теней датчика в позиции на экране. Чтобы правильно составить формулы, потребовалось довольно много тригонометрии, но в итоге всё работает вполне неплохо. На фотографии виден результат этапа калибровки: цветные линии указывают на места, в которых, по мнению PC, находятся светодиоды; каждый светодиод отмечен отдельным цветом. Чёрной полосой внизу указано распознанное местоположение датчика. Ни одно из этих значений не задано в коде, они определяются касанием девяти точек на экране.

Результат и выводы

Итак, сработало ли это? Смотрите сами (оригинал на Youtube):

Как видите, мой тест оказался достаточно успешным. Однако у этой техники есть недостатки: например, тачскрин работает только в той части экрана, где есть белое окно. Если коснуться области вне его, то или палец начинает отбрасывать тень только от одного светодиода, из-за чего становится невозможно определить его местоположение, или тень выходит за границы датчика (который короче ширины экрана). Эту проблему можно решить использованием дополнительных светодиодов, возможно, расположенных дальше от экрана, и датчиком большего размера/несколькими датчиками.

Само ПО тоже неидеально: оно не работает в качестве драйвера, поэтому нельзя передавать касания операционной системе. Кроме того, оно видит каждый кадр, как отдельную сущность: если из-за бага распознавания палец за 1/50 секунды переместится из одной стороны экрана в другую и обратно, то ПО никак это не отфильтрует.

Могу ли я как-то улучшить систему по перечисленным пунктам? Не думаю. Хоть с мультитач-экраном и интересно играться, а математика его расчётов была интересной задачкой, мне всё равно больше нравятся мои клавиатура и трекбол. Если кто-то захочет усовершенствовать систему, то я буду за: и ПО для PC, и прошивку для ATMega можно скачать, они выпущены под лицензией GNU GPLv3. Если вам удастся сделать с ними что-нибудь интересное, то мне бы хотелось узнать об этом.