Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.
В прошлой статье мы рассмотрели как инициализировать контроллер в этой будем разбираться как получить координаты касания и откалибровать сенсорную панель.
Перед тем как определять координаты касания его надо зафиксировать, для этой цели воспользуемся специально предназначенным выводом T_IRQ, при касании на этом выводе появляется логический ноль. Хорошо было бы его повесить на ножку, отвечающую за внешние прерывания, кстати, у ATmega16 таких ножек 3, но у меня они все уже заняты, поэтому повесим его на любую другую ножку, сконфигурированную как вход.
Давайте теперь попробуем получить значение координаты X, значение координаты Y получается из аналогичных рассуждений.
На картинке выше видно, что получить значение координаты Х можно в двух случаях, в первом случай(А0=1,А1=0,А2=0) в качестве опорных напряжений будут выступать YP и YN, во втором(А0=1,А1=1,А2=0) YP и XN, мы реализуем первый. При таком подключении АЦП будет работать в линейном режиме (ratiometric), за счёт этого уменьшается ошибка измерений. Также для начала преобразования надо установить стартовый бит S в единицу. Давайте освежим в памяти формат посылки.
Таким образом, для получения координаты Х надо послать контроллеру 0х90, в ответ он отправит нам 12 бит данных. Так как мы за одни раз можем принять только 8 бит принимать будем два раза, а с помощью операций сдвига получим результат. Давайте все выше сказанное оформим в виде функции, в которую будем передавать указатели на переменные координат.
Теперь давайте перейдём к калибровке. Идея калибровки состоит в том, чтобы при нажатии на экран точка отрисовывалась там, где произошло нажатие. Для того чтобы сделать это нам надо решить 3 вопроса, первый — это, учесть нечувствительные области, дело в том что в идеальном случае если провести стилусом от одной стороны к противоположной, то координаты нажатия(данные с 12-битного АЦП) должны изменяться от 0 до 4095. На самом деле они буду изменяться примерно от 300 до 3800, это примерные данные, но суть от этого не меняется, возле каждой из сторон есть нечувствительная область. Второе — для того чтобы отрисовать точку на дисплее её координаты должны входить в диапазон от 0 до 239 по Х и от 0 до 319 по Y, а так как АЦП 12-битное, координаты точки касания будут изменяться 0 до 4095. И третье, как выяснилось позже, начала координат дисплея и сенсорной панели лежат в противоположных углах, поэтому если мы не хотим нажимать в одном углу, а чтобы точка рисовалась в другом, то надо это учесть.
Для этой задачи была написана простенькая функция, с помощью которой мы сможем наблюдать на экране координаты точки касания.
Функция itoa преобразует числовое значение в строчный эквивалент с указанным основанием.
char *itoa(int num, char *str, int radix)
int num - числовое значение, которое хотим преобразовать
char *str - указатель на результат, типа char
int radix - основание системы счисления для записи выходной строки
Если мы передадим значение 1711 и выберем десятичную систему исчисления, то число 1 преобразуется в соответствующий символ ASCII кода, код этого символа 49, число 7 в символ с кодом 55 и так далее.
Теперь давайте прошьем контроллер и посмотрим, что получилось.
Читателю не видно, поэтому просто придётся мне поверить, во-первых, начала координат дисплея и сенсорной панели находятся в противоположных углах, во-вторых, результат почему-то получился 11-битный, вместо 12-битного. Давайте подключимся логическим анализатором и посмотрим, что да как.
Канала D0 - T_CLK, канал D1 - T_DO, остальные отключил, чтобы не мешали.
Теперь давайте посмотрим отладчиком, что шлёт контроллер.
Данные, которые шлёт контроллер полностью совпадают с тем, что мы видим на осциллограмме. Вся осциллограмма не поместилась, но на ней видно, что данные надо считывать, по возрастающему фронту, начиная со второго такта, что полностью соответствует написанному в даташите, это я прозевал.
Тогда в коде выше надо изменить строчку touch_y >>= 4; на touch_y >>= 3;
Давайте загрузим программу в контроллер и посмотрим, правильно ли мы рассуждали.
Отлично посылки стал двенадцати битными. Теперь надо провести стилусом вдоль каждой из сторон при этом значение не должно изменяться. То есть если мы проводим вдоль стороны X, то должен изменяться Y, а X нет. Если значение неожиданно изменяется, то увеличиваем расстояние от стороны вдоль которой ведем. Такое значение мы должны найти с каждой стороны и записать их на листке. В итоге у нас получится 4 значения, Xmax, Xmin, Ymax, Ymin. Далее, возьмём полученные значения и вычислим размеры рабочей области.
Ywork = Ymax - Ymin
Xwork = Xmax - Xmin
У меня получилось Ymax = 3900, Ymin = 400, Ywork = 3500, Xmax = 3900, Xmin = 300, Xwork = 3600. Давайте порассуждаем, на 240 точек на дисплее по X у нас приходится 3600 точек на сенсорной панели, то есть 1 к 15, а на 320 точек по Y у нас приходится 3550 точек на сенсорной панели, то есть 1 к 11, 09. Желательно чтобы в отношении получились целые числа, иначе, так как мы работаем с целыми числами произойдет округление, а дробная часть будет являться причиной ошибки. Значит, при нажатии на любую точку в полученном квадрате размером 15 на 11 на сенсорной панели, мы должны нарисовать на дисплеи одну и ту же точку.
Для того чтобы отобразить точку нажатия по X надо от полученной координаты отнять Xmin и разделить на 15, для Y аналогично.
Если сейчас попробовать отрисовывать точки по касанию, то они будут появляться симметрично относительно центра экрана, так как начала координат лежат в противоположных углах. Поэтому для икса надо от 240 отнимать полученный результат, а для игрика от 320 отнимать полученный результат. В итоге функция с помощью которой мы получаем координаты примет следующий вид.
Для калибровки надо вызвать в бесконечном цикле функцию калибровки и раскомментировать строчку #define calibr.
Иногда случается, что в момент когда стилус отрывается от дисплея, координаты нажатия изменяются, на не соответствующие точке нажатия, но результат в таком случае всегда одинаковый Х = -410, а Y = -20. Поэтому это будет легко учесть, при обработке точки нажатия.
Общая схема подключения изображена ниже.
На этом всё, в следующей статье мы преобразуем картинку в массив для того, чтобы вывести её на дисплей.
Проект для Atmel Studio 6.2 в архиве
Перед тем как определять координаты касания его надо зафиксировать, для этой цели воспользуемся специально предназначенным выводом T_IRQ, при касании на этом выводе появляется логический ноль. Хорошо было бы его повесить на ножку, отвечающую за внешние прерывания, кстати, у ATmega16 таких ножек 3, но у меня они все уже заняты, поэтому повесим его на любую другую ножку, сконфигурированную как вход.
Давайте теперь попробуем получить значение координаты X, значение координаты Y получается из аналогичных рассуждений.
На картинке выше видно, что получить значение координаты Х можно в двух случаях, в первом случай(А0=1,А1=0,А2=0) в качестве опорных напряжений будут выступать YP и YN, во втором(А0=1,А1=1,А2=0) YP и XN, мы реализуем первый. При таком подключении АЦП будет работать в линейном режиме (ratiometric), за счёт этого уменьшается ошибка измерений. Также для начала преобразования надо установить стартовый бит S в единицу. Давайте освежим в памяти формат посылки.
Таким образом, для получения координаты Х надо послать контроллеру 0х90, в ответ он отправит нам 12 бит данных. Так как мы за одни раз можем принять только 8 бит принимать будем два раза, а с помощью операций сдвига получим результат. Давайте все выше сказанное оформим в виде функции, в которую будем передавать указатели на переменные координат.
#define chy 0x90 //координатные оси дисплея и тачскрина поменяны местами(там где у дисплея Х у тачсрина Y)
#define chx 0xD0 //за основу возьмём оси дисплея
void Get_Touch_xy( volatile uint16_t *x_kor,volatile uint16_t *y_kor)
{
volatile uint16_t touch_x = 0;
volatile uint16_t touch_y = 0;
Spi_Master_Transmit(chx); //отправляем запрос координаты X
Spi_Master_Transmit(0X00); //получаем старшие 8 бит
touch_x = SPDR;
touch_x <<= 8;
Spi_Master_Transmit(0X00); //получаем младшие 4 бита
touch_x |= SPDR;
touch_x >>= 4;
_delay_us(100);
Spi_Master_Transmit(chy); //отправляем запрос координаты Y
Spi_Master_Transmit(0X00); //получаем старшие 8 бит
touch_y = SPDR;
touch_y <<= 8;
Spi_Master_Transmit(0X00); ////получаем младшие 4 бита
touch_y |= SPDR;
touch_y >>= 4;
*x_kor = touch_x;
*y_kor = touch_y;
}
Теперь давайте перейдём к калибровке. Идея калибровки состоит в том, чтобы при нажатии на экран точка отрисовывалась там, где произошло нажатие. Для того чтобы сделать это нам надо решить 3 вопроса, первый — это, учесть нечувствительные области, дело в том что в идеальном случае если провести стилусом от одной стороны к противоположной, то координаты нажатия(данные с 12-битного АЦП) должны изменяться от 0 до 4095. На самом деле они буду изменяться примерно от 300 до 3800, это примерные данные, но суть от этого не меняется, возле каждой из сторон есть нечувствительная область. Второе — для того чтобы отрисовать точку на дисплее её координаты должны входить в диапазон от 0 до 239 по Х и от 0 до 319 по Y, а так как АЦП 12-битное, координаты точки касания будут изменяться 0 до 4095. И третье, как выяснилось позже, начала координат дисплея и сенсорной панели лежат в противоположных углах, поэтому если мы не хотим нажимать в одном углу, а чтобы точка рисовалась в другом, то надо это учесть.
Для этой задачи была написана простенькая функция, с помощью которой мы сможем наблюдать на экране координаты точки касания.
void Callibrate()
{
volatile uint16_t touch_kor_x1= 0;
volatile uint16_t touch_kor_y1= 0;
uint8_t data_x [4] = {0} ;
uint8_t data_y [4] = {0} ;
if(!(PINC & (1<<PINIRQ)))
{
Draw_Simbol(10,50,green,0XFFFF, &simbols[0x38*8],3);//рисуем Х
Draw_Simbol(10,90,green,0XFFFF, &simbols[0x1D*8],3);//рисуем =
Draw_Simbol(40,50,green,0XFFFF, &simbols[0x39*8],3);//рисуем Y
Draw_Simbol(40,90,green,0XFFFF, &simbols[0x1D*8],3);//рисуем =
Get_Touch_xy(&touch_kor_x1, &touch_kor_y1);//получаем координаты точки касания
itoa( touch_kor_x1,(char*) data_x, 10);//преобразуем для вывода координаты X
itoa( touch_kor_y1,(char*) data_y, 10);//преобразуем для вывода координаты Y
for (uint8_t k = 0; k < 4; k++)
{ //выводим значения X и Y
Draw_Simbol(40,130 + 8*k*5,green,0XFFFF, &simbols[(data_y[k]-0x20)*8],3);
Draw_Simbol(10,130 + 8*k*5,green,0XFFFF, &simbols[(data_x[k]-0x20)*8],3);
}
}
}
Функция itoa преобразует числовое значение в строчный эквивалент с указанным основанием.
char *itoa(int num, char *str, int radix)
int num - числовое значение, которое хотим преобразовать
char *str - указатель на результат, типа char
int radix - основание системы счисления для записи выходной строки
Если мы передадим значение 1711 и выберем десятичную систему исчисления, то число 1 преобразуется в соответствующий символ ASCII кода, код этого символа 49, число 7 в символ с кодом 55 и так далее.
Теперь давайте прошьем контроллер и посмотрим, что получилось.
Читателю не видно, поэтому просто придётся мне поверить, во-первых, начала координат дисплея и сенсорной панели находятся в противоположных углах, во-вторых, результат почему-то получился 11-битный, вместо 12-битного. Давайте подключимся логическим анализатором и посмотрим, что да как.
Канала D0 - T_CLK, канал D1 - T_DO, остальные отключил, чтобы не мешали.
Теперь давайте посмотрим отладчиком, что шлёт контроллер.
Данные, которые шлёт контроллер полностью совпадают с тем, что мы видим на осциллограмме. Вся осциллограмма не поместилась, но на ней видно, что данные надо считывать, по возрастающему фронту, начиная со второго такта, что полностью соответствует написанному в даташите, это я прозевал.
Тогда в коде выше надо изменить строчку touch_y >>= 4; на touch_y >>= 3;
Давайте загрузим программу в контроллер и посмотрим, правильно ли мы рассуждали.
Отлично посылки стал двенадцати битными. Теперь надо провести стилусом вдоль каждой из сторон при этом значение не должно изменяться. То есть если мы проводим вдоль стороны X, то должен изменяться Y, а X нет. Если значение неожиданно изменяется, то увеличиваем расстояние от стороны вдоль которой ведем. Такое значение мы должны найти с каждой стороны и записать их на листке. В итоге у нас получится 4 значения, Xmax, Xmin, Ymax, Ymin. Далее, возьмём полученные значения и вычислим размеры рабочей области.
Ywork = Ymax - Ymin
Xwork = Xmax - Xmin
У меня получилось Ymax = 3900, Ymin = 400, Ywork = 3500, Xmax = 3900, Xmin = 300, Xwork = 3600. Давайте порассуждаем, на 240 точек на дисплее по X у нас приходится 3600 точек на сенсорной панели, то есть 1 к 15, а на 320 точек по Y у нас приходится 3550 точек на сенсорной панели, то есть 1 к 11, 09. Желательно чтобы в отношении получились целые числа, иначе, так как мы работаем с целыми числами произойдет округление, а дробная часть будет являться причиной ошибки. Значит, при нажатии на любую точку в полученном квадрате размером 15 на 11 на сенсорной панели, мы должны нарисовать на дисплеи одну и ту же точку.
Для того чтобы отобразить точку нажатия по X надо от полученной координаты отнять Xmin и разделить на 15, для Y аналогично.
touch_x -= 300;
touch_x = touch_x/15;
touch_y -= 350;
touch_y = touch_y/11;
Если сейчас попробовать отрисовывать точки по касанию, то они будут появляться симметрично относительно центра экрана, так как начала координат лежат в противоположных углах. Поэтому для икса надо от 240 отнимать полученный результат, а для игрика от 320 отнимать полученный результат. В итоге функция с помощью которой мы получаем координаты примет следующий вид.
#define chy 0x90
#define chx 0xD0
#define Xmax 3900
#define Xmin 400
#define Ymax 3900
#define Ymin 300
//#define calibr
void Get_Touch_xy( volatile uint16_t *x_kor,volatile uint16_t *y_kor)
{
volatile uint16_t touch_x = 0;
volatile uint16_t touch_y = 0;
Spi_Master_Transmit(chx);
Spi_Master_Transmit(0X00);
touch_x = SPDR;
touch_x <<= 8;
Spi_Master_Transmit(0X00);
touch_x |= SPDR;
touch_x >>= 3;
_delay_us(100);
Spi_Master_Transmit(chy);
Spi_Master_Transmit(0X00);
touch_y = SPDR;
touch_y <<= 8;
Spi_Master_Transmit(0X00);
touch_y |= SPDR;
touch_y >>= 3;
#ifndef calibr
touch_x -= 300;
touch_x = 240 - touch_x/((Xmax-Xmin)/240);
touch_y -= 350;
touch_y = 320 - touch_y/((Ymax-Ymin)/320);
#endif
*x_kor = touch_x;
*y_kor = touch_y;
}
Для калибровки надо вызвать в бесконечном цикле функцию калибровки и раскомментировать строчку #define calibr.
Иногда случается, что в момент когда стилус отрывается от дисплея, координаты нажатия изменяются, на не соответствующие точке нажатия, но результат в таком случае всегда одинаковый Х = -410, а Y = -20. Поэтому это будет легко учесть, при обработке точки нажатия.
Общая схема подключения изображена ниже.
На этом всё, в следующей статье мы преобразуем картинку в массив для того, чтобы вывести её на дисплей.
Проект для Atmel Studio 6.2 в архиве
Похожие статьи