GxTFT library - modifying it to work on other STM32 boards

Hi Jean-Marc,

I will give that a test, I’ve been testing 2.1.2 with LVGL this morning and I found when I left ICache and DCache disabled it caused poor performance/ screen tearing.
Re-enabling it again improved performance with minor screen tear.

I would like to do a comparison with FMC, also take in consideration of the topic link you mention with regards to changing the FMC address regions.

Attached is a video of demo widget in action with ICache & DCache enabled.

VID-20210218-WA0002_216x464.zip (1.81 MB)

Hi Jean-Marc,

Just tried FMC on the Nucleo with my SSD1963 7" display and extremely impressed.
I didn’t realise you already sorted the FMC address region to bypass DCache.

I’ve attached another video showing FMC in action with demo widget, you can noticeably tell the difference in speed compared to the previous video with P16.

Cheers
Ryan

Nucleo 144 LVGL FMC demo video.zip (1.93 MB)

Vertical Scroll is trivial. Drawing to a "moved rectangle" makes your head hurt.

Your Horizontal re-draw is impressive.
Did you re-draw the whole screen or just the changed rectangles ?

David.

Thanks David,

I be honest it was a lot trial and error to get it to work well as I found trying to speed things up caused issues with displaying correctly.

Started of with pushing individual pixels which was slow

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint16_t x, y;

for (y = area->y1; y <= area->y2; y++) {
    for (x = area->x1; x <= area->x2; x++) {
        tft.drawPixel(x, y, color_p->full); // Put a pixel to the display.
        color_p++;
    }
}

Then I set a window once for the whole screen and pushed as many pixels as I can

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  uint16_t c;
    tft.setWindow(area->x1, area->y1, area->x2, area->y2 ); // set the working window 
    for (int y = area->y1; y <= area->y2; y++) {
      for (int x = area->x1; x <= area->x2; x++) {
        c = color_p->full; 
        tft.pushColor(c, 1);
        color_p++;
     }
  }
  lv_disp_flush_ready(disp); // tell lvgl that flushing is done 
}

This all interfaces with LVGLs display driver API

You only use drawPixel() as a last resort. It involves setting a one pixel window and writing one colour to it.

Drawing a rectangle is much more efficient. You set a multi-pixel window and just write each colour.
If the whole rectangle is the same colour you can optimise too.

You just have to look at your particular application.
How many pixels need to be re-drawn?
Is it quicker to draw a full rectangle or just individual pixels?

David.

It is something I would like to look at, I did attempt fillrect but I wasn't drawing the individual pixels correct and so was getting plain colour rectangles for areas I was updating.
I will dig out my attempt I made and post on here to see where I was going wrong with it.

I found it easier just to create a loop for the individual pixels, I removed the '1' from tft.pushColor(c, 1) as no need for the inner loop as well on that.

What about optimising the loop so I can push 32 pixels in one iteration instead of individually

Its quicker to redraw the screen but seems to leave stray pixels on the screen when scrolling

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
 static uint16_t temp[32];
  uint16_t c;
  int i=0;
  tft.setWindow(area->x1, area->y1, area->x2, area->y2); // set the working window 

  for (int y = area->y1; y <= area->y2; y++) {
    for (int x = area->x1; x <= area->x2; x++) {
       temp[i] = color_p->full;
       i++;
       if (i==32){   
       tft.pushColors(temp, i);
       i = 0;
       }
      color_p++;
    }
  }
  lv_disp_flush_ready(disp); // tell lvgl that flushing is done 
}

Version 2.1.4 of library GxTFT is available.

  • added support for blue STM32F103ZET6 board
  • supported by classes GxIO_STM32F103ZET6_P16 and GxIO_STM32F103ZET6_FSMC
  • fixed pushColors methods
  • implemented writeRect and pushRect methods (these were empty)

Jean-Marc

Thanks alot Jean-Marc, another great update

Hi all :),

I´m looking for library which could work with Arduino, SSD1963 LCD and LVGL and it could run on this (STM34F407VET6 )board. I have also STM32F407 Discovery board which has STM34F407VGT6 MCU and 7" LCD screen with SSD1963 and I would like to know if GxTFT library could work in this setup?

I so far worked with ESP32 and Arduino IDE and I would prefer to use those to program my firmware, but for the start I would like to know if this is doable with this library.

Many thanks for answer.

@dronecz83, Hi,

I need to lean out of the window to pretend, yes, it can be done.

It may be easy, if you use the FSMC data pins in the correct order to connect the data bus of your display.
Else you need to adapt a IO driver class to your wiring. You need to "study" how this is done, but I tried to comment.

I don't know if my driver class for SSD1963 is specific to the display I have, and how easy it would be to adapt.
In general you search for driver code for your display, and adapt the GxTFT class, e.g. init code.

I can't answer for LVGL, as I have not (yet) used it with my library.

Jean-Marc

@dronecz83

I have been using GxTFT with LVGL and it works very well, I really do like the library as its broken down which makes it a bit easier to modify to suit your requirements.
I have adapted the library to just use FMC commands with the display I’m using, I don’t need any other commands with LVGL.

The source code for MKS-TFT70 is on github (MKS-TFT70-Firmware) with a selection of display configs depending on what ID is read from the controller, in theory from a quick scroll of the source code to see the hardware used - you should be able to use ZinggJM GxTFT library with minimal modification: the LCD pins look like they correspond with FSMC (you would have to double check).

Modify the init (see below) as already mentioned for your specific display

Init (SSD1963 Display)

void init() //lv_driver      https://github.com/lvgl/lv_drivers/blob/master/display/SSD1963.c  
{
  rotation = 1;               // landscape is default
  FMC::writeCommand(0xE2);    //PLL multiplier, set PLL clock to 120M
  FMC::writeData(0x23);       //N=0x36 for 6.5M, 0x23 for 10M crystal
  FMC::writeData(0x02);
  FMC::writeData(0x04);       // original SSD1963_800ALT
  FMC::writeCommand(0xE0);    // PLL enable
  FMC::writeData(0x01);
  delay(10);
  FMC::writeCommand(0xE0);
  FMC::writeData(0x03);       // now, use PLL output as system clock
  delay(10);
  FMC::writeCommand(0x01);    // software reset
  delay(100);
  FMC::writeCommand(0xE6);    //PLL setting for PCLK, depends on resolution
  FMC::writeData(0x04); //0x01  HX8257C
  FMC::writeData(0x93); //0x33  HX8257C
  FMC::writeData(0xE0); //0x33  HX8257C

  FMC::writeCommand(0xB0);    //LCD SPECIFICATION
  FMC::writeData(0x00);       // 0x24
  FMC::writeData(0x00);
  FMC::writeAddrMSBfirst(physical_width - 1); // Set HDP
  //FMC::writeData(0x03);     //Set HDP 799
  //FMC::writeData(0x1F);
  FMC::writeAddrMSBfirst(physical_height - 1); // Set VDP
  //FMC::writeData(0x01);     //Set VDP 479
  //FMC::writeData(0xDF);
  FMC::writeData(0x00);

  FMC::writeCommand(0xB4);    //HSYNC
  FMC::writeData(0x03);       //Set HT  928
  FMC::writeData(0xA0);
  FMC::writeData(0x00);       //Set HPS 46
  FMC::writeData(0x2E);
  FMC::writeData(0x30);       //Set HPW 48
  FMC::writeData(0x00);       //Set LPS 15
  FMC::writeData(0x0F);
  FMC::writeData(0x00);

  FMC::writeCommand(0xB6);    //VSYNC
  FMC::writeData(0x02);       //Set VT  525
  FMC::writeData(0x0D);
  FMC::writeData(0x00);       //Set VPS 16
  FMC::writeData(0x10);
  FMC::writeData(0x10);       //Set VPW 16
  FMC::writeData(0x00);       //Set FPS 8
  FMC::writeData(0x08);

  FMC::writeCommand(0xBA);
  //FMC::writeData(0x05);     //GPIO[3:0] out 1
  FMC::writeData(0x07);       //GPIO[3:0] out 1

  FMC::writeCommand(0xB8);
  FMC::writeData(0x07);       //GPIO3=input, GPIO[2:0]=output
  FMC::writeData(0x01);       //GPIO0 normal

  FMC::writeCommand(0x36);    //rotation
  FMC::writeData(0x22);       // -- Set to 0x21 to rotate 180 degrees

  FMC::writeCommand(0xF0);    //pixel data interface
  FMC::writeData(0x03);

  delay(10);

  // SetWindow, physical addresses, even if default rotation is changed
  FMC::writeCommand(0x2a);
  FMC::writeAddrMSBfirst(0);
  FMC::writeAddrMSBfirst(800 - 1); //799
  FMC::writeCommand(0x2b);
  FMC::writeAddrMSBfirst(0);
  FMC::writeAddrMSBfirst(480 - 1); //479

  FMC::writeCommand(0x29);    //display on

  FMC::writeCommand(0xBE);    //set PWM for B/L
  FMC::writeData(0x06);
  FMC::writeData(0xF0);
  FMC::writeData(0x01);
  FMC::writeData(0xF0);
  FMC::writeData(0x00);
  FMC::writeData(0x00);

  FMC::writeCommand(0xD0);
  FMC::writeData(0x0D);

  FMC::writeCommand(0x2C);
}

LVGL display driver (This works fine with GxTFT out of the box)

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  uint16_t c;
    tft.setWindow(area->x1, area->y1, area->x2, area->y2 ); // set the working window 
    for (int y = area->y1; y <= area->y2; y++) {
      for (int x = area->x1; x <= area->x2; x++) {
        c = color_p->full; 
        tft.pushColor(c);
        color_p++;
     }
  }
  lv_disp_flush_ready(disp); // tell lvgl that flushing is done 
}

@dronecz83,

I need to add one important point to check for use with FSMC: the pins used for CS and RS.

The CS pin determines which FSMC bank to use (FSMC_NE pin).
The RS pin determines the address offset for Data access versus Command access (FSMC address line).

Jean-Marc

Hi @ZinggJM and @rgarrett93,

sorry for such late reply but I did not have time for this project, but I want to move forward. I download and Installed library to Arduino and I tried compile GxTFT_FSMC_BlackSTM32F407V example but I can not compile it.

I get this error:

I tried to Google some combination of error but without luck.

Also @rgarrett93 could you please post some sketch for Arduino & LVGL which is working for you?

Many thanks for your help.

@dronecz83,

please post the error messages in a code window. I don't read error info with a magnifying glass.

Jean-Marc

You can not read it even when you resize it to originil size?

Anyway, here is that error as a text:

GxTFT_FSMC_BlackSTM32F407V:24:1: error: 'GxIO_Class' does not name a type; did you mean 'GxCTRL_Class'?
   24 | GxIO_Class io; // #define GxIO_Class is in the selected header file
      | ^~~~~~~~~~
      | GxCTRL_Class
GxTFT_FSMC_BlackSTM32F407V:28:25: error: 'io' was not declared in this scope
   28 | GxCTRL_Class controller(io); // #define GxCTRL_Class is in the selected header file
      |                         ^~
GxTFT_FSMC_BlackSTM32F407V:32:15: error: 'io' was not declared in this scope
   32 | TFT_Class tft(io, controller, 800, 480); // landscape 240x320
      |               ^~
C:\Users\dronecz\AppData\Local\Temp\arduino_modified_sketch_744397\GxTFT_FSMC_BlackSTM32F407V.ino: In function 'void setup()':
GxTFT_FSMC_BlackSTM32F407V:44:65: error: 'io' was not declared in this scope
   44 |   Serial.println(String(controller.name) + " Test on " + String(io.name));
      |                                                                 ^~
Použití knihovny GxTFT-2.1.7 ve verzi 2.1.7 v adresáři: C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7 
Použití knihovny SrcWrapper ve verzi 1.0.1 v adresáři: C:\Users\dronecz\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.9.0\libraries\SrcWrapper 
Použití knihovny SPI ve verzi 1.0 v adresáři: C:\Users\dronecz\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.9.0\libraries\SPI 
exit status 1
'GxIO_Class' does not name a type; did you mean 'GxCTRL_Class'?

If I change that tp GxCTRL_Class io I get even more errors:

GxTFT_FSMC_BlackSTM32F407V:24:14: error: no matching function for call to 'GxCTRL_SSD1963::GxCTRL_SSD1963()'
   24 | GxCTRL_Class io; // #define GxIO_Class is in the selected header file
      |              ^~
In file included from C:\Users\dronecz\AppData\Local\Temp\arduino_modified_sketch_45151\GxTFT_FSMC_BlackSTM32F407V.ino:21:
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:15:5: note: candidate: 'GxCTRL_SSD1963::GxCTRL_SSD1963(GxIO&, uint16_t, uint16_t)'
   15 |     GxCTRL_SSD1963(GxIO& io, uint16_t phy_w, uint16_t phy_h) : GxCTRL(io), physical_width(phy_w), physical_height(phy_w) {};
      |     ^~~~~~~~~~~~~~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:15:5: note:   candidate expects 3 arguments, 0 provided
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:14:5: note: candidate: 'GxCTRL_SSD1963::GxCTRL_SSD1963(GxIO&)'
   14 |     GxCTRL_SSD1963(GxIO& io) : GxCTRL(io), physical_width(800), physical_height(480) {};
      |     ^~~~~~~~~~~~~~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:14:5: note:   candidate expects 1 argument, 0 provided
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:11:7: note: candidate: 'constexpr GxCTRL_SSD1963::GxCTRL_SSD1963(const GxCTRL_SSD1963&)'
   11 | class GxCTRL_SSD1963 : public GxCTRL
      |       ^~~~~~~~~~~~~~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:11:7: note:   candidate expects 1 argument, 0 provided
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:11:7: note: candidate: 'constexpr GxCTRL_SSD1963::GxCTRL_SSD1963(GxCTRL_SSD1963&&)'
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h:11:7: note:   candidate expects 1 argument, 0 provided
GxTFT_FSMC_BlackSTM32F407V:32:39: error: no matching function for call to 'GxTFT::GxTFT(GxCTRL_SSD1963&, GxCTRL_SSD1963&, int, int)'
   32 | TFT_Class tft(io, controller, 800, 480); // landscape 240x320
      |                                       ^
In file included from C:\Users\dronecz\AppData\Local\Temp\arduino_modified_sketch_45151\GxTFT_FSMC_BlackSTM32F407V.ino:5:
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:279:5: note: candidate: 'GxTFT::GxTFT(GxIO&, GxCTRL&, uint16_t, uint16_t)'
  279 |     GxTFT(GxIO& io, GxCTRL& controller, uint16_t w, uint16_t h);
      |     ^~~~~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:279:17: note:   no known conversion for argument 1 from 'GxCTRL_SSD1963' to 'GxIO&'
  279 |     GxTFT(GxIO& io, GxCTRL& controller, uint16_t w, uint16_t h);
      |           ~~~~~~^~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:276:7: note: candidate: 'constexpr GxTFT::GxTFT(const GxTFT&)'
  276 | class GxTFT : public Print
      |       ^~~~~
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:276:7: note:   candidate expects 1 argument, 4 provided
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:276:7: note: candidate: 'constexpr GxTFT::GxTFT(GxTFT&&)'
C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7\src/GxTFT.h:276:7: note:   candidate expects 1 argument, 4 provided
Použití knihovny GxTFT-2.1.7 ve verzi 2.1.7 v adresáři: C:\Users\dronecz\Dropbox\@Arduino\Arduino\libraries\GxTFT-2.1.7 
Použití knihovny SrcWrapper ve verzi 1.0.1 v adresáři: C:\Users\dronecz\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.9.0\libraries\SrcWrapper 
Použití knihovny SPI ve verzi 1.0 v adresáři: C:\Users\dronecz\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.9.0\libraries\SPI 
exit status 1
no matching function for call to 'GxCTRL_SSD1963::GxCTRL_SSD1963()'

Here is how I changed my skecth:

// modified by Jean-Marc Zingg to be an example for the GxTFT library
// original source taken from https://github.com/Bodmer/TFT_HX8357

////#include <GxTFT_GFX.h> // deprecated, uses Adafruit_GFX; you would need to copy it from extras/src for use
#include <GxTFT.h> // Hardware-specific library

// select one display class
////#define TFT_Class GxTFT_GFX // deprecated, uses Adafruit_GFX; you would need to copy it from extras/src for use
#define TFT_Class GxTFT

// select one GxIO class, 
// note: "error: 'GxIO_Class' does not name a type": indicates target board selection mismatch
// this version is for use with Arduino package "STM32 Boards (select from submenu)" (STMicroelectronics), board "Generic STM32F4 series" part e.g. "BLACK F407ZG".
//#include <GxIO/STM32MICRO/GxIO_STM32F4_FSMC/GxIO_STM32F4_FSMC.h>
// this version is for use with Arduino package STM32DUINO, board "Generic STMF407V series".
//#include <GxIO/STM32DUINO/GxIO_STM32F4_FSMC/GxIO_STM32F4_FSMC.h>
// this version is for use with Arduino package STM32GENERIC, board "BLACK F407VE/ZE/ZG boards".
#include <GxIO/STM32GENERIC/GxIO_STM32F4_FSMC/GxIO_STM32F4_FSMC.h>

// select one GxCTRL class
#include <GxCTRL/GxCTRL_SSD1963/GxCTRL_SSD1963.h> // 240x320

// create instance for the selected GxIO class
GxCTRL_Class io; // #define GxIO_Class is in the selected header file
// note: "error: 'GxIO_Class' does not name a type": indicates target board selection mismatch

// create instance for the selected GxCTRL class
GxIO_Class controller(io); // #define GxCTRL_Class is in the selected header file

// select one or adapt
//TFT_Class tft(io, controller, 240, 320); // portrait 240x320
TFT_Class tft(io, controller, 800, 480); // landscape 240x320

Thanks for your help.

I don't want to. Avoid such remarks if you expect any help!

// note: "error: 'GxIO_Class' does not name a type": indicates target board selection mismatch

What board and processor did you select to compile for?
package STM32GENERIC is considered deprecated, you should use the official STM32 package instead.
But it compiles without errors on the installation I have.

1 Like

2 posts were split to a new topic: STM32F407ZE like board error messages

A post was split to a new topic: Which display do you use with STM32?