in SSD1963 MAX11802 GUI STM32DISCOVERY FSMC INT070ATFT-TS ~ read.
7" TFT touchscreen control with STM32F407 device and FSMC peripheral

7" TFT touchscreen control with STM32F407 device and FSMC peripheral

To develop a simple graphical interface, I purchased a while ago a 7" TFT touchscreen made by Displaytech. This large screen is controlled via an integrated SSD1963 graphic controller and the touch events and coordinates are gathered via a MAX11802 resistive touchscreen controller from Maxim.

Originally, I started to interface this screen with a PIC32 running @ 80MHz and the final result was not satisfactory in terms of refresh rate. Recently I got one of those cheap (yet so powerful) STM32Discovery board and tried to interface it with the touchscreen...

Required hardware :

  • STM32DISCOVERY board
  • INT070ATFT-TS screen or a similar one equipped with SSD1963 controller
  • Power supply, jumper wires, etc.

You can download the wiring I used here.

Required software :

  • IDE to debug and program STM32F407VGT6 (I used Keil µVision 5)

Initializing the SSD1963 screen controller

The SSD1963 is interfaced with the MCU via a parallel port configured in Intel 8080 or Motorola 6800 modes. Both 6800 and 8080 support 8-bit, 9-bit, 16-bit, 18-bit and 24-bit data bus. Depending on the width of the data bus, the display data are packed into the data bus in different ways. Take a look at the datasheet to find out which data bus length is suitable for your application. I chose to use the 16-bit format which is very common.

Instead of controlling a 16-bit GPIO port of the board, I set up the Flexible Static Memory Controller (FSMC) feature. Originally used to communicate with flash memories, it can be employed to send data to a graphical controller.

The following code is an extract of the tft.c source file. It is used to initialize the TFT screen :

#ifndef TFT_H
#define TFT_H
#include <tft.h>
#endif

//Set all control lines as input to allow auto-init
void TFT_CtrlLinesReset(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//Enable clocks
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_11 | GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_Init(GPIOE, &GPIO_InitStructure);
}


//Set control lines for FSMC
void TFT_CtrlLinesConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/* Enable GPIOD, GPIOE, GPIOF, GPIOG and AFIO clocks */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF, ENABLE);

	/*-- GPIO Configuration ------------------------------------------------------*/
	/* SRAM Data lines,  NOE (/RD) and NWE (/WR) configuration */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

	GPIO_Init(GPIOD, &GPIO_InitStructure);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_Init(GPIOE, &GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource10 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource11 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource12 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource13 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource14 , GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource15 , GPIO_AF_FSMC);

	/* SRAM Address lines configuration (/RS)*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOD, &GPIO_InitStructure);  
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);           

	/* NE3 configuration (/CS)*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; 

	GPIO_Init(GPIOD, &GPIO_InitStructure);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource7, GPIO_AF_FSMC);
	
	/*/RESET */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	GPIO_SetBits(GPIOD, GPIO_Pin_12);
}

//FSMC configuration
void TFT_FSMCConfig(void)
{
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef  p;

	/* Enable FMC clock */
	RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);

	/*-- FSMC Configuration ------------------------------------------------------*/
	/*----------------------- SRAM Bank 1 ----------------------------------------*/
	/* FSMC_Bank1_NORSRAM1 configuration */
	p.FSMC_AddressSetupTime = 5;
	p.FSMC_AddressHoldTime = 1;
	p.FSMC_DataSetupTime = 9;
	p.FSMC_BusTurnAroundDuration = 0;
	p.FSMC_CLKDivision = 1;
	p.FSMC_DataLatency = 0;
	p.FSMC_AccessMode = FSMC_AccessMode_A;
	/* Color LCD configuration ------------------------------------
	LCD configured as follow:
	- Data/Address MUX = Disable
	- Memory Type = SRAM
	- Data Width = 16bit
	- Write Operation = Enable
	- Extended Mode = Enable
	- Asynchronous Wait = Disable */

	FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
	FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
	FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
	FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
	FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
	FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;

	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);   

	/* Enable FSMC NOR/SRAM Bank1 */
	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);
}


void TFT_Initialization(void)
{
	//Disable power supply
	TFT_5V_Disable();
	TFT_33V_Disable();
	
	//Control lines as input
	TFT_CtrlLinesReset();
	
	//Enable power supply
	TFT_33V_Enable();
	delay_1ms(1000);
	TFT_CtrlLinesConfig();
	delay_1ms(100);
	TFT_FSMCConfig();
	delay_1ms(100);
	
	//Display on + backlight power on
	SSD1963__set_display_on();
	TFT_5V_Enable();
	
	//Set pixel data interface to 16-bit (565)
	SSD1963__set_pixel_data_interface(0x03);
	
	//Set address mode
	SSD1963__set_address_mode(0x02);
	
	//Brightness @ 100%
	SSD1963__set_pwm_conf(0x06,0xD9,0x01,0xF0,0x00,0x00);
}

Initializing the MAX11802 touchscreen controller

The MAX11802 is interfaced with the MCU via a SPI interface. Every touch event set an interruption of the main program and the MCU ask for event's coordinates. X and Y coordinates are returned to the MCU by the controller on a range from 0 to 65535.

Here is the max1802.c source file containing the related function to operate the controller. Take a look at the whole repository for a better understanding (interrupts).

#ifndef MAX11802_H
#define MAX11802_H
#include <max11802.h>
#endif

void MAX11802_Initialization(void)
{
	SPI_InitTypeDef SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitDef;
	EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
 
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	 
	SPI_Init(SPI2, &SPI_InitStructure);
	 
	 
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	 
	GPIO_InitDef.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitDef.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
	GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOB, &GPIO_InitDef);
	 
	GPIO_InitDef.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
	GPIO_Init(GPIOB, &GPIO_InitDef);
	 
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI1);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
	

	//Start SPI2 with interrupts
	SPI_Init(SPI2,&SPI_InitStructure);
	NVIC_InitStruct.NVIC_IRQChannel = SPI2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);
	
	//Start SPI bus
	SPI_Cmd(SPI2, ENABLE);
	
	delay_1ms(100);
	
	//MAX11802 registers configuration
	TP_Write_Register(0x01,0x00);
	TP_Write_Register(0x02,0x00);
	TP_Write_Register(0x03,0x00);
	TP_Write_Register(0x04,0x00);
	TP_Write_Register(0x05,0x80);
	TP_Write_Register(0x06,0x55);
	TP_Write_Register(0x07,0x10);
	TP_Write_Register(0x08,0x00);
	TP_Write_Register(0x09,0x00);
	TP_Write_Register(0x0A,0x00);
	TP_Write_Register(0x0B,0x00);
	
	
	//--START--INTERRUPT CONFIG (PB0/TIRQ)
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	//External Interrupt as input
	GPIO_InitDef.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
	//Initialize pins
	GPIO_Init(GPIOB, &GPIO_InitDef);
	
	/* Tell system that you will use PB0 for EXTI_Line0*/
        SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0);
	
	/* PB0 is connected to EXTI_Line0 */
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStruct);

	/* Add IRQ vector to NVIC */
	/* PD0 is connected to EXTI_Line0, which has EXTI0_IRQn vector */
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	//--END--INTERRUPT CONFIG (PB0/TIRQ)
	tp_int=0;
}

void TP_Write_Register(unsigned char reg, unsigned char val)
{
    unsigned char byte0;
    byte0=reg<<1;

    SET_TP_CS();
    delay_1ms(1);
    SPI_I2S_SendData(SPI2, byte0);
    delay_1ms(1);
	  SPI_I2S_SendData(SPI2, val);
    delay_1ms(1);
    RESET_TP_CS();
}

void SET_TP_CS(void)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

void RESET_TP_CS(void)
{
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

//Ask the controller for X coordinate
void TP_Get_X(void)
{
    unsigned int tp_x_msb=0;
		unsigned int tp_x_lsb=0;
	
	  SET_TP_CS();
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xF0);
    delay_1ms(5);
    RESET_TP_CS();

    delay_1ms(5);

    SET_TP_CS();
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xA5);
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xFF);
    delay_1ms(5);
    tp_x_msb=tp_buffer;
    SPI_I2S_SendData(SPI2, 0xFF);
    delay_1ms(5);
    tp_x_lsb=tp_buffer;
    delay_1ms(5);
    RESET_TP_CS();

    tp_x=(tp_x_msb<<8)|(tp_x_lsb);
}

//Ask the controller for Y coordinate
void TP_Get_Y(void)
{
    unsigned int tp_y_msb=0;
		unsigned int tp_y_lsb=0;
	
	  SET_TP_CS();
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xF4);
    delay_1ms(5);
    RESET_TP_CS();

    delay_1ms(5);

    SET_TP_CS();
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xA9);
    delay_1ms(5);
    SPI_I2S_SendData(SPI2, 0xFF);
    delay_1ms(5);
    tp_y_msb=tp_buffer;
    SPI_I2S_SendData(SPI2, 0xFF);
    delay_1ms(5);
    tp_y_lsb=tp_buffer;
    delay_1ms(5);
    RESET_TP_CS();

    tp_y=(tp_y_msb<<8)|(tp_y_lsb);
}

void TP_Check(void)
{
	TP_Get_X();
	TP_Get_Y();
	TFT_DrawFullRect(0,440,100,40,0x1107);
	TFT_Put_Figure(0,448,tp_x,0xFFFF);
	TFT_Put_Figure(0,464,tp_y,0xFFFF);
}

comments powered by Disqus