8-bit parallel TFT driver for micropython ?

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
Hugh-maingauche
Posts: 15
Joined: Sun Aug 15, 2021 9:34 pm

8-bit parallel TFT driver for micropython ?

Post by Hugh-maingauche » Sun Aug 15, 2021 9:46 pm

Hi all,

I've been searching for an available driver for my TFT screen screen

Unfortunately, this device does not use SPI and I have no other choice to use the 8-bit parallel interface. The circuitpython displayio library seems to offer a parallel bus driver but I do not find anything equivalent for micropython.

Since i was able to have this TFT screen running fine witn my ESP32 Devkit (ESP32-WROOM-32 chip) with the arduino framework, I know it uses an ILI9341 parallel bus. I'd really like to have it running with micropython, using the same board.

Any chance someone would have developped such a driver or any ideas on how to reuse the circuitpython library with micropython ?

Thank you for any help or suggestion

Hugh

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: 8-bit parallel TFT driver for micropython ?

Post by pythoncoder » Mon Aug 16, 2021 7:05 am

I have written this driver for SPI which could be adapted for 8 bit parallel. However it doesn't support touch.
Peter Hinch
Index to my micropython libraries.

Hugh-maingauche
Posts: 15
Joined: Sun Aug 15, 2021 9:34 pm

Re: 8-bit parallel TFT driver for micropython ?

Post by Hugh-maingauche » Mon Aug 16, 2021 7:34 am

Thank you pythoncoder,

I am far from being an expert in micropython (and coding in general) and i'm trying to understand the way your driver is working. I see you are using viper code and I guess the purpose is to achieve the pixel operations in a reduced time (I think if not it would take ages to get a drawing/picture).

I understand from your code that the commands are sent through the SPI connection with the following instruction :

Code: Select all

self._spi = spi
...
self._spi_init = init_spi
...
if self._spi_init:  # A callback was passed
            self._spi_init(spi)  # Bus may be shared
...
#and in the _wcd and _wcmd functions :
self._spi.write(arg)
the SPI connection must have been previously opened in the main program (using hardware SPI I guess) and passed as an arg of the ILI9341 class, but to be honest I have no clue on how to convert this method to an 8-bit parallel bus.

With a few hints from you or other members I could try and do some experiments.

Thanks again !

fdufnews
Posts: 76
Joined: Mon Jul 25, 2016 11:31 am

Re: 8-bit parallel TFT driver for micropython ?

Post by fdufnews » Mon Aug 16, 2021 8:01 am

Concerning circuitpython.displayio it is a .cpp module that is compiled with circuitpython so it can't be added as is in micropython.

In my opinion, the fastest way to do it is accessing the registers of the microcontroler using machine.mem32. With mem32 you can read/write directly in the registers. This need to dive in the processor's datasheet to find the organization of the register and their addresses. It will never be as fast as a .cpp module, but it can be quite efficient if IOs are well allocated.
This code will obviously not be portable to another plateform than the one it was desgned for.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: 8-bit parallel TFT driver for micropython ?

Post by pythoncoder » Mon Aug 16, 2021 8:12 am

It's fairly challenging for a newcomer. The driver is documented here.

It uses 4-bit color to minimise the size of the frame buffer because of the relatively large size of the display. The color value has to be expanded to 16 bits at runtime which is time consuming hence the Viper code. You would need to retain this feature. 4-bit color does limit the capabilities of the display: you aren't going to be able to display pictures, but it works well with my GUI designs which use a limited set of colors.

You may want to start by stripping the driver down to its bare minimum. If you don't intend to do asynchronous coding, remove the do_refresh coroutine. Remove these two lines and this line along with the spi constructor arg, and this line. The constructor will now need to initialise all the pins and save them to bound variables.

You will now need to write a write method which takes an 8 bit value as an arg. That's the tricky bit which needs you to bit bang the pins to match the interface. Check the existing bit-banged cs and dc behaviours match your interface.

Finally replace all three calls to self._spi.write(x) with self.write(x).

I've probably missed a few details here but the gist is to replace 8-bit writes to the SPI interface with 8-bit writes to a parallel interface.
Peter Hinch
Index to my micropython libraries.

Hugh-maingauche
Posts: 15
Joined: Sun Aug 15, 2021 9:34 pm

Re: 8-bit parallel TFT driver for micropython ?

Post by Hugh-maingauche » Mon Aug 16, 2021 12:37 pm

@fdufnews : I saw this method in various posts while I was searching for a solution but I also understood that the use of this command (mem32 or mem8) was slowing the whole process.

@peter : Thank you for the hints. I still do not really understand how to rewrite the "write" function. Do you mean I could listen to the command using a scope or a logic analyzer and then cut the SPI sequence in an appropriate way to send it in 8 different parts to the data pins of my TFT using 8 available pins of my board? (-> that's what "parallel" means, right ?)

Since I'm here, and I have this TFT running with the Arduino framework using the MCUfriend driver, I could listen to all ports using various commands and rewrite everything from scratch, do you think that could be done?

... yeah I know it sounds like I'm looking for an excuse to buy me a logic analyzer ;) ...

Thanks again

Hugh-maingauche
Posts: 15
Joined: Sun Aug 15, 2021 9:34 pm

Re: 8-bit parallel TFT driver for micropython ?

Post by Hugh-maingauche » Sat Aug 21, 2021 6:05 pm

Update.

I started coding an IL9341 TFT driver using parallel interface. I did not use the piece of code suggested by peter, but I started from the MCUFriend_kbv driver written in C for the arduino framework, since I had it running on my ESP32 board. This avoid any source of erors since I'm using the exact same board and identical pins. What I do is basically tranlate, one function after another, the methods writen in C into micropython scripts.

It takes time but I'm learning a lot in the process !

For the parallel write and read method i'm using the mem32 method wich allows me to access and modify the following GPIO registers (from the ESP32 technical reference manual).
output registers :
GPIO_OUT1_W1TS_REG
GPIO_OUT1_W1TC_REG
-> very usefull to set only the pins you need

input register:
GPIO_IN_REG

Since I have now a logic analyzer (Yes!) I can see that speeds are significantly different between the arduino C driver and the Micropython method. (nanoseconds vs microseconds)

But I already managed to read, from the python REPL, the device ID sent by my TFT screen. I assume it is a good start.

When I'll have a full working display test script will probably try to modify the code to see what the viper decorator can improve.

Hugh

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: 8-bit parallel TFT driver for micropython ?

Post by Roberthh » Sun Aug 22, 2021 6:32 am

For faster access, you could use viper code, which adds a huge speed improvement over pure bytecode.
You may have a look at the driver for SSD1963, which uses an 8 bit parallel + 3 control lines. The low level drivers will have to be re-written, since they are made for PyBoard or RP2. The low level code is a mix of viper code and assymbly. The latter is not available for ESP32.

https://github.com/robert-hh/SSD1963-TF ... and-RP2040

Hugh-maingauche
Posts: 15
Joined: Sun Aug 15, 2021 9:34 pm

Re: 8-bit parallel TFT driver for micropython ?

Post by Hugh-maingauche » Sun Aug 22, 2021 3:32 pm

Thank you Robert,

I already found and used some of your posts on this forum (mostly this one).
I will certainly find some inspiration in your code, but since I'm in a learning process, I am first trying to make my own amateur code draw something using the mem32 commands and starting from scratch. If I succeed I will introduce some viper code to observe the difference.

Since I have now an operational method to send commands and data to my TFT, and to received data from it. I'am currently experimenting and understanding the use of the TFT ILI9341 driver datasheet (available here). As you can see, rewriting it all from scratch is just a way to learn, and from there i will play with the speed using different method. I understood that, to use assembly, I will have to change to another board, maybe a pi pico since I ordered one.

Anyway, any perspective on having the asm_thumb decorator for ESP32 micopython some day ?

Thank you again for the hints

Hugh

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: 8-bit parallel TFT driver for micropython ?

Post by Roberthh » Sun Aug 22, 2021 3:46 pm

Anyway, any perspective on having the asm_thumb decorator for ESP32 micopython some day ?
I did not follow that topic. And it will not be asm_thumb, but the espressif assembler. But viper is OK. Viper execution times are about twice the one of Assembler, but much easier to write. And direct memory access in viper is fast, way faster that mem32. Toggling a bit on GPIO you can create a ~10Mhz square signal using viper code.

Edit: Small example for a 1 MHz square at Pin 12 with ~500ps rise/fall time. If you shorten that you'll get a higher frequency

Code: Select all

from machine import Pin, freq

freq(240000000)

@micropython.viper
def run():
    p=Pin(12, Pin.OUT)
    GPIO=ptr32(0x3FF44004)
    while True:
        GPIO[1] = 1 << 12  # GPIO12 set
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12
        GPIO[1] = 1 << 12

        pass
        GPIO[2] = 1 << 12  # GPIO12 clear
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12
        GPIO[2] = 1 << 12

run()



Post Reply