Monthly Archives: March 2015

TP-Link WDR4300 Router Recovery

My new wireless router decided to stop working at the weekend. It has been behaving rather strange for the past few days. I’ve tried power cycling in the vain hope a reboot may fix the issue. Which it didn’t. And to make things worse now it wont even boot. Pinging it gets no response either. The original firmware was replaced with DD WRT, a great Linux alternative firmware, so the first thing I did was head over to the DD WRT homepage for help. I tried the recommended 30/30/30 reset procedure which didn’t solve the issue.

Luckily there is a ton of resources out there surrounding these routers. Once such resource I found extremely useful was the OpenWRT Wiki page. Apparently the WDR4300 has an Atheros AR9344 SOC running at 560 MHz with 8MB of flash and 128 MB DRAM. It also has both a JTAG and serial programming/debug connection. The flash memory is made up of the boot loader (U-Boot), the operating firmware and the ART (which contains MAC addresses and calibration data for the wifi). It’s also the boot loaders responsibility for configuring the serial interface.

WDR4300_1With nothing to lose I begun disassembling the router to see if I could get access to this serial port and maybe diagnose the problem. After removing the aerials and the screws holding the case together I had the router disassembled. There are two unpopulated headers one 14 pin, which I assume must be the JTAG connection, and one 4 pin which turns out to be the serial connection. The four pins comprising the two supply lines and the TX and RX lines. I soldered in a pin header to the board and connected my USB to serial converter. The connection settings according to the OpenWRT Wiki page are 115200 8N1 with no flow control. Using puTTY I managed to capture the routers output during power up.

 U-Boot 1.1.4 (Apr 25 2012 - 18:29:12)  
 U-boot DB120  
 DRAM: 128 MB  
 id read 0x100000ff  
 flash size 8MB, sector count = 128  
 Flash: 8 MB  
 Using default environment  
 In: serial  
 Out: serial  
 Err: serial  
 Net: ag934x_enet_initialize...  
 No valid address in Flash. Using fixed address  
 wasp reset mask:c03300  
 WASP ----> S17 PHY *  
 : cfg1 0x7 cfg2 0x7114  
 eth0: ba:be:fa:ce:08:41  
 athrs17_reg_init: complete  
 eth0 up  
 eth0  
 Autobooting in 1 seconds  
 ## Booting image at 9f020000 ...  
 Uncompressing Kernel Image ... OK  
 Starting kernel ...  

The output shows the boot loader initialising the serial port. After initialisation the boot loader attempts to auto boot the image at address 0x9F020000. Firstly by uncompressing the kernel image and then starting it. Additional serial output follows before we encounter a raft of file system page read errors before stopping.

 start service  
 starting Architecture code for wasp  
 [ 1.960000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 1.960000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.030000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.030000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.100000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.100000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.170000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.170000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.240000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.240000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.310000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.310000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.380000] SQUASHFS error: lzma returned unexpected result 0x1  
 [ 2.380000] SQUASHFS error: Unable to read page, block c523, size dd54  
 [ 2.450000] SQUASHFS error: lzma returned unexpected result 0x1  

So it appears the firmware may have become corrupted. The obvious next step then was to try re-flashing it. After some reading around on the subject (being a complete Linux novice) I discovered the boot loader supports network transfer via the Trivial File Transfer Protocol (TFTP). So it is possible to transfer the firmware image via TFTP and re-flash it.

The first step was to download the required firmware images and other supporting software. I downloaded the DD-WRT firmware image files from here and then downloaded and installed the client side TFTP server program TFTPD32 from here.

The next step was to break execution of the boot loader in order to run tftpboot. To do this, within putty after power cycling, simply wait for the message “Autobooting in 1 second” to appear begin typing “tpl” and pressing enter until the sequence stops and the prompt “db12x>” appears. Once the prompt appeared I ran “tftpboot”. I then made a note of the expected server address “192.168.1.100” and load address “0x81000000”. I then exited tftpboot by pressing Ctrl+C.

 db12x> tftpboot  
 dup 1 speed 1000  
  Warning: no boot file name; using '6F01A8C0.img'  
 Using eth0 device  
 TFTP from server 192.168.1.100; our IP address is 192.168.1.111  
 Filename '6F01A8C0.img'.  
 Load address: 0x81000000  
 Log: *  
 TFTP error: 'Access violation' (2)  
 Starting again  

With the router connected directly via an ethernet cable and all wireless adaptors disabled I set the network adaptor to the static IP address “192.168.1.100”. I then ran TFTPD32, browsed for the folder containing the firmware images and set the server address to “192.168.1.100”.
I then ran “tftpboot” again with required load address and source firmware parameters. The new firmware then begun transferring. Upon completion the message “done” appeared and the number of bytes transferred was shown.

 db12x> tftpboot 0x81000000 factory-to-ddwrt.bin  
 Using eth0 device  
 TFTP from server 192.168.1.100; our IP address is 192.168.1.111  
 Filename 'factory-to-ddwrt.bin'.  
 Load address: 0x81000000  
 Lg: #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      #################################################################  
      ############################  
 done  
 Bytes transferred = 8126464 (7c0000 hex)  

Before the new firmware image could now be copied the destination flash the destination flash first needed to be erased. The destination address being 0x9F020000 (which we know from the initial captured output) and destination length being the size of the transferred firmware image 0x7C0000 bytes.

 db12x> erase 0x9f020000 +7c0000  
 First 0x2 last 0x7d sector size 0x10000                                                                                                 125  
 Erased 124 sectors  

Once erased the new firmware could then be copied over from the destination flash. Again the destination address being 0x9F020000, the source address being 0x81000000 and the length 0x7C0000 bytes.

 db12x> cp.b 0x81000000 0x9f020000 0x7c0000  
 Copy to Flash... write addr: 9f020000  
 done  

So that all appeared fine all that all that remained was to reboot and hopefully everything would be working.

 db12x> reset  

Much to my relief that appeared to have fixed the issue. The router now booted fine and I was able to access to the DD-WRT web interface. WDR4300_7I then went ahead and performed a web flash using the web flash firmware image file previously downloaded. I am not entirely sure this stage was necessary but some of the guides I had read previously did and I cant imagine it would do any harm doing so.

So overall a great result. Saved myself some money not having to rush out and buy a new router and learnt a little bit about Linux in the process.

 

Advertisements

Nucleo Mod Player

Found myself with a bit of free time this evening so I decided to port my mod player code over to the STM32 Nucleo F401 development board. I haven’t bothered with a display and rather than using an SD card I have converted a couple of modules (using bin2c) and included them in the code. The HWB on the Nucleo is used to move through the mod files stored in the device.

 

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.