Use STM32CubeMX to configure FSMC module to drive LCD screen (based on punctual atomic process)

Foreword

In the process of learning STM32, I just learned the LCD screen. I use STM32F103ZET6, and the screen is punctual and atomic. But I encountered a lot of problems when lighting up the LCD screen in my own new project. After solving it, share it here, hoping to help friends who encounter this confusion.
If you want to quickly drive the LCD screen, please jump directly to CubeMX configuration

A brief introduction to FSMC

The full name of FSMC is Flexible Static Memory Controller (FSMC), which is an interface of single-chip microcomputer, which can connect synchronous or asynchronous memory, 16-bit PC memory card and LCD module. All external memories connected by FSMC share addresses, data and control signals, but have their own chip select signals, so FSMC can only access one external device at a time.
FSMC interface is used to drive external memory, and can also be used to drive TFT LCD with 8080 interface.
From the perspective of FSMC, the external memory is divided into 4 fixed-size storage areas, each with a size of 256MB, and the functions of each area are described as follows.

Bank1 can connect up to 4 NOR Flash or PSRAM/SRAM storage devices, because it is divided into 4 sub-areas (subbank), each sub-area has a capacity of 64MB, and has a dedicated chip select signal. These sub-areas are suitable for connecting TFT LCD.
Bank 1 -NOR/PSRAM 1, chip select signal NE1.
Bank 1 -NOR/PSRAM 2, chip select signal NE2.
Bank 1 -NOR/PSRAM 3, chip select signal NE3. (on the development board to connect to external SRAM)
Bank 1 -NOR/PSRAM 4, chip select signal NE4. (The development board is used to connect to the TFT LCD)

Bank2 and Bank3 are used to access NAND Flash memory, and each storage area is connected to a device.

Bank4 is used to connect PC card devices.

A brief introduction to TFT LCD

TFT LCD is a thin film transistor (Thin Film Transistor) LCD, which has the advantages of low radiation, low power consumption, and full color. It is a display device commonly used in various electronic devices. TFT LCD (hereinafter referred to as LCD) usually uses a standard 8080 parallel port, which has 16-bit data lines and several control lines. The function of the 8080 parallel interface line of TFTLCD is shown in the figure below.

CubeMX configuration

FSMC sub-area configuration

LCD schematic on the development board

It can also be seen from the schematic diagram that the sub-area should be configured as NE4, and the corresponding pin of LCD_BL should be set to output mode. (I correspond to PB0 here)

FSMC related parameter configuration

RCC clock configuration

When configuring the clock tree, configure HCLK to 72MHz (maximum) by default.

LCD driver rewriting

Because the LCD process corresponding to each development board will be different, and in order to ensure that the LCDs driven by different chips can be lit. So please download the TFT LCDHAL library history of the STM32F103 minimum system board in the punctual atomic data download center to rewrite.

Import LCD related files into the newly created routine.

Header file replacement

(1) The lcd.c file contains lcd.h, font.h and main.h
(2) The lcd.h file only needs to include main.h

Replacement of basic data types

Use the shortcut key CTRL + F to come to the replacement interface. Replace u8, u16 and u32 with uint8_t, uint16_t and uint32_t in both files.

Then replace vuint16_t with __IO uint16_t.

Replacement of functions

Use the above method to replace all delay_ms and delay_us with HAL_Delay (the actual measurement does not affect the program operation).

Comment the repeated code in lcd.c

This link is said to be repeated code comments, in fact, the main purpose is to facilitate the porting of programs. Because fsmc.
The file will have the same initialization section as the original lcd.c file.
1. Annotate the initial fsmc initialization part of the LCD_Init() function
That is to comment all the codes in the following parts (this part has been executed in the MX_FSMC_Init() function).

// GPIO_InitTypeDef GPIO_Initure;
// FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;
// FSMC_NORSRAM_TimingTypeDef FSMC_WriteTim;

// __HAL_RCC_GPIOB_CLK_ENABLE(); // Turn on the GPIOB clock
// GPIO_Initure.Pin=GPIO_PIN_0; //PB0, backlight control
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //Push-pull output
// GPIO_Initure.Pull=GPIO_PULLUP; //Pull up
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//High speed
// HAL_GPIO_Init(GPIOB, &GPIO_Initure);

//TFTSRAM_Handler.Instance=FSMC_NORSRAM_DEVICE;
//TFTSRAM_Handler.Extended=FSMC_NORSRAM_EXTENDED_DEVICE;

// TFTSRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK4; //Use NE4
// TFTSRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE; //Address/data lines are not multiplexed
// TFTSRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM; //SRAM
// TFTSRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16; //16-bit data width
// TFTSRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE; // Whether to enable burst access, only valid for synchronous burst memory, not used here
// TFTSRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW; //The polarity of the waiting signal is only useful in burst mode access
// TFTSRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS; //Whether the memory enables NWAIT one clock cycle before the wait cycle or during the wait cycle
// TFTSRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE; //Memory write enable
// TFTSRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE; //Wait enable bit, not used here
// TFTSRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_ENABLE; //Reading and writing use different timings
// TFTSRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE; // Whether to enable the waiting signal in synchronous transfer mode, which is not used here
// TFTSRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE; //Disable burst write
//
// //FMC read timing control register
// FSMC_ReadWriteTim.AddressSetupTime=0x06; //Address setup time (ADDSET) is 7 HCLK 13.8ns*7=96.6ns
// FSMC_ReadWriteTim.AddressHoldTime=0;
// FSMC_ReadWriteTim.DataSetupTime=26; //Data saving time is 27 HCLK =13.8*27=372.6ns
// FSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A;//Mode A
// //FMC write timing control register
// FSMC_WriteTim.BusTurnAroundDuration=0; //The duration of the bus turnaround phase is 0. If this variable is not assigned a value, it will be automatically changed to 4 inexplicably. cause the program to run normally
// FSMC_WriteTim.AddressSetupTime=3; //Address setup time (ADDSET) is 4 HCLK =55.2ns
// FSMC_WriteTim.AddressHoldTime=0;
// FSMC_WriteTim.DataSetupTime=0x06; //Data saving time is 13.8ns*7 HCLK=96.6ns
// FSMC_WriteTim.AccessMode=FSMC_ACCESS_MODE_A; //Mode A
// HAL_SRAM_Init( &TFTSRAM_Handler, &FSMC_ReadWriteTim, &FSMC_WriteTim);

// HAL_Delay(50); // delay 50 ms

2. Comment function void HAL_SRAM_MspInit(SRAM_HandleTypeDef *hsram)
This function is to configure GPIO for fsmc-related pins.

Finishing work

1. Comment the code at the end of the LCD_Init() function (as follows).

LCD_LED = 1;

Change the code to turn on the backlight (make the screen bright).

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,1);

2. Comment the printf code line in the LCD_Init() function as follows.

printf(" LCD ID:%x\r\\
", lcddev.id); //print LCD ID

Because the original process uses serial port 1 for serial port debugging, if serial port 1 is not initialized, will always be stuck here during runtime.
3. Finally, include the lcd.h header file in the main function, and add the following code to run it.

LCD_Init();
  LCD_ShowString(100,100,100,24,24,"114514");

4. When you want to transplant the LCD program in the future, copy the relevant files to the corresponding project, including the header file, and call the initialization function to use it. save time!