feat: implement SPIBusFast driver with 90° rotation for AXS15231B QSPI#451
feat: implement SPIBusFast driver with 90° rotation for AXS15231B QSPI#451straga wants to merge 3 commits intolvgl-micropython:mainfrom
Conversation
- Add SPIBusFast bus driver with partial updates support - Enable LVGL partial mode instead of full frame updates - Implement 90° rotation with pixel coordinate transformation - Add RTOS task for double buffering - Support QSPI 4-wire interface for AXS15231B controller
|
I really like that you took the initiative with this.. I am sure it has given you a much better understanding of how the software rotation works and how to manage keeping the tasks in synchronization with each other to keep from corrupting the buffer data... There is always a but... so here it is.... I would really like to provide the software rotation across all of the different available busses. The hitch is adding it without adding new bus classes to facilitate it and to only use a single set of rotation functions and task codes for all of the busses. would you be willing to assist me with doing this? I have started doing on it several different occasions but I ended up getting interrupted with something else and didn't finish or I thought of a better way to go about it... an additional argument would need to be passed to the init function of the bus drivers that would signal that software rotation needs to be used. that would then cause the bus driver to do the rotation. I want to move all IO related operations over to the other core so all bus types would need to run a task where the buffer gets passed over to that task to be sent to the display. that flag that gets passed to the init function would be for determining whether or not the buffer needs to get rotated while being copied to an intermediate buffer that is used for transmit. We would need something that would instruct the bus driver on how large to allocate that destination frame buffer. |
|
The approach you outlined makes perfect sense - using a single set of rotation functions and moving all IO operations to the other core will be much cleaner than having separate bus classes. |
|
It's a big overhaul of the code to make it work. If I had some help with it that would help out greatly. I started to do it on more than one occasion and ended up scraping it because I ended up making it overly complicated. I want to keep the code as simple as possible and that's the hard part. Maintainability needs to be high on the priority list. |
|
I didn't dive too deep into the code you wrote in this PR. Does it work like the RGB driver where you now have the ability to set partial buffers with LVGL or does it still need to render full framebuffers? |
|
@kdschlosser: yes, I use RGB as reference. |
|
I was able to compile a firmware with the new SPI driver and the 90° rotation works! Thank you both for your work. |
kdschlosser
left a comment
There was a problem hiding this comment.
Check out the couple of comments I made.
| } | ||
| // Do NOT initialize to zero - leave uninitialized for performance | ||
|
|
||
| self->panel_io_config.trans_queue_depth = 10; |
There was a problem hiding this comment.
This may not be a large enough queue size. You need to double check to see how many transactions are getting created when you iterate over the full frame buffer
| self, | ||
| ESP_TASK_PRIO_MAX - 1, | ||
| &self->copy_task_handle, | ||
| tskNO_AFFINITY // Do not pin to specific CPU core |
There was a problem hiding this comment.
You don't want to use tskno_AFFINITY here because of how FreeRTOS handles assigning the core the task runs on. Once this decision gets made the task never changes cores because it never stop running. If FreeRTOS determines that the main core has less load then it will assign the task to the same core that MicroPython and LVGL is running on which defeats the whole purpose of having the rotation task running. If the task is running on the same core as LVGL the rotation and transmit end up being blocking calls.
| // Allocate two full frame buffers for double buffering | ||
| uint32_t fb_size = width * height * self->bytes_per_pixel; | ||
|
|
||
| self->active_fb = (uint8_t*)heap_caps_malloc(fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); |
There was a problem hiding this comment.
The full buffers need to be allocated in DMA memory otherwise the transmit of the buffer ends up being a blocking call. We need it to not be blocking this way the CPU is able to copy the data from the transmitting buffer to the idle buffer to keep them in sync properly. This allows 2 things to happen at once. Without using DMA memory not only does the task have to wait until the transmit finishes it then has to copy the buffer data afterwards. This length of time will end up being over 20 milliseconds which can cause the main core to have to sit and wat because the likely hood of LVGL rendering to both partial buffers in that time is highly probable.
When you look at the RGB bus code it's a bit confusing with respect to this. When that driver sits and waits at the event that is signaled from the vsync callback it is not waiting for the buffer that was just passed to be transmitted. It is waiting to know when the previous buffer has finished transmitting so that way it knows it is able to write data to it without causing corruption.
| offset += current_chunk; | ||
| remaining -= current_chunk; | ||
| chunk_count++; | ||
| } |
There was a problem hiding this comment.
The code above is the reason why I did not merge this PR. The code is specific to a display IC and it would not be able to be used with any other display IC.
New SPI python driver:
https://github.com/straga/micropython_lcd/tree/master/device/JC3248W535/new_SPI