TFT Displays

Introduction

A wide range of displays is available for use with the Arduino system. For the WeMos D1 mini system I found 3 display boards. Each of them is based a on different controller chip and has different resolution. Here they are are:

  • 64x48 pixel 0.66" TFT screen with SSD1306 controller. The SSD1306 uses the I2C bus
  • 128x128 pixel 1.4" display based on the ST7735 controller. The ST7735 uses the SPI bus
  • 320x240 pixel 2.4" display based on the ILI9341 display and the XPT2046 touch controller.
64x48 pixels with SSD1306 128x128 with ST7735 320x240 pixel touch screen with ILI9341
ssd1306.png tftScreen.png lolon2.4inch.png
Each of them has its advantages and disadvantages. The 0.66" display is really tiny and its resolution is very limited allowing to write just a few characters. In addition it is monochrome only. However, the reference display driver included in MicroPython uses this controller chip. There are also higher resolution and color screens using this controller but I did not find one with the WeMos D1 mini bus interface.

The 1.4" display is already quite a bit better even though 128x128 bit resolution is still not enough for sophisticated graphics. It is enough however to write a few lines of text and produce simple graphics, e.g. creating a graph of sensor readout results.

The 2.4" display is clearly the most powerful device, adding touch functionality. It is however so big that it is difficult to plug it onto the WeMos D1 bus, even if the connector is available. The better solution is an adapter board and a connecting cable.

Since the 1.4" display is enough for most our applications, it can be nicely plugged onto the bus just like any other sensor or actuator shield and it is substantially cheaper than the 2.4" solution, this is the device we chose for the course.

Preparing the hardware

Before using the display its solder jumpers on the back must be configured. There are 4 solder fields for configuration

  • TFT LED. his allows to control the display back light. I leave it unconfigured which results in the back light LED being permanently on
  • TFT_CS: D0. TFT_CS and TFT_DC can be selected onto D0, D3, D4 or D8 corresponding to GPIO 26, 17,16, 5. Since D3 and D4 cannot be used on the CPU with PSRAM the only selections possible are D0 and D8. I select D0 for CS and D8 for TFT_DC
  • TFT_DC: D8
  • TFT_RST is connected to the system reset
Here is a photo of the jumper settings:

tftJumpers.png

The driver software

Searching the Internet I found many display drivers, none of which really satisfied me.

  • The ssd1306 reference driver included in MicroPython. This driver is very basic and it relies very heavily on its framebuffer superclass.
  • A pure Python version of a ST7735 driver for the ESP32: https://github.com/boochow/MicroPython-ST7735. This a port of a ST7735 driver written by Guy Carver for stm32 processors (https://github.com/GuyCarver/MicroPython/blob/master/lib/ST7735.py). At first glance, this is exactly what I was looking for. However, Guy Carver himself was not too happy about its speed and, compared to other drivers, it has limited functionality.
    There is a demo program putting images up onto the screen and another one exercising the driver's graphic functions like
    • drawing line
    • drawing rectangles
    • drawing filled rectangles
    • drawing text
  • A C-module of the above driver also written by Guy. Finding this code was not so easy. It is hidden in a fork of MicroPython, made some years back, and it was again developed for stm32 processors. Since the method of integrating C-modules into MicroPython has been made substantially easier in recent versions of MicroPython quite a bit of work was needed to adapt this driver. When trying it, it was even slower than the Python code! I figured out that the reason was the way the SPI commands are sent to the controller. If you send commands and data in large chunks then you can speed up graphics considerably. This however is true for the Python and the C code.
  • A driver for displays based on the SSD1351 controller: https://github.com/rdagger/micropython-ssd1351 The display module used has 128x128 resolution just like the module we have. The driver comes with a big number of demo programs, some of which are rather sophisticated. It also provides many more functions than the ST7735 driver. A very nice tutorial with Youtube video is also available. Unfortunately loading fonts is extremely slow.
    I decided to try porting this library to the St7735 chip in such a way that the demo programs would run with a minimum of modification. Speeding up font loading and a port of the demo programs from the above ST7735 driver should be part of the process.

Developing our own driver

The first goal for our own driver was to be able to run all the demo programs on it. Since the calling sequences of the drivers are different (different method names, different way to pass parameters) some modifications to the demo code was of course necessary. In addition the method names and parameters defined in the framebuffer should be available.

Comparing the original ST7735 driver to the SSD1351 one we see the following differences

ST7735 SSD1351
clips drawing elements when they exceed display boundaries drops drawing elements if any pixel would exceed display boundaries
Provides 3 fonts as Python files Provides many different fonts as C files
These fonts must be loaded before use, which takes a long time
uses .bmp files for images uses binary rgb565 files for images
does not support sprites allows use of sprites
Comes with 2 demo programs for graphics primitives
and image display
Comes with man demo programs, some of which are rather sophisticated
Even supplies simple Arcade games
Finally I decided to come up with a mixture of both because I prefer drawing element clamping fro just dropping the element. In addition to the original naming convention of the methods additional names were introduced following the convention in the framebuffer driver.

Fonts

Fonts are normally loaded into RAM before use. However, this takes time and uses a big amount of RAM memory, a very scarce resource on the standard ESP32. (We are using an ESP32 with additional PSRAM where the problem is much less critical).

Since font loading in the SSD1351 driver takes ages (the demo-font program using 9 different fonts took ~ 30s before the first character could be written. After that text writing was fast) I searched for another method and ended up asking the MicroPython forum. Peter Hinch sent me a link to his micropython-font-to-py git repository explaining how the font data can be kept in flash and you can nevertheless get fast access to the them. The fonts are implemented as Python source files providing not only the font data but also access routines to the pixel data. These font files can be frozen into MicroPython and are thus stored in flash. When a character is written, only the pixel data for this character is loaded into RAM.

The problem was now how to convert the font files in C supplied with the SSD1351 driver to Python font files compatible with micropython-font-to-py. I wrote a Python script:

https://afnog.iotworkshop.africa/pub/IoT_Course_English/TFTDisplay/convertFont.py.txt

doing exactly this. The output is actually executable (on the PC) and will print the pixel data of the letter "A" for this font.

font.png

Finally I copied the fonts into the modules/fonts directory of the MicroPython sources and froze them into the binary.

You can use the fonts by importing the MicroPython module:

from  ST7735 import Display, color565 import fonts/sysfont as sysfont sck = Pin(18)
miso= Pin(19)
mosi= Pin(23)
SPI_CS = 26
SPI_DC = 5
spi = SPI(2, baudrate=32000000, sck=sck, mosi=mosi, miso=miso) display=Display(spi,SPI_CS,SPI_DC)
display.draw_text(0, 0, 'Hello World!', sysfont, color565(255, 0, 0))    

In addition to font handling I received a link to micropython-nano-gui a lightweight and minimal MicroPython GUI library for display drivers based on th e framebuf class. This library contains a few widgets to display sensor data and includes a plot library.

The frame buffer

There are two different approaches to the display driver:

  • Every graphics primitive results in a direct write to the display controller
  • All graphics primitives write to frame buffer memory. When all drawing is done the frame buffer memory as a whole is copied to the hardware
The SSD1351 driver uses the first approach while the reference driver for the SSD1306 in MicroPython uses the second method. Peter Hinch's libraries depend on the underlying framebuf class.

The goal

I now had:

  • a simple demo program I wrote for the SSD1306 module
  • 2 demo programs running on the original ST7735 driver
  • a bunch of demo programs from the SSD1351 driver
  • Peter Hinch's library for font handling and his nanogui library
I set myself the goal to write a driver which was capable of running all of these demo programs.

I ended up writing a class with methods implementing both design principles. There is a method

  • draw_line(x0,y0,x11,y1,color) drawing the line directly on the hardware
  • and a framebuf compatible method line(x0,y0,x11,y1,color)
    which draws the line in the frame buffer memory
When using the frame buffer method you have to call the show() method to copy the framebuf memory to the hardware to make the drawing visible.

Here are the methods:

  • size(): returns width and height of the display as a tuple
  • clear(): clears the display
  • display_on(), display_off() switches the display on or off
  • cleanup(): clears the display, switches it of and deinits the SPI bus
  • draw_pixel(x,y,pixel): draw a single pixel
  • draw_hline(x,y,w,color): draw a horizontal line of length w, starting at the point x,y
  • draw_vline(x,y,w,color): you guess!
  • draw_line(x0,y0,x1,y1,color): draw a line from point x0,y0 to point x1,y1
  • draw_lines(coords,color): draw lines where the point coordinates are given as a list of coordinate pairs
  • draw_rectangle(x,y,w,h,color): draw a rectangle of width w and height h starting at point x,y
  • draw_polygon(sides,x0,y0,r,color,rotate=0)
  • draw_circle(x0,y0,r,color): draw a circle of radius r with center point x0,y0
  • draw_ellipse(x0,y0,a,b.color): draw an ellipse with center point x0,y0 and half axis a,b
  • draw_filledCircle(x0, y0, r, color): same as draw:circle but the circle is filled
  • draw_filledEllipse(x0, y0, a, b, color)
  • draw_filledPolygon(sides, x0, y0, r, color, rotate=0)
  • draw_letter(self, x, y, letter, font, color, background=0,landscape=False): draw the glyph for letter at position x,y using the font font. If landscape is false the letter is drawn left to right, if landscape is true, the letter is rotated counter clock wise by 90° and is drawn from bottom to top
  • draw_text(self, x, y, text, font, color, background=0,landscape=False, spacing=1, nowrap = False): I think you can guess! If nowrap is true and the text is larger than the length of a text line it is wrapped to the next line.
  • draw_image(path,x0,y0,w=128, h=128): draw an image from a rgb565 raw image file to the display
  • load_sprite(path, w, h): load a sprite of width w and height h from a rgb565 raw image file
  • draw_sprite(buf, x, y, w, h): draw the sprite, which was previously loaded into buf

-- Uli Raich - 2020-07-04

Comments

Topic attachments
I Attachment History Action Size Date Who Comment
Texttxt convertFont.py.txt r1 manage 8.6 K 2020-07-21 - 11:26 UliRaich  
PNGpng font.png r1 manage 2.7 K 2020-07-21 - 09:22 UliRaich  
PNGpng fonts.png r1 manage 1253.6 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7952.png r1 manage 707.3 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7955.png r1 manage 872.8 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7958.png r1 manage 738.0 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7959.png r1 manage 751.6 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7960.png r1 manage 825.8 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7961.png r1 manage 811.9 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7965.png r1 manage 841.2 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7968.png r1 manage 866.2 K 2020-07-26 - 16:41 UliRaich  
PNGpng img_7975.png r1 manage 657.9 K 2020-07-26 - 16:41 UliRaich  
PNGpng lolon2.4inch.png r1 manage 218.8 K 2020-07-04 - 09:45 UliRaich  
PNGpng ssd1306.png r1 manage 158.9 K 2020-07-04 - 09:44 UliRaich  
PNGpng tftJumpers.png r1 manage 626.4 K 2020-07-17 - 16:16 UliRaich  
PNGpng tftScreen.png r1 manage 144.4 K 2020-07-04 - 09:45 UliRaich  
Edit | Attach | Watch | Print version | History: r13 | r8 < r7 < r6 < r5 | Backlinks | Raw View | Raw edit | More topic actions...
Topic revision: r6 - 2020-07-26 - UliRaich
 
  • Edit
  • Attach
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback