Ремонт резистивного тачскрина своими руками


Хоть резистивный touchscreen и является устаревшим и активно вытесняется емкостными сенсорами, но тем не менее он еще не скоро канет в Лету. Во первых из-за простоты и дешевизны, а во вторых из-за элементарной работы с ним.

Конструктив
Итак, как он устроен. Там все очень и очень просто. Есть две пленки, сделанные из проводящего материала, а между ними гранулы диэлектрика. Когда касаемся пальцем, то продавливаем зазор между пленками и контачим верхнюю на нижнюю. Ну, а определить координаты касания уже дело несложное.

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

Теперь достаточно концы одной пленки подтащить к шинам питания (банально заведя туда лог 0 и лог 1), чтобы она образовала обычный резистивный делитель. А с другой пленки снять получившееся напряжение. Которое будет пропорционально координате. Так как сопротивления входа АЦП огромно, то жалкие сотни ом, что составляет остаток пленки (R1) нам никак не помешает. Также не помешает и болтающийся конец (R2).
Теперь меняем положение, растягивая между шинами питания уже другую пленку и снимаем вторую координату. Элементарно!

Вилы в стогу
Но при попытке взять схему в лоб, то получаем первые вилы в бок. Дело в том, что если мы будем тупо менять по очереди пленки и сканировать значения, то много мы не намеряем. Т.к. когда касания нет, то пленки не перескаются, а значит каналы АЦП во время измерения висят в воздухе, ловя всякий мусор. И как их отличить от нажатия? Да никак! АЦП без разницы, что на вход пришло.
Так что нам надо замер координат делать не непрерывно, а тогда и только тогда, когда есть касание. До этого на тачскрин даже не отвлекаться.
Делается это просто — мы одну пленку подтягиваем к земле. А вторую вешаем на вход с pullup. Был тачскрин, стала обычная кнопка.

Ее мы лениво опрашиваем, в ожидании нажатия. Но тут прячутся вторые вилы, на которые мы будем нарываться еще не раз и не два. Дело в том, что тачскрин это две проводящие пленки. Большие плоские пленки, находящиеся очень близко друг от друга. Ничего не напоминает? Правильно — конденсатор, мать его. А значит у него нефиговая емкость. И если ты его переключишь в режим отслеживания нажатия и тут же проверишь — получишь нажатие. Т.к. емкость еще не успела зарядиться, а значит коротит. Так что после перевода порта в режим опроса надо подождать около миллисекунды. А только потом начинать щупать. То же касается, кстати, и режима смены замеров координат. Начнешь быстро переключать пленки с координаты на координату — получишь полную херню на выходе. Переключил, подождал, замерил. Переключил, подождал, замерил. Только так.

Под это дело я набросал небольшой пример кода для ATMega16 на Pinboard.

Код
Код в виде обычного конечного автомата. Правда он разнесен на процедуру и прерывание.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 //========================================= //Область задач //========================================= void ScanTouch(void) { switch(TouchState) { case 0: // Ожидание нажатия. Подготовка. { DDRA &=0xF0; PORTA &=0xF0; DDRA |=1<<XA | 1<<XB; // XA и XB гонят землю PORTA |=1<<YA | 1<<YB; // YA и YB слушают уровень на входе с подтяжкой TouchState = 1; // Переход к стадии проверки. SetTimerTask(ScanTouch,10); // Выдержка для установления состояния и зарядки емкости. break; } case 1: // Проверка нет ли нажатия { if(PINA & 1<<YA) // Если на входе 1, т.е. нажатия нет { TouchState = 0; // Возврат на прошлый шаг. SetTimerTask(ScanTouch,10); } else // Если 0, т.е. нажатие. Готовим замер! { PORTA &=0xF0; // Зануляем четыре бита, чтобы с чистого листа. DDRA &=0xF0; // Сейчас Y пленка гонит потенциал, Х считывает. PORTA |=1<<YA; // YА на Vcc YB на GND остальные в HiZ нюхают АЦП DDRA |=1<<YA|1<<YB; // - TouchState=2; // Переход на другую стадию. SetTimerTask(ScanTouch,10); // Выдержка на переходный процесс. } break; } case 2: // Запускаем АЦП для снятия координат { TouchState = 3; ADMUX = 1<<REFS0 | 1<<ADLAR | 2<<MUX0; //Меняем канал АЦП на чтение Х ADCSRA = 1<<ADEN | 1<<ADIE | 1<<ADSC | 3<<ADPS0; // Запускаем преобразование. break; // До встречи в обработчике прерывания. } case 5: // Координаты сняты. Можно обрабатывать. { TouchState = 0; if (!UDR_Busy) // Если UART не занят { UDR_Busy = 1; // Занимаем его OutBuff[0] = 'Y'; // Пишем в буфер мессагу OutBuff[1] = '='; BIN8toASCII3(OutBuff[2],OutBuff[3],OutBuff[4],coord_Y); // Вписываем координаты OutBuff[5] = ' '; OutBuff[6] = 'X'; OutBuff[7] = '='; BIN8toASCII3(OutBuff[8],OutBuff[9],OutBuff[10],coord_X); OutBuff[11] = 10; //CR - не забываем перевод каретки Buff_index = 0; // Обнуляем индекс UDR = OutBuff[Buff_index]; // Поехали! } SetTimerTask(ScanTouch,100); // Повтор сканирования. Не торопимся break; } default: { TouchState = 0; } } } // Продолжаем веселиться в обработчике прерывания от АЦП ISR(ADC_vect) { PORTA &=0xF0; // Зануляем четыре бита, чтобы с чистого листа. DDRA &=0xF0; if(TouchState == 3) { coord_X = ADCH; // Прошлый замер дал X // Сейчас уже X пленка гонит потенциал, Y считывает PORTA |=1<<XA; // ХА на Vcc XB на GND остальные в HiZ нюхают АЦП DDRA |=1<<XA|1<<XB; // - ADMUX = 1<<REFS0 | 1<<ADLAR | 3<<MUX0; // Перебрасываем канал АЦП на другую пленку TouchState = 4; // Перекидываем автомат ADCSRA = 1<<ADEN | 1<<ADIE | 1<<ADSC | 3<<ADPS0; // Запускаем конверсию и до встречи тут же. } else if (TouchState == 4) // Второй вход в прерывание { coord_Y = ADCH; // Прошлый замер дал Y TouchState = 5; // Стадия обработки SetTask(ScanTouch); // Обе координаты считаны. Готово. Вызваем задачу готовности. } }

