Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

willing to give a lift #1

Open
zladay opened this issue Jun 30, 2020 · 109 comments
Open

willing to give a lift #1

zladay opened this issue Jun 30, 2020 · 109 comments

Comments

@zladay
Copy link

zladay commented Jun 30, 2020

I am into Sitronix ST7735 style (e.g. ili9341, ili9488 and the like) TFT controllers. I am also working with different TCs (STMPE811, XPT2046 etc.)
I am interested in a more ESP-IDF-ish driver infrastructure with a streamlined lvgl interface.
Willing to share ideas, designs and code examples.
Looking for some guidance since this is an empty space now.

@kisvegabor
Copy link
Member

Hi @zladay

Glad to see that you are interested in contributing to the project. Every help is welcome! 🙂

@C47D, the maintainer of lv_port_esp32, is also into this topic.

We are still in the planning phase, finding the right API, layers, etc. Do you already have any ideas?

@C47D
Copy link
Collaborator

C47D commented Jul 2, 2020

Hi @zladay,

The initial work is already done on the lv_port_esp32 repo, we will copy the lvgl_esp32_drivers directory into here, so users can use the drivers without needing to use the whole lv_port_esp32 project (we are also working on the lvgl and lv_examples repo to make them easier to use with ESP-IDF framework and other frameworks that use Kconfig).
We already have support for ILI9341, ILI9488, ILI9486, HX8357B/HX8357D, ST7789, ST7735S, SH1107, SSD1306 and IL3820 display controllers, and XPT2046, FT3236 and STMPE610 touch controllers.

Support for those drivers will be gradually added into the lvgl_drivers repo, this repo requires work.

Hopefully I can start working in this repo this weekend, I'm currently trying to fix a bug in the IL3820 display controller.

Any ideas let us know.

Regards

@zladay
Copy link
Author

zladay commented Jul 2, 2020

I am aware of the drivers you mention. I started working with an original version of @kisvegabor's ESP32 demo. (There was only ili9341 and xpt2046 support at that time).
I put together a completely new driver architecture initially targeting ili9341 and STPME811 only (this was the only hardware I had back then).
Design objectives included

  • support for multiple device (disp, indev) instances (same or different type) for concurrent handling of multiple screens
  • proper sync and thread safe operation
  • layered approach (drivers do not require lvgl)
  • streamlined SPI bus handling to get the most out of what ESP-IDF offers for increased efficiency
  • support for multiple hardware types (tft, tc)
  • device independent interface toward lvgl (lvgl does not have to 'know' the hardware type)
  • flexible initialisation and configuration (nothing is decided in compilation time - no conditional compiling)
  • efficient operation, low CPU and memory footprint
  • generalized conversion between TFT and TC coordinate spaces
  • etc.
    Most of the above goals are already met. Code is ready and tested.
    Current HW support include:
  • TFT: all Sitronix style TFTs including ili9341, ili9488, st7735s (st7789 probably works, too, but not tested)
  • TC: STMPE811, STMPE611, XPT2046
    I have not yet focused on adding support for the monochrome devices (e.g. SSD1306 style), however.

@kisvegabor
Copy link
Member

@zladay
That's great!
Could you upload your code somewhere to see how it looks like?

@zladay
Copy link
Author

zladay commented Jul 2, 2020

Sure.
I need some time to clean it up, though.
I will link it if it is done.

@C47D
Copy link
Collaborator

C47D commented Jul 5, 2020

I can upload the existing code here and you take over with your improvements, I'm sure there's a lot to be done with the existing code on the lv_port_esp32 display and touch (indev) code.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

I have already set up a repository with my drivers. In order to demonstrate the new approach, I need to come up with a new demo program, too. I have also set up a repository for this. I have been using a pretty old version of lvgl and an lvgl example. The "Write List Chart" one. I decided to include lvgl and lv_examples as submodules in this repo. It is now done. In this process, I switched to the master branch of both of these lvgl parts. At this moment, my ancient main.c does not compile with this. I have just recently realised that my favourite "Write Chart List" is gone. I need to find another demo program. (Any suggestions?) I had to fight the build system a bit, but it works now. Nothing serious, I am on track. It has, however, delayed my attempt to clean up and reorganise the driver code. I am asking for a little patience still.
I am planning to link my repos when it is worth to show them.
Given the above circumstances, however, I am not sure if it is worth to populate this repo with the old drivers now.
At least it does not help me at this moment.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

I have just changed the demo to lv_widgets_demo. Demo compiles and works on first try with the latest lvgl, wow. ili9341 (320x240) and xpt2046. Touch works, too. Demo and drivers are committed to the Github repos.
I can start the cleanup and reorganisation now.
I will keep you updated.

@C47D
Copy link
Collaborator

C47D commented Jul 5, 2020

Hi,

For start you can only show a label in the display, once it's working we can work on showing any of the lv_example demos.

Maybe we can use your architecture as a base and add the old drivers on top of that, updating any necessary code, we have to remember this repo will be used as submodule on the lv_port_esp32 repo, what do you think? I have some drivers so we can test with them.

The lv_port_esp32 repo has three components, lvgl, lv_examples and lvgl_esp32_drivers. I'm working on separate those, currently writing the Kconfig file for LVGL.

Do you have your repos available somewhere?

OFFTPIC:
It would be nice to have a discord server about LVGL.

@C47D
Copy link
Collaborator

C47D commented Jul 5, 2020

Also, are we going to compare both approaches? I haven't measured how busy the SPI master is in the current approach.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

As I wrote earlier, the lv_example problem is solved. This widget demo is scrolling through the TFT right now. (I am missing the "Write List Chart" BTW, that looked much nicer :)
Oh yes, the repo question! I did not think it is going to be this complex when I showed up here.
@kisvegabor asked me to upload the code somewhere. This is why I had to set up a repo for the drivers. Later, I realised that the driver code can not be used in itself. It has a completely new initialisation and lvgl interfacing. I could come up with some sort of documentation to describe how it is intended to be used, but I thought it is better to show it in a working demo application. This is why I started to build it and created a separate repo for this. The demo repo is constructed in a way that it uses lvgl, lv_examples and my drivers as submodules. My drivers are organised in a separate demo as multiple ESP-IDF components (yes, not one component, but more components). You can exclude certain components from the build. If, e.g. you do not have STMPE811 in your hardware setup, you can leave the corresponding component out. The driver repo is designed in a way that it is not strongly connected to the demo app. It can be used for any application preferably as a git submodule. This is meant to contain all the code. If you want to exclude something from the build, you need to make the necessary changes in CMakeLists.txt under main. (All components are included by default).
The components in the driver repo are organised in a hierarchical manner. There are common components (e.g. SPI handling for TC aka tc_spi). These are required for all the TCs. If you want to use STMPE811 e.g., it is enough to specify that component, it will pull in dependencies automagically (e.g. tc_spi).
This driver repo can, of course, also be a part of lv_port_esp32. lv_port_esp32 (its main.c) needs to be heavily reworked in order to use this drivers, though. Until that happens, we can use the repo I put together. It is not meant to be final. I have not yet implemented all my ideas in the drivers & the demo. I have yet not touched lvgl itself yet, but I have ideas how we could make interfacing much better with that. My code is meant to be a work in progress. I do not want to break the applications of many people using the publicly available code.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

