Having fun with TFT displays

Over the last couple of days I have been somewhat struggling to get a TFT display module I recently purchased from eBay working. There seems to be an abundance of posts littered all over the internet regarding these cheap Chinese TFT displays. The problem seems to be that sellers are miss stating the controller these modules are fitted with.

tft

The module I purchased allegedly had an SPFD5408 controller fitted. The reason I went for this module was that it has a Arduino Uno foot print so should be compatible with the Nucleo development board. Another bonus is it also comes with a 4 wire resistive touch screen and micro SD card socket.

Display Connections

TFT Arduino nucLEO
LCD_RST A4 PC_1
LCD_CS A3 PB_0
LCD_RS A2 PA_4
LCD_WR A1 PA_1
LCD_RD A0 PA_0
LCD_D2 D2 PA_10
LCD_D3 D3 PB_3
LCD_D4 D4 PB_5
LCD_D5 D5 PB_4
LCD_D6 D6 PB_10
LCD_D7 D7 PA_8
LCD_D0 D8 PA_9
LCD_D1 D9 PC_7
SD_CS D10 PB_6
SD_DI D11 PA_7
SD_DO D12 PA_6
SD_SCK D13 PA_5

As you can see from the pin out the controller uses an 8 bit parallel interface with a further five dedicated control pins. So using my Nucleo development board I started writing a basic driver, something to simply initialise the display and maybe draw some simple test patterns just so I could confirm it was working.

 void MW_TFT_Init(void)  
 {       
      GPIO_InitTypeDef GPIO_InitDef;  
      uint16_t id;  
      /* Enable LCD control pin clocks */  
      RCC_AHB1PeriphClockCmd(MW_TFT_RST_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_CS_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_RS_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_WR_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_RD_CLK, ENABLE);  
      /* LCD_RST */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_RST_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_RST_PORT, &GPIO_InitDef);  
      /* LCD_CS */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_CS_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_CS_PORT, &GPIO_InitDef);  
      /* LCD_RS */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_RS_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_RS_PORT, &GPIO_InitDef);  
      /* LCD_WR */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_WR_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_WR_PORT, &GPIO_InitDef);  
      /* LCD_RD */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_RD_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_RD_PORT, &GPIO_InitDef);  
      /* Enable LCD data pin clocks */  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD0_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD1_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD2_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD3_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD4_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD5_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD6_CLK, ENABLE);  
      RCC_AHB1PeriphClockCmd(MW_TFT_LCD7_CLK, ENABLE);  
      /* Set LCD data pins outputs */  
      MW_TFT_SetWriteDirection();  
      /* Set all control lines high */  
      MW_TFT_RST_HIGH;  
      MW_TFT_CS_HIGH;  
      MW_TFT_RS_HIGH;  
      MW_TFT_WR_HIGH;  
      MW_TFT_RD_HIGH;  
      /* Reset TFT */  
      MW_TFT_RST_LOW;  
      Delayms(2);  
      MW_TFT_RST_HIGH;  
      /* Resync */  
      MW_TFT_WriteData(0);  
      MW_TFT_WriteData(0);  
      MW_TFT_WriteData(0);  
      MW_TFT_WriteData(0);  
 }  
 void MW_TFT_WriteData(uint16_t data)  
 {  
      MW_TFT_CS_LOW;  
      MW_TFT_RS_HIGH;  
      MW_TFT_RD_HIGH;  
      MW_TFT_WR_HIGH;  
      /* Send high byte */  
      MW_TFT_Write8(data>>8);  
      MW_TFT_WR_LOW;  
      __asm("nop");  
      MW_TFT_WR_HIGH;  
      /* Send low byte */  
      MW_TFT_Write8(data);  
      MW_TFT_WR_LOW;  
      __asm("nop");  
      MW_TFT_WR_HIGH;  
      MW_TFT_CS_HIGH;  
 }  
 void MW_TFT_WriteCommand(uint16_t cmd)  
 {  
      MW_TFT_CS_LOW;  
      MW_TFT_RS_LOW;  
      MW_TFT_RD_HIGH;  
      MW_TFT_WR_HIGH;  
      /* Send high byte */  
      MW_TFT_Write8(cmd>>8);  
      MW_TFT_WR_LOW;  
      __asm("nop");  
      MW_TFT_WR_HIGH;  
      /* Send low byte */  
      MW_TFT_Write8(cmd);  
      MW_TFT_WR_LOW;  
      __asm("nop");  
      MW_TFT_WR_HIGH;  
      MW_TFT_CS_HIGH;  
 }  
 uint16_t MW_TFT_ReadData(void)  
 {  
      uint16_t data = 0;  
      MW_TFT_CS_LOW;  
      MW_TFT_RS_HIGH;  
      MW_TFT_RD_HIGH;  
      MW_TFT_WR_HIGH;  
      /* Read high byte */  
      MW_TFT_RD_LOW;  
      Delay(10);  
      data = MW_TFT_Read8();  
      data <<= 8;  
      /* Read low byte */  
      MW_TFT_RD_HIGH;  
      MW_TFT_RD_LOW;  
      Delay(10);  
      data |= MW_TFT_Read8();  
      MW_TFT_RD_HIGH;  
      MW_TFT_CS_HIGH;  
      return data;  
 }  
 uint16_t MW_TFT_ReadRegister(uint16_t addr)  
 {  
      MW_TFT_WriteCommand(addr);  
      return MW_TFT_ReadData();  
 }  
 void MW_TFT_WriteRegister(uint16_t addr, uint16_t data)  
 {  
      MW_TFT_WriteCommand(addr);  
      MW_TFT_WriteData(data);  
 }  
 void MW_TFT_SetWriteDirection(void)  
 {  
      GPIO_InitTypeDef GPIO_InitDef;  
      /* LCD_D0 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD0_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD0_PORT, &GPIO_InitDef);  
      /* LCD_D1 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD1_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD1_PORT, &GPIO_InitDef);  
      /* LCD_D2 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD2_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD2_PORT, &GPIO_InitDef);  
      /* LCD_D3 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD3_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD3_PORT, &GPIO_InitDef);  
      /* LCD_D4 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD4_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD4_PORT, &GPIO_InitDef);  
      /* LCD_D5 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD5_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD5_PORT, &GPIO_InitDef);  
      /* LCD_D6 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD6_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD6_PORT, &GPIO_InitDef);  
      /* LCD_D7 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD7_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD7_PORT, &GPIO_InitDef);  
 }  
 void MW_TFT_SetReadDirection(void)  
 {  
      GPIO_InitTypeDef GPIO_InitDef;  
      /* LCD_D0 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD0_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD0_PORT, &GPIO_InitDef);  
      /* LCD_D1 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD1_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD1_PORT, &GPIO_InitDef);  
      /* LCD_D2 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD2_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD2_PORT, &GPIO_InitDef);  
      /* LCD_D3 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD3_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD3_PORT, &GPIO_InitDef);  
      /* LCD_D4 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD4_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD4_PORT, &GPIO_InitDef);  
      /* LCD_D5 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD5_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD5_PORT, &GPIO_InitDef);  
      /* LCD_D6 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD6_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD6_PORT, &GPIO_InitDef);  
      /* LCD_D7 */  
      GPIO_InitDef.GPIO_Pin = MW_TFT_LCD7_PIN;  
      GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;  
      GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;  
      GPIO_InitDef.GPIO_OType = GPIO_OType_PP;  
      GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;  
      GPIO_Init(MW_TFT_LCD7_PORT, &GPIO_InitDef);  
 }  
 void MW_TFT_Write8(uint8_t data)  
 {  
      if( data & 0x80 ) MW_TFT_LCD7_HIGH;  
      else MW_TFT_LCD7_LOW;  
      if( data & 0x40 ) MW_TFT_LCD6_HIGH;  
      else MW_TFT_LCD6_LOW;  
      if( data & 0x20 ) MW_TFT_LCD5_HIGH;  
      else MW_TFT_LCD5_LOW;  
      if( data & 0x10 ) MW_TFT_LCD4_HIGH;  
      else MW_TFT_LCD4_LOW;  
      if( data & 0x08 ) MW_TFT_LCD3_HIGH;  
      else MW_TFT_LCD3_LOW;  
      if( data & 0x04 ) MW_TFT_LCD2_HIGH;  
      else MW_TFT_LCD2_LOW;  
      if( data & 0x02 ) MW_TFT_LCD1_HIGH;  
      else MW_TFT_LCD1_LOW;  
      if( data & 0x01 ) MW_TFT_LCD0_HIGH;  
      else MW_TFT_LCD0_LOW;  
 }  
 uint8_t MW_TFT_Read8(void)  
 {  
      uint8_t data = 0;  
      if( MW_TFT_LCD7_STATE ) data |= 0x80;  
      if( MW_TFT_LCD6_STATE ) data |= 0x40;  
      if( MW_TFT_LCD5_STATE ) data |= 0x20;  
      if( MW_TFT_LCD4_STATE ) data |= 0x10;  
      if( MW_TFT_LCD3_STATE ) data |= 0x08;  
      if( MW_TFT_LCD2_STATE ) data |= 0x04;  
      if( MW_TFT_LCD1_STATE ) data |= 0x02;  
      if( MW_TFT_LCD0_STATE ) data |= 0x01;  
      return data;  
 }  

Display Initialisation

I find the biggest issue with any TFT display module is initialisation. There are normally a whole host of registers within the controller which need setting up before you can start writing to the display. Now I am not a big fan of datasheets. They are a necessary evil in my opinion. Some times you have no choice. But if I can figure things out without having to refer to a couple of thousand pages a broken English then all the better.

With that in mind I did a quick search for the controller supposedly fitted to the module, hoping to find example code or maybe an Arduino sketch which I could extract the initialisation sequence from. To cut a long story short I ported sequences from numerous sources trying to get this module working to no avail. Rather worryingly it was looking like I may have to start referring to the datasheet for help after all.

Then by chance I stumbled upon an Arduino sketch which had a simple driver check built in. It turns out the first register (index 0) in these controllers often contains a controller ID. Reading this register resulted in the ID 0x7783 being returned. Now from previous hunting around I know a common controller used on these cheap TFT display modules is the ST7783. Surely it couldn’t be that simple right? Well turns out it was that simple. Armed with this information I found a sample initialisation sequence and surprise surprise the display now initialises perfectly.

 void MW_TFT_SendInitSequence2(void)  
 {  
      MW_TFT_WriteRegister(0x00FF, 0x0001);  
      MW_TFT_WriteRegister(0x00F3, 0x0008);  
      MW_TFT_WriteCommand(0x00F3);  
      MW_TFT_WriteRegister(0x0001, 0x0100);  
      MW_TFT_WriteRegister(0x0002, 0x0700);  
      MW_TFT_WriteRegister(0x0003, 0x1030);  
      MW_TFT_WriteRegister(0x0008, 0x0302);  
      MW_TFT_WriteRegister(0x0009, 0x0000);  
      MW_TFT_WriteRegister(0x0010, 0x0000);  
      MW_TFT_WriteRegister(0x0011, 0x0007);  
      MW_TFT_WriteRegister(0x0012, 0x0000);  
      MW_TFT_WriteRegister(0x0013, 0x0000);  
      Delay(1000);  
      MW_TFT_WriteRegister(0x0010, 0x14B0);  
      Delay(500);  
      MW_TFT_WriteRegister(0x0011, 0x0007);  
      Delay(500);  
      MW_TFT_WriteRegister(0x0012, 0x008E);  
      MW_TFT_WriteRegister(0x0013, 0x0C00);  
      MW_TFT_WriteRegister(0x0029, 0x0015);  
      Delay(500);  
      MW_TFT_WriteRegister(0x0030, 0x0000);  
      MW_TFT_WriteRegister(0x0031, 0x0107);  
      MW_TFT_WriteRegister(0x0032, 0x0000);  
      MW_TFT_WriteRegister(0x0035, 0x0203);  
      MW_TFT_WriteRegister(0x0036, 0x0402);  
      MW_TFT_WriteRegister(0x0037, 0x0000);  
      MW_TFT_WriteRegister(0x0038, 0x0207);  
      MW_TFT_WriteRegister(0x0039, 0x0000);  
      MW_TFT_WriteRegister(0x003C, 0x0203);  
      MW_TFT_WriteRegister(0x003D, 0x0403);  
      MW_TFT_WriteRegister(0x0050, 0x0000);  
      MW_TFT_WriteRegister(0x0051, st7783Properties.width - 1);  
      MW_TFT_WriteRegister(0x0052, 0X0000);  
      MW_TFT_WriteRegister(0x0053, st7783Properties.height - 1);  
      MW_TFT_WriteRegister(0x0060, 0xa700);  
      MW_TFT_WriteRegister(0x0061, 0x0001);  
      MW_TFT_WriteRegister(0x0090, 0X0029);  
      MW_TFT_WriteRegister(0x0007, 0x0133);  
      Delay(500);  
      MW_TFT_WriteCommand(0x0022);  
 }  

So the moral of this story don’t always believe everything you read especially on eBay.

1 thought on “Having fun with TFT displays

Leave a comment