//========================================= //Область задач //========================================= void ScanTouch(void) { switch(TouchState) { case 0: // Ожидание нажатия. Подготовка. { DDRA &=0xF0; PORTA &=0xF0; DDRA |=1<<XA | 1<<XB; // XA и XB гонят землю PORTA |=1<<YA | 1<<YB; // YA и YB слушают уровень на входе с подтяжкой TouchState = 1; // Переход к стадии проверки. SetTimerTask(ScanTouch,10); // Выдержка для установления состояния и зарядки емкости. break; } case 1: // Проверка нет ли нажатия { if(PINA & 1<<YA) // Если на входе 1, т.е. нажатия нет { TouchState = 0; // Возврат на прошлый шаг. SetTimerTask(ScanTouch,10); } else // Если 0, т.е. нажатие. Готовим замер! { PORTA &=0xF0; // Зануляем четыре бита, чтобы с чистого листа. DDRA &=0xF0; // Сейчас Y пленка гонит потенциал, Х считывает. PORTA |=1<<YA; // YА на Vcc YB на GND остальные в HiZ нюхают АЦП DDRA |=1<<YA|1<<YB; // - TouchState=2; // Переход на другую стадию. SetTimerTask(ScanTouch,10); // Выдержка на переходный процесс. } break; } case 2: // Запускаем АЦП для снятия координат { TouchState = 3; ADMUX = 1<<REFS0 | 1<<ADLAR | 2<<MUX0; //Меняем канал АЦП на чтение Х ADCSRA = 1<<ADEN | 1<<ADIE | 1<<ADSC | 3<<ADPS0; // Запускаем преобразование. break; // До встречи в обработчике прерывания. } case 5: // Координаты сняты. Можно обрабатывать. { TouchState = 0; if (!UDR_Busy) // Если UART не занят { UDR_Busy = 1; // Занимаем его OutBuff[0] = 'Y'; // Пишем в буфер мессагу OutBuff[1] = '='; BIN8toASCII3(OutBuff[2],OutBuff[3],OutBuff[4],coord_Y); // Вписываем координаты OutBuff[5] = ' '; OutBuff[6] = 'X'; OutBuff[7] = '='; BIN8toASCII3(OutBuff[8],OutBuff[9],OutBuff[10],coord_X); OutBuff[11] = 10; //CR - не забываем перевод каретки Buff_index = 0; // Обнуляем индекс UDR = OutBuff[Buff_index]; // Поехали! } SetTimerTask(ScanTouch,100); // Повтор сканирования. Не торопимся break; } default: { TouchState = 0; } } } // Продолжаем веселиться в обработчике прерывания от АЦП ISR(ADC_vect) { PORTA &=0xF0; // Зануляем четыре бита, чтобы с чистого листа. DDRA &=0xF0; if(TouchState == 3) { coord_X = ADCH; // Прошлый замер дал X // Сейчас уже X пленка гонит потенциал, Y считывает PORTA |=1<<XA; // ХА на Vcc XB на GND остальные в HiZ нюхают АЦП DDRA |=1<<XA|1<<XB; // - ADMUX = 1<<REFS0 | 1<<ADLAR | 3<<MUX0; // Перебрасываем канал АЦП на другую пленку TouchState = 4; // Перекидываем автомат ADCSRA = 1<<ADEN | 1<<ADIE | 1<<ADSC | 3<<ADPS0; // Запускаем конверсию и до встречи тут же. } else if (TouchState == 4) // Второй вход в прерывание { coord_Y = ADCH; // Прошлый замер дал Y TouchState = 5; // Стадия обработки SetTask(ScanTouch); // Обе координаты считаны. Готово. Вызваем задачу готовности. } }

