Работа с резистивным сенсорным экраном

05.06.2015 16:22, автор SergSXM

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

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

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

Для подключения сенсорного экрана к микроконтроллеру требуется 4 вывода. Схематично подключение выглядит так:

Считывание данных происходит в 3 этапа:

Этап

Режим вывода X1

Режим вывода X2

Режим вывода Y1

Режим вывода Y2

Примечание

Определение нажатия

Вход с подтягивающим резистором к напряжению питания

Z-состояние

Выход лог. 0

Выход лог. 0

Проверяется вход X1. Лог. 0 означает нажатие. Лог. 1 означает что нажатия нет, можно не производить считывание координат.

Считывание координаты X

АЦП

Z-сотояние

Выход лог. 0

Выход лог. 1

Считывается координата Х с АЦП

Считывание координаты Y

Выход лог. 0

Выход лог. 1

АЦП

Z-сотояние

Считывается координата Y с АЦП

Таким образом после считывания данных с АЦП у нас имеются следующие данные: факт нажатия на экран, координаты X и Y в единицах АЦП. Чтобы преобразовать координаты из единиц АЦП в пиксельные координаты экрана я рекомендую пользоваться следующими формулами:

Дело в том, что данные формулы позволяют устранить ошибки, возникающие при установке сенсорной панели под произвольным углом по отношению к дисплею.

Калибровка сенсорного экрана заключается в нахождении коэффициентов из этих формул. Чтобы не утомлять читателя математическими выкладками приведу код калибровки экрана на языке C++ для нахождения этих коэффициентов:

// Переменные для хранения коэффициентов
double ax, bx, dx, ay, by, dy;

// Функции преобразования координат нажатия из единиц АЦП в пиксельные координаты экрана
short getTouchX(void)
{
long temp;
temp = (long)(ax * getTouchAdcX() + bx * getTouchAdcY() + dx);
if (temp > 32767) temp = 32767;
if (temp < -32767) temp = -32767;
return temp;
}

short getTouchY(void)
{
long temp;
temp = (long)(ay * getTouchAdcX() + by * getTouchAdcY() + dy);
if (temp > 32767) temp = 32767;
if (temp < -32767) temp = -32767;
return temp;
}

