Now that the screen lights up and displays something, next on my list was a way to make the screen show useful things dynamically with as little code as possible. I decided to go with LVGL which is a framework for creating embedded GUIs on small graphical displays. It's implemented in C and is impressively flexible whilst leaving the programming interface familiar to anyone who's used Qt, GTK etc.
To add LVGL to my project I just added their Github repo as a sub-module:
git submodule add https://github.com/lvgl/lvgl.git Middlewares/Third_Party/lvgl
Once that's in I added the sources by modifying the STM32-for-VSCode.config.yaml which caused an update to the makefile the VSCode plugin uses. I'm not very keen on this plugin and using it to control a project. I'd like the project to be buildable outside of the IDE (e.g. in a CI system, or after the extension is no longer maintained) so I'm planning on setting the project up properly, probably in CMake. There are probably already drivers for this display out there as it is a fairly common one but I decided to do my own port. It's fairly simple, within the LVGL source tree there's an "examples" folder and within that there is a "porting" folder. I copied the display (
lv_port_disp_template) files into my source tree and removed the
template from them. The code modifications are fairly small, I had to:
- Pick a buffering strategy, they're described in the template but I chose the full double-buffered approach with two full screen sized buffers which is the most memory intensive but I picked the board because it has 8MB of SDRAM for this very purpose.
- Allocate the buffers, for now I've statically allocated them in the SDRAM as there's nothing else there yet. Later in the project I'll put the heap in SDRAM and just malloc() the buffers.
- Initialise the display. This is more or less copied from the sample code I used to get the screen working with a few simplifications where appropriate to keep the code as short as possible. (Shorter code has fewer bugs, as long as it's not hard to read).
- Provide a flush function to copy the frame buffer (or a fraction of it) to the display. This was also pretty much taken from the "fill screen" function in the demo, except that instead of the constant colour I used a pointer to the current pixel's colour.
I also had to copy the
lv_conf_template.h file from the root of the submodule into my project, rename it and modify it to suit my platform. I've only made minimal changes so far, I plan to tweak it more later on to use system malloc() and memcpy() functions and customise the font options etc.
With all that done I tried compiling and running a very basic demo that just puts a single button on the display. Without any input drivers I knew I'd not be able to interact with the button yet so I haven't bothered with a callback event handler function. I tried it and spent a long time in the debugger because the board kept crashing unrecoverably. I found that the cortex-debug add-on for VSCode is still as bad as ever and eventually managed to get cppdbg setup with OpenOCD and found the issue (more on that in another post).
In the end it turned out to be that I was missing the initialisation for the external SDRAM chip. The controller was all being correctly configured by the STM32CubeMX code, but it omitted the setup for the actual SDRAM chip itself. I've used the SDRAM controller on the STM32F4xx chips a few times but usually built the setup from scratch, this time I'd relied on CubeMX and it failed me. Once I knew what I was looking for I found the necessary setup in the demo files that ship with CubeMX but since the registers being configured are within the external ISSI RAM chip it isn't covered by the ST tools.
With the external SDRAM now working for a framebuffer and the LVGL code drawing a button I have a working GUI framework, albeit one with a horrendously slow refresh rate. Something else to solve in a future post.
Slow Draw: A very slow but fully operational LVGL demo
You can check out the state of my code at this point in the project at github.