Освоение STM32F103VE + TFT LCD + touch screen (часть 4)

Часть 1. Начало. Работа с LCD через порты.
Часть 2. Работа с LCD через FSMC, инициализация экрана.
Часть 3. Работа с touch screen через SPI.

Калибрация экрана нужна для того, чтобы значения координат сенсорного экрана совпадали со значениями координат LCD. Можно выполнить простые математические операции и более или менее привести значения в соответствие. Но получится не точно, а ещё каждый раз придётся менять программу под отдельное утройство. А если сенсорный экран приклеен с перекосом, тогда тут простыми операциями не справиться.

touch screen calibration

Алгоритм работы при работе с сенсорным экраном прост:
1. Получение координат с контроллера сенсорного экрана (Xt, Yt)
2. Приведение координат с помощью уравнений (Xd, Yd)


touch screen calibration

Коэфициенты A, B, C, D, E, F нужно высчитать с помощью функции калибрации. Будем высчитывать по трём точкам.

калибрация по 3м точкам

Ставим поочерёдно 3 точки как показано на рисунке и сохраняем полученные координаты сенсорного экрана. Важно выводить точки на экран поочерёдно, чтобы пользователь не перепутал.

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

Формулы взяты из AVR Application Note.

формулы высчитывания коэфициентов

После высчитывания координаты можно сохранить в память (к сожалению, у STM32F103VE нет EEPROM) и использовать после выключения, а калибрацию производить только когда это нужно.

Заголовки:


#define TOUCH_X_MAX 240
#define TOUCH_Y_MAX 320
#define TOUCH_MIN_X 0
#define TOUCH_MIN_Y 0

#define RESCALE_FACTOR 1000000
#define DIV_TOUCH 1

#define ADC_X_MIN 6000
#define ADC_X_MAX 31000
#define ADC_Y_MIN 5500
#define ADC_Y_MAX 31500
#define ADC_X_APP_MAX (ADC_X_MAX / DIV_TOUCH)	 //approach X max
#define ADC_Y_APP_MAX (ADC_Y_MAX / DIV_TOUCH)	 //approach Y max
 
#define GET_X 0x98		//TSC2043 param
#define GET_Y 0xD8		//TSC2043 param

volatile int32_t d_in_x, d_in_y;

volatile int32_t cali_A, cali_B, cali_C, cali_D, cali_E, cali_F;


Теперь программа (много кода):


void Touch_Calib (void)
{
uint32_t xd1 = (50 * TOUCH_X_MAX) / 100, xd2 = (90 * TOUCH_X_MAX) / 100, xd3 = (10 * TOUCH_X_MAX) / 100;
uint32_t yd1 = (10 * TOUCH_Y_MAX) / 100, yd2 = (50 * TOUCH_Y_MAX) / 100, yd3 = (90 * TOUCH_Y_MAX) / 100;
uint32_t xt1, xt2, xt3;
uint32_t yt1, yt2, yt3;
double temp1, temp2;
double cal_A = 0.0, cal_B = 0.0, cal_C = 0.0, cal_D = 0.0, cal_E = 0.0, cal_F = 0.0;

//get all coordinates to d_cal_X[] and d_cal_Y[]

Lcd_String_8X16 ("Calibrate Touch Screen\n", 20, 2, RED, GREEN);
//POINT 1
LCD_Draw_Circle (xd1,yd1,5,BLACK,1); //point 1
while (1)
	{
	spi_touch_chat (250);
	if (d_in_x)	   //if we have point coordinates 
		{
		xt1 = d_in_x;				//save point here
		yt1 = d_in_y;
		break;
		}
	}

//POINT 2
LCD_Draw_Circle (xd2,yd2,5,BLACK,1); //point 2
while (1)
	{
	spi_touch_chat (250);
	if (d_in_x)	   //if we have point coordinates 
		{
		xt2 = d_in_x;				//save point here
		yt2 = d_in_y;				//save point here
		break;
		}
	}

//POINT 3
LCD_Draw_Circle (xd3,yd3,5,BLACK,1); //point 3
while (1)
	{
	spi_touch_chat (250);
	if (d_in_x)	   //if we have point coordinates 
		{
		xt3 = d_in_x;				//save point here
		yt3 = d_in_y;							//save point here
		break;
		}
	}

	//finish get coordinates 
Lcd_Fill_Screen ( RGB(0,255,0) );
Lcd_String_8X16 ("Thanks. Wait...\n", 20, 20, RED, GREEN);

//A
temp1 = ((double) xd1 * ((double) yt2 - (double) yt3)) + ((double) xd2 * ((double) yt3 - (double) yt1)) + ((double) xd3 * ((double) yt1 - (double) yt2));
temp2 = ((double) xt1 * ((double) yt2 - (double) yt3)) + ((double) xt2 * ((double) yt3 - (double) yt1)) + ((double) xt3 * ((double) yt1 - (double) yt2));
cal_A = temp1 / temp2;
cali_A = (int32_t) ((double)cal_A * RESCALE_FACTOR);

//B
temp1 = (cal_A * ((double) xt3 - (double) xt2)) + (double) xd2 - (double) xd3;
temp2 = (double) yt2 - (double) yt3;
cal_B = temp1 / temp2;
cali_B = (int32_t) ((double)cal_B * RESCALE_FACTOR);

//C
cal_C = (double) xd3 - (cal_A * (double) xt3) - (cal_B * (double) yt3);
cali_C = (int32_t) (cal_C * RESCALE_FACTOR);

//D
temp1 = ((double) yd1 * ((double) yt2 - (double) yt3)) + ((double) yd2 * ((double) yt3 - (double) yt1)) + ((double) yd3 * ((double) yt1 - (double) yt2));
temp2 = ((double) xt1 * ((double) yt2 - (double) yt3)) + ((double) xt2 * ((double) yt3 - (double) yt1)) + ((double) xt3 * ((double) yt1 - (double) yt2));
cal_D = (double)temp1 / (double)temp2;
cali_D = (int32_t) (cal_D * RESCALE_FACTOR);

//E
temp1 = (cal_D * ((double) xt3 - (double) xt2)) + (double) yd2 - (double) yd3;
temp2 = (double) yt2 - (double) yt3;
cal_E = (double)temp1 / (double)temp2;
cali_E = (int32_t) (cal_E * RESCALE_FACTOR);

//F
cal_F = (double) yd3 - cal_D * (double) xt3 - cal_E * (double) yt3;
cali_F = (int32_t) (cal_F * RESCALE_FACTOR);
}


RESCALE_FACTOR нужен, чтобы не сохранять коэфициенты в виде числа double.

Теперь калибрационные коэфициенты в памяти, выполняем вычисление реальных координат с помощью функции:


void touch_correct (uint32_t in_x,uint32_t in_y)
{
	d_in_x = (cali_A * in_x + cali_B * in_y + cali_C) / RESCALE_FACTOR;
	d_in_y = (cali_D * in_x + cali_E * in_y + cali_F) / RESCALE_FACTOR;
	if (d_in_x >= TOUCH_X_MAX || d_in_y >= TOUCH_Y_MAX)
		{
		d_in_x = 0;
		d_in_y = 0;
		}
}


Получаем реальные координаты дисплея и можем отобразить в виде пикселя или круга.

Небольшое демо-видео того, что получилось.



Спасибо за внимание.
Если кому нужен полный архив с проектом, пишите в личку.
  • +7
  • 15 ноября 2013, 16:16
  • ilus

Комментарии (11)

RSS свернуть / развернуть
RESCALE_FACTOR лучше выбрать как степень двойки и вместо деления делать сдвиг.
0
да луше степень двойки, а если компилятор умный, вместо деления сам сделает сдвигом.
0
чтобы значения координат экрана совпадали со значениями координат экрана
Исправьте.
0
Спасибо.
0
0
Мне пришлось ещё давление нажатия определять, иначе от нерезкого нажатия пальцев (в противоположность стилусу) координаты скачут как попало.
0
  • avatar
  • rius
  • 16 ноября 2013, 10:46
Есть у меня один дисплей 2.4", в котором тачскрин очень странный. Настолько, что простая калибровка по трем точкам не помогает. Нижняя правая четверть очень нелинейная, все остальные же три четверти работают точно, не нуждаясь в калибровке вообще. Пришлось именно для этого дисплея захардкодить отдельную ветку с эмпирически подобранными формулами.
0
Может он поврежден? Я из МВ выудил древний КПК (CASIO Pocket Viewer) с похожей граблей — отказывалось калиброваться в одном из углов (правом нижнем) и потом криво опознавало в нем касания. Оказалось — коррозией съело нижнюю горизонтальную дорожку на таче. Восстановил серебряным лаком — стало работать как положено.
0
Присмотрелся — точно, нижняя полоска повреждена посередине. Но не коррозией.
0
Вообще, для таких экранов надо не спецформулы хардкодить, а просто отказываться работать. PV на неисправном отказывался калиброваться, обнаружив что точки касаний на укладываются в ожидаемые пределы.

P.S. — это уже к самому топику. Обычно устройства хороших фирм калибруются не по трем точкам, а по 4-5. Вероятно, чтобы заодно проверять исправность сенсора и вносить поправки на то, что юзер попадает в указанные точки не очень точно. Вот китайцы — по двум-трем.
0
Можно и по пяти. Алгоритм похожий, а формула перевода координат с сенсорной панели на экрат та же.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.