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

main.cpp

Committer:
hudakz
Date:
2020-09-25
Revision:
1:47c996032a9e
Parent:
0:fa952828e34c

File content as of revision 1:47c996032a9e:


   /* 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;
char                orient = 0;

/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
//I2S_HandleTypeDef hi2s3;
//PCD_HandleTypeDef   hpcd_USB_OTG_FS;
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);
//static void         MX_I2S3_Init(void);
//static void         MX_USB_OTG_FS_PCD_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 * 65536) + (g * 256) + 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();

    //    MX_I2S3_Init();
    //    MX_USB_OTG_FS_PCD_Init();
    /* USER CODE BEGIN 2 */
    //    PinName  dataPins[] = { PD_14, PD_15, PD_0, PD_1, PE_7, PE_8, PE_9, PE_10 };    // FSMC [D0:D7] -> LCD [D0:D7]
    // LCD_RST :    PB_12   -> LCD_RST
    // FSMC_NE1:    PD_7    -> 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 = 320, LCDSIZE_Y = 480
    myLCD->set_orientation(orient);
    myLCD->background(backgroundcolor);                                     /* set background to black */
    myLCD->foreground(foregroundcolor);                                     /* set chars to white */
    printf("\n\nSystem Core Clock = %.3f MHZ\r\n", (float)SystemCoreClock / 1000000);
    tmr.start();

    printf("Target: %s\n", MBED_STRINGIFY(TARGET_NAME));

    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {

        myLCD->set_orientation((++orient) % 4);
        //myLCD->set_orientation(1);

        /*
      * myLCD->set_orientation(2);
      */
        myLCD->cls();                                                       /* clear the screen */
        myLCD->locate(0, 30);
        myLCD->printf("Display ID: %.8X\r\n", myLCD->tftID);
        printf("Display ID: %.8X\r\n", myLCD->tftID);

        /* mem 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);                              /* write line */
        }

        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 ? "FAIL" : "PASS");

        ThisThread::sleep_for(2000);
        myLCD->cls();
        //myLCD->set_font((unsigned char*)Arial12x12, 32, 127, false);        /* variable width disabled */
        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");

        ThisThread::sleep_for(2000);
        tmr.reset();
        myLCD->cls();
        time1 = tmr.read_us();
        myLCD->locate(2, 55);
        myLCD->printf("cls: %.3fms", (float)time1 / 1000);
        printf("cls: %.3fms\r\n", (float)time1 / 1000);

        ThisThread::sleep_for(2000);
        myLCD->cls();
        tmr.reset();

        //draw some graphics ;
        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, 55);
        myLCD->set_font((unsigned char*)Arial12x12);
        myLCD->printf("plot: %.3fms", (float)time1 / 1000);
        printf("plot: %.3fms\r\n", (float)time1 / 1000);

        ThisThread::sleep_for(2000);
        myLCD->cls();
        tmr.reset();

        Bitmap_s    pic = { 64, // XSize
            64, // YSize
            8, // Bytes in Line
            burp // Pointer to picture data
        };

        myLCD->Bitmap_BW(pic, myLCD->width() - 64, 0);
        time1 = tmr.read_us();
        myLCD->locate(2, 55);
        myLCD->printf("bmp: %.3fms", (float)time1 / 1000);
        printf("bmp: %.3fms\r\n", (float)time1 / 1000);

        ThisThread::sleep_for(2000);
        myLCD->cls();
        myLCD->set_font((unsigned char*)Arial43x48_digits, 46, 58, false);    /* only numbers, variable-width disabled */
        tmr.reset();

        myLCD->locate(0, 0);
        myLCD->printf("%d", 12345);
        time1 = tmr.read_us();
        myLCD->locate(2, 55);
        myLCD->set_font((unsigned char*)Arial12x12);
        myLCD->printf("Big Font: %.3fms", (float)time1 / 1000);
        printf("Big Font: %.3fms\r\n", (float)time1 / 1000);

        ThisThread::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);
        }
        myLCD->copy_to_lcd();
        time1 = tmr.read_us();

        ThisThread::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);
        }
        myLCD->copy_to_lcd();
        time2 = tmr.read_us();

        myLCD->cls();
        myLCD->locate(2, 55);
        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);

        ThisThread::sleep_for(2000);
        /* scroll test, only for TFT */
        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());
        ThisThread::sleep_for(1000);
        myLCD->scroll(1);                   /* up 1 */
        ThisThread::sleep_for(1000);
        myLCD->scroll(0);                   /* center */
        ThisThread::sleep_for(1000);
        myLCD->scroll(myLCD->sizeY() - 1);  /* down 1 */
        ThisThread::sleep_for(1000);
        myLCD->scroll(myLCD->sizeY());      /* same as 0, center */
        ThisThread::sleep_for(1000);
        myLCD->scroll(myLCD->sizeY() >> 1); /* half screen */
        ThisThread::sleep_for(1000);
        myLCD->scrollreset();               /* center */
        ThisThread::sleep_for(1000);
        for (unsigned short i = 1; i <= myLCD->sizeY(); i++) {
            myLCD->scroll(i);
            ThisThread::sleep_for(2);
        }

        ThisThread::sleep_for(2000);
        /* color inversion */
        for (unsigned short i = 0; i <= 8; i++) {
            myLCD->invert(i & 1);
            ThisThread::sleep_for(200);
        }

        ThisThread::sleep_for(2000);
        /* bmp 16bit 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, 55);
        myLCD->set_font((unsigned char*)Arial12x12);
        myLCD->printf("Bmp speed: %.3fms", (float)time1 / 1000);
        printf("Bmp speed: %.3fms\r\n", (float)time1 / 1000);

        ThisThread::sleep_for(2000);
        for (int i = 0; i < 3000; 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));
        }

        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");
        }

        for (unsigned int i = 0; i < 200000; 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));
        }

        for (int i = 0; i < 1000; 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->rect(x1, y1, x2, y2, RGB(r, g, b));
            myLCD->fillrect(x1, y1, x2, y2, RGB(r, g, b));
        }

//        for (int i = 0; i < 1500; i++) {
//            int    x1 = rand() % myLCD->width();
//            int    y1 = rand() % myLCD->height();

//            int    rad = rand() % myLCD->height() / 2;

//            int    r = rand() % 0xff;
//            int    g = rand() % 0xff;
//            int    b = rand() % 0xff;

//            myLCD->circle(x1, y1, rad, RGB(r, g, b));
//            myLCD->fillcircle(x1, y1, rad, 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();
    }

    //  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
    //  PeriphClkInitStruct.PLLI2S.PLLI2SN = 316;
    //  PeriphClkInitStruct.PLLI2S.PLLI2SR = 7;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
        Error_Handler();
    }
}

/**
* @brief I2S3 Initialization Function
* @param None
* @retval None
*/
//static void MX_I2S3_Init(void)
//{
//    /* USER CODE BEGIN I2S3_Init 0 */
//    /* USER CODE END I2S3_Init 0 */
//    /* USER CODE BEGIN I2S3_Init 1 */
//    /* USER CODE END I2S3_Init 1 */
//    hi2s3.Instance = SPI3;
//    hi2s3.Init.Mode = I2S_MODE_MASTER_TX;
//    hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
//    hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B;
//    hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
//    hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_44K;
//    hi2s3.Init.CPOL = I2S_CPOL_LOW;
//    hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
//    hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
//    if (HAL_I2S_Init(&hi2s3) != HAL_OK) {
//        Error_Handler();
//    }
//    /* USER CODE BEGIN I2S3_Init 2 */
//    /* USER CODE END I2S3_Init 2 */
//}
/**
* @brief USB_OTG_FS Initialization Function
* @param None
* @retval None
*/
//static void MX_USB_OTG_FS_PCD_Init(void)
//{
//    /* USER CODE BEGIN USB_OTG_FS_Init 0 */
//    /* USER CODE END USB_OTG_FS_Init 0 */
//    /* USER CODE BEGIN USB_OTG_FS_Init 1 */
//    /* USER CODE END USB_OTG_FS_Init 1 */
//    hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
//    hpcd_USB_OTG_FS.Init.dev_endpoints = 4;
//    hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
//    hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
//    hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
//    hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
//    hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
//    hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
//    hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
//    hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
//    if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK) {
//        Error_Handler();
//    }
//    /* USER CODE BEGIN USB_OTG_FS_Init 2 */
//    /* USER CODE END USB_OTG_FS_Init 2 */
//}

/**
* @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****/