stm32f429 tft lcd ili9341 in stock
STM32F429 has also LTDC driver for LCD like that, but this driver we will use later. For now we will use SPI for driving in serial mode and some other pins for controlling.
Remember: This library can also be used, if you are not using STM32F429 Discovery. It can be used in previous STM32F4 Discovery board. All pins can be changed in defines.h file which is included in project.
You can refer to the examples under STM32CubeF4 package to see their structure and get inspired from them to configure your files: STM32Cube_FW_F4_V1.21.0\Projects\STM32F429I-Discovery\Applications\STemWin
I am testing IlI9341 3.2 TFT LCD on stm32F4-discovery. I wired based on Discovery datasheet. I also read ILI9341 datasheet and went through all registers.
As you learn about more of your microcontroller’s peripherals and start to work with more types of sensors and actuators, you will probably want to add small displays to your projects. Previously, I wrote about creating a simple program to draw data to an SSD1331 OLED display, but while they look great, the small size and low resolution can be limiting. Fortunately, the larger (and slightly cheaper) ILI9341 TFT display module uses a nearly-identical SPI communication protocol, so this tutorial will build on that previous post by going over how to draw to a 2.2″ ILI9341 module using the STM32’s hardware SPI peripheral.
We’ll cover the basic steps of setting up the required GPIO pins, initializing the SPI peripheral, starting the display, and then finally drawing pixel colors to it. This tutorial won’t read any data from the display, so we can use the hardware peripheral’s MISO pin for other purposes and leave the TFT’s MISO pin disconnected. And as with my previous STM32 posts, example code will be provided for both the STM32F031K6 and STM32L031K6 ‘Nucleo’ boards.
There are actually multiple sets of pins mapped to the SPI1 peripheral, even on the 32-pin STM32xKx chips. I’ll use pin B3 for SCK and pin B5 for MOSI. Pin B4 is mapped to MISO, but I’ll use it as a general-purpose output to drive the D/C pin on the TFT. As long as the MISO pin is not configured as ‘alternate function’, the peripheral will ignore it and we can use pin B4 as a normal GPIO pin. Finally, pins A12 and A15 are mapped to CS and RST respectively:
With the pins set up, it is also a good idea to set them all to a known starting state, and tell the ILI9341 display to reset by pulling the ‘Reset’ pin low/high with a delay to give the display time to perform its reset sequence:
The STM32’s SPI peripheral resets to a convenient state for simple communication, but there are still a few options that we need to configure. First up is the clock ‘polarity’ and ‘phase’ – the SCK clock pin will toggle up and down as data is sent, and these two bits tell the peripheral when the data pins should be written and read. The ‘clock polarity’ defines the clock pin’s resting state when data is not being transferred, and the ‘clock phase’ defines whether the devices should read data on the ‘falling’ or ‘rising’ edge of the clock signal. The ILI9341 seems to like a polarity and phase of either 1 and 1 or 0 and 0; you can inspect the timing diagram in its datasheet, or just try and see what works best.
Next, we need to tell the peripheral that the STM32 will be the one initiating communications by setting its MSTR flag. And to avoid unnecessary complexity, it is also a good idea to tell the STM32’s SPI peripheral not to use its hardware CS pin – just like the ILI9341 has a CS (‘Chip Select’) pin which tells it whether it should listen to the clock/data lines, I think that the STM32 has a similar CS signal which tells it whether to read/write, called NSS in the datasheets. Fortunately, we can ignore all of that by using a software ‘Chip Select’ signal (the SSM flag) and leaving it ‘high’ to permanently enable communication (the SSI flag):
Then all we have to do is set the PE (Peripheral Enable) flag to start communications. The ILI9341 expects its data to be sent with the MSB (Most-Significant Bit) first with 8 bits per data frame, but those are the default reset settings on the STM32’s SPI peripheral so we don’t need to change them:
The second important thing to note in that function is that the Data Register is cast to a pointer to an 8-bit integer. The peripheral behaves differently depending on how many bits are set in the register. If you simply write to the register – even with an expression like SPI1->DR = (uint8_t)(dat & 0xFF); – the peripheral will send a full 16 bits of data, and the extra byte of zeros will definitely confuse the ILI9341.
I actually couldn’t get the STM32L0 line to write the full 16 bits this way – I think they have a slightly different peripheral configuration. Anyways, for the ILI9341’s “4-Wire” SPI protocol, we also need to write a ‘send command byte’ method which pulls the D/C pin low during communication. Since we don’t want to change the D/C pin while the peripheral is still sending data in its transmit queue, we should wait for the peripheral’s BSY (Busy) flag to be cleared before changing the state of the D/C GPIO pin:
The pins on your ILI9341 module should be labeled, although if you are using a generic module the labels might be on the back side of the board. The connections are about what you would expect; plug the VCC and LED pins into your board’s +3.3V supply, and connect GND to Ground. The CS, RESET, DC/RS, SDI/MOSI, and SCK pins should connect to the corresponding microcontroller pins, and the SDO/MISO pin can be left unconnected. DC/RS is a different acronym for our D/C ‘Data/Command’ pin, and SDO/SDI are starting to become popular labels on SPI boards – they stand for ‘Serial Data Out/In’, so SDI on the listening device corresponds to the MOSI SPI line and SDO is not needed since we won’t be listening to the display.
When the ILI9341 first powers on it should show a uniform bright white color, but that’s just the backlight LEDs. The display will not try to show anything at all until it is initialized. Be aware that a broken display might still show a bright white screen when power is applied, but these modules are fairly sturdy. I’ve gone so far as to pry them apart and remove the backlights, and the panels worked even after being bluntly removed from the case.
So short of taking a hammer to the screen, you shouldn’t be able to damage them too much by bumping them around or dropping them from a tabletop. Anyways, to start the display and put it into a state where it can draw things, we need to send it a series of startup commands. Like with the SSD1331 display, most commands are followed by one or more ‘option’ bytes, but unlike the SSD1331, those ‘option’ bytes should be sent with the D/C pin held high, not low. You can see all of the commands in the ILI9341 datasheet, but some commands appear to be undocumented, so it is a good idea to look at an existing library for a starting sequence that should work for most purposes.
Since Adafruit is awesome, they provide an ILI9341 library which is compatible with the Arduino IDE and devices which are supported by that – take a look at the .cpp file’s void Adafruit_ILI9341::begin(...) method. The command macros such as ILI9341_PWCTR1 are defined in the library’s .h file. The writeCommand method is similar to our hspi_cmd one, and spiWrite is used to write a byte over the SPI protocol, like our hspi_w8 method. So, our startup sequence can look something like this:
The ILI9341 is a good display driver to know how to use. Screens using it come in sizes from about 2.2″ – 3.2″ with a resolution of 240 x 320 pixels, and they are very affordable. Their contrast is not as good as the SSD1331 OLED displays, but they get you a lot more pixels on a hobbyist’s budget.
I"m using this library but the problem is that I get only two colors at my LCD screen. Black and Purple. That"s because this library is made for 8-bit databus.
1. use 5v to led pin, 3.3v to vcc and 1k / 1.5k resistor voltage dividers to get it to work. 1k resistor in series from Ar-duino to tft logic pin, 1.5k from tft pin to ground.
It has 40 pins interface and SD card and Flash reader design. It is a powerful and mutilfunctional module for your project. The Screen include a controller ILI9341, it"s a support 8/16 bit data interface , easy to drive by many MCU like arduino families? STM32, AVR and 8051. It is designed with a touch controller in it . The touch IC is XPT2046 , and touch interface is included in the 40 pins breakout. It is the version of product only with touch screen and touch controller.
262K color320*2403.2 inchWide viewing angleILI9341 : 320 TFT Driver X 240 RGBIntegrated Power, Gate and Source Driver With RAMXPT2046-WIRE TOUCH,WIRE TOUCH, UP TO 125kHz CONVERSION RATE, SERIAL INTERFACEVoltage type : 5v or 3v voltage input voltage?input is selectable. Because TFT can only work under 3.3 V voltage, so when the input voltage VIN is 5V, need through the 3.3 V voltage regulator IC step down to 3.3V , when the input voltage of 3.3 V, you need to use the zero resistance make J2 short , is equivalent to not through the voltage regulator IC for module and power supply directly.Note: the factory TFT module, are the 5 v power supply. By default.Carrying on board SD holder, its work to SPI mode.By the use of Stylus we can write anything on Display.
As you learn about more of your microcontroller’s peripherals and start to work with more types of sensors and actuators, you will probably want to add small displays to your projects. Previously, I wrote about creating a simple program to draw data to an SSD1331 OLED display, but while they look great, the small size and low resolution can be limiting. Fortunately, the larger (and slightly cheaper) ILI9341 TFT display module uses a nearly-identical SPI communication protocol, so this tutorial will build on that previous post by going over how to draw to a 2.2″ ILI9341 module using the STM32’s hardware SPI peripheral.
We’ll cover the basic steps of setting up the required GPIO pins, initializing the SPI peripheral, starting the display, and then finally drawing pixel colors to it. This tutorial won’t read any data from the display, so we can use the hardware peripheral’s MISO pin for other purposes and leave the TFT’s MISO pin disconnected. And as with my previous STM32 posts, example code will be provided for both the STM32F031K6 and STM32L031K6 ‘Nucleo’ boards.
There are actually multiple sets of pins mapped to the SPI1 peripheral, even on the 32-pin STM32xKx chips. I’ll use pin B3 for SCK and pin B5 for MOSI. Pin B4 is mapped to MISO, but I’ll use it as a general-purpose output to drive the D/C pin on the TFT. As long as the MISO pin is not configured as ‘alternate function’, the peripheral will ignore it and we can use pin B4 as a normal GPIO pin. Finally, pins A12 and A15 are mapped to CS and RST respectively:
With the pins set up, it is also a good idea to set them all to a known starting state, and tell the ILI9341 display to reset by pulling the ‘Reset’ pin low/high with a delay to give the display time to perform its reset sequence:
The STM32’s SPI peripheral resets to a convenient state for simple communication, but there are still a few options that we need to configure. First up is the clock ‘polarity’ and ‘phase’ – the SCK clock pin will toggle up and down as data is sent, and these two bits tell the peripheral when the data pins should be written and read. The ‘clock polarity’ defines the clock pin’s resting state when data is not being transferred, and the ‘clock phase’ defines whether the devices should read data on the ‘falling’ or ‘rising’ edge of the clock signal. The ILI9341 seems to like a polarity and phase of either 1 and 1 or 0 and 0; you can inspect the timing diagram in its datasheet, or just try and see what works best.
Next, we need to tell the peripheral that the STM32 will be the one initiating communications by setting its MSTR flag. And to avoid unnecessary complexity, it is also a good idea to tell the STM32’s SPI peripheral not to use its hardware CS pin – just like the ILI9341 has a CS (‘Chip Select’) pin which tells it whether it should listen to the clock/data lines, I think that the STM32 has a similar CS signal which tells it whether to read/write, called NSS in the datasheets. Fortunately, we can ignore all of that by using a software ‘Chip Select’ signal (the SSM flag) and leaving it ‘high’ to permanently enable communication (the SSI flag):
Then all we have to do is set the PE (Peripheral Enable) flag to start communications. The ILI9341 expects its data to be sent with the MSB (Most-Significant Bit) first with 8 bits per data frame, but those are the default reset settings on the STM32’s SPI peripheral so we don’t need to change them:
The second important thing to note in that function is that the Data Register is cast to a pointer to an 8-bit integer. The peripheral behaves differently depending on how many bits are set in the register. If you simply write to the register – even with an expression like SPI1->DR = (uint8_t)(dat & 0xFF); – the peripheral will send a full 16 bits of data, and the extra byte of zeros will definitely confuse the ILI9341.
I actually couldn’t get the STM32L0 line to write the full 16 bits this way – I think they have a slightly different peripheral configuration. Anyways, for the ILI9341’s “4-Wire” SPI protocol, we also need to write a ‘send command byte’ method which pulls the D/C pin low during communication. Since we don’t want to change the D/C pin while the peripheral is still sending data in its transmit queue, we should wait for the peripheral’s BSY (Busy) flag to be cleared before changing the state of the D/C GPIO pin:
The pins on your ILI9341 module should be labeled, although if you are using a generic module the labels might be on the back side of the board. The connections are about what you would expect; plug the VCC and LED pins into your board’s +3.3V supply, and connect GND to Ground. The CS, RESET, DC/RS, SDI/MOSI, and SCK pins should connect to the corresponding microcontroller pins, and the SDO/MISO pin can be left unconnected. DC/RS is a different acronym for our D/C ‘Data/Command’ pin, and SDO/SDI are starting to become popular labels on SPI boards – they stand for ‘Serial Data Out/In’, so SDI on the listening device corresponds to the MOSI SPI line and SDO is not needed since we won’t be listening to the display.
When the ILI9341 first powers on it should show a uniform bright white color, but that’s just the backlight LEDs. The display will not try to show anything at all until it is initialized. Be aware that a broken display might still show a bright white screen when power is applied, but these modules are fairly sturdy. I’ve gone so far as to pry them apart and remove the backlights, and the panels worked even after being bluntly removed from the case.
So short of taking a hammer to the screen, you shouldn’t be able to damage them too much by bumping them around or dropping them from a tabletop. Anyways, to start the display and put it into a state where it can draw things, we need to send it a series of startup commands. Like with the SSD1331 display, most commands are followed by one or more ‘option’ bytes, but unlike the SSD1331, those ‘option’ bytes should be sent with the D/C pin held high, not low. You can see all of the commands in the ILI9341 datasheet, but some commands appear to be undocumented, so it is a good idea to look at an existing library for a starting sequence that should work for most purposes.
Since Adafruit is awesome, they provide an ILI9341 library which is compatible with the Arduino IDE and devices which are supported by that – take a look at the .cpp file’s void Adafruit_ILI9341::begin(...) method. The command macros such as ILI9341_PWCTR1 are defined in the library’s .h file. The writeCommand method is similar to our hspi_cmd one, and spiWrite is used to write a byte over the SPI protocol, like our hspi_w8 method. So, our startup sequence can look something like this:
The ILI9341 is a good display driver to know how to use. Screens using it come in sizes from about 2.2″ – 3.2″ with a resolution of 240 x 320 pixels, and they are very affordable. Their contrast is not as good as the SSD1331 OLED displays, but they get you a lot more pixels on a hobbyist’s budget.
With new UI working it was a matter of several night to implements new IO subsystem for FSMC and get new UI to MKS Robin TFT. Then I could start working on new display resolution and touch support.
According to the Setup, the LCD_D2 is connected to the PA15. So if I want to write the DATA to the LCD_D2 pin, first I will select the 2nd bit of the data (d & (1<<2)), and than shift this by 13 using <<13. This will be like adding 2 with 13 to make a total of 15, and that’s where the LCD_D2 is connected to.
Similarly, LCD_D7 is connected to PA5. So to write the data, first we will select the 7th bit of the data (d & (1<<7)), and this time shift it RIGHT by 2 (>>2). This is like subtracting 7-2=5. And that’s where, the D7 is connected to.
The process here remains the same. Except, we have to first select the GPIO Pin, and than shift it according to the position of the LCD Pin, that it is connected to. In the function above, we are first selecting the PB0 pin, and as it is connected to LCD_D0, we don’t need to shift it anywhere. Same for the PB1 also.
Next, we are selecting PA15, and as this one is connected to the LCD_D2, we need to shift it by 13 to the right ( >>13). This process continues for all other pins too.
I have tried to compile the example file GxTFT_FSMC_BlackSTM32F407V.ino. The necessary assisting files should be in place including STM32GENERIC in the hardware folder. After having worked for quite some time Arduino IDE stops with an error message. I list the last page of the verbose output:
"C:\Users\Peas\AppData\Local\Arduino15\packages\arduino\tools\arm-none-eabi-gcc\4.8.3-2014q1/bin/arm-none-eabi-g++" -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DF_CPU=168000000L -mthumb -DSTM32GENERIC -DRAM_LENGTH=131072 -DFLASH_LENGTH=524288 -c -g -Os -w -std=gnu++11 -ffunction-sections -fdata-sections -nostdlib -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -MMD -DSTM32F4 -DARDUINO=10807 -DARDUINO_BLACK_F407VE -DARDUINO_ARCH_STM32 -DSTM32F407VE -DHSE_VALUE=8000000 -DMENU_SERIAL_AUTO=SerialUSB "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\cores\arduino/stm32" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\cores\arduino/usb" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/CMSIS" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/STM32F4/CMSIS_Inc" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/STM32F4/CMSIS_Src" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/STM32F4/HAL_Inc" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/STM32F4/HAL_Src" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\system/STM32F4/stm32_chip" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\cores\arduino" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\variants\BLACK_F407VE" "-IC:\Users\Peas\Documents\Arduino\libraries\GxTFT\src" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\libraries\SPI\src" "-IC:\Users\Peas\Documents\Arduino\libraries\Adafruit_GFX_Library" "-IC:\Users\Peas\Documents\Arduino\hardware\STM32GENERIC-master\STM32\libraries\stm32_dma\src" "C:\Users\Peas\Documents\Arduino\libraries\GxTFT\src\GxIO\STM32GENERIC\GxIO_STM32F407ZGM4_FSMC\GxIO_STM32F407ZGM4_FSMC.cpp" -o "C:\Users\Peas\AppData\Local\Temp\arduino_build_724464\libraries\GxTFT\GxIO\STM32GENERIC\GxIO_STM32F407ZGM4_FSMC\GxIO_STM32F407ZGM4_FSMC.cpp.o"
C:\Users\Peas\Documents\Arduino\libraries\GxTFT\src\GxIO\STM32GENERIC\GxIO_STM32F407ZGM4_FSMC\GxIO_STM32F407ZGM4_FSMC.cpp: In constructor "GxIO_STM32F407ZGM4_FSMC::GxIO_STM32F407ZGM4_FSMC(bool)":
C:\Users\Peas\Documents\Arduino\libraries\GxTFT\src\GxIO\STM32GENERIC\GxIO_STM32F407ZGM4_FSMC\GxIO_STM32F407ZGM4_FSMC.cpp:97:11: error: "PG12" was not declared in this scope
C:\Users\Peas\Documents\Arduino\libraries\GxTFT\src\GxIO\STM32GENERIC\GxIO_STM32F407ZGM4_FSMC\GxIO_STM32F407ZGM4_FSMC.cpp:98:11: error: "PG0" was not declared in this scope
The STM32 LTDC has a peripheral called LTDC LCD TFT Display Controllerwhichprovides a digital parallel interface(DPI) for a variety of LCD and TFT panels. It sends RGB data in parallel to the display and generates signals for horizontal and vertical synchronization (HSYNC, VSYNC), as well as pixel clock (PCLK) and not data enable (DE) signals:
In this example I use the display on the STM32F429-Discovery board, which is driven by the ILI9341 display controller. The ILI9341 can drive a QVGA (Quarter VGA) 240×320 262,144 colors LCD display. The controller can be configured via SPI (or parallel interface, depending on the panel settings) to use a digital parallel 18 bit RGB interface (since only 6 lines per color channel are wired on the board to the LTDC). Since the display pixel format is less than 8 bit per channel (RGB666 in this case), the RGB display data lines are connected to the most significant bits of the LTDC controller RGB data lines:
Before enabling the LTDC we must configure the clock system. The LTDC uses a specific clock LCD_CLOCK to generate the pixel clock signal and it must be configured and enabled during the system initialization phase:
To display an image we must convert an image file to an array (possibly a const one, so it can be stored in flash memory) of bytes. To do this I used LCD image converter, a simple but powerful application that can convert a file to a variety of different pixel formats:
In this example the framebuffers have a RGB888 color depth and for a 240×320 display that makes 225 KiB of memory for each buffer (3 bytes per pixel x 240 x 320 pixels) so they must be stored in external SRAM (the STM32F429I-DISCOVERY has a 64Mbit external SRAM so we’re good). The FMC Flexible Memory Controller has to be initialized and the address of the two frame buffers has to be configured. Drawing on the framebuffer is a matter of writing the right bytes in order to change the color. Once all pixels are drawn (bytes are written) the buffers are switched and the code can draw the next frame: