0:09
Hello and welcome to controllers tech.
0:12
In the past, we have interfaced various
0:14
types of displays with the STM32
0:18
microcontroller. These include character
0:20
LCDs, OLED displays, and several TFT
0:24
displays such as the ST7735,
0:30
and others. Today, we will be working
0:33
with another popular display module that
0:35
features the GC9 A01 controller IC.
0:40
We'll explore how to interface this
0:42
display with STM32 using the SPI
0:45
peripheral. In addition, we'll integrate
0:48
the LVGL graphics library into our
0:50
project. And finally, we'll use Square
0:53
Line Studio to design a simple test
0:56
interface. The GC9 A01 is typically
1:01
found in round display formats. This
1:03
makes it especially suitable for
1:05
displaying analog or digital clocks,
1:08
circular data visualizations like pie
1:10
charts, rounded images and animations,
1:13
compass graphics, and more. For this
1:16
demonstration, I'll be using the
1:21
development board from WAX Studio. If
1:24
you're located in India, you can
1:25
purchase this board directly from
1:27
shop.controllerssteech.com.
1:30
This particular board features 1 mgabyte
1:32
of flash memory and 640 kilob of SRAMM,
1:37
making it an ideal candidate for running
1:39
the LVGL library efficiently. You can
1:42
also purchase the GC9 A01 based round
1:46
display from the same website. All
1:49
right, let's launch STM32
1:51
Cube IDE and begin by creating a new
1:54
project. As I mentioned earlier, I'm
2:01
RGT6 microcontroller for this setup.
2:03
Let's assign a name to the project and
2:06
then click finish to proceed. Before we
2:09
begin configuring the system clock,
2:11
let's take a quick look at the schematic
2:13
of the development board. As shown here,
2:15
the board includes an 8 megahertz
2:18
crystal oscillator. Now, let's return to
2:20
the CubeMX configuration view. We will
2:23
begin with the clock configuration by
2:25
enabling the external high-speed crystal
2:28
as our clock source. Since the board
2:30
includes an 8 MHz crystal, we'll use the
2:34
PLL phase locked loop to scale the
2:37
system clock up to its maximum frequency
2:40
of 250 MHz. Next, go to the SPI
2:44
configuration section and enable half
2:46
duplex master mode. Because the display
2:49
only receives data and does not send any
2:52
data back, a half duplex configuration
2:55
is sufficient. You'll notice that pins
2:58
PA5 and PA7 are assigned as the SPI
3:02
clock SC and MOS master out slave in
3:07
pins respectively. Before we proceed to
3:10
configure additional pins, let's take a
3:12
look at the wiring diagram. This diagram
3:18
display is connected to the STM32.
3:21
The display is powered by a 3.3V supply
3:25
directly from the microcontroller. The
3:28
clock pin of the display connects to PA5
3:31
which is the SPI clock line. The data
3:34
pin connects to PA7. The MOSI pin for
3:38
SPI communication. The DC pin on the
3:41
display distinguishes between command
3:43
and data signals and it is connected to
3:45
PA4 on the MCU. The chip select CS pin
3:50
is used to enable or disable the display
3:53
module and is wired to PA6. The reset
3:56
pin of the display is connected to PA3
4:00
on the microcontroller. Now that the two
4:02
SPI pins are already configured, we'll
4:05
go ahead and set up the remaining
4:07
control pins. All of these pins should
4:10
be configured as generalpurpose output
4:12
pins. Also be sure to rename them
4:15
appropriately according to their
4:16
function for better readability in your
4:19
code. With all pins now configured,
4:22
let's proceed to adjust the SPI
4:24
communication parameters. The data size
4:27
should be set to 8 bits with the MSB
4:30
most significant bit transmitted first.
4:33
Before selecting the SPI pre-scaler
4:35
value, we'll take a moment to examine
4:38
the SPI clock source. The SPI peripheral
4:41
uses the PLLL1 Q output. So, we can
4:45
reduce the clock frequency by adjusting
4:47
this factor. I'm setting the factor to
4:49
10, which brings the SPI clock down to
4:52
50 MHz. Then, I set the SPI pre-scaler
4:56
to 4, which further reduces the
4:59
effective SPI clock to 12.5 MHz. This
5:03
frequency is suitable for driving the
5:05
display. The clock polarity is
5:07
configured to low and the clock phase is
5:10
set to the first edge. All remaining
5:13
settings are left at their default
5:15
values. You also have the option to
5:17
enable DMA for SPI transmission. The
5:20
display driver library supports DMA
5:23
transfers which you can activate within
5:25
a library code. DMA should be configured
5:28
for transmiton mode with the data
5:31
direction set for memory to peripheral.
5:34
Set the DMA mode to normal and the data
5:37
width should be specified as bite.
5:40
Additionally, ensure that the DMA
5:42
interrupt is enabled in the NVC
5:45
configuration tab. In my case, I will
5:48
not enable DMA because setting it up on
5:50
the Cortex M33 core is a bit more
5:53
involved. However, you can follow the
5:56
steps I just outlined if you wish to
5:57
enable it. That completes the
5:59
configuration process. Go ahead and
6:02
click save to generate the project
6:04
files. Now we will begin by copying the
6:07
necessary library files required to run
6:11
AE01 display. Here I have both the GC9
6:15
A01 source file and the corresponding
6:18
header file ready for use. You can
6:20
obtain these files by downloading the
6:22
complete project package. The download
6:25
link is provided in the description
6:27
section of this video. So be sure to
6:29
check that out. These files are
6:31
organized into their respective
6:33
directories. One is meant for the source
6:36
folder and the other belongs in the
6:38
include folder just as I'll be placing
6:40
them now. Go ahead and copy the C file
6:44
which is the source code file into the
6:46
src directory of your STM32 project.
6:50
Similarly, copy the.h file which
6:54
contains the function declarations and
6:56
configuration macros into the ink
6:58
directory. It's worth noting that these
7:01
library files are designed to work
7:05
microcontroller. They are not restricted
7:08
to this particular development board.
7:10
That means you can use them in different
7:14
projects with minimal adjustments. There
7:16
is no need to make any modifications to
7:18
the GC9 A01 source file itself. As I
7:23
mentioned earlier in this video, DMA
7:26
support has already been integrated into
7:28
this library. You can clearly see that
7:30
the DMA based transmit functions are
7:33
present in the code. However, these
7:36
functions are currently wrapped within a
7:38
use DMA flag. So, they won't be active
7:42
unless that flag is enabled. Let's move
7:44
on to the GC9 A0 header file. Here we
7:49
will need to make a few changes to align
7:51
the library with the configuration we
7:53
set up earlier in CubMx. First you need
7:56
to define the correct SPI instance that
7:59
you're using in your project. If you
8:01
plan to use DMA for SPI transmission,
8:04
make sure to set the use DMA flag to one
8:08
to enable that functionality in the
8:09
code. Next, you'll define the control
8:12
pins CS, DC, and reset, and also specify
8:17
the GPIO ports to which each of these
8:20
pins is connected. This display library
8:22
comes with a set of core functions.
8:25
These include display initialization,
8:28
sending commands, and writing data to
8:30
the screen. Additionally, it contains
8:33
specific functions intended for use with
8:35
the LVJL graphics library such as
8:38
display flush, set address window, and
8:41
others. These LVJ gel related functions
8:44
are essential for allowing smooth and
8:47
efficient integration of the display
8:49
with LVGL's rendering pipeline. We will
8:52
test the LVGL integration a little later
8:55
in this video, but for now, let's focus
8:58
on verifying the basic functionality of
9:03
display. Start by including the GC9
9:08
header file in your main.c file. Now,
9:12
within the main function, call the GC9
9:17
function. This will initialize the
9:19
display and prepare it for use. To
9:22
verify that everything is working
9:23
properly, we'll perform a simple test by
9:26
filling the screen with different
9:28
colors. Use the fill rectangle function
9:31
inside the main while loop. This
9:33
function allows us to fill a specific
9:36
rectangular area on the display with a
9:39
solid color. The parameters of this
9:41
function include the X and Y coordinates
9:44
for the starting point of the rectangle,
9:46
the width and height of the rectangle,
9:48
and a 16- bit color code representing
9:51
the color you want to fill it with.
9:52
Let's begin by filling the screen with a
9:54
solid red color. After that, we'll
9:57
insert a delay of 1 second to make the
10:00
transition visible. We'll then repeat
10:02
the same procedure for several other
10:05
colors. Here I've defined a set of four
10:08
different colors that will cycle through
10:10
the screen switching every 1 second.
10:13
This creates a simple but effective
10:16
demonstration of how the display is
10:18
working. Let's now build the project to
10:21
compile the code. There are no
10:22
compilation errors. So we can go ahead
10:25
and flash the firmware to the board. As
10:27
you can see on the screen, the display
10:29
is switching between colors every second
10:33
just as we programmed it. The colors are
10:35
also rendering accurately according to
10:38
our specified configurations. This
10:40
confirms that the GC9 A01 display is
10:45
initializing properly and we are
10:47
successfully able to draw colored
10:49
rectangles on the screen. This basic
10:51
test verifies that the hardware
10:53
interface as well as the library are
10:56
functioning as expected. Now we will
10:59
move forward and add LVGL, the light and
11:02
versatile graphics library to our
11:05
current project. I have already created
11:07
a complete video series that explains
11:09
the LVGL library in detail. If you're
11:12
new to it or want a better
11:14
understanding, you can check out that
11:16
series by clicking the link in the top
11:18
right corner of this video. For this
11:20
project, we'll be using LVGL version
11:23
9.2, 2, which is the latest version
11:26
currently supported by Square Line
11:28
Studio. Start by downloading the LVGL
11:31
9.2 package in zip format from the
11:34
official repository. Once the download
11:37
is complete, go ahead and extract the
11:39
contents of the zip file. After
11:41
extracting the files, rename the top
11:44
level folder as LVGL to keep things
11:48
clean and consistent. Next, we need to
11:51
integrate this library into our STM32
11:54
project. To do that, go into your
11:57
project structure and create a new
11:59
folder inside the drivers directory. I
12:01
am naming this newly created folder LVGL
12:05
to clearly indicate that it contains the
12:07
graphics library. Now, copy the entire
12:10
downloaded LVGL folder into this new
12:15
directory. Once that's done, the first
12:17
step in the setup process is to copy the
12:20
LVGL configuration template file into
12:23
the appropriate location right beside
12:25
the LVGL folder, not inside it. It's
12:28
important to place the configuration
12:30
file at the same directory level as the
12:32
LVGL source folder so that it can be
12:35
easily included in your project build.
12:37
Now, rename the template file from its
12:40
default name to LVNF.H, H which stand
12:44
for LVGL configuration header. Next,
12:47
open the configuration file in your IDE.
12:50
To activate the configuration content,
12:52
locate the macro that disables the file
12:55
content and set it to one to enable
12:58
everything. For now, I'm leaving all the
13:00
configuration settings at their default
13:02
values since they are suitable for this
13:05
basic implementation. Let's try building
13:07
the project once to check for any
13:10
initial errors. As expected, we're
13:13
seeing multiple errors, and that's
13:15
because the LVGL library path hasn't
13:18
been added to the project settings yet.
13:20
To fix this, open the project settings
13:22
by right-clicking the project and
13:24
navigating to C/ C++ build settings.
13:29
Under the compiler section, go to the
13:31
include paths tab. Click the add button
13:34
to add a new include path relative to
13:36
the project workspace. Now browse to the
13:39
main LVGL folder that we manually added
13:42
earlier. Make sure not to select any
13:45
LVGL subfolders. We need to add the path
13:48
to the top level folder that we created
13:50
ourselves. Click okay and then apply and
13:54
close the configuration settings. Now
13:56
rebuild the project again. You'll see
13:58
that all the previous errors are now
14:00
resolved and we're ready to move on to
14:02
the next step. The next task is to
14:05
connect the display driver with the LVGL
14:08
library. To do this, we'll use a display
14:11
porting file which is already included
14:13
in the LVGL library package. Navigate to
14:17
the path LVGL examples porting. Here
14:20
you'll find two files named LVport
14:23
display template C and LVport display
14:27
template.h. Let's copy the C file to the
14:31
project's SRC directory and theH file to
14:34
the ink directory. Once copied, rename
14:37
both files by removing the word template
14:39
from their names. Open the source file
14:42
first and again look for the macro that
14:45
disables the files content. Set it to
14:47
one to enable the code inside. Also,
14:51
make sure to correct the include path at
14:53
the top of the file so it properly
14:55
includes the header. All right, before
14:57
we go further, we need to define the
15:00
resolution of our display. This should
15:02
be done inside the display port header
15:04
file. Since the GC9A01
15:08
display has a resolution of 240x
15:12
240 pixels, set the appropriate width
15:15
and height definitions accordingly. Now,
15:17
rebuild the project again. You'll notice
15:20
that the earlier warning related to
15:22
resolution has now disappeared. Next, we
15:25
need to initialize the display. The
15:27
initialization function for the GC9
15:30
A01 display is already written in the
15:34
displays header file. So, we just need
15:36
to include that header here. Within the
15:38
function display init, go ahead and call
15:41
the function gc9 a01_init
15:45
to initialize the display hardware.
15:47
After initialization, we'll configure
15:50
the flush function. The flush function
15:52
is used by LVGL to send color data to
15:56
the display memory. As I mentioned
15:58
earlier, our library already provides a
16:04
flush function that is designed
16:06
specifically for this purpose. The
16:08
parameters of the flush function include
16:10
the pixel color data referred to as
16:13
px_map and the display area defined by
16:16
x1, y1, x2 and y2 which indicate the
16:21
rectangular region to be updated. If you
16:24
look at the implementation of GC901
16:27
flush, you'll see that it first sets the
16:30
address window on the display and then
16:32
transfers the pixel data over SPI either
16:36
using normal blocking mode or DMA
16:39
depending on the configuration. After
16:41
the transfer is complete, the function
16:43
calls flush ready. This flush ready
16:46
function is defined as a weak function,
16:49
which means it can be redefined
16:51
elsewhere in your project. Let's define
16:53
our custom version of flush ready inside
16:56
the display port source file. Within
16:59
this function, call LV display flush
17:01
ready to inform LVGL that the flushing
17:04
operation has completed and it is now
17:06
ready to send the next frame. So here's
17:08
the flow. LVGL calls the flush function.
17:12
The flush function transmits the color
17:15
data. Once transmission completes, it
17:18
calls flush ready. and flush ready tells
17:21
LVGL it can start the next flush. If
17:24
you're using DMA, the GC9 A01
17:29
flush function sends the pixel data via
17:32
the DMA peripheral. Once the DMA
17:34
transfer is complete, an interrupt is
17:37
triggered and the transmit complete
17:39
callback function is invoked. Inside
17:42
this callback, flush ready is called to
17:45
wrap up the flushing cycle. Now open the
17:48
function LV port display in it. Inside
17:51
this function you'll find different
17:53
buffering strategies available for
17:55
rendering frames to the display. If
17:58
you're using DMA, it is recommended to
18:00
use two buffers for partial rendering to
18:03
reduce tearing and increase performance.
18:06
I have explained these rendering
18:07
techniques in more detail in the LVGL
18:10
introduction video. So be sure to watch
18:13
that if you're interested in how
18:14
buffering works. By default, the library
18:17
is set up to render 10 lines at a time,
18:20
but this value can be adjusted depending
18:22
on how much RAM your microcontroller has
18:24
available. Let's now build the project
18:26
again to check for any remaining issues.
18:29
We're seeing an error in the LV display
18:32
flush ready function call. This error is
18:35
due to the fact that the display object
18:37
being passed to this function was
18:39
declared within the flush functions
18:41
local scope and therefore isn't
18:43
accessible from the flush ready
18:45
function. To resolve this, we need to
18:48
declare a global display pointer. I'm
18:50
calling this pointer my display. Once
18:52
the display is created during
18:54
initialization, assign it to this global
18:57
variable. We will then use this globally
19:00
defined display pointer in our call to
19:02
LV display flush ready. Now rebuild the
19:06
project once again. The error is gone
19:09
and the project builds successfully.
19:11
Next, open a main.c file. At this point,
19:15
we no longer need to call the GC9 A01
19:19
specific test functions. Instead,
19:22
include the display port header file
19:24
that we just configured. Inside the main
19:26
function, begin by calling LV init to
19:30
initialize the LVGL core library. Then
19:34
call LV port display in it to initialize
19:38
and register the display driver with
19:40
LVGL. Inside the main while loop, make
19:43
sure to call LV timer handler every 5
19:46
milliseconds. This is required for LVGL
19:50
to process events and handle animations.
19:53
Now open the interrupt source file
19:55
typically named STM32
19:58
interrupt C. Here include the LVGL
20:02
header file which is located inside the
20:05
LVJL directory that we added earlier.
20:07
Scroll down to the cy tick handler
20:09
function. Inside it call LV tick in one.
20:13
This function increments LVGL's internal
20:16
tick counter by 1 millisecond on each
20:19
system tick. This setup is essential to
20:21
keep the LVGL timers running accurately
20:24
in real time. With this, our LVGL
20:27
integration is complete. We're now ready
20:30
to move on and start designing and
20:32
displaying UI elements on the screen. As
20:35
you can see here, the basic
20:37
implementation of LVGL in this project
20:40
is occupying around 357 kilob of flash
20:44
memory. It's important to ensure that
20:46
you're using a microcontroller with
20:48
sufficient flash memory to support this
20:51
usage. Although you can reduce the size
20:53
slightly by tweaking the LVGL
20:56
configuration file, even a minimal LVGL
20:59
setup still requires a decent amount of
21:01
flash to handle graphic rendering on the
21:04
display. Now that we have the basic
21:06
setup ready, let's go ahead and try out
21:08
some example animations from the LVGL
21:11
library itself. Navigate to the folder
21:13
path LVGL examples animation. From here,
21:17
I'm going to use one of the built-in
21:19
examples named animation_2.
21:22
The first step is to include the header
21:24
file associated with this example into
21:26
our main.c file. This header file can be
21:29
found inside the same examples/animation
21:33
folder of the LVGL library. Once
21:36
included and after the LVGL display port
21:39
has been initialized, go ahead and call
21:44
example function to trigger the
21:45
animation. That's all the setup needed
21:47
for now. Let's build the project to
21:49
check whether everything compiles
21:51
without any issues. The project builds
21:54
successfully. There are no errors. So,
21:57
let's proceed to flash the firmware to
21:59
the board. Now, on the display, you'll
22:02
notice that the ball animation is
22:03
running. The motion is very smooth,
22:06
indicating that the display refresh and
22:08
data transfer are working well. However,
22:12
there's a slight issue. The color of the
22:14
ball should be red, but here it appears
22:16
to be different. If you refer to the
22:18
official LVGL documentation and look up
22:21
this particular animation example,
22:24
you'll find that the ball is indeed
22:26
supposed to be red in color. This kind
22:28
of color mismatch is common when using
22:31
SPI based displays and it's typically
22:33
due to how color bytes are arranged and
22:36
sent. Earlier versions of LVGL used to
22:39
include a flag in the configuration file
22:41
that allowed easy color format swapping,
22:44
but this flag has been removed in newer
22:46
versions. So, we'll correct this
22:49
manually using a software approach. Open
22:52
a file named LVport display. C which is
22:56
the source file for the LVGL display
22:58
port. Inside the display flush function,
23:02
insert a call to the function LV color
23:07
This function is used to swap the color
23:09
bytes correctly for RGB 565 format. Pass
23:13
the pixel data buffer to this function
23:16
as the first parameter. For the second
23:18
parameter, pass the size of the buffer,
23:21
which is simply the area of the
23:23
rectangle being updated on the display.
23:25
Make sure this function is called before
23:27
the flushing process starts so that the
23:30
data is already in the correct format
23:33
when sent. Now, rebuild the project and
23:36
flash it again to the development board.
23:38
This time, you'll see that the ball is
23:40
rendered in red and the animation is
23:43
still running smoothly. So now we have a
23:45
fully functional LVGL implementation on
23:48
our display with proper color correction
23:51
for the SPI interface. With that
23:53
confirmed, let's move to the final
23:55
section of this video where we will
23:57
design a graphical UI using Square Line
24:00
Studio. Launch the Square Line Studio
24:03
application. Click on the create section
24:06
and under project type, choose Eclipse
24:08
with SDL for PC. I'll be selecting LVGL
24:12
version 9.2. 2, which is the latest
24:15
version currently supported. That's the
24:18
reason we downloaded LVGL9.2
24:21
earlier in the setup. On the right hand
24:23
side of the screen, provide a location
24:25
for the project folder. Now, set the
24:28
display resolution to 240x
24:31
240 pixels, which matches our GC9A01
24:36
display. The display shape should be set
24:38
to circular and the color depth must be
24:41
16 bits corresponding to RGB 565. Make
24:46
sure to select the correct LVGL version
24:49
which matches the one already integrated
24:51
in the STM32 project. Click create to
24:55
generate the new UI project. Now you'll
24:57
see the circular screen preview for the
24:59
new design. Let's begin designing by
25:02
adding an image element to the screen.
25:04
I've already prepared an image in PNG
25:06
format. Resize to exactly 240x 240
25:10
pixels. We'll import this image into the
25:13
asset section of Square Line Studio.
25:16
Now, under the image widget, choose the
25:19
image we just added to the assets. Next,
25:22
I'll also add a spinner widget, which
25:24
creates an animated arc, useful for
25:27
visually indicating activity or loading.
25:30
Go to the main style settings and adjust
25:32
the arc width to suit the design. Then
25:35
adjust the indicator style settings for
25:37
the spinner to match your preferences.
25:39
Try experimenting with different colors
25:41
for the ark until you find one that
25:46
Once you're satisfied with the design,
25:48
open the project settings here. Here we
25:52
need to define a folder path to which
25:54
the UI files will be exported. Inside
25:56
the main project folder, create a new
25:59
folder and name it UI. Now set this
26:02
folder as the export path for the
26:04
generated UI files. The LVGL include
26:08
path in our project is located inside
26:10
another folder called LVGL. So include
26:13
the path accordingly. Click apply
26:16
changes to save the export
26:18
configuration. Now click on export to
26:20
generate the UI files based on the
26:22
design. Once the export is complete,
26:25
open the project folder and navigate to
26:27
the UI directory. You'll see all the UI
26:30
source and header files generated by
26:32
Square Line Studio. Copy this entire UI
26:36
folder and place it inside the LVJL
26:39
folder that we added to our STM32
26:41
project earlier. This keeps all the LVJL
26:45
related files grouped together for
26:47
better maintainability. Now go back to
26:49
main C. To use the new UI in your
26:52
project, include the header file UI.h
26:56
which is located in the UI folder. After
26:58
initializing the LV gel display driver
27:01
using LVport display in it, call the
27:07
to initialize and load the UI onto the
27:09
screen. Let's build the project to make
27:12
sure everything compiles correctly. The
27:14
project builds without any errors. So go
27:17
ahead and flash it to the board. On the
27:20
display, you should now see the image
27:22
and animated spinner loaded and running.
27:25
The animation is working very smoothly,
27:28
confirming that both the display and the
27:30
LVGL integration are functioning
27:32
properly. We now have a complete setup
27:35
where the GC9 A01 display works with
27:38
LVGL graphics and the UI is generated
27:42
from Square Line Studio. That brings us
27:44
to the end of this video. I hope you now
27:47
have a clear understanding of how to
27:48
interface the GC9 A01 round display with
27:54
how to implement the LVJL graphics
27:56
library, and how to design and export UI
28:00
using Square Line Studio. In the next
28:02
video, we will continue working with
28:04
this display and create a custom analog
28:07
watch face. We'll be using the real time
28:10
clock peripheral to display the current
28:12
time on the screen. You can download the
28:14
complete project files from the link
28:16
provided in the video description. If
28:18
you have any questions or run into
28:20
issues, feel free to leave a comment
28:22
below. And if you found this video
28:24
helpful or informative, consider giving
28:27
a thumbs up. Thanks for watching and
28:29
have a great day ahead.