3.5" inch TFT LCD Display Module 480X320 driven with FSMC.

TFT LCD Display Module 480X320 driven with FSMC

I have recently bought a 3.5" inch TFT LCD Touch Screen Display Module 480X320 with a www.mcufriend.com label on the back side. The display was equipped with an 8bit parallel interface. First I decided to test it with the UniGraphic library using the BUS_8 protocol. The display was very slow but improved when I switched to the PAR_8 protocol. Because I heard about the possibility to use a Flexible Static Memory Controller (FSMC), built into some STM MCU's, to drive LCD's (read/write to LCD's memory rather than to an external SRAM) I thought it would be a fun to try it out.

https://os.mbed.com/media/uploads/hudakz/lcd_3.5_tft_480x320_mcufriend_front.png

Below is the brief story of what I did:

  • Selected FSMC in the Connectivity category and configured it as below: https://os.mbed.com/media/uploads/hudakz/arch_max_fsmc_conf.png
  • Let the STM32CubeIDE generate the code (files).
  • Created a new program for the Seeed Arch Max target in the Mbed Online Compiler by selecting a mbed os blinky template.
  • Replaced the main.cpp with the main.c content of the STM32CubeIDE project.
  • Copy & Pasted the other files with codes from the STM32CubeIDE project to the online compiler project.
  • Renamed and modified:
    "stm32f4xx_it.h" to "stm32f4xx_it_msp.h"
    "stm32f4xx_it.c" to "stm32f4xx_it_msp.c"
  • Added the UniGraphic library to the online compiler project.
  • Extended the UniGraphic library with a FSMC_8 protocol and replaced the TFT::set_orientation(int orient) function with the one used by mcufriend for arduino.
  • Modified the main.cpp as needed.
https://os.mbed.com/media/uploads/hudakz/stm32f407vet6_st-link03.pnghttps://os.mbed.com/media/uploads/hudakz/lcd_3.5_tft_480x320_mcufriend_back.png


Wiring

STM32F407VETFT LCD module
+3.3V3V3
GNDGND
PB_12LCD_RST
GNDLCD_CS
PD_13 (RS)LCD_RS
PD_5 (WR)LCD_WR
PD_4 (RD)LCD_RD
PD_14 (DB00)LCD_D0
PD_15 (DB01)LCD_D1
PD_0 (DB02)LCD_D2
PD_1 (DB03)LCD_D3
PE_7 (DB04)LCD_D4
PE_8 (DB05)LCD_D5
PE_9 (DB06)LCD_D6
PE_10 (DB07)LCD_D7



Results
Execution times
Used protocolBUS_8FSMC_8
Operation \ Timemsms
Clear2283.98038.454
Plot192.06611.365
8bit BMP63.80541.338
Large Font163.8727.895
Sparce pixels2072.265/1458.05174.107/52.168
16bit BMP2288.58959.904
Revision:
0:fa952828e34c
Child:
1:47c996032a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun May 10 10:44:31 2020 +0000
@@ -0,0 +1,520 @@
+
+   /* USER CODE BEGIN Header */
+
+/**
+******************************************************************************
+* @file           : main.c
+* @brief          : Main program body
+******************************************************************************
+* @attention
+*
+* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
+* All rights reserved.</center></h2>
+*
+* This software component is licensed by ST under BSD 3-Clause license,
+* the "License"; You may not use this file except in compliance with the
+* License. You may obtain a copy of the License at:
+*                        opensource.org/licenses/BSD-3-Clause
+*
+******************************************************************************
+*/
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* Private includes ----------------------------------------------------------*/
+
+/* USER CODE BEGIN Includes */
+#ifdef __MBED__
+#include "mbed.h"
+#include "string"
+#include "Arial12x12.h"
+#include "Arial24x23.h"
+#include "Terminal6x8.h"
+#include "Arial43x48_digits.h"
+#include "pict.h"
+#include "pavement_48x34.h"
+#include "TFT_MIPI.h"
+#endif
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN PTD */
+/* USER CODE END PTD */
+/* Private define ------------------------------------------------------------*/
+/* USER CODE BEGIN PD */
+TFT_MIPI * myLCD;
+Timer               tmr;
+int                 time1;
+int                 time2;
+unsigned short      backgroundColor = Black;
+unsigned short      foregroundColor = White;
+uint8_t             orient = 0;
+
+/* USER CODE END PD */
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN PM */
+/* USER CODE END PM */
+/* Private variables ---------------------------------------------------------*/
+SRAM_HandleTypeDef  hsram1;
+
+/* USER CODE BEGIN PV */
+/* USER CODE END PV */
+/* Private function prototypes -----------------------------------------------*/
+//static void         SystemClock_Config(void);
+static void         MX_GPIO_Init(void);
+static void         MX_FSMC_Init(void);
+/* USER CODE BEGIN PFP */
+/* USER CODE END PFP */
+/* Private user code ---------------------------------------------------------*/
+
+/* USER CODE BEGIN 0 */
+uint16_t RGB(uint16_t r, uint16_t g, uint16_t b)
+{
+    return(r << 16) | (g << 8) | b;
+}
+
+/* USER CODE END 0 */
+
+/**
+* @brief  The application entry point.
+* @retval int
+*/
+int main(void)
+{
+    /* USER CODE BEGIN 1 */
+
+    /* USER CODE END 1 */
+    /* MCU Configuration--------------------------------------------------------*/
+    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
+    //    HAL_Init();
+    /* USER CODE BEGIN Init */
+    /* USER CODE END Init */
+    /* Configure the system clock */
+    //    SystemClock_Config();
+    /* USER CODE BEGIN SysInit */
+    /* USER CODE END SysInit */
+    /* Initialize all configured peripherals */
+    MX_GPIO_Init();
+    MX_FSMC_Init();
+    /* USER CODE BEGIN 2 */
+    // LCD_RST :    PB_12   -> LCD_RST
+    // FSMC_NE1:    not used
+    //              GND     -> LCD_CS
+    // FSMC_A18:    PD_13   -> LCD_RS
+    // FSMC_NWE:    PD_5    -> LCD_WR
+    // FSMC_NOE:    PD_4    -> LCD_RD
+    // FSMC_D0 :    PD_14   -> LCD_D0
+    // FSMC_D1 :    PD_15   -> LCD_D1
+    // FSMC_D2 :    PD_0    -> LCD_D2
+    // FSMC_D3 :    PD_1    -> LCD_D3
+    // FSMC_D4 :    PE_7    -> LCD_D4
+    // FSMC_D5 :    PE_8    -> LCD_D5
+    // FSMC_D6 :    PE_9    -> LCD_D6
+    // FSMC_D7 :    PE_10   -> LCD_D7
+    //TFT_MIPI myLCD(BUS_8, buspins, PD_7, PB_12, PD_13, PD_5, PD_4, "myLCD", 320, 480); // CS, reset, DC, WR, RD
+    //                                      CS  , RST,   DC,    WR,   RD
+    myLCD = new TFT_MIPI(FSMC_8, PB_12, "myLCD", 320, 480); // Protocol, Pin->LCD_RST, name , LCDSIZE_X, LCDSIZE_Y
+    myLCD->set_orientation(1);
+    myLCD->background(backgroundColor);
+    myLCD->foreground(foregroundColor);
+    printf("\n\nSystem Core Clock = %.3f MHZ\r\n", (float)SystemCoreClock / 1000000);
+    printf("Target: %s\n", MBED_STRINGIFY(TARGET_NAME));
+
+    myLCD->cls();
+    myLCD->locate(0, 30);
+    myLCD->printf("Display ID: %.8X\r\n", myLCD->tftID);
+    printf("Display ID: %.8X\r\n", myLCD->tftID);
+
+    tmr.start();
+    /* USER CODE END 2 */
+    /* Infinite loop */
+    /* USER CODE BEGIN WHILE */
+    while (1) {
+        myLCD->set_orientation((++orient) % 4);
+
+        /* LCD memory write/read test */
+        unsigned short  readback;
+        unsigned short  colorstep = (0x10000 / myLCD->width());
+
+        for (unsigned short i = 0; i < myLCD->width(); i++) {
+            myLCD->pixel(i, 0, i * colorstep);
+        }
+
+        bool    readerror = false;
+
+        for (unsigned short i = 0; i < myLCD->width(); i++) {
+
+            /* verify line */
+            readback = myLCD->pixelread(i, 0);
+            if (readback != i * colorstep) {
+                readerror = true;
+                printf("pix %.4X readback %.4X\r\n", i * colorstep, readback);
+            }
+        }
+
+        myLCD->locate(0, 10);
+        myLCD->printf("Pixelread test %s\r\n", readerror ? "FAILED" : "PASSED");
+        thread_sleep_for(2000);
+
+        /* Auto carriage return test */
+        myLCD->cls();
+        myLCD->set_font((unsigned char*)Arial12x12);
+        myLCD->locate(0, 0);
+        myLCD->printf("Display Test\r\nSome text just to see if auto carriage return works correctly");
+        printf("  Display Test \r\n");
+        thread_sleep_for(2000);
+
+        /* Clear screen test */
+        tmr.reset();
+        myLCD->cls();
+        time1 = tmr.read_us();
+        myLCD->locate(2, 75);
+        myLCD->printf("cls: %.3fms", (float)time1 / 1000);
+        printf("cls: %.3fms\r\n", (float)time1 / 1000);
+        thread_sleep_for(2000);
+
+        myLCD->cls();
+        tmr.reset();
+
+        /* Simple graphics test */
+        myLCD->set_font((unsigned char*)Arial24x23);
+        myLCD->locate(10, 10);
+        myLCD->printf("Test");
+        myLCD->line(0, 0, myLCD->width() - 1, 0, foregroundColor);
+        myLCD->line(0, 0, 0, myLCD->height() - 1, foregroundColor);
+        myLCD->line(0, 0, myLCD->width() - 1, myLCD->height() - 1, foregroundColor);
+        myLCD->rect(10, 30, 50, 40, foregroundColor);
+        myLCD->fillrect(60, 30, 100, 40, foregroundColor);
+        myLCD->circle(150, 32, 30, foregroundColor);
+        myLCD->fillcircle(140, 20, 10, foregroundColor);
+
+        double  s;
+
+        for (unsigned short i = 0; i < myLCD->width(); i++) {
+            s = 10 * sin((long double)i / 10);
+            myLCD->pixel(i, 40 + (int)s, foregroundColor);
+        }
+
+        time1 = tmr.read_us();
+        myLCD->locate(2, 75);
+        myLCD->set_font((unsigned char*)Arial12x12);
+        myLCD->printf("plot: %.3fms", (float)time1 / 1000);
+        printf("plot: %.3fms\r\n", (float)time1 / 1000);
+        thread_sleep_for(2000);
+
+        /* Bitmap test */
+        Bitmap_s    bmp =
+        {
+            64,     // XSize
+            64,     // YSize
+            8,      // Bytes in Line
+            burp    // Pointer to picture data
+        };
+
+        tmr.reset();
+        myLCD->cls();
+        myLCD->Bitmap_BW(bmp, myLCD->width() - 64, 0);
+        time1 = tmr.read_us();
+        myLCD->locate(2, 75);
+        myLCD->printf("bmp: %.3fms", (float)time1 / 1000);
+        printf("bmp: %.3fms\r\n", (float)time1 / 1000);
+        thread_sleep_for(2000);
+
+        /* Large font test */
+        myLCD->cls();
+        myLCD->set_font((unsigned char*)Arial43x48_digits, 46, 58, false);    /* only digits, variable-width disabled */
+        tmr.reset();
+        myLCD->locate(0, 0);
+        myLCD->printf("%d", 12345);
+        time1 = tmr.read_us();
+        myLCD->locate(2, 75);
+        myLCD->set_font((unsigned char*)Arial12x12);
+        myLCD->printf("Large Font: %.3fms", (float)time1 / 1000);
+        printf("Large Font: %.3fms\r\n", (float)time1 / 1000);
+        thread_sleep_for(2000);
+
+        /* Sparse pixels test */
+        myLCD->cls();
+        myLCD->FastWindow(false);
+        tmr.reset();
+        for (unsigned int i = 0; i < 20000; i++) {
+            myLCD->pixel((i + (i * 89)) % myLCD->width(), (i + (i * 61)) % myLCD->height(), White);
+        }
+        time1 = tmr.read_us();
+        thread_sleep_for(2000);
+
+        myLCD->cls();
+        myLCD->FastWindow(true);
+        tmr.reset();
+        for (unsigned int i = 0; i < 20000; i++) {
+            myLCD->pixel((i + (i * 89)) % myLCD->width(), (i + (i * 61)) % myLCD->height(), White);
+        }
+        time2 = tmr.read_us();
+
+        myLCD->cls();
+        myLCD->locate(2, 75);
+        myLCD->printf("std:%.3fms fastw:%.3fms", (float)time1 / 1000, (float)time2 / 1000);
+        printf("std: %.3fms fastw: %.3fms\r\n", (float)time1 / 1000, (float)time2 / 1000);
+        thread_sleep_for(2000);
+
+        /* Scroll test */
+        myLCD->cls();
+        myLCD->set_font((unsigned char*)Arial24x23);
+        myLCD->locate(2, 10);
+        myLCD->printf("Scrolling");
+        myLCD->rect(0, 0, myLCD->width() - 1, myLCD->height() - 1, White);
+        myLCD->rect(1, 1, myLCD->width() - 2, myLCD->height() - 2, Blue);
+        myLCD->setscrollarea(0, myLCD->sizeY());
+        thread_sleep_for(1000);
+        myLCD->scroll(1);                   /* up 1 */
+        thread_sleep_for(1000);
+        myLCD->scroll(0);                   /* center */
+        thread_sleep_for(1000);
+        myLCD->scroll(myLCD->sizeY() - 1);  /* down 1 */
+        thread_sleep_for(1000);
+        myLCD->scroll(myLCD->sizeY());      /* same as 0, center */
+        thread_sleep_for(1000);
+        myLCD->scroll(myLCD->sizeY() >> 1); /* half screen */
+        thread_sleep_for(1000);
+        myLCD->scrollreset();               /* center */
+        thread_sleep_for(1000);
+        for (unsigned short i = 1; i <= myLCD->sizeY(); i++) {
+            myLCD->scroll(i);
+            thread_sleep_for(2);
+        }
+        thread_sleep_for(2000);
+
+        /* Color inversion */
+        for (unsigned short i = 0; i <= 8; i++) {
+            myLCD->invert(i & 1);
+            thread_sleep_for(200);
+        }
+        thread_sleep_for(2000);
+
+        /* 16bit bitmap test */
+        myLCD->cls();
+        tmr.reset();
+        for (int y = 0; y < myLCD->height(); y += 34) {
+            for (int x = 0; x < myLCD->width(); x += 48)
+                myLCD->Bitmap(x, y, 48, 34, (unsigned char*)pavement_48x34);
+        }
+        time1 = tmr.read_us();
+        myLCD->locate(2, 75);
+        myLCD->set_font((unsigned char*)Arial12x12);
+        myLCD->printf("16bit bitmap speed: %.3fms", (float)time1 / 1000);
+        printf("16bit bitmap speed: %.3fms\r\n", (float)time1 / 1000);
+        thread_sleep_for(2000);
+
+        /* Drawing random lines */
+        for (int i = 0; i < 6000; i++) {
+            int    x1 = rand() % myLCD->width();
+            int    y1 = rand() % myLCD->height();
+
+            int    x2 = rand() % myLCD->width();
+            int    y2 = rand() % myLCD->height();
+
+            int    r = rand() % 0xff;
+            int    g = rand() % 0xff;
+            int    b = rand() % 0xff;
+
+            myLCD->line(x1, y1, x2, y2, RGB(r, g, b));
+        }
+
+        /* Drawing text at random position */
+        for (int i = 0; i < 3000; i++) {
+            int    x1 = rand() % myLCD->width();
+            int    y1 = rand() % myLCD->height();
+
+            int    r = rand() % 0xff;
+            int    g = rand() % 0xff;
+            int    b = rand() % 0xff;
+
+            myLCD->locate(x1, y1);
+            myLCD->foreground(RGB(r, g, b));
+            myLCD->printf("Hello World");
+        }
+
+        /* Drawing random pixels */
+        for (int i = 0; i < 500000; i++) {
+            int    x1 = rand() % myLCD->width();
+            int    y1 = rand() % myLCD->height();
+
+            int    r = rand() % 0xff;
+            int    g = rand() % 0xff;
+            int    b = rand() % 0xff;
+
+            myLCD->pixel(x1, y1, RGB(r, g, b));
+        }
+
+        /* Drawing random rectangles */
+        for (int i = 0; i < 500; i++) {
+            int    x1 = rand() % myLCD->width();
+            int    y1 = rand() % myLCD->height();
+
+            int    x2 = rand() % myLCD->width();
+            int    y2 = rand() % myLCD->height();
+
+            int    r = rand() % 0xff;
+            int    g = rand() % 0xff;
+            int    b = rand() % 0xff;
+
+            myLCD->fillrect(x1, y1, x2, y2, RGB(r, g, b));
+        }
+
+        /* USER CODE END WHILE */
+        /* USER CODE BEGIN 3 */
+    }
+
+    /* USER CODE END 3 */
+}
+
+/**
+* @brief System Clock Configuration
+* @retval None
+*/
+//void SystemClock_Config(void)
+//{
+//    RCC_OscInitTypeDef          RCC_OscInitStruct = { 0 };
+//    RCC_ClkInitTypeDef          RCC_ClkInitStruct = { 0 };
+//    RCC_PeriphCLKInitTypeDef    PeriphClkInitStruct = { 0 };
+//    /** Configure the main internal regulator output voltage
+//*/
+
+//    __HAL_RCC_PWR_CLK_ENABLE();
+//    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
+
+//    /** Initializes the CPU, AHB and APB busses clocks
+//*/
+//    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+//    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+//    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+//    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+//    RCC_OscInitStruct.PLL.PLLM = 8;
+//    RCC_OscInitStruct.PLL.PLLN = 336;
+//    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+//    RCC_OscInitStruct.PLL.PLLQ = 7;
+//    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
+//        Error_Handler();
+//    }
+
+//    /** Initializes the CPU, AHB and APB busses clocks
+//*/
+//    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
+//    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+//    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+//    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+//    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+//    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
+//        Error_Handler();
+//    }
+
+//    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
+//        Error_Handler();
+//    }
+//}
+
+/**
+* @brief GPIO Initialization Function
+* @param None
+* @retval None
+*/
+static void MX_GPIO_Init(void)
+{
+    /* GPIO Ports Clock Enable */
+
+    __HAL_RCC_GPIOB_CLK_ENABLE();
+    __HAL_RCC_GPIOC_CLK_ENABLE();
+    __HAL_RCC_GPIOD_CLK_ENABLE();
+    __HAL_RCC_GPIOE_CLK_ENABLE();
+    __HAL_RCC_GPIOH_CLK_ENABLE();
+}
+
+/* FSMC initialization function */
+static void MX_FSMC_Init(void)
+{
+    /* USER CODE BEGIN FSMC_Init 0 */
+
+    /* USER CODE END FSMC_Init 0 */
+    FSMC_NORSRAM_TimingTypeDef  Timing = { 0 };
+
+    /* USER CODE BEGIN FSMC_Init 1 */
+
+    /* USER CODE END FSMC_Init 1 */
+    /** Perform the SRAM1 memory initialization sequence
+*/
+    hsram1.Instance = FSMC_NORSRAM_DEVICE;
+    hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
+
+    /* hsram1.Init */
+    hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
+    hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
+    hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
+    hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_8;
+    hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
+    hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
+    hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
+    hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
+    hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
+    hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
+    hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
+    hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
+    hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
+    hsram1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
+
+    /* Timing */
+    Timing.AddressSetupTime = 0;
+    Timing.AddressHoldTime = 0;
+    Timing.DataSetupTime = 12;
+    Timing.BusTurnAroundDuration = 0;
+    Timing.CLKDivision = 16;
+    Timing.DataLatency = 17;
+    Timing.AccessMode = FSMC_ACCESS_MODE_A;
+
+    /* ExtTiming */
+    if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK) {
+        Error_Handler();
+    }
+
+    /* USER CODE BEGIN FSMC_Init 2 */
+    /* USER CODE END FSMC_Init 2 */
+}
+
+/* USER CODE BEGIN 4 */
+/* USER CODE END 4 */
+
+/**
+* @brief  This function is executed in case of error occurrence.
+* @retval None
+*/
+void Error_Handler(void)
+{
+    /* USER CODE BEGIN Error_Handler_Debug */
+
+    printf("Error_Handler called\r\n");
+    while (true) { }
+
+    /* User can add his own implementation to report the HAL error return state */
+    /* USER CODE END Error_Handler_Debug */
+}
+
+#ifdef USE_FULL_ASSERT
+
+/**
+* @brief  Reports the name of the source file and the source line number
+*         where the assert_param error has occurred.
+* @param  file: pointer to the source file name
+* @param  line: assert_param error line source number
+* @retval None
+*/
+void assert_failed(uint8_t* file, uint32_t line)
+{
+    /* USER CODE BEGIN 6 */
+
+    /* User can add his own implementation to report the file name and line number,
+  tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
+    /* USER CODE END 6 */
+}
+#endif /* USE_FULL_ASSERT */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/