You have a lot of questions :) I am trying to answer all, however.
Discord server (or any other real-time comms application)? I am definitely in. I have suggested this to @kisvegabor earlier.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

"Maybe we can use your architecture as a base and add the old drivers on top of that"
This is my intention, too. Later. This time, I think, it is a bit early. As I said, I need to reorganise the code first. It does not yet fully reflect my ideas and objectives yet, not to talk about other people. Your suggestions are also welcome.
My initial plan is to support the following hardware:

  • TFT: ST7735S, ILI9341, ILI9488
  • TC: STMPE811, STMPE610, XPT2046
    This is what I have and, therefore, this is what I can test. I also have a couple of monochrome OLED displays, but I do not want to go into that now (sub-byte addressing of the screen buffer, no touch, meek graphical experience).
    I know that your list is longer than this. If you want to add other drivers, you are welcome. I would put the emphasis on the architecture design first, however.

@embeddedt
Copy link
Member

embeddedt commented Jul 5, 2020

Discord server (or any other real-time comms application)? I am definitely in. I have suggested this to @kisvegabor earlier.

We typically prefer to use communication mediums that can be searched more easily (i.e. GitHub issues and the forum). It helps reduce the number of duplicate questions.

EDIT: I forgot to mention that the forum actually behaves very similarly to a real-time chat system; I'm pretty sure you get notified almost instantly if browser notifications are enabled.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

What do I mean by reorganisation?
Maybe it is news for you, but the above TFTs do not need separate drivers. Only the initialisation is different, and that difference is only one bit in the MADCTL register (RGB order).
I want to write a generic driver (not writing it actually, since any will do, of course) as a common component and a very thin specific driver for each of those.
Similarly, there is no visible difference between the STMPE811 and STMPE610. One generic driver is enough. But, just not to confuse people, there is going to be two different initialisations for it for each device.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

Performance measurements?
I did some with an oscilloscope and logic analyzer.
The color buffer is transmitted on the SPI bus via DMA (from day one). This is very fast. It can not be improved. Neither could I. What you can optimise is the setup of those transfers. Probably I did, but it is insignificant. I did a measurement with @kisvegabor's original demo (at 40 MHz if I remember correctly) and the bus was utilised at about 16% with the demo app slide show.
I, hopefully, could improve the robustness of the TFT driver, however. I saw that the public demo has a pinned task for some reason. I never used that technique and never experienced any problems (maybe I was not trying hard enough).
I could optimise the TC drivers. I added support for the FIFO (it is available on the STMPE devices only) and simplified the handling substantially.
Unfortunately, TCs work in polling mode. Further improvement could be achieved if lvgl supported interrupt driven indev handling.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

"Do you have your repos available somewhere?"
Yes, here on Github.
I will link them if it is going to be worth to see them. I need some days still.

@zladay
Copy link
Author

zladay commented Jul 5, 2020

There is one catch with the TFT drivers.
As I said, they do not need separate drivers.
There is a problem with the supported RGB modes (on the 4 wire MCU interface, aka SPI), indeed.
If I remember correctly, the supported modes are as follows:

  • ili9341: 16 bit RGB 565, 18 bit (takes 24 bits on SPI) RGB 666
  • st7735s: 12 bit RGB 444, 16 bit RGB 565, 18 bit (takes 24 bits on SPI) RGB 666
  • ili9488: 3 bit RGB 111, 18 bit (takes 24 bits on SPI) RGB 666

The RGB 666 seems to be Satanic for lvgl (not just because of its name). It takes 24 bits on the SPI. 1 octet per channel. The 2 LSBs are ignored (do not care). This goes well with 24bit color depth (some resolution is lost, only 262k colors instead of 16.7M). Unfortunately, however, this does not go well with lvgl. Lvgl dropped support for 24 bit color depth.
This is a real pain with the ili9488, since it does not support 16 bit RGB (on SPI).
One ugly workaround exists (as demonstrated in the public driver, too). It is the conversion of the 16 bit RGB 565 into 18 bit (effectively 24 bit) RGB 666 in the driver code before transfering the buffer to the DMA to send it out. Ahhh.
Performance? Memory footprint?
The only correct way would be if lvgl supported 24 bit mode. I looked at the relevant code portions, and it was visible that it would take substantial effort to add it. In fact, it would necessitate a serious refactoring of the low level color handling there.
Actually, the situation is even worse. Only one color depth is supported concurrently. Color depth needs to be decided at compilation time.
Even though my new driver architecture supports the concurrent handling of any number or any type of displays, lvgl is limited to a single RGB model. It is, therefore, impossible e.g. to use a monochrome OLED together with a color TFT in one device.
Could you please inform @kisvegabor about it?
What do you suggest with the ili9488? Shall the driver do the ugly workaround?

@kisvegabor
Copy link
Member

Chat
Can you describe why do you think the chat is required?

Personally I'm happy with forum/github. I got a lot of emails and notifications from these platforms. On top of these a chat - that requires real-time presence - would be too much for me. Besides - as @embeddedt mentioned - the issues and forum topics can be easily linked to answering questions quickly.

TFT + monchrome
You can use the set_px_cb to write the buffers as you wish. They are used anyway because some monochrome displays pack 8 pixels into a byte.