Выброс данных в UART идет следующим образом:

1 2 3 4 5 6 7 8 9 10 11 12 13 ISR(USART_TXC_vect) { Buff_index++; // Увеличили индекс if(Buff_index != 12) // Если мессадж не ушел { UDR = OutBuff[Buff_index]; // Шлем дальше } else { Buff_index = 0; // Иначе все зануляем. UDR_Busy = 0; } }

ISR(USART_TXC_vect) { Buff_index++; // Увеличили индекс if(Buff_index != 12) // Если мессадж не ушел { UDR = OutBuff[Buff_index]; // Шлем дальше } else { Buff_index = 0; // Иначе все зануляем. UDR_Busy = 0; } }

Все очень и очень просто, без задержек и тормозов. Все на конечных автоматах и диспетчере. Но диспетчер тут не принципиален, может быть что угодно. Хоть тот же флаговый автомат. Главное, чтобы была служба таймеров. Весь проект в архиве в конце статьи.

BIN8toASCII3 это макрос переводящий бинарное значение в три разряда ASCII символов, пригодных к выводу в терминалку.

Выглядит оно так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Bin to ASCII // IN = value // A B C = ascii code of digits #define BIN8toASCII3(a_,b_,c_,in_) \ do \ { \ asm volatile( \ "LDI %A[RA],-1+'0'" "\n\t" \ "bcd1%=:" "INC %A[RA]" "\n\t" \ "SUBI %A[RC],100" "\n\t" \ "BRCC bcd1%=" "\n\t" \ "LDI %A[RB],10+'0'" "\n\t" \ "bcd2%=:" "DEC %A[RB]" "\n\t" \ "SUBI %A[RC],-10" "\n\t" \ "BRCS bcd2%=" "\n\t" \ "SBCI %A[RC],-'0'" "\n\t" \ \ : [RA]"=a" (a_), [RB]"=a" (b_), [RC]"=a" (c_) \ : "[RC]" (in_) \ ); \ \ } \ while(0)

// Bin to ASCII // IN = value // A B C = ascii code of digits #define BIN8toASCII3(a_,b_,c_,in_) \ do \ { \ asm volatile( \ "LDI %A[RA],-1+'0'" "\n\t" \ "bcd1%=:" "INC %A[RA]" "\n\t" \ "SUBI %A[RC],100" "\n\t" \ "BRCC bcd1%=" "\n\t" \ "LDI %A[RB],10+'0'" "\n\t" \ "bcd2%=:" "DEC %A[RB]" "\n\t" \ "SUBI %A[RC],-10" "\n\t" \ "BRCS bcd2%=" "\n\t" \ "SBCI %A[RC],-'0'" "\n\t" \ \ : [RA]"=a" (a_), [RB]"=a" (b_), [RC]"=a" (c_) \ : "[RC]" (in_) \ ); \ \ } \ while(0)

Нет, это не УЖОСНАХ, а GCCшная ассемблерная вставка. Я корячил алгоритм BIN2ASCII и так и эдак, потом решил, что красивей, короче и быстрей чем на ассемблере не получится и воткнул его в код так. О том как писать такие вставки в GCC неплохо написал Alatar в нашем сообществе. Если кто не читал, то рекомендую. Также, если будут желающие, то я дополнительно постараюсь разжевать эту тему до состояния манной каши.

В железе
Сам touchscreen надыбал в Китае, на Dealextrem. Обошелся он мне в пару баксов. Можно еще не очень дорого купить у всяких ремонтников сотовых. Конкретно этот от Nintendo DS, но там множество вариантов с разной диагональю и по различной цене.

Аккуратно припаялся к пленочке, выставив минимальную температуру паяльника (около 230 градусов), чтобы не поплавить нежную пленочку. А потом, для демонстрации, прибил все к какому-то блокнотику, что завалялся под рукой.

Да подключил все лапшой к контроллеру. Данные традиционно вывожу на терминалку.


Источник: http://easyelectronics.ru/rabota-s-rezistivnym-sensornym-ekranom.html



Рекомендуем посмотреть ещё:


Закрыть ... [X]

Ремонт резистивного тачскрина - Остальные вопросы (курилка) Форум Львята своим руками


Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками Ремонт резистивного тачскрина своими руками