// Процедура калибровки сенсорного экрана
void CalibrateTouchScreen (void)
{
unsigned short drawX[5];
unsigned short drawY[5];
unsigned long touchX[5];
unsigned long touchY[5];
double ka[3], kb[3], kc[3], kd[3];
double d, dx1, dx2, dx3, dy1, dy2, dy3;
unsigned char i;
// Устанавливаем координаты пяти точек, по которым будет производится калибровка экрана (для экрана 800*480)
drawX[0] = (800 / 2);
drawX[1] = 1 * (800 / 5);
drawX[2] = 4 * (800 / 5);
drawX[3] = 4 * (800 / 5);
drawX[4] = 1 * (800 / 5);
drawY[0] = (480 / 2);
drawY[1] = 1 * (480 / 5);
drawY[2] = 1 * (480 / 5);
drawY[3] = 4 * (480 / 5);
drawY[4] = 4 * (480 / 5);
// Поочередно выводим перекрестье для каждой из пяти точек и считываем координаты нажатия
for (i = 0; i < 5; i++)
{
// Выводим перекрестье
DrawCross(drawX[i], drawY[i]);
// Ожидаем нажатия на сенсорный экран
while (getTouchState() == 0)
{
delay(100);
}
// Считываем координаты АЦП
touchX[i] = getTouchAdcX();
touchY[i] = getTouchAdcY();
// Ожидаем отпускания нажатия на сенсорный экран
while (getTouchState() == 1)
{
delay(100);
}
}
// Производим расчёты
for (i = 0; i < 3; i++)
{
ka[i] = 0;
kb[i] = 0;
kc[i] = 0;
kd[i] = 0;
}
for (i = 0; i < 5; i++)
{
ka[2] = ka[2] + (double)(touchX[i]);
kb[2] = kb[2] + (double)(touchY[i]);
kc[2] = kc[2] + (double)(drawX[i]);
kd[2] = kd[2] + (double)(drawY[i]);
ka[0] = ka[0] + (double)(touchX[i]) * (double)(touchX[i]);
ka[1] = ka[1] + (double)(touchX[i]) * (double)(touchY[i]);
kb[0] = ka[1];
kb[1] = kb[1] + (double)(touchY[i]) * (double)(touchY[i]);
kc[0] = kc[0] + (double)(touchX[i]) * (double)(drawX[i]);
kc[1] = kc[1] + (double)(touchY[i]) * (double)(drawX[i]);
kd[0] = kd[0] + (double)(touchX[i]) * (double)(drawY[i]);
kd[1] = kd[1] + (double)(touchY[i]) * (double)(drawY[i]);
}
ka[0] = ka[0] / ka[2];
ka[1] = ka[1] / kb[2];
kb[0] = kb[0] / ka[2];
kb[1] = kb[1] / kb[2];
kc[0] = kc[0] / ka[2];
kc[1] = kc[1] / kb[2];
kd[0] = kd[0] / ka[2];
kd[1] = kd[1] / kb[2];
ka[2] = ka[2] / 5;
kb[2] = kb[2] / 5;
kc[2] = kc[2] / 5;
kd[2] = kd[2] / 5;

d = (ka[0] - ka[2]) * (kb[1] - kb[2]) - (ka[1] - ka[2]) * (kb[0] - kb[2]);
dx1 = ((kc[0] - kc[2]) * (kb[1] - kb[2]) - (kc[1] - kc[2]) * (kb[0] - kb[2])) / d;
dx2 = ((kc[1] - kc[2]) * (ka[0] - ka[2]) - (kc[0] - kc[2]) * (ka[1] - ka[2])) / d;
dx3 = (kb[0] * (ka[2] * kc[1] - ka[1] * kc[2]) + kb[1] * (ka[0] * kc[2] - ka[2] * kc[0]) + kb[2] * (ka[1] * kc[0] - ka[0] * kc[1])) / d;
dy1 = ((kd[0] - kd[2]) * (kb[1] - kb[2]) - (kd[1] - kd[2]) * (kb[0] - kb[2])) / d;
dy2 = ((kd[1] - kd[2]) * (ka[0] - ka[2]) - (kd[0] - kd[2]) * (ka[1] - ka[2])) / d;
dy3 = (kb[0] * (ka[2] * kd[1] - ka[1] * kd[2]) + kb[1] * (ka[0] * kd[2] - ka[2] * kd[0]) + kb[2] * (ka[1] * kd[0] - ka[0] * kd[1])) / d;
// Сохраняем коэффициенты
ax = dx1;
bx = dx2;
dx = dx3;
ay = dy1;
by = dy2;
dy = dy3;
}

В данном коде в качестве коэффициентов используются числа с плавающей точкой, что заметно снижает скорость выполнения кода. Если ввести коэффициент перемножения TOUCH_MULTIPLY_FACTOR, то можно избавится от операций с плавающей точкой при считывании координат нажатия:

#define TOUCH_MULTIPLY_FACTOR 10000
// Переменные для хранения коэффициентов
long ax, bx, dx, ay, by, dy;

// Функции преобразования координат нажатия из единиц АЦП в пиксельные координаты экрана
short getTouchX(void)
{
long temp;
temp = (long)((ax * getTouchAdcX() + bx * getTouchAdcY() + dx) / TOUCH_MULTIPLY_FACTOR);
if (temp > 32767) temp = 32767;
if (temp < -32767) temp = -32767;
return temp;
}

short getTouchY(void)
{
long temp;
temp = (long)((ay * getTouchAdcX() + by * getTouchAdcY() + dy) / TOUCH_MULTIPLY_FACTOR);
if (temp > 32767) temp = 32767;
if (temp < -32767) temp = -32767;
return temp;
}

// Процедура калибровки сенсорного экрана
void CalibrateTouchScreen (void)
{
.............
.............
.............
// Сохраняем коэффициенты
ax = (long)(dx1 * TOUCH_MULTIPLY_FACTOR);
bx = (long)(dx2 * TOUCH_MULTIPLY_FACTOR);
dx = (long)(dx3 * TOUCH_MULTIPLY_FACTOR);
ay = (long)(dy1 * TOUCH_MULTIPLY_FACTOR);
by = (long)(dy2 * TOUCH_MULTIPLY_FACTOR);
dy = (long)(dy3 * TOUCH_MULTIPLY_FACTOR);
}

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