Architecture design
We should keep in mind that the new drivers' interface shouldn't be specific to ESP only.

  • Work with or without OS
  • Support I2C, Parallel port (8/16 bit), SPI, frame buffer in SRAM
  • Support vendor-specific drivers? (E.g. STM MCUs with internal FB needs quite the same driver

So I see these abstraction layers:

  • Display (mainly commands to configure, write pixels, set orientation (?))
  • Display communication (I2C, SPI, ..., OS or no-OS)
  • Peripheries (How to initialize and use SPI, I2SC ... )

What do you think?

@zladay
Copy link
Author

zladay commented Jul 6, 2020

Chat

@C47D proposed this. I will leave this to him to answer.

TFT + monochrome

I am aware of this. I never said that the drivers can not be written for MC OLEDs (especially because those are already written). I just said that drivers tend to be complicated because of the sub-byte addressing.
TFT drivers are different since the minimum practical resolution (16 bit) exceeds one byte. There are problems with color depth support. There is sometimes a mismatch between lvgl and the TFT capabilities (ili9488 is the only known example for me so far). Currently, my drivers support 16 bit color depth for the TFT only. It is, however, not difficult to add support for any RGB model the HW supports.
Let's see what is available in general:

  • RGB 111 (8 color) - no support for this in lvgl, but I doubt that anyone is missing this
  • RGB 444 (4k color) - not supported by lvgl, but I doubt that anyone is missing this
  • RGB 565 (65k color) - supported by lvgl and drivers
  • RGB 666 (262k color - uses 24bit format on the bus) - would need 24 bit color depth in lvgl, but it is not supported
  • RGB 888 (16.7M color) - ditto

Lvgl does support 32 bit depth (RGB 8888). This is incompatible with RGB 666, so (down) conversion is required. It is just slightly better than up converting from RGB 565, since it is offering only 2 bits extra color resolution at a cost having 2x bigger buffer sent from lvgl compared to the RGB 565 case.
My preference is to sort out the RGB model issue (What is supposed to be supported by the driver?) before going to an entirely different area of monochrome displays (there are no questions related to MC displays on my side so far). I am, however, open for different suggestions.
I was, however, making the point that mixing entirely different displays (e.g. monochrome and color TFT) in one device is currently impossible because of an lvgl limitation (even though my driver architecture supports that scenario apart from the fact that monochrome drivers are not yet added).

Architecture design

I fully agree on your objectives in theory, but there are some problems in practice.
When designing the driver architecture I kept the guidelines you mention in my mind. I have (distant) plans to extend this to other frameworks, too (e.g. mbed, ST Cube, etc.)

Work with or without OS

I am not sure if we understand the same for "with OS". I suppose that it means that the development framework contains a scheduler with task handling and synchronization primitives (e.g. FreeRTOS).
When a framework has this (e.g. ESP-IDF), it is actually a must to do proper synchronization and CPU monopolization (busy waits) should be avoided. In an environment like this, a "no OS" version does not make sense.
When there is an OS, FreeRTOS can not be taken for granted. Some frameworks (e.g. mbed, Nuttx) use something else for this purpose. The only way to make the drivers OS independent is to develop something like an "OS abstraction layer". It is not impossible, but I consider this idea a bit farfetched now.
What is the reason behind having drivers for a "without OS" scenario? I can not recall a useful framework that does not have an "OS". (Fun fact: Even Arduino for ESP32 does have an OS. AFAIK, the ESP32 Arduino core is implemented using ESP-IDF. The "OS" stuff is, however, carefully hidden under the hood.)

Support for multiple transport interfaces (I2C, parallel, etc.)

I fully agree on this, but current code supports SPI only. Transport layer can be detached and a multiplexer can be built in to use multiple different types of interfaces. As I said, I have not done it yet. Simply because I did not want to jump that long right now.

Support for vendor-specific drivers (e.g. ST)

I do not really get it. All the drivers are vendor specific (Sitronix, Illitec, Solomon Systech, Xptek). The only difference in the mentioned ST case is that the display driver hw is within the MCU (but the touch controller is external, it is STMPE). The GRAM can be reached via an internal high speed parallel bus. This is technically (as far as the drivers are concerned) not too different from the situation when an external controller is connected via a parallel interface.
(BTW, I have been having an STM32F7 Discovery kit (TFT+touch) for a while. It was set aside, because I could not find a good graphics library. I do not want to look at it now, because ESP32 drivers would be delayed then :)

Abstraction layers

My architecture is similar to what you wrote. As I said, it is missing the transport multiplexing layer yet. I want to make it available before it happens, because I am interested in the opinion and suggestions of the people showing up here as early as possible. It is not perfect, but I hope that it is going te be a good starting point and we could develop it further.

Anyways, I think I need to stop talking too much. It takes away time from reorganization of the code. I am progressing with that. I will keep you updated.

@kisvegabor
Copy link
Member

RGB 666 and RGB888 are really not supported and I don't see an easy way to do it because the current formats can be natively addressed in every case (1,2 or 4 byte color types). Let's postpone it a little.

mixing entirely different displays (e.g. monochrome and color TFT) in one device is currently impossible because of an lvgl limitation

"Impossible" is not entirely true. You can convert the colors to the desired format in the flush_cb. It's ugly though but not impossible. For small displays, it shouldn't make a big difference,

If support for e.g. ST framebuffers fit well into the design then all good fro my side :)

@zladay
Copy link
Author

zladay commented Jul 6, 2020

Just a small clarification for the RGB problem.
There is no need to support RGB666 in lvgl. RGB888 would suffice. Why?
TFTs (including ili9488) expect RGB666 in 3 bytes. One byte for each color channel. These 6 bits are expected in the MSB part of the byte (D7-D2). TFT controller disregards D1-D2. Using RGB 888 here is fine. The HW does the "conversion". Two LSBs would be lost for each channel. Application graphics designed for RGB 888 would work with a slight image quality degradation.

The workaround you mention makes the concurrent use of a monochrome and a color TFT possible indeed. It is not impossible, you are right, but this is so ugly, that I was not even considering this as a "possibility".
If color TFT uses 16 bit RGB, the same is forced to the monochrome. This requires 16 times bigger buffer than normally needed plus the CPU cycles of converting and the 1 bit/pixel buffer that needs to be sent on SPI.
RAM is pretty limited on the ESP32. I had to decrease the flush buffer size even when I had only one ill9488 with the conversion needed, otherwise the app would not fit (linker error). With small flush buffers performance suffers even more.

@C47D
Copy link
Collaborator

C47D commented Jul 7, 2020

I suggested the chat because it's more real time, only intended for developers, not for general use. But thinking again about it, we should make the discussion public so anyone can add suggestions.

Work with or without OS
I am not sure if we understand the same for "with OS". I suppose that it means that the development framework contains a scheduler with task handling and synchronization primitives (e.g. FreeRTOS).
When a framework has this (e.g. ESP-IDF), it is actually a must to do proper synchronization and CPU monopolization (busy waits) should be avoided. In an environment like this, a "no OS" version does not make sense.
When there is an OS, FreeRTOS can not be taken for granted. Some frameworks (e.g. mbed, Nuttx) use something else for this purpose. The only way to make the drivers OS independent is to develop something like an "OS abstraction layer". It is not impossible, but I consider this idea a bit farfetched now.
What is the reason behind having drivers for a "without OS" scenario? I can not recall a useful framework that does not have an "OS". (Fun fact: Even Arduino for ESP32 does have an OS. AFAIK, the ESP32 Arduino core is implemented using ESP-IDF. The "OS" stuff is, however, carefully hidden under the hood.)

We need to remember that the drivers supported by the lv_port_esp32 are planned to be ported to lv_drivers. Which are going to be used in bare metal and RTOS projects.

I fully agree on this, but current code supports SPI only. Transport layer can be detached and a multiplexer can be built in to use multiple different types of interfaces. As I said, I have not done it yet. Simply because I did not want to jump that long right now.

SPI and I2C are currently supported because that's the interface of the displays we have available, I can't do any parallel because I have no display to test it and sometimes is slower to wait for test results. Sure we can do an abstraction layer so it doesn't matter what SPI, I2C, parallel or custom interface is used.

So I see these abstraction layers:
Display (mainly commands to configure, write pixels, set orientation (?))
Display communication (I2C, SPI, ..., OS or no-OS)
Peripheries (How to initialize and use SPI, I2SC ... )

I think we shouldn't do everything, users should init the SPI, or any peripheral they are using. They should set some callbacks (functions that takes an array and send it over whatever interface they are using), we only need to arrange that array of bytes with the color data.

As @zladay suggested, we can make a generic ILI9x driver and make the initialization and rotation code driver dependent.

@kisvegabor
Copy link
Member

@zladay

RAM is pretty limited on the ESP32. I had to decrease the flush buffer size even when I had only one ill9488 with the conversion needed, otherwise the app would not fit (linker error). With small flush buffers performance suffers even more.

