# Single chip microcomputer -- HLK-W801 driving touch screen

## Background introduction

Recently, I'm studying lvgl, an open-source embedded image display framework, which is enough to support some resource deficient MCU to display some professional interfaces, such as the following

And this

Isn't it cool

Put down Lvgl and don't watch for the time being. Because it is a user interface, most screens can be equipped with touch operation, and there is no need for additional keyboard and mouse. Therefore, today, let's learn to configure the touch screen in my hand.

## Resistance screen

Unlike our mobile phones, most embedded devices are equipped with resistive screens because of their high accuracy and low cost.

The most common is my 4-wire resistive screen. It's four wires, x +, X -, y +, Y -.
The principle is as follows:
The four wires are mainly composed of two layers of films plated with ITO coating. One layer has a vertical bus at the left and right edges of the screen, and the other layer has a horizontal bus at the top and bottom of the screen. If you apply voltage on two buses of a thin film, a uniform electric field will be formed on the ITO coating. When the user touches the touch screen, the two films of the touch point will contact, and the voltage value of the contact point can be measured on the other film.

So how to measure the four lines
First step: apply 0V voltage on X - and VCC on X +, then measure the voltage VPX on Y - (or y +) electrode, and then calculate the X coordinate of contact point P.
Step 2: apply a voltage of 0V on Y -, apply VCC on y +, and measure the voltage value VPY on X - (or x +) electrode. Then calculate the Y coordinate of contact point P.
The above two steps can form a measurement cycle and obtain a set of (X,Y) coordinates.

## ADC measurement method (free method)

In principle, I temporarily call it ADC measurement method, because it is obtained by measuring voltage through ADC. Now that the principle is clear, you can start to solve it. Use four gpios to connect the four lines of the screen and program according to the measurement method. This is called requirement transformation code.

critical code

//Get the value of X and y, where x is a pointer to the stored variable
void get_map_x_y(int* x,int* y)
{
GPIO_InitTypeDef GPIO_InitStruct;

//Configure x + as high push-pull and X - as low push-pull
GPIO_InitStruct.Pin = P_TOUCH_X_JIA;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIA, GPIO_PIN_SET);

GPIO_InitStruct.Pin = P_TOUCH_X_JIAN;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIAN, GPIO_PIN_RESET);

//Configure y + as adc mode and Y - as floating input
{
Error_Handler();
}
GPIO_InitStruct.Pin = P_TOUCH_Y_JIAN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);

HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIA, GPIO_PIN_RESET);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIAN, GPIO_PIN_RESET);

//Configure y + as high push-pull and Y - as low push-pull
GPIO_InitStruct.Pin = P_TOUCH_Y_JIA;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIA, GPIO_PIN_SET);

GPIO_InitStruct.Pin = P_TOUCH_Y_JIAN;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIAN, GPIO_PIN_RESET);

//Configure x + as adc mode and X - as floating input

{
Error_Handler();
}

GPIO_InitStruct.Pin = P_TOUCH_X_JIAN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);

HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIA, GPIO_PIN_RESET);
HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIAN, GPIO_PIN_RESET);
}

The operation is completely translated according to the schematic diagram. Here, we need to pay attention to that the measurement points X + and Y + should be converted in ADC mode and output mode. Therefore, we should choose the pin that supports both GPIO and ADC conversion. Otherwise, the value cannot be measured.

However, I studied this method for 5 hours, and then found that the voltage measured by the X coordinate was changing on the whole screen. Then I carefully found that there was an ADC chip on the board and the cable had been connected. Therefore, I suspect that this voltage change has something to do with the external chip, so I gave up this practice.
However, this approach is entirely feasible. And there is no need for additional chips. In other words, this practice is free.

The chip on the board is ads7846, which is a very mainstream resistance screen driver chip. It converts the measurement of 4-wire resistance into data reading of SPI bus, and provides interrupt and docking with MCU, which is called a 6.

Six wires are provided

Pinexplain
CLKSPI clock
CSFilm selection
MOSIMaster output slave input
MISOHost input and slave output
BUSYBusy signal
PENInterrupt signal

Here we use GPIO simulation.
GPIO initialization

static void TOUCH_GPIO_Init(void)
{

GPIO_InitTypeDef GPIO_InitStruct;

__HAL_RCC_GPIO_CLK_ENABLE();

GPIO_InitStruct.Pin = TDIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, TDIN, GPIO_PIN_RESET);

GPIO_InitStruct.Pin = TCLK;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, TCLK, GPIO_PIN_RESET);

GPIO_InitStruct.Pin = TCS;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(P_TOUCH_PORT, TCS, GPIO_PIN_RESET);

GPIO_InitStruct.Pin = DOUT;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);

GPIO_InitStruct.Pin = PEN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);

HAL_NVIC_SetPriority(GPIOA_IRQn, 0);
HAL_NVIC_EnableIRQ(GPIOA_IRQn);

}

Note here that to open the interrupt, the PEN pin is at a low level when there is a touch. When the hand is taken away, it is at a high level. Therefore, if you want to read continuously, you should constantly judge the battery of PEN pin.

BUSY signal can also be used. It can be initialized to read in mode.

#define CMD_ RDX 0X90 / / 0b10010000, i.e. read X coordinates in differential mode
#define CMD_RDY 	 0xd0 / / 0b11010000, i.e. read Y coordinates in differential mode

//The read X and Y coordinate values must be greater than 100
//1 is returned for success and 0 is returned for failure
//The reading is limited to 100 ~ 3800
const float y_sin=0.1348;
const float x_sin=0.1739;

int get_map_x_y(int* X,int* Y)
{
int px=0,py=0;
start_spi();//Start SPI

//The maximum conversion time of ADS7846 is 6us
TCLK_SET(1);
delay_us(3);
TCLK_SET(0);
delay_us(3);

//The maximum conversion time of ADS7846 is 6us
TCLK_SET(1);
delay_us(3);
TCLK_SET(0);
delay_us(3);

TCS_SET(1);
if((px>100)&&(py>100)&&(px<3800)&&(py<3800))
{
int abs_px,abs_py;
abs_px=px-100;
abs_py=py-100;
*X=x_sin*abs_px;
*Y=y_sin*abs_py;
}
else
{
}
}

I first tried the coordinates of the four corners, and then estimated the units of the x-axis and y-axis, that is, the coordinates read. How many pixels does one unit represent. The following two values are obtained.
const float y_sin=0.1348;
const float x_sin=0.1739;

Then you can remove the starting value according to the read value and multiply it by the coefficient just now to get the specific coordinates of my 320 * 240 screen.
abs_px=px-100;
abs_py=py-100;
X=x_sinabs_px;
Y=y_sinabs_py;

## Drawing board

In the previous 16 articles, the drive line has been completed, Single chip microcomputer HLK-W801 parallel port drive ST7789
Then combined with today's touch screen, we can make a drawing board.

Interrupt judgment, here is only a flag bit, which will be triggered if there is a touch operation.

void HAL_GPIO_EXTI_Callback(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
{
if ((GPIOx == P_TOUCH_PORT) && (GPIO_Pin == PEN))
{
key_flag = 1;
}
}

Then, in the main function, continue to read and draw points.

while (1)
{
if (key_flag == 1)
{
HAL_Delay(20);
{
int pos_x=0, pos_y=0;
get_map_x_y(&pos_x,&pos_y);
LCD_DrawPoint(320-pos_x,pos_y, 0xf000);
HAL_Delay(5);
}
key_flag = 0;
}
}

In HAL_Delay has different rendering effects under different conditions.
HAL_Delay(20)

HAL_Delay(5)

Remove delay

It can be seen that when there is a delay, the coordinates are relatively clean. When there is no delay, there will be many drift points when writing and starting. This drift effect is like the effect of sand painting.