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

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

О самом сенсорном экране написано не мало хороших статей. Здесь одна из них. Удобство современных китайских модулей LCD состоит в том, что на них уже есть готовый контроллер, такие как TSC2046 или ADS7846, с которым можно общаться по SPI шине.

touch screen controller

В двух словах вся работа происходит так. Два слоя замыкаются, образуя два резистора по оси X, два по оси Y. В контроллере находится мультиплексор и ADC. Приходит команда по SPI — измерение по оси X. Контроллер подключает X+ на VCC, X- на землю, замеряет напряжение на линии Y+ (или Y-), и передаёт его опять же по SPI. Подобным образом происходит замер по оси Y.


Как происходит обмен данными по SPI? Открываем Data Sheet TSC2046 — 8ми битный обмен данными:

обмен данными по SPI

Жёлтым обозначена отправка команды (контрольное слово), синим — принимаем 2 байта данных (цифровое значение измерения). ADC может работать в 12-ти или 8-ми битном режиме. Понятно, что в 12-ти битном режиме резолюция экрана будет выше, но для экрана разрешением 320 на 240 это хватит и 8-ми бит, чтобы попадать (в идеале) в каждый пиксель. Я использую всё-таки 12-ти битный режим.

Схема подключения:

SPI touch screen

Настроим SPI для работы с TSC2046:


void spi_touch_conf (void)
{
//ports SPI (A and B config)
RCC->APB2ENR 	|= RCC_APB2ENR_IOPAEN;	 //Включаем Port A
RCC->APB2ENR 	|= RCC_APB2ENR_IOPBEN;	 //Включаем Port B
//in-out config
//PA5 - SCL	- Alternative func. push-pull	  (A)
//PB7 - CS - GPIO - soft	(4)
//PA6       - MOSI Alternative func.  push-pull- OUT  (A)
//PA7	    - MISO Input floating / Input pull-up - IN
//PB6 - Interrupt	 - input
GPIOA->CRL = 0xA4A44444;	// Port A (Low)  Bit 5,6  - alt func	7 - input
GPIOB->CRL = 0x24444444;	// Port B (Low)	bit 7 - out, bit 6 - interrupt in

//SPI1 CR1 configure
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;     //Enable oscil SPI1
  SPI1->CR1 |= SPI_CR1_BR;                //Baud rate = Fpclk/256
  SPI1->CR1 &= ~SPI_CR1_CPOL;             //Polarity cls signal CPOL = 0;
  SPI1->CR1 &= ~SPI_CR1_CPHA;             //Phase cls signal    CPHA = 0;
  SPI1->CR1 &= ~SPI_CR1_DFF;              //8 bit data
  SPI1->CR1 &= ~SPI_CR1_LSBFIRST;         //MSB will be first
  SPI1->CR1 |= SPI_CR1_SSM;               // Program mode NSS
  SPI1->CR1 |= SPI_CR1_SSI;               // анналогично состоянию, когда NSS 1
  SPI1->CR2 |= SPI_CR2_SSOE;              //вывод NSS -  slave select
  SPI1->CR1 |= SPI_CR1_MSTR;              //Mode Master
  SPI1->CR1 |= SPI_CR1_SPE;               //Enable SPI1

//--------------------------
SPI1->CR2 = 0x00000000;  //No interrupt enable
}


Теперь SPI готов. Пишем функцию отправки команды и получения данных с ADC:


#define CS_TOUCH_ON   (GPIOB->BSRR =GPIO_BSRR_BR7)    //negative logic
#define CS_TOUCH_OFF  (GPIOB->BSRR =GPIO_BSRR_BS7)    //negative logic

int get_touch (uint8_t command)	 //communication with TSC2046
{
int d_in;                //zero data in
CS_TOUCH_ON;	         //chip select TOUCH
Delay (10);
SPI1->DR = command;	              //send command
while(!(SPI1->SR & SPI_SR_RXNE));	//wait
d_in = SPI1->DR;			//ignore this enter
SPI1->DR = 0;	   //send zero bytes - cont receiving data from module
while(!(SPI1->SR & SPI_SR_RXNE));  //wait
d_in = SPI1->DR;	//receive high byte
d_in <<= 8;		//move it 8 
SPI1->DR = 0;	        //send zero bytes - cont receiving data from module
while(!(SPI1->SR & SPI_SR_RXNE));	//wait
d_in |= SPI1->DR;	 //receive low byte
CS_TOUCH_OFF;
Delay (10);
return d_in;
}


SPI здесь работает в режиме одновременной отправки и приёма данных. В первой отправке — контрольное слово — наша команда (command) (померь по оси X) — на входе ничего (игнорируем нули, которые получены). Во второй части мы ничего не отправляем (вернее, отправляем нули) и принимаем высокий байт данных, сдвигаем его влево. В третьей — опять ничего не отправляем (нули) и принимаем низкий байт. Затем возвращаем полученные данные.

Чтобы получить значение по оси X в 12-ти битном режиме, нужно отправить команду 0x98, а по Y 0xD8. Полная таблица находится в data sheet. TSC2046 умеет даже измерять силу нажатия и напряжение питания.

Я написал такую функцию получения X и Y:


#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

void spi_touch_chat (uint8_t i)	//i - how many scans to do (resolution)
{
int tmpx=0, tmpy=0;
d_in_x = get_touch (GET_X) / DIV_TOUCH;
d_in_y = get_touch (GET_Y) / DIV_TOUCH;
if (d_in_y == 0) d_in_x = 0;
if (d_in_x == 0) d_in_y = 0;
for (i=i; i>0; i--)
	{
	tmpx = get_touch (GET_X) / DIV_TOUCH;
	tmpy = get_touch (GET_Y) / DIV_TOUCH;
	if (tmpx == 0) tmpy = 0;
	if (tmpy == 0) tmpx = 0;
	if (!tmpx) 
		{
		d_in_x = 0;
		d_in_y = 0;
		break;
		}
	else 
		{
		d_in_x = (d_in_x + tmpx) / 2;
		d_in_y = (d_in_y + tmpy) / 2;
		}
	}
	if (d_in_x)	//normalize direction of ACD outs
		{
		d_in_x = ADC_X_APP_MAX - d_in_x;
		d_in_y = ADC_Y_APP_MAX - d_in_y;
		//calculate real coordinates
		}
	Delay (500);		//Pause
}



Здесь i — количество замеров. Известно, что иногда бывает дрифт напряжений. Из-за этого может быть неправильное значение. Поэтому можно сделать, например, 40 замеров и взять среднее значение. Если не нужно замерять среднее значение, то замер выполнится 1 раз.

Если хоть одна из координат будет 0 — то и второе тоже должно обнулиться. Значения ADC при нажатии могут быть примерно от 2000 до 32000. Если нужно поменять направление оси, то (как в моём случае) отнимаем полученное значение от примерного максимально возможного — d_in_x = ADC_X_APP_MAX — d_in_x;

Теперь у нас есть полученные координаты. В следующей часть рассмотрим, как обработать полученные координаты в реальные координаты экрана и как выполнить калибрацию touch screen.
  • +4
  • 15 ноября 2013, 14:03
  • ilus

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

RSS свернуть / развернуть
для экрана разрешением 320 на 240 это хватит и 8-ми бит, чтобы попадать (в идеале) в каждый пиксель.
да ладно. как так 320 пикселей в 8 бит упихнуть умудрились?
0
  • avatar
  • xar
  • 15 ноября 2013, 14:52
даже если в 2 пикселя попасть одновременно, точка будет небольшая. А по 2 точки 160 получится. В любом случае используем 12-ти битную шину.
0
Поэтому можно сделать, например, 40 замеров и взять среднее значение.
тут надо подходить более основательно. в первую очередь определиться с необходимой точностью позиционирования.
если высокая точность не так уж и нужна (рисовать не планируется) усредняем слабенько и учитываем перемещения (минимальное и максимальное за единицу времени). то есть определяем область, перемещение в пределах которой будет считаться нажатием без перемещения. выход за пределы уже можно считать движением. от импульсных помех спасет максимальное перемещение за единицу времени. то есть если произошел моментальный выход за определенную область — это помеха. чем то похоже на усреднение с отбрасыванием минимума и максимума, но тут определены конкретные рамки.
0
  • avatar
  • xar
  • 15 ноября 2013, 15:01
согласен. При калибрации лучше использовать по 200 или по 300 замеров. А при рисовании — в зависимости от режима.
От импульсных помех защиту пока не поставил.
0
А вот так рекомендует TI, работает все на отлично.

On software side: you may use multiple samples, with the sequence as following:
1.After power up, enable /PENIRQ (by sending command 0xD8 (if 8-bit resolution))
2.Wait until /PENIRQ low (the touch panel is touched)
To read X, say, 3 times by
Command 0xD9, get X1;
Command 0xD9, get X2;
Command 0xD8, get X3;

Check the /PENIRQ and make sure /PENIRQ is still low (otherwise, ignore X and end)
To read Y similarly by
Command 0x99, get Y1;
Command 0x99, get Y2;
Command 0x98, get Y3;

Filtering the sampled Xs and Ys, for example, eliminate the one that is away from others, and average the rest 2, for more example, refer to Section 4 of the app note sbaa155), and get an X and a Y.

3. Check /PENIRQ again, if low, go back 2. Otherwise, end.

Note that, (1) Do not read touch data in such a sequence: X1, Y1, X2, Y2, X3, Y3;
and (2) do not turn off the driver and ADC between multi-samplings.

e2e.ti.com/support/other_analog/touch/f/750/t/202636.aspx
0
То есть лучше сначала читать по одной оси, а потом по другой?
В последнем Note что имеется ввиду? Не поднимать CS?
0
Так как у меня было всего одно устройство на шине, то CS был закорочен на 0. А читать данные я начинал по сигналу PENIRQ. Вначале читал как ты в цикле X,Y,X,Y… на матрице в 3 дюйма вроде неплохо, но в один прекрасный день принесли матрицу в 15 дюймов, вот тут и начались проблемы, данные начали скакать, пальцем нажал — одни показания, стайлусом — другие. И тут я решил залезть на сайт TI и поискать у них решение. И ура, я его нашел…
Сделал все как у них написано:
1 — При запуске посылаем команду 0xD8
2 — Потом по сигналу PENIRQ начинаю считывать координату X три раза командами 0xD9 0xD9 0xD8, затем
3 — Потом по сигналу PENIRQ начинаю считывать координату Y три раза командами 0x99 0x99 0x98
4 — Одно значение по X и одно значение по Y будет неправильным, а по два оставшихся будут практически идентичными.

После того, как переделал все по этому алгоритму, значения стали стабильными, перестали плавать от силы нажатия и метода нажатия ( стайлус или палец ).
0
Я в своей программе PENIRQ не использую пока. Обнаруживаю, что есть касание, когда обе координаты перестают быть нулями. (обычно если не касаться, то одна из них — 0). Опрос идёт постоянно и поочерёдно. Наверное, стоит попробовать поработать с прерыванием, но CS прижать к 0 не могу, она подключена к контроллеру к FSMC выходу.
0
Так испробуй по сигналу CS подать команду 0xD8, а затем пункты 1-4.
0
Ошибочка, пункты 2-4.
0
Есть вопрос. Может я что-то не понял.
Отправляешь команду 0xD9 (например), сразу считываешь значение? Не отправляешь, как у меня в программе, 0x00 чтобы считать только второй и третий байт данных?
0
Отправляю. 0xD9 — это команда запроса байта, а потом отправляю пустые для считывания байта.
0
Теперь понятно. Попробую. Может, получится от случайных точек избавиться при слабом нажатии.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.