If you use 32 bit color depth for LVGL you can make the conversation in the color buffer as well without allocating a new temporary buffer.


Seemingly we are on the same page. 🙂

@zladay
Copy link
Author

zladay commented Jul 7, 2020

@zladay

current code supports SPI only

@C47D

I2C are currently supported

Let me resolve the contradiction here.
I referred to my code.
@C47D talks about the public version.
I am aware that the public version has support for eye squared see.

I2C is practical (or available at all) at small, mostly monochrome displays. I have already written SSD1306 (and SH1107 which is almost the same) drivers for an other platform (mbed, if I remember correctly). I have experience with that even on I2C. So, I can tell you that I am not afraid of them.
The reason I would not focus on that now is that I want to finish the work what I started (written above) first. This is bringing a lot of news into the driver arena already.
My second priority is to extend this to other transports (I2C and parallel).
@C47D seems to have a different agenda and priorities. He is focused on the a breadth of HW support instead (whatever architecture those are in).

@zladay
Copy link
Author

zladay commented Jul 7, 2020

We need to remember that the drivers supported by the lv_port_esp32 are planned to be ported to lv_drivers. Which are going to be used in bare metal and RTOS projects.

I am not sure that I know what "drivers supported by the lv_port_esp32 are planned to be ported to lv_drivers" means.

I am also not sure if your comment was intended to support or dispute my thoughts.

If it is RTOS, it is obviously not a "without OS" scenario.
If bare metal, you are not confined to a certain framework. You can choose whatever scheduler or OS you want.

@zladay
Copy link
Author

zladay commented Jul 7, 2020

I can't do any parallel because I have no display to test it and sometimes is slower to wait for test results.

I do have some TFTs with parallel interface, but I do not want to work with them now, because

  1. Parallel does not go well with ESP32, because there are not many pins to form the bus
  2. I do not want my efforts to be too fragmented

@zladay
Copy link
Author

zladay commented Jul 7, 2020

I think we shouldn't do everything, users should init the SPI, or any peripheral they are using. They should set some callbacks (functions that takes an array and send it over whatever interface they are using), we only need to arrange that array of bytes with the color data.

I disagree strongly. (It is a pretty long story why.)

@zladay
Copy link
Author

zladay commented Jul 7, 2020

As @zladay suggested, we can make a generic ILI9x driver and make the initialization and rotation code driver dependent.

It is almost ready now. I am currently fighting with the rotation story. It is not simple if you want to give a general solution. There are a couple of mines there. I am sorting this out at this moment. I hope that you are going to be able to see the results soon.

@zladay
Copy link
Author

zladay commented Jul 7, 2020

If you use 32 bit color depth for LVGL you can make the conversation in the color buffer as well without allocating a new temporary buffer.

Maybe I do not fully understand how RGB 8888 (32 bit color depth) works, but I am afraid that your above solution does not work.

RGB 8888 mode populates the flush buffer with 32 bits/pixel. Four bytes for pixel one, then four bytes for pixel two and so on.
For RGB 666 e.g., the TFT expects 3 bytes/pixel.
If I am receiving a buffer from lvgl in RGB8888 format, how could I transfer it to the SPI without converting it and allocating a buffer for conversion?
The received buffer should be sent out en bloc, not per byte, since SPI/DMA does the heavy lifting. It expects an entire buffer that is already properly formatted.

@zladay
Copy link
Author

zladay commented Aug 4, 2020

@kisvegabor

Could you organize the project into directories like:
....
The goal would be to easily see where to add the custom (board/mcu/platform/framework) specific code and what are the independent part.

I could, of course, reorganize this, but I do not want to. I fully agree on the opinion that it is not well structured, but there is a reason why it is how it is.
I wanted to divide parts of the code into ESP-IDF components. Such a component is defined by a folder (directory) and a CMakeLists.txt file sitting in it. (The content of that file matters, too, of course.). In ISP-IDF, the component is the elementary build unit. Each component is compiled to a .a (static library), therefore, a component is the smallest unit that can be either included into or excluded from the image in the linking process. With this organization, there is the possibility for the app developer to minimize the code by including only the necessary components into the binary image.
I understand that the above written considerations do not necessarily preclude the reorganization of the code similarly to what you wrote. The problem is, however, that the recent ESP-IDF build system does not support the concept of nested components. Even the recent organization required an addition to the project level (project root) CMakeLists.txt file. This is EXTRA_COMPONENT_DIRS components/lvgl_drivers_esp32/components. If I reorganized this in a hierarchy, a lot of similar entries would be required in EXTRA_COMPONENT_DIRS. Since this is in the project root, it is the app developer who has to do it correctly. It is too much hassle and very error prone.

@kisvegabor
Copy link
Member

I see. However, the code size is not an issue as the unused components can be disabled via menuconfig.

What about one folder with names like "platforms" with files like "esp32.c" and later "stm32f4.c" etc.

BTW, in the filename of "stsd_common.c" what does "stsd" mean?

@zladay
Copy link
Author

zladay commented Aug 4, 2020

@kisvegabor

I see. However, the code size is not an issue as the unused components can be disabled via menuconfig.

How?

@C47D
Copy link
Collaborator

C47D commented Aug 5, 2020

This is just my personal opinion:
A complete rewrite of the esp32 drivers will take some time, and we can wait for it because it will improve the code by a lot, thanks @zladay. My hurry to move the driver code over here is because this repo has been empty for over a month and we had some users waiting for this.

I would like to propose some things:

  • Move the code over here on the main branch.
  • The new driver architecture on a development branch.
  • I can ad support for the missing monochrome displays, under the new architecture.

What do you think @zladay?

@kisvegabor :
The current lvgl_esp32_drivers supports only one display, the new architecture can support multiple, I haven't worked with multiple-display applications, so I got a question, all the displays use the same color depth? Or we can use one monochrome and another RGB565 display?

@zladay
Copy link
Author

zladay commented Aug 5, 2020

@C47D

This is just my personal opinion:
A complete rewrite of the esp32 drivers will take some time, and we can wait for it because it will improve the code by a lot, thanks @zladay. My hurry to move the driver code over here is because this repo has been empty for over a month and we had some users waiting for this.

I am neither trying to argue against taking the public demo code here, nor trying to argue for it.
I am just attemping to give a view from a different perspective. There is no code here, but we are at comment number 92 already. We can not say that we did nothing. We can not even say that there is just a discussion, because the new code exists and it works. If we look at this repo as a place for discussing where the drivers should go, this is not empty, but rather, it is on the contrary.
I do not feel the void here.

You say that there are "users waiting for this". Maybe. The fact is, that there are only 4 participants on this issue and this is the only one opened for this repo. I do not hear crowds banging on the door demanding for code here.

I would like to propose some things:

  • Move the code over here on the main branch.
  • The new driver architecture on a development branch.
  • I can ad support for the missing monochrome displays, under the new architecture.

What do you think @zladay?

