0:09
Hello and welcome to controllers tech.
0:12
In the previous video, we explored how
0:14
to interface the 1.28 in GC901
0:20
round LCD display with the STM32
0:23
microcontroller. We also integrated LVGL
0:27
into our project and designed a basic
0:29
user interface using Square Line Studio.
0:32
Today we will continue with the same
0:34
topic, but this time I will demonstrate
0:36
how to display an analog clock on the
0:39
GC9 A01 display. To achieve this, I will
0:43
first design the clock in Square Line
0:45
Studio and then use the STM32 RDC module
0:49
to keep the clock updated with the
0:51
correct time. For this project, we will
0:54
need images for the clock face and its
0:56
hands. I have already downloaded the
0:59
required images which include the watch
1:01
face, the hour hand, the minute hand,
1:04
and the second hand. These images must
1:07
be in PNG format. As this format
1:10
supports transparency which is crucial
1:12
for creating a realistic clock, separate
1:15
images for each element are necessary
1:19
because we need to rotate the individual
1:21
hands to simulate the clock's movement.
1:24
The hardware connection between the
1:26
display and STM32 will remain exactly
1:30
the same as in the previous tutorial. I
1:32
will also continue with the same project
1:35
file from the previous video, but I have
1:37
renamed it to GC9 A01 part 2 for better
1:41
organization. Now let's open Squareline
1:44
Studio to start designing the analog
1:46
clock. Begin by creating a new project
1:49
and select the latest version of LVGL
1:53
which is 9.2. Choose the Eclipse with
1:56
SDL option for PC and then create a
1:59
dedicated folder for this project. The
2:04
display has a resolution of 240x
2:08
240 pixels and it is a circular display.
2:12
The color depth is 16 bits and for this
2:15
project we will work with the latest
2:17
LVGL version which is 9.2.2.
2:20
All right, we now have our circular
2:22
screen ready in the workspace. Let's
2:25
start by adding the first image to the
2:27
screen. As I mentioned earlier, I
2:29
already have a required images. So the
2:31
next step is to import these images into
2:33
the assets panel. You need to add them
2:36
one by one. The first image we will add
2:38
is the watch face. After that add the
2:41
second image which will be the second
2:43
hand of the clock. Let's position this
2:46
second's hand correctly on top of the
2:48
watch face. To create the motion of the
2:50
second hand, we will need to rotate this
2:53
image. Let's experiment with different
2:55
rotation values to see how it behaves.
2:59
You will notice that the entire image
3:01
rotates when we change the rotation
3:03
parameter. However, to achieve proper
3:06
clock movement, we need to define a
3:08
pivot point for this image so that it
3:10
rotates around a fixed position just
3:12
like a real clock hand. I will set this
3:15
pivot point for the second hand. Now,
3:17
the pivot point for the image can be set
3:19
by entering the corresponding X and Y
3:22
coordinates. These coordinates are
3:24
defined relative to the image itself.
3:27
Meaning the origin point 0 0 is located
3:31
at the top left corner of the image. To
3:33
determine the correct pivot coordinates,
3:36
you can use an image editing tool such
3:38
as Let's open this image in
3:40
to find the exact values. Here you can
3:43
see the starting point of the image is
3:45
located at 0 0. I need the coordinates
3:49
of a specific point on a second's hand
3:52
where the rotation should occur. You can
3:54
see the coordinate values displayed at
3:57
the bottom left corner of the screen as
3:59
you hover over the image. For this
4:01
particular second hand image, the pivot
4:03
point turns out to be at 227.
4:07
Let's enter these coordinates for the
4:08
pivot and then rotate the image again.
4:11
Now you can see that the image rotates
4:13
perfectly around the pivot point just
4:15
like a real second hand on an analog
4:18
clock. Let's experiment with a few
4:20
different rotation values. From this
4:23
test, you can see that 5 seconds of
4:25
movement are represented by 300 rotation
4:28
units. This means that each second
4:31
corresponds to 60 rotation units. Make
4:34
sure to keep this calculation in mind as
4:36
we will need it later while writing the
4:38
code to update the clock. Next, let's
4:41
add another image. This time, I will use
4:44
the hourhand of the clock. Align the
4:47
hourhand carefully so that its center
4:49
overlaps exactly with the pivot point of
4:53
the second hand. Just like before, we
4:55
also need to find a pivot point for this
4:57
hourhand image. So, let's open this
5:00
image in and identify the correct
5:02
coordinates. The pivot point for the
5:04
hourhand will be set at this particular
5:07
position which is located at the center
5:09
of the circle with coordinates 4633.
5:14
Let's set these coordinates as the pivot
5:16
point and test the rotation of the
5:18
hourhand. The hourhand is rotating
5:21
smoothly just as expected. Now let's
5:24
move on and add one more image for the
5:26
minute hand. We will align this image in
5:29
a similar manner, ensuring that the
5:31
center of the circle is aligned with the
5:33
pivot points of the other clock hands.
5:36
Next, we need to determine the pivot
5:38
coordinates for the minute hand. The
5:40
correct pivot point for this hand is
5:45
Let's enter these coordinates and test
5:47
the rotation for the minute hand.
5:49
Perfect. All three hands, the hour,
5:52
minute, and second hands are now
5:55
rotating properly. The next step is to
5:58
align all the clock hands to midnight
6:00
hours, which is the 12:00 position. The
6:03
second hand needs to be rotated by 1,800
6:06
units, which will serve as its offset.
6:09
Remember this value as we will use it
6:11
later. For the hour hand, we need to
6:14
rotate it by 600 units. This will be the
6:17
offset for the hourhand. Now, finally,
6:21
we will adjust the minute hand. Let me
6:23
fine-tune the alignment of all three
6:26
hands so that they are perfectly
6:28
centered. All right, they look good.
6:29
Now, after some minor adjustments, the
6:32
hour hand now has an offset of 620
6:36
units. Next, let's calculate the offset
6:39
for the minute hand. Since we need to
6:41
move it in the counterclockwise
6:43
direction, we will use negative rotation
6:45
values. A rotation of minus 550 units
6:49
aligns the minute hand exactly to the
6:53
12:00 position. So our final offsets are
6:57
plus 1800 for the second hand plus 620
7:01
for the hour hand minus 550 for the
7:04
minute hand. This completes all the
7:07
design work required for now. Let's move
7:09
to the project settings. Here we need to
7:12
specify the path where the generated UI
7:14
files will be stored. I am creating a
7:17
new folder named UI inside the project
7:20
folder to keep everything organized. The
7:23
LVGL path remains the same as we
7:25
discussed in the previous video. Now
7:28
save the project and export the UI files
7:30
so they can be used inside our firmware.
7:36
cube project that we created in the
7:38
previous tutorial and delete the
7:40
existing UI folder from the LVJL
7:42
directory. Copy the new UI folder that
7:45
we just generated into the same location
7:48
inside the project, replacing the old
7:50
one. If you open the UI_creen.c
7:53
file, you will see the updated code for
7:56
all the images we designed in Squareline
7:58
Studio. This confirms that the export
8:00
worked correctly. Let's build this
8:03
project to check for any errors. There
8:06
are no errors here. So, let's flash the
8:08
project to the board. You can see that
8:10
the UI has been loaded on the display.
8:12
All three hands are aligned at 12 just
8:15
as we designed them. Now, we will use
8:17
the RDC peripheral of the STM32
8:20
to fetch the current time. Let's open
8:23
CubeMX to configure the RDC. Go to the
8:26
timer section, select RDC, and activate
8:29
the clock source. Also, make sure to
8:32
activate the calendar feature. If your
8:35
development board has a 32.768
8:38
kHz external low-speed crystal, you can
8:41
enable it in the RCC configuration. I am
8:44
leaving the RTC clock source as the
8:47
low-speed internal oscillator at 32 kHz.
8:51
Let's configure the RDC parameters. Now,
8:53
I am keeping the 24-hour time format.
8:56
The asynchronous and synchronous
8:58
pre-ivider values together act as the
9:01
pre-scaler for the RDC clock. We must
9:04
choose these values so that the RDC
9:06
clock is reduced to exactly one hertz.
9:09
You can find more details about these
9:11
dividers in the reference manual of your
9:13
microcontroller. Look inside the RTC
9:16
section of the reference manual. The
9:18
formula to calculate the final clock is
9:21
shown there. Let's do the calculations.
9:23
If we use the default values in the
9:25
formula, the RDC clock will be
9:28
approximately 0.98 herz. These default
9:32
values are actually configured for an
9:33
external crystal of 32.768
9:37
kHz, which results in an accurate 1Hz
9:40
clock. But as I mentioned earlier, I'm
9:42
using a 32 kHz source clock here.
9:45
Therefore, I will modify the synchronous
9:48
prede 250. This adjustment results in
9:52
the RDC clock being exactly one hertz
9:55
which is what we need. Let's input these
9:58
values in a cubx. The asynchronous prede
10:01
divider value is already set to 127. So
10:05
we will set the synchronous pre-ivider
10:09
Now configure the data format as binary
10:12
and enter some random time. We will set
10:14
the correct time later through the code
10:16
itself. That's it for the RDC
10:19
configuration. Leave the rest of the
10:21
settings as default and click save to
10:24
generate the project code. We now have
10:29
init function which is responsible for
10:32
initializing the RDC. The first half of
10:35
this function initializes the RDC
10:38
peripheral while the later half
10:40
configures the time and date. Let's copy
10:42
the part that sets the time and comment
10:44
it out here. This is done to prevent the
10:47
RDC initialization function from
10:50
resetting the time every time the board
10:52
is rebooted. Instead, we will create a
10:55
new function to configure the time. And
10:58
this function will accept time values as
11:00
parameters. Inside this function, we
11:03
will define an RDC time structure and
11:05
initialize it with zeros. We will then
11:08
pass the time data from the parameters
11:11
into the respective elements of this
11:12
structure. Since we're only working with
11:15
the time and not the date, there is no
11:17
need to set the date here. We will call
11:20
this set time function inside the main
11:22
function. So whenever the board is
11:24
reset, the same input time will be
11:26
configured for the RDC. To avoid
11:29
resetting the time repeatedly, we can
11:31
use the RDC backup register. For this,
11:35
we will wrap the entire time setting
11:37
process inside an if condition, which
11:40
first checks the value of backup
11:42
register 0. The predefined time will
11:44
only be configured if the value stored
11:46
in backup register 0 is not 0x 4321.
11:52
This value is not special or fixed. It's
11:55
just a random value I chose for this
11:57
check. Obviously when the code runs for
11:59
the very first time backup register zero
12:02
will not contain this value. Therefore
12:05
the input time will be configured to the
12:07
RDC. After setting the time we will call
12:10
the right function to store this value
12:15
into backup register 0. Now imagine the
12:18
board is reset after the first run. In
12:21
that case, the function will again check
12:26
321 which is now present in backup
12:29
register 0. Since the value matches, the
12:32
function will not reset the time again
12:34
and we'll simply exit. Next, let's
12:37
define another function to update the
12:39
clock display. In this function, we will
12:42
first define RDC time and date
12:45
structures. Then we will call the
12:50
get time to read the current time. The
12:53
time will be stored in the structure g
12:55
time and we will use the binary data
12:57
format for simplicity. We also need to
13:00
call how underscore RDC
13:03
get date even though we are not using
13:05
the date directly because reading the
13:07
date is required to unlock the RDC
13:10
shadow registers. Now we will start by
13:13
updating the second hand on the clock
13:15
using the current time data. Let's open
13:18
square line studio to recall the
13:20
movement of the second's hand. The
13:22
second hand sweeps 60 rotation units for
13:25
every second and remember that it has an
13:28
initial offset of plus 1800. Let's
13:32
define assigned integer variable to
13:34
store the rotation value. We need to
13:36
calculate this rotation value based on
13:39
how many seconds have passed since zero.
13:42
For this, multiply the second value by
13:44
60 because each second corresponds to 60
13:48
rotation units. After this calculation,
13:51
add the offset to align the second hand
13:54
to its correct position. Now that we
13:56
have the final rotation value, we will
13:59
call the function LV MG set angle to set
14:03
this rotation for image 2, which
14:06
represents the second hand. Next, we
14:09
will calculate the rotation value for
14:11
the minute's hand. To do this, fetch the
14:14
current minute value from the time
14:15
structure and multiply it by 60. The
14:18
minute's hand also sweeps 60 rotation
14:22
units for every minute, which is why we
14:24
multiply it by 60. The offset for the
14:27
minute's hand is - 550. So we will use
14:31
this value directly in the code. If we
14:34
leave the calculation as it is right
14:35
now, the minute's hand will work, but it
14:38
will jump abruptly from 1 minute to the
14:40
next, sweeping 60 rotation units
14:43
instantly. I will demonstrate this
14:46
behavior shortly. To make the movement
14:48
smoother and visually appealing, we will
14:51
add the seconds data into the
14:53
calculation. So the minute's hand can
14:55
move gradually rather than jumping
14:57
suddenly. Let's see how this works. For
15:00
example, if the rotation value is
15:02
currently zero, the minute's hand points
15:05
to 9 minutes. If it instantly sweeps 60
15:08
rotations, it will directly jump to 10
15:11
minutes. But when we also include the
15:13
seconds data in the calculation, the
15:16
minute's hand moves slowly and
15:18
transitions from 9 to 10 minutes in a
15:21
continuous motion. We now have the
15:23
correct rotation value for the minute's
15:25
hand. So let's apply it to the
15:27
corresponding image. Next, we will
15:30
perform the same calculation for the
15:32
hour's hand. The hour's hand sweeps 300
15:35
rotation units for every hour. So we
15:38
need to multiply the current hour value
15:40
by 300. Also note that the minutes hand
15:43
corresponds to image 5, not image 4. The
15:47
offset for the hour's hand is plus 620.
15:51
To make the hour's hand movement
15:53
gradual, we will also add the minutes
15:55
data into this calculation. But this
15:58
time we will multiply the minutes by
16:00
five. This is because each minute sweeps
16:03
60 rotation units and a full 60 minutes
16:06
should sweep 300 rotations for the hour
16:09
hand which equals 60 * 5. Finally, we
16:13
will apply this calculated rotation
16:15
value to image 4 which represents the
16:18
hour's hand. Now inside the main
16:21
function after initializing the UI call
16:24
the function to set the time to the RDC.
16:27
I am setting it 1 minute ahead of the
16:29
current time for demonstration purposes.
16:32
Inside the while loop, we will call the
16:34
update clock function to continuously
16:36
refresh the clock UI based on the
16:39
current time data. Even though this
16:41
function is called every 5 milliseconds,
16:44
the RDC time itself updates every second
16:47
and therefore the clock hands rotate
16:49
smoothly according to real time. Let's
16:52
build the project now. There are no
16:54
errors in the build. So, let's flash the
16:56
project to the board. You can now see
16:59
that the clock is displaying the current
17:01
time, which is 12 hours and 12 minutes.
17:04
The second hand is ticking every second
17:08
and it is sweeping the correct angle for
17:10
each tick. Now, I will reset the board
17:13
to check if the time gets reset to our
17:15
predefined values. As you can see, the
17:18
time does not reset to the defined
17:20
values. Instead, it continues to tick
17:23
from the current time. This behavior is
17:26
due to the backup register we configured
17:28
earlier before setting up the time. So
17:31
everything is working exactly as
17:33
expected. We are successfully displaying
17:35
a stylish analog clock on the GC9 801
17:40
display. The movement of all the clock
17:43
hands is smooth and accurate. You can
17:46
try experimenting with different clock
17:48
face designs and even extend this
17:50
project by adding features such as date
17:53
and day display on the analog clock.
17:56
That's all for this video. I hope all
17:58
the steps were explained clearly. You
18:00
can download the complete project from
18:02
the link provided in the description. If
18:04
you have any doubts or questions, feel
18:06
free to leave a comment. Keep watching
18:09
and have a great day ahead.