Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

В прошлой статье мы рассмотрели как инициализировать контроллер в этой будем разбираться как получить координаты касания и откалибровать сенсорную панель.
Перед тем как определять координаты касания его надо зафиксировать, для этой цели воспользуемся специально предназначенным выводом T_IRQ, при касании на этом выводе появляется логический ноль. Хорошо было бы его повесить на ножку, отвечающую за внешние прерывания, кстати, у ATmega16 таких ножек 3, но у меня они все уже заняты, поэтому повесим его на любую другую ножку, сконфигурированную как вход.
Давайте теперь попробуем получить значение координаты X, значение координаты Y получается из аналогичных рассуждений.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

На картинке выше видно, что получить значение координаты Х можно в двух случаях, в первом случай(А0=1,А1=0,А2=0) в качестве опорных напряжений будут выступать YP и YN, во втором(А0=1,А1=1,А2=0) YP и XN, мы реализуем первый. При таком подключении АЦП будет работать в линейном режиме (ratiometric), за счёт этого уменьшается ошибка измерений. Также для начала преобразования надо установить стартовый бит S в единицу. Давайте освежим в памяти формат посылки.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Таким образом, для получения координаты Х надо послать контроллеру 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 и так далее.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Теперь давайте прошьем контроллер и посмотрим, что получилось.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Читателю не видно, поэтому просто придётся мне поверить, во-первых, начала координат дисплея и сенсорной панели находятся в противоположных углах, во-вторых, результат почему-то получился 11-битный, вместо 12-битного. Давайте подключимся логическим анализатором и посмотрим, что да как.
Канала D0 - T_CLK, канал D1 - T_DO, остальные отключил, чтобы не мешали.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Теперь давайте посмотрим отладчиком, что шлёт контроллер.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

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

Тогда в коде выше надо изменить строчку touch_y >>= 4; на touch_y >>= 3;
Давайте загрузим программу в контроллер и посмотрим, правильно ли мы рассуждали.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Отлично посылки стал двенадцати битными. Теперь надо провести стилусом вдоль каждой из сторон при этом значение не должно изменяться. То есть если мы проводим вдоль стороны 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. Поэтому это будет легко учесть, при обработке точки нажатия.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

Общая схема подключения изображена ниже.
Как получить координаты точки касания и произвести калибровку резистивной сенсорной панели на примере XPT2046.

На этом всё, в следующей статье мы преобразуем картинку в массив для того, чтобы вывести её на дисплей.
Проект для Atmel Studio 6.2 в архиве xpt2046_spi__ssd1289_i8080_example.rar [63,94 Kb] (cкачиваний: 667)
комментарии
0