I am trying to be honest. I am feeling very little motivation to add support for the monochrome displays. I consider developing the driver architecture (although it is fully functional, but has not been finished yet) as a much higher priority. Why do I want to defer the addition of monochrome display support? I think that the primary target of lvgl is the color displays with touch indev capability. The monochrome modules are not just lacking the color depth, but they are usually very low resolution and have no indev built in. There are technical problems with them, too. They need sub-byte pixel addressing. This needs to be figured out how we can handle that. (I am expecting @embeddedt replying to this that we can use rounder_cb.)
In the meantime, there are a couple of not well worked out details with RGB model support and encoding description even with the color displays.

BTW, I have nothing against any of the three bullet points above. Maybe with one addition. The new driver architecture code should not be made public now. Contributors are, however, welcome. I can give access to my private repo to anyone who is serious about it. I do not yet want to handle user requests with regards to the drivers. Neither want I hear rants that there are fundamental changes sometimes and that broke her app code. I would roll this out when it is reasonably thought after and tested not only by myself, but some other people also.

@kisvegabor :
The current lvgl_esp32_drivers supports only one display, the new architecture can support multiple, I haven't worked with multiple-display applications, so I got a question, all the displays use the same color depth? Or we can use one monochrome and another RGB565 display?

This was not addressed to me, but if @kisvegabor allows, I am trying to answer this.
Yes, all the displays use the exact same color depth and RGB encoding. It is an lvgl limitation.
The usage of a monochrome display and color one concurrently is still possible.
How? You must use 16 bit depth (RGB565) setting in lvgl. A monochrome display driver (e.g. SSD1306) needs to be written for (or ported to, if you like it better) the new architecture. This driver has to convert the color buffer into 1 bit depth before handing it over to the transport layer.
I barely dare to say that the aesthetics of this solution is not necessarily comparable to the Venus de Milo statue.

@zladay
Copy link
Author

zladay commented Aug 5, 2020

@kisvegabor

I was thinking about the question of @C47D with regard to multiple displays.
I gave an answer based on the assumption that lvgl itself supports multiple concurrent displays indeed.
There are some related questions. One such question is: Is it true that only one particular display can be in a state of "being refreshed" at a given time?
I have found only the following function to identify which display needs to be notified about flush finished:

lv_disp_t * _lv_refr_get_disp_refreshing(void)

It seems to be obvious from this prototype that the answer for the above question is yes.
Why? Why is it not possible that multiple displays are being refreshed (waiting for notification about the completion) at the same time?

@embeddedt
Copy link
Member

Why? Why is it not possible that multiple displays are being refreshed (waiting for notification about the completion) at the same time?

The library is built with a single-threaded model in mind, so only one set of objects can be processed at a time. Hence there is no major advantage to refreshing multiple displays.

Theoretically it would be possible to cache the buffers and render them in parallel but the assumption is that there normally isn't enough available RAM on the device to make this worthwhile.

@zladay
Copy link
Author

zladay commented Aug 6, 2020

@embeddedt

The library is built with a single-threaded model in mind, so only one set of objects can be processed at a time.

Being it single-threaded does not preclude processing multiple set of objects "at a time".
As far as I understand, lvgl - although single threaded - uses an event loop type scheduler.
With that, it is possible to have a loop that iterates on display objects. If a display is in "being refreshed" state, there is nothing to do with that. The loop goes on to the next display. If display refresh is relatively slow to the rendering, multiple displays can be in "being refreshed" state at a given moment. Your explanation - in itself - does not hold.

... render them in parallel but the assumption is that there normally isn't enough available RAM on the device to make this worthwhile

This is again something I do not understand. AFAIK, lvgl works the following way.

  • I define a driver (the allocated memory area, i.e. the buffers are assigned to this)
  • I register this driver with lvgl and this creates a new display instance (puts it in a linked list)

This means that buffers are separately allocated to the display instances. They do not share a single (set of) buffer(s).

If "the assumption is that there normally isn't enough available RAM on the device to make this worthwhile", why is there the possibility to define as many displays (together with their memory footprint) as I wish?
From a different perspective: if I can define any number of displays (with their dedicated resources), how can it happen that only one can be in "being refreshed" state at a given time?

This is the current "refresh finished" function. It is adapted from @kisvegabor's original demo:

void refresh_finished(void *notif_id)
{
    lv_disp_t *disp = _lv_refr_get_disp_refreshing();
    lv_disp_flush_ready(&disp->driver);
}

A particular display needs to be notified (penultimate line) via its driver. It is ok. It would be normal if this was the only thing I needed to do. (I could guess the lvgl display instance, since it is in a one-to-one type relation to the driver display instance).

Why do I need to call _lv_refr_get_disp_refreshing()?
Why is it that there could be only one being refreshed? Is this really the case?

@kisvegabor
Copy link
Member

@zladay
The unused display and touch drivers are disabled like this.
Let's say in ili9341.c:

#if CONFIG_LVGL_TFT_DISPLAY_CONTROLLER_ILI9341
... all the codes here ...
#endif

Being it single-threaded does not preclude processing multiple set of objects "at a time".
As far as I understand, lvgl - although single threaded - uses an event loop type scheduler.
With that, it is possible to have a loop that iterates on display objects. If a display is in "being refreshed" state, there is nothing to do with that. The loop goes on to the next display. If display refresh is relatively slow to the rendering, multiple displays can be in "being refreshed" state at a given moment. Your explanation - in itself - does not hold.

What you say is theoretically possible but imagine what would happen in the practice:

  1. Display 1 has a large area to refresh and the display buffer is limited so it will be redrawn in 2 parts.
  2. The first part of Display 1's area is redrawn and we need to wait 3 ms for the DMA.
  3. While waiting let's render Display 2. Rendering took 15 ms.
  4. Go back to Display 1 and render the second area.

So there will be a 15 ms delay between rendering the 2 areas of display 1 which looks very ugly e.g. in case of a scrolling animation.

BTW, you can use the same disp_buf for multiple displays. I never tried it but it should work. If the buffer is occupied for any reason (indicated by the flushing flag) LVGL will wait until it's freed. Doesn't matter which display uses the buffer.

@C47D
I do agree to move the drivers here and add @zladay's drivers to a dev branch. It moves us closer to my dream to turn the lvgl and lv_examples repo to ESP components. 🙂

@zladay
Copy link
Author

zladay commented Aug 8, 2020

@kisvegabor

Drivers reorganization

Touch controllers as plug-ins

As planned earlier, touch "subsystem" now supports a plug-in architecture similarly to the {display} - {display-transport} relation. Support code for a particular hardware (e.g. XPT2046) needs to implement a touch controller object with the appropriate interface. This controller "driver" has very little functionality. Apart from the object housekeeping stuff, its only task is to provide raw X, Y data read from the controller. Any other touch related function (coord space translation, orientation handling, etc.) is in an object where the touch controller "drivers" plug in to.

If there is a need of support for a new touch controller, only a new implementation of this touch controller "driver" is required.
New touch "drivers" (plug-ins) are written for all the devices supported earlier. The new setup is tested and works. Code is pushed to GitHub.

Object hierarchy, modularity

Current object hierarchy looks like this:

{device} - {touch controller}
 
{display} - {disp transport}

I want to take a step further and merge the two object "trees". Apart from that, I am about to detach backlight handling from the display driver, since it does not belong to there. The resulting object hierarchy should look like this:

{device} -+- {touch controller}
          | 
          +- {backlight controller}
          |
          +- {display controller} - {display transport}

{device} is a new object. It is meant to encapsulate everything a physical device (display panel) contains. It is the single root of the (planned) object tree. It has a couple of advantages over the earlier approaches.

It is a unified interface towards the hardware. For example, if orientation needs to be set (or changed later), it is enough to specify it at one place and one place only. Either in the device interface configuration (when creating the object) or with a later call on the object. Display orientation change is reflected in screen layout and in touch coordinate translation simultaneously.

Another advantage is that this architecture is modular. Modules (objects) are completely independent. If they implement the appropriate docking interface, they can be used in this system. If someone wants to implement support for a new touch controller e.g., she can use an existing object implementation as a template. She does not need to understand coordinate space translation or orientation handling, not to talk about display controllers. This approach is also very flexible since the different components are completely detached. Any combination of those is giving a working device. If someone wants to set the intensity (or the color) of the back light, she can implement a new back light controller for that. App developers can choose from any implemented back light controllers or they can roll their own.

Drivers - lvgl boundary

Only the leaf nodes are closely tied to the development framework (ESP-IDF in this case) in the above object tree. {device} and {display controller} do not contain platform specific code. There is a possibility that lvgl core (or how to call that?) could just swallow them. And yes, including the display "drivers".
Theoretically, it is also possible that the {touch controller}s are broken down into two objects with the transport implemented in a different one similarly to how it is with displays. It is, however, questionable if this is practically justified, since {touch controller}s do not do too much.

Framework or platform independency

As I said above, non-leaf objects are not closely tied to the particular framework. They are, however, not fully independent. There are two areas where they are still bound to the framework:

  • error handling
  • general (not hardware related) OS stuff (this includes e.g. semaphores, queues, blocking waits and perhaps dynamic memory allocation also)

Error handling

As far as error handling is concerned, I still do not know what the best approach would be to achieve framework independency.

General OS stuff

General OS stuff is different. I think I have a workable solution for that. This is to implement an OS abstraction layer. This is what I have already proposed. The implementation of this abstraction layer (AL) is not only for the drivers, tough. The whole lvgl could and should use it. If it is properly implemented, lvgl core (or how to call it?) could be refactored in order to become multi-threaded safely. Multi-core MCUs are appearing on the market. It is happening not just on the high end, since even the dirt cheap ESP32 chips have dual cores in most cases. If lvgl stays single threaded, she is not going to be able to use half or more (depending on the number of cores) of CPU resources.
Someone can worry about the "no OS" scenario. To be honest, I do not understand this. With 32 or 64 bit MCUs with multiple cores and hundreds of kilobytes of RAM, who would want to use a rudimentary "no OS" scenario? A "no OS" scenario might be justified with a 8 bit or a low end 32 bit MCU. Those are, however, recommended for very simple tasks. No high resolution color displays are involved there. There would not be enough RAM for screen buffers or CPU power for an acceptable responsiveness anyway. I think if lvgl is aiming beyond hobbyist's tinkering, a "no OS" scenario is a dead end.

Code modularity, reusable code blocks

As lvgl is growing in functionality, it might easily get stuck in the mud with its monolithic architecture. As I am trying to demonstrate with the drivers, an object oriented approach should be followed instead. (BTW, for God's sake, #define is not an interface!) As seen here, object oriented code (even with inheritance) can be written in plain C also. This is, however, begging for C++ instead. The drivers would be architecturally the same in C++, but the whole thing would be more straightforward, concise and easier to read. And this does not apply to the drivers only. It applies to the OS AL and everything.

I would appreciate your comments.

@kisvegabor
The new code is reorganized, components and files were renamed in order to reflect the architectural changes. I kept the flat structure of components, since I have not received any response to my question how we could get effective code modularity without it and without resorting to overly complex and error-prone CMakeLists.txt files.

@zladay
Copy link
Author

zladay commented Aug 8, 2020

@kisvegabor

@zladay
The unused display and touch drivers are disabled like this.
Let's say in ili9341.c:

#if CONFIG_LVGL_TFT_DISPLAY_CONTROLLER_ILI9341
... all the codes here ...
#endif

First, I did not understand what you are referring to here. And after a couple of minutes of thinking, I came to the conclusion that this is the response to the question how we could include/exclude unnecessary modules from the image (instead of using what the framework offers for this purpose).
Well, technically speaking, you are absolutely right, but this answer made me cry in pain.
I want to chop a tree trunk, and I have a chain saw. Are you really saying that I should use a stone axe instead?
I am sorry, but I am a little bit slow in adaptation, I still need some more time to become a real experimental archeologist.
Shall we also initiate pull requests to the ESP-IDF team in order to extend this technique to the rest of the components?
Maybe mbed folks would be interested in this, too.

Being it single-threaded does not preclude processing multiple set of objects "at a time".
As far as I understand, lvgl - although single threaded - uses an event loop type scheduler.
With that, it is possible to have a loop that iterates on display objects. If a display is in "being refreshed" state, there is nothing to do with that. The loop goes on to the next display. If display refresh is relatively slow to the rendering, multiple displays can be in "being refreshed" state at a given moment. Your explanation - in itself - does not hold.

What you say is theoretically possible but imagine what would happen in the practice:

  1. Display 1 has a large area to refresh and the display buffer is limited so it will be redrawn in 2 parts.
  2. The first part of Display 1's area is redrawn and we need to wait 3 ms for the DMA.
  3. While waiting let's render Display 2. Rendering took 15 ms.
  4. Go back to Display 1 and render the second area.

So there will be a 15 ms delay between rendering the 2 areas of display 1 which looks very ugly e.g. in case of a scrolling animation.

I think this is an arbitrarily fictitious scenario. It is not based on inevitable and necessary conditions. This is also rather hazy. "large area", "redrawn in two parts". How large? Does not it fit the RAM in one piece? Lengthy rendering? What MCU, what screen size? I am sorry, but it does not make too much sense to me.
You could simply sa, that you can not envision an MCU environment where two displays could be handled "simultaneously" with acceptable responsiveness. This would make sense. In this case, however, I do not understand what is the point in the possibility of creating multiple displays in lvgl.

Let us suppose that your example is real. (It is rather arbitrarily taken, but can be real.) In that example the responsiveness of the second screen suffers. Is that a better scenario than the first being a bit flattering?

Even that flattering can be prevented (at the cost of delayed service for the other display). This is easy. Rendering of the next screen of the second display does not start before the rendering of the whole series of screens finishes on the first. If I understand you correctly, this is exactly what happens. (Otherwise you can not prevent flattering on the first.) But this has nothing to do with the question "why can multiple screens not be in "being refreshed" state simultaneously"?
In your example, the problem is that the CPU is the bottleneck (rendering). You can not remedy or improve this situation by saying that "there should be only one display in "being refreshed" state at a given moment". In fact, it does not have any effect on that. What you could do instead is that you schedule rendering differently.
Let us not forget, that the sequence of events is always as follows:
rendering completes -> flush initiated -> display in "being refreshed" state

I hope that I could describe why I still do not understand why only one display can be in "being refreshed" state at a given moment? Why is _lv_refr_get_disp_refreshing() necessary? It does not solve the problem you are describing in your example (but rendering rescheduling does, but that is completely unrelated).

Now, let us see the opposite scenario. It is not the CPU, but the bus transport throughput that is the bottleneck. Let there be two displays on separate SPI buses. At least one of them is (or both are) slow (only works on low clock rate). The rendering of the second display is ready (while the first one is still being refreshed). Refresh of the second can not start until the first finishes, since there can be only one in "being refreshed" state at the given moment. This blocks everything. Rendering is blocked, too. (In a double buffer scenario it happens a bit later, but it does.) Is it good?

Let me summarize this: we have something that does not help in the example you gave, but can potentially lead to a screeching slowdown in mine. It does not make any sense to me.

BTW, you can use the same disp_buf for multiple displays. I never tried it but it should work. If the buffer is occupied for any reason (indicated by the flushing flag) LVGL will wait until it's freed. Doesn't matter which display uses the buffer.

I do not want to do this. The only reason I mentioned this was that memory footprint would not increase substantially with multiple displays only if they shared buffers. This is not the case.@embeddedt said that the system is designed this way, because there is not enough memory. OK. But, if this is the case, why are multiple displays supported? This is inconsistent.

@C47D
I do agree to move the drivers here and add @zladay's drivers to a dev branch. It moves us closer to my dream to turn the lvgl and lv_examples repo to ESP components. 🙂

@C47D
The license permits you to do whatever you want with my driver code. You can even publish that (you can put that in a public repo), but the license also says that it is on an "as-is" basis, no warranties, no support, no nothing.
I just want to remind you that I am not willing to provide user support at this stage. My preference is to keep it only for knowledgable developers who are supposed to bring no less than they take.

@kisvegabor
Copy link
Member

@zladay
In the new architecture, I still can't see where to put my platform dependent part. Honestly, this the only part which is really important for me. The internals of drivers is not interesting for the users. They care about 2 things:

  1. How to port it to my platform (add custom transport layer):
    I understand that you're using ESP as an example system to see how things working together. However the "platform interface" is a very important part of the drivers and I find it not reasonable to move forward until it's missing.
  2. Configure existing displays and input devices:
    Kconfig, menuconfig, #ifdef are common patterns to exclude some parts of the code. CMake also works - of course - and can be even better but we are developing a platform-independent library (independent from the build system too) and can't force the user to use any of them. Obviously it's good if we support some common build systems.

General OS stuff

We also try to be flexible in this regard as well. All you need to do is using a mutex around LVGL related functions. It's some work from the user but very flexible.

As lvgl is growing in functionality, it might easily get stuck in the mud with its monolithic architecture.

We don't want to grow to the infinity with features. In fact, we are planning to clean up features for v8. We need to clearly see what are the boundaries of LVGL. The goal is to make it as lean as possible.

As seen here, object oriented code (even with inheritance) can be written in plain C also. This is, however, begging for C++ instead.

C surely will remain the main language for embedded for some years. If C++ will be more popular for user code (e.g. Arduino) probably it's still worth keeping the core features in C for performance reasons. The Linux kernel is also in C although a CPU-based system has "unlimited resource" resources compared to an MCU.


Multi-display handling
It's not really an arbitrary example. If multiple displays use the same transport and refreshing is mixed the screen refreshing will lag. It's better to finish one display to get a quick refresh.

The case is different if the display transports are independent (e.g. on different SPI). In this case, LVGL can render one screen while an independent display is refreshed. It also can lag if rendering takes longer the refreshing the other display. But it's a much clearer situation. If the user can indicate which displays have independent transport then LVGL really can make some parallelism.


The license permits you to do whatever you want with my driver code. You can even publish that (you can put that in a public repo), but the license also says that it is on an "as-is" basis, no warranties, no support, no nothing.
I just want to remind you that I am not willing to provide user support at this stage. My preference is to keep it only for knowledgable developers who are supposed to bring no less than they take.

@C47D made a great job in adding and testing drivers, answering issues, reviewing PRs, fixing bugs and so on. Big thank you for this from me and from the people you have helped!

We all know that everything can be improved but we have limited time and resources so sometimes we need to live with compromises.

The best we can do is creating a welcoming place where people are happy to come in their free time to work on stuff they like. Speaking publicly about a private repo is certainly not welcoming stuff. You say the license allows us the share it but I feel unethical to disclose a private repo. So @zladay in order to use your code please send a pull request to this repo. It's another important step to get closer to your code.

Regarding the support and maintenance: it really means a lot of extra work. We can't expect anybody to take this. However, we are here with LVGL and its community for years and we will live with the project for long. Therefore, any code we accept should look and work in a way are willing to support and maintain. So I understand if you are not interested in support but you should take the word of people who will do the support.

We need to clarify what is the goal of the individuals. From my side: I devote most of my capacity to the core LVGL and can not deal with the drivers on a daily basis. As I mentioned above my checkpoint are:

  • simple, clear and easy to use platform interface
  • platform independence (including hardware and build system)
  • welcoming and friendly vibe.

If these are met, I'm happy.

@kisvegabor
Copy link
Member

kisvegabor commented Aug 23, 2020

@zladay
Could you send a pull request to merge your repo to the dev branch?
On the forum someone is also interested in the drivers but we can't show him the current state. You can add a line to the readme like "Work in progress" to avid any future complains if things are changing.

@zladay
Copy link
Author

zladay commented Aug 23, 2020

@kisvegabor @C47D

Drivers reorganization - full object tree

As planned before, I have completed the reorganization of the driver architecture. New code is committed and pushed to GitHub.
The full object tree looks like this:

{device} -+- {touch controller}
          | 
          +- {backlight controller}
          |
          +- {display controller} - {display transport}

This is a snippet from main.c showing the driver initialization sequence:

	// initialize SPI bus using ESP-IDF, display and TC are on the same bus
	// only one bus initialization is required
	ESP_ERROR_CHECK(spi_bus_initialize(BOARD_SPI_HOST, &sbc, 1));

	// device (TFT module) build starts here

	// create all necessary parts

	// create the device object
	ESP_ERROR_CHECK(device_create(&d_msp3218, &d));
	// create display controller object
	ESP_ERROR_CHECK(ili9341_create(&dc_msp3218, &dc));
	// create display specific SPI transport object
	ESP_ERROR_CHECK(disp_transport_spi_create(&dts_gen, &dct));
	// create touch controller object
	ESP_ERROR_CHECK(xpt2046_create(&tc_xpt2046, &tc));
	// create back light controller object
	ESP_ERROR_CHECK(backlight_onoff_create(&bc_onoff, &bc));

	// start the assembly of the parts

	// add device specific SPI transport to the display object
	disp_generic_handle_t c = dc->controller;
	c->add_transport(c, dct);
	// add display controller to the device
	d->add_display(d, dc);
	// add touch controller to the device
	d->add_touch(d, tc);
	// add back light controller to the device
	d->add_backlight(d, bc);

	// device is fully assembled, reset it
	ESP_ERROR_CHECK(d->reset(d));

	lv_init();

	lv_disp_buf_init(&disp_buf, buf1, buf2, DISP_BUF_SIZE);

	lv_disp_drv_t disp_drv;
	lv_disp_drv_init(&disp_drv);
	disp_drv.flush_cb = flush;
	disp_drv.buffer = &disp_buf;
	lv_disp_drv_register(&disp_drv);

	// tell device whom to notify when refresh is finished (flush completed)
	d->notify(d, lv_disp_flush_ready, &disp_drv);
	// device initialization is now complete

	lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.read_cb = read_touch;
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    lv_indev_drv_register(&indev_drv);

The device object is meant to encapsulate everything found on a display panel. This has a couple of benefits over the former scattered approach. One such example is the orientation handling. It needs to be specified at one location. It looks like this:

d->orientation(d, 3);

d is the handle of the {device} object. This single call on that object changes the orientation of the display and the TC simultaneously to 270 deg CW relative to the base orientation. In the previous version, the two were separate, two different calls were needed (one on display and one on TC) for the same effect.

I would really appreciate some form of feedback to this. I do not even mind, if someone tried this on real hardware. I would then see if it is worth a pull request.

Framework independence (platform agnosticism)

Peripheral related (platform specific) code is only found in the leaves.
We can not say that the rest is fully framework agnostic, though.
There are two areas where it is tied to the framework:

  • error handling is ESP-IDF compliant
  • "OS related" stuff (blocking wait, semaphore, etc.) is from FreeRTOS

As previously suggested, the "OS related" problem can be solved by implementing an OS abstraction layer (OSAL).
For the error handling, I have the following idea. LVGL specific error codes should be defined and introduced. If an error code is coming from a framework call (e.g. ESP-IDF spi_bus_initialize()), that code shall be translated run time to an LVGL specific error code. This code set should be defined in a way that it can accommodate error codes from all frameworks (platforms). This is what I call Error Handling Abstraction Layer (EHAL).
If these two ALs are implemented, we can make the non-leaves completely framework independent.
As far as the leaves are concerned, the situation is not that simple. We could invent a Hardware Abstraction Layer (HAL). If we want to be more precise, this is rather a Peripheral Abstraction Layer (PAL) instead. A HAL (or PAL) could make the above driver architecture completely framework agnostic. There is no free lunch, however. This would come at the expense of the HAL being framework specific. The problem is not solved, but pushed further. This potentially leads to a more complex and less efficient code. Peripheral handling is always time critical, hence there is a lot to lose. I would stay away from this approach. It is better to re-implement leaf stuff on other platforms.

Next steps

If we want to extend this reference implementation to other frameworks, we need to implement OSAL and EHAL. This two components shall not only be used by the drivers, but LVGL core, too. IMHO, these two things (especially OSAL) are very critical from performance and stability perspectives. These should not be left to the application developers. There is a high chance that they would screw it up and put the blame on LVGL.
I have already expressed my views about C++. The drivers are put together in an object oriented approach. I clearly see the benefits of this. Do you agree on this, BTW?
Some people have the belief that efficient code can be written only in C, C++ is inefficient. This belief is a misconception. In fact, the drivers could be written more efficiently in C++. I do not want to go into the explanation here.
I am tempted to rewrite everything in C++. As far as OSAL and EHAL is concerned, I would implement them in C++ right away. While not losing efficiency (or even gaining), the code would be much easier to read. An additional related benefit is that it would be much easier for other people to contribute. Drivers, OSAL, EHAL, everything.
@kisvegabor
I would need a clear statement on the part of the LVGL team if C++ is accepted or it is an outcast.
I would need a definite answer to this, since it is important for me to know this to plan my next courses of action.

@embeddedt
Copy link
Member

@zladay Could you comment on what @kisvegabor wrote above?

@zladay
Copy link
Author

zladay commented Aug 24, 2020

@embeddedt

Could you comment on what @kisvegabor wrote above?

If you are referring to this, I did here, indeed.

@kisvegabor
Copy link
Member

@zladay
The architecture looks good in general.

I'm ok with OSAL and EHAL too on driver level. It might be possible to add OSAL to LVGL but it's quite complicated and needs serious and long consideration. Simply it's not on the roadmap now. We should discuss it later.
EHAL seems independent from LVGL itself as LVGL will never create errors such as I2C_TIMEOUT. These can come from the driver.

I would need a clear statement on the part of the LVGL team if C++ is accepted or it is an outcast.

Short answer: C++ is not accepted.
Long answer: Although a lot of platforms/SDKs already support C++ some of them still aren't. I accept that C++ might have some advantages over C but it's much less gain than not supporting some platforms.

We meant to comment on this

Could you send a pull request to merge your repo to the dev branch?
On the forum someone is also interested in the drivers but we can't show him the current state. You can add a line to the readme like "Work in progress" to avid any future complains if things are changing.

@zladay
Copy link
Author

zladay commented Aug 25, 2020

@kisvegabor

We meant to comment on this

Could you send a pull request to merge your repo to the dev branch?
On the forum someone is also interested in the drivers but we can't show him the current state. You can add a line to the readme like "Work in progress" to avid any future complains if things are changing.

Ok. The comment is then already given.

As far as the forum is concerned: I have registered to the forum at lvgl.io.
The question is completely off topic and hard to understand. Does he want to use ESP32 drivers on NXP?
Anyways, I responded with a link to this issue.
I also asked for clarification of the problem. I am waiting for his response.

@zladay
Copy link
Author

zladay commented Aug 25, 2020

@kisvegabor

Long answer: Although a lot of platforms/SDKs already support C++ some of them still aren't. I accept that C++ might have some advantages over C but it's much less gain than not supporting some platforms.

Can you give some platform examples where C++ is not available?

On the other hand:
LVGL is an open source project. I understand that it is itself not making it necessary to use FOSS tools for development, but this is a natural choice.
GCC supports C++. It has support even for peculiar platforms like ESP (xtensa) and microchip/Atmel AVR.

@kisvegabor
Copy link
Member

kisvegabor commented Aug 26, 2020

I would really appreciate some form of feedback to this. I do not even mind, if someone tried this on real hardware. I would then see if it is worth a pull request.

"Someone" is now limited to Carlos and me.
I don't see why it is a big issue to make the drivers public in a dev branch.

Anyway if you could attach a zipped project, I'd try it on an ILI9341 display.

Regarding the C++ topic: Microchip has very weak support for C++. See here. It seems for PIC32 C++ is already free but there is no C++ compiler for 16 bit PICs (already capable of driving a display).
So almost all major platforms support it but we can't limit ourselves to the major platforms. C is "compatible" with everything but C++ isn't. We don't know all platforms and cases where people want to use LVGL therefore we need to provide maximal compatibility.

@C47D
Copy link
Collaborator

C47D commented Sep 1, 2020

The question is completely off topic and hard to understand. Does he want to use ESP32 drivers on NXP?

If I understand correctly, he would like to know the required work to have the drivers (display and indev) available in the lv_port_esp32 repo on the lv_drivers repository so they can use it with their NXP microcontroller.

I suggested them to take a look at this issue because I think the new architecture can achieve this, separate the driver code from the mcu/platform code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants