Getting Started with LvGL Part 3: Touch Integration

In the pervious guide (here), we have successfully integrated LCD. In part 3, we shall integrate the touch part of the LCD.

10. Touch Connection:

The connection of Touch pins as following:

ILI9341STM32F767Zi Nucleo-144
T_CLKPB13
T_CSPA3
T_DINPC2
T_OUTPB15

PB13, PB15 and PC2 are for SPI2 peripheral of the STM32F767Zi

11. Initialize SPI2:

Create new source and header file with name of SPI2.c and SPI2.h respectively:

Within the header file:

#ifndef SPI2_H_
#define SPI2_H_


#include "stdint.h"

void SPI2_Pins_Init(void);
void SPI2_Init(void);
void SPI2_TX(uint8_t *data,uint32_t size);
void SPI2_RX (uint8_t * data, uint8_t length);




#endif /* SPI2_H_ */

In the source file:

#include "SPI2.h"
#include "stm32f7xx.h"



#define SPI2_AF 0x05


void SPI2_Pins_Init(void)
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOBEN|RCC_AHB1ENR_GPIOCEN;

	GPIOB->MODER|=GPIO_MODER_MODER13_1|GPIO_MODER_MODER15_1;
	GPIOB->MODER&=~(GPIO_MODER_MODER13_0|GPIO_MODER_MODER15_0);

	GPIOB->OSPEEDR|=GPIO_OSPEEDR_OSPEEDR15|GPIO_OSPEEDR_OSPEEDR13;

	GPIOB->AFR[1]|=(SPI2_AF<<GPIO_AFRH_AFRH5_Pos)|(SPI2_AF<<GPIO_AFRH_AFRH7_Pos);



	GPIOC->MODER|=GPIO_MODER_MODER2_1;
	GPIOC->MODER&=~(GPIO_MODER_MODER2_0);

	GPIOC->OSPEEDR|=GPIO_OSPEEDR_OSPEEDR2;

	GPIOC->AFR[0]|=(SPI2_AF<<GPIO_AFRL_AFRL2_Pos);

}


void SPI2_Init(void)
{

	RCC->APB1ENR|=RCC_APB1ENR_SPI2EN;

	//software slave management
	SPI2->CR1|=SPI_CR1_SSM|SPI_CR1_SSI;

	/*Set clock to fPCLK/16*/
	SPI2->CR1 |=(SPI_CR1_BR_0|SPI_CR1_BR_1);

	// set SPI as master mode
	SPI2->CR1|=SPI_CR1_MSTR;

	/*Set CPOL to 0 and CPHA to 0*/
	SPI2->CR1 &=~SPI_CR1_CPOL;
	SPI2->CR1 &=~SPI_CR1_CPHA;

	/*Set data size to 8-bit*/
	SPI2->CR2&=~(SPI_CR2_DS_Msk);
	SPI2->CR2|=(0x07<<SPI_CR2_DS_Pos);

	SPI2->CR2|=SPI_CR2_FRXTH;

	SPI2->CR1|=SPI_CR1_SPE;

}

void SPI2_TX(uint8_t *data,uint32_t size)
{



	uint32_t i=0;
	while(i<size)
	{
		/*Wait until TXE is set*/
		while(!(SPI2->SR & (SPI_SR_TXE)))
		{}

		/*Write the data to the data register*/
		*((volatile uint8_t *)&SPI2->DR) = data[i];
		i++;
	}
	/*Wait until TXE is set*/
	while(!(SPI2->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI2->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI2->DR;
	(void)SPI2->SR;

}


void SPI2_RX (uint8_t * data, uint8_t length)
{

	while(length)
	{
		/*Send dummy data*/
		*((volatile uint8_t *)&SPI2->DR) =0xFF;

		/*Wait for RXNE flag to be set*/
		while(!(SPI2->SR & (SPI_SR_RXNE))){}

		/*Read data from data register*/
		*data++ = *((volatile uint8_t *)&SPI2->DR);
		length--;
	}

}

We shall read the touch in polling mode.

12. CS Pin Initialization:

Create new source and header file with name of TouchPins.c and TouchPins.h

This file will handle the CS pin initialization.

Within the header file:

#ifndef TOUCHPINS_H_
#define TOUCHPINS_H_

#include "stdint.h"

void TouchPin_CS_Init(void);
void TouchPin_CS_Low(void);
void TouchPin_CS_High(void);


#endif /* TOUCHPINS_H_ */

In source file:

#include "TouchPins.h"
#include "stm32f7xx.h"


void TouchPin_CS_Init(void)
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;

	GPIOA->MODER|=GPIO_MODER_MODER3_0;
	GPIOA->MODER&=~GPIO_MODER_MODER3_1;

	GPIOA->BSRR=GPIO_BSRR_BS3;


}

void TouchPin_CS_Low(void)
{
	GPIOA->BSRR=GPIO_BSRR_BR3;
}

void TouchPin_CS_High(void)
{
	GPIOA->BSRR=GPIO_BSRR_BS3;
}

13. Touchpanel initialization:

Create new source and header file with name of xpt2046.c and xpt2046.h respectively.

Within the header file:

#ifndef INC_TOUCH_H_
#define INC_TOUCH_H_

#include "stdint.h"


#define TP_PRES_DOWN 0x80
#define TP_CATH_PRES 0x40
#define CMD_RDX 0xD0
#define CMD_RDY 0x90
#define XPT_XMIN 300
#define XPT_YMIN 300
#define XPT_XMAX 3600
#define XPT_YMAX 3400
#define XPT_WIDTH (XPT_XMAX - XPT_XMIN)
#define XPT_HEIGHT (XPT_YMAX - XPT_YMIN)

void XPT2046_Init(void);
void XPT2046_Update(uint16_t *x, uint16_t *y);
uint8_t XPT2046_IsReasonable(uint16_t x, uint16_t y);



#endif /* INC_TOUCH_H_ */

Within the source file:

Taken from arduino library and modified.

#include "xpt2046.h"
#include "time_base.h"
#include "TouchPins.h"
#include "SPI2.h"




static void XPT2046_SetCS(void)
{
	TouchPin_CS_High();
}

static void XPT2046_ResetCS(void)
{
	TouchPin_CS_Low();
}


/**********************************************************************************************************/

#define T_IRQ XPT2046_ReadIRQ()
#define T_CS_ON XPT2046_SetCS()
#define T_CS_OFF XPT2046_ResetCS()

#define READ_TIMES 	5
#define LOST_VAL 	1
#define ERR_RANGE 50
#define Z_THRESHOLD     400
#define Z_THRESHOLD_INT	75
#define MSEC_THRESHOLD  3

static uint8_t XPT2046_initilazed = 0;


static uint16_t XPT2046_Read_AD(uint8_t CMD)
{
	uint8_t num[2];
	uint16_t ret;

	T_CS_OFF;
	SPI2_TX(&CMD,1);
	delay(6);

	SPI2_RX(num, 2);
	T_CS_ON;

	ret = num[0] << 8 | num[1];
	ret >>= 3;
	ret &= (1<<12)-1;

	return ret;
}

static int16_t besttwoavg( int16_t x , int16_t y , int16_t z ) {
  int16_t da, db, dc;
  int16_t reta = 0;
  if ( x > y ) da = x - y; else da = y - x;
  if ( x > z ) db = x - z; else db = z - x;
  if ( z > y ) dc = z - y; else dc = y - z;

  if ( da <= db && da <= dc ) reta = (x + y) >> 1;
  else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
  else reta = (y + z) >> 1;   //    else if ( dc <= da && dc <= db ) reta = (x + y) >> 1;

  return (reta);
}

void XPT2046_Init(void)
{
	SPI2_Pins_Init();
	SPI2_Init();
	TouchPin_CS_Init();


	XPT2046_initilazed = 1;
}

void XPT2046_Update(uint16_t *x, uint16_t *y)
{
	int16_t data[6];
	static uint32_t ptime = 0;

	if (XPT2046_initilazed == 0) {
		return;
	}

	if (Tick_Get() - ptime < MSEC_THRESHOLD) {
		return;
	}

	int16_t z1 = XPT2046_Read_AD(0xb1); // z1
	int32_t z = z1 + 4095;
	int16_t z2 = XPT2046_Read_AD(0xc1); // z2
	z -= z2;
	if (z >= Z_THRESHOLD) {
		XPT2046_Read_AD(0x91);  // dummy 1st X measure
		data[0] = XPT2046_Read_AD(0x91);
		data[1] = XPT2046_Read_AD(0xd1);
		data[2] = XPT2046_Read_AD(0x91);
		data[3] = XPT2046_Read_AD(0xd1);
	} else {
		data[0] = data[1] = data[2] = data[3] = 0;
	}
	data[4] = XPT2046_Read_AD(0x91);
	data[5] = XPT2046_Read_AD(0xd0);
	ptime = Tick_Get();
	if (z < 0) z = 0;
	int16_t intx = besttwoavg( data[1], data[3], data[5] );
	int16_t inty = besttwoavg( data[0], data[2], data[4] );
	if (z >= Z_THRESHOLD) {
		*x = intx;
		*y = inty;
	}
}

uint8_t XPT2046_IsReasonable(uint16_t x, uint16_t y)
{
	if (x >= XPT_XMIN && x <= XPT_XMAX && y >= XPT_YMIN && y <= XPT_YMAX) {
		return 1;
	}
	return 0;
}

Note: The these values are near enough to make the touch screen work. In your application, you will need to implement touch screen calibration or calibrate before release.

14. Rotating the LCD:

Since this driver will only work in landscape mode, we shall rotate the LCD by 90.

In ILI9341.c file, modify the memory access control (MAC) to become as following:

	LCD_Write_Cmd (ILI9341_MAC); // memory access control
	LCD_Write_Data((1<<3)|(1U<<5)|(1<<2));

In LCDController.c, we shall change the dimensions as following:

#define MY_DISP_HOR_RES 320


#define MY_DISP_VER_RES 240

Thats all for rotating screen.

15. Touch Integration:

Create new source and header file with name of TouchController.c and TouchController.h.

Within the header file:

#ifndef INC_TOUCHCONTROLLER_H_
#define INC_TOUCHCONTROLLER_H_

/*********************
 *      INCLUDES
 *********************/
#include "lvgl.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 * GLOBAL PROTOTYPES
 **********************/
void lv_port_indev_init(void);

/**********************
 *      MACROS
 **********************/



#endif /* INC_TOUCHCONTROLLER_H_ */

Within the source file:

#include "TouchController.h"
#include "lvgl.h"
#include "stdio.h"
#include "xpt2046.h"

#include "ILI_9341.h"
/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);


/**********************
 *  STATIC VARIABLES
 **********************/
lv_indev_t * indev_touchpad;


/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_indev_init(void)
{
    /**
     * Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    static lv_indev_drv_t indev_drv;

    /*------------------
     * Touchpad
     * -----------------*/

    /*Initialize your touchpad if you have*/
    touchpad_init();

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    indev_touchpad = lv_indev_drv_register(&indev_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/


static void ConvXPTtoILI(uint16_t *x, uint16_t *y)
{
	uint16_t tx,ty;
	ty = (int16_t)(((int32_t)*x - XPT_YMIN) * ILI9341_HEIGHT / XPT_HEIGHT);
	ty = (ty < 0) ? 0 : ty;
	ty = (ty >= ILI9341_HEIGHT) ? ILI9341_HEIGHT-1 : ty;

	tx = (int16_t)(((int32_t)*y - XPT_XMIN) * ILI9341_WIDTH / XPT_WIDTH);
	tx = (tx < 0) ? 0 : tx;
	tx = (tx >= ILI9341_WIDTH) ? ILI9341_WIDTH-1 : tx;
	*y = ty;
	*x = ILI9341_WIDTH-tx;
}

struct {
	lv_coord_t x;
	lv_coord_t y;
}get_xy;
/*------------------
 * Touchpad
 * -----------------*/

/*Initialize your touchpad*/
static void touchpad_init(void)
{
    XPT2046_Init();
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {

    	last_x = get_xy.x;
    	last_y = get_xy.y;
        data->state = LV_INDEV_STATE_PR;
    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;
}


/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
	static uint16_t prevx = ILI9341_WIDTH;
	static uint16_t prevy = ILI9341_HEIGHT;
	uint16_t intx, inty;
	XPT2046_Update(&intx, &inty);

	if (XPT2046_IsReasonable(intx, inty))
	{



		ConvXPTtoILI(&intx, &inty);

		if (intx != prevx || inty != prevy)
		{
			prevx = intx;
			prevy = inty;
			get_xy.x = (int32_t)intx;
			get_xy.y = (int32_t)inty;
			return true;
		}
	}
	return 0;
}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}

Thats all for the touch integration.

16. Main code:

Include the touch controller header file

#include "TouchController.h"

Call the touch initialization after the display initialization:

lv_port_indev_init();

17. Results:

Happy coding 🙂

Add Comment

Your email address will not be published. Required fields are marked *