Skip to main content
Louie88
Senior
January 18, 2025
Solved

Massive Analog/Digital clock time delay in TouchGFX clock examples

  • January 18, 2025
  • 7 replies
  • 3977 views

Board STM32H7474I-DISCO, TouchGFX V4.24.2.

If you keep the analog/digital clock examples running (in the evaluatation board) for (say) 10 minutes then the clock on the LCD screen will be more than 15-25 seconds behind your watch. This is a massive delay: 15-25 seconds delay during 600 seconds is not nothing.

Probably reasons (my ideas)

  1. The SystemTick (VSYNC = 60Hz, 16.6667ms) is not accurate and it is not generated by an MCU Timer. Hard to believe it.
  2. All the examples update the hour/minute/second display (on the LCD) from the TouchGFX's handleTickEvent() interrupt service routine. The update runs once in every second. I can imagine that updating the LCD takes more than 16.6667ms so when the next system tick interrupt occurs the interrupt service is still in service. This way we can miss a couple of system ticks.

It would be nice to know which statement is right and how to resolve this issue.

I tried to open TouchGFX Academy Tutorial3 example in STMCubeMX and create a new, dedicated timer with 20ms interrupt for the LCD update, but STMCubeMX did not allow to edit any Timer: properties of any enabled timers were empty. I also do not know how I can consume a Timer Interrupt in TouchGFX.Thanks for your help.
Louis

PS: I also would like to mention that TouchGFX Academy/Tutorial 3 increments the minute counter in every second... not in every minute. This is not a big issue but it should be fixed in the tutorial.

 

 

Best answer by unsigned_char_array

Just sync it periodically with the RTC.

I call this in my screen view handleTickEvent():

 //clock
 //When every N tick execute C++ code
 //Execute C++ code
 tm const* timeinfoPtr = nullptr;
 static tm timeinfoOld = tm();// {0};
 
 #ifdef SIMULATOR
 	time_t rawtime;	
 
 	time(&rawtime);
 	timeinfoPtr = localtime(&rawtime);
 #else
 	//tm timeinfo={};
 	//timeinfoPtr = &timeinfo;
 
 	timeinfoPtr = getTimeStruct();
 #endif
 
 
 if (timeinfoPtr != nullptr)
 {
 	bool timeChanged = 	timeinfoPtr->tm_sec!= timeinfoOld.tm_sec||
 				timeinfoPtr->tm_min != timeinfoOld.tm_min ||
 				timeinfoPtr->tm_hour != timeinfoOld.tm_hour;
 
 
 	bool dateChanged = 	timeinfoPtr->tm_mday != timeinfoOld.tm_mday ||
 				timeinfoPtr->tm_mon != timeinfoOld.tm_mon ||
 				timeinfoPtr->tm_year != timeinfoOld.tm_year;
 
 	timeinfoOld = *timeinfoPtr;
 
 
 	
 	static bool timeInitialized = false;
 	
 	if (!timeInitialized)
 	{
 		timeInitialized = true;
 		// disable animation the first time to prevent arms from rotating a lot
 		analogClock1.initializeTime24Hour(timeinfoPtr->tm_hour, 
 							timeinfoPtr->tm_min, 
 							timeinfoPtr->tm_sec);
 	}
 	else
 	{
 		// analogClock need to be updated more than 1 time per second to allow animation
 		analogClock1.setTime24Hour(timeinfoPtr->tm_hour, 
 						timeinfoPtr->tm_min, 
 						timeinfoPtr->tm_sec);
 	}
 	
 	if (timeChanged)
 	{
 		digitalClock1.setTime24Hour(	timeinfoPtr->tm_hour, 
 							timeinfoPtr->tm_min, 
 							timeinfoPtr->tm_sec);
 	}
 	
 
 	if(dateChanged)
 	{
 		Unicode::snprintf(dateBuffer, DATE_SIZE, 
 						"%04d-%02d-%02d", 
 						timeinfoPtr->tm_year + TM_YEAR_BASE, 
 						timeinfoPtr->tm_mon - TM_MONTH_OFFSET, 
 						timeinfoPtr->tm_mday);
 			
 		date.resizeToCurrentText();
 		date.invalidate();
 	}
 }

 

 

7 replies

Louie88
Louie88Author
Senior
January 19, 2025

I investigated a bit, and as I expected STM supports the performance measurement. (Thanks for that STM) In STM32H474I-DISCO board' PJ8 = RENDER_TIME and PJ3 = VSYNC_FREQ. These port pins are available on the CN6 ARDUINO connector (easy access). You need a scope and you can mesaure the VSYNC frame rate and the time is necessary to render graphics on the LCD screen.

On the attached 20250119_125653.bmp screenshot the yellow trace is the VSYNC_FREQ = 58.96Hz. It is not 60.0 Hz! This is the reason of the not accurate time measurement. The blue trace the time is necessary to render a label and a floating-point number (system tick increment time) on LCD. To render some text and a float number we need more than 15ms which just fit into the refresh rate. There is no LCD rendering overflow.

On the other screenshot (20250119_130344.bmp) the LCD is updated in every second (yellow). The blue rendering time is running time of the code which checks whether the 1 second (or more) elapsed in the handleTickEvent() interrupt service routine.

Therefore, supposing that if we increment a counter in every TouchGFX's handleTickEvent() interrupt service routine and check whether its value = 60 means 1 second is faulty and cannot be used. For accurate clock we need to keep track the elapsed time (float) and show HH:mm:ss digital clock value from the elapsed time. The only problem is you have to specify the exact VSYNC (system tick) frequency for the app.

Rather than specifying the VSYNC frequency it would be better solution using a dedicated TIMER of the MCU which generates (say) 20ms interrupt and updates the LCD from that interrupt instead of handleTickEvent().

I think if I open the TouchGFX project's IOC file in STM32CubeMX, then I can create an auto repeat timer with enabled interrupt. The question is how I can use the Timer interrupt in TouchGFX?

I have no idea about that.

Louis

GaetanGodart
Technical Moderator
January 20, 2025

Hello @Louie88 ,

 

The tick timer is meant to enable 60 fps, therefore it is not extremely accurate as it is not meant to be used as a real clock.

To a timer interrupt in TouchGFX, you have look at these videos :

 

Basically, there is 2 ways when you receive your interrupt, either you use a task from your RTOS that will change your time or you just set a value and react to that value in the model.cpp.

 

Regards,

Louie88
Louie88Author
Senior
January 20, 2025

Hi Gaetan,

I thought the examples are for demonstrating the features not for an accurate product. But should not we (users especially beginners) be warned that the clock is not accurate, it just an example how to implement the GUI and show the time and it is not intended to be an Apple watch?

Thanks for the reply and the links. They seem to be very useful. 

If I find the way how the clock works right (accurate time) then I will post. it.

Thanks,

Louis

 

Andrew Neil
Super User
January 20, 2025
A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Louie88
Louie88Author
Senior
January 20, 2025

Hi MM..1,

Thanks for your notes. The reason why I implement my own digital clock was to be familiar with TGFX and how I can use the MCU peripherals in TGFX to show date in a nice form. The digital/analog clock app seemed to be perfect pick for this purpose. But when I got 20s delay for 10 minutes, I wanted to understand why it happens, then I wanted to correct it. Yes, I know the MCU has a dedicated RTC but using it in TGFX seemed to be impossible for a beginner with 30+ years practice (with Intel 8051, Silicon Labs Ti, STM MCUs) in embedded systems.

Louis

 

 

Louie88
Louie88Author
Senior
January 21, 2025

Hi guys,

I created 20ms time base by using Timer1 in H7474I-DISCO board:
Clock = 200MHz
Prescaler = 40 000
Counter = 100
Resulting 50Hz Timer1 interrupt.

In main.c from the HAL_TIM_PeriodElapsedCallback() I toggle PK1 port (Available on CN5.3 pin) and I increment lcdUpdateTick variable to show how many times the Timer 1 interrupt happened. The plan is to check if (lcdUpdateTick % 50) = 50 * 20ms = 1 sec, in mainView.cpp somehow and update the clock display.

...
uint32_t lcdUpdateTick = 0;
...

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	// Existing system tick code
	if (htim->Instance == TIM6) {
		HAL_IncTick();
	} 

	// My added code to toggle PK1 pin
	if (htim->Instance == TIM1) {
		lcdUpdateTick++;
		HAL_GPIO_TogglePin(GPIOK, GPIO_PIN_1);
	}
}

On scope I got 25.0000Hz, which is right because PK1 is toggled in every interrupt so the 50Hz is divided by 2 = 25Hz, I attached the screenshot.

Now my problem is how can detect this TIMER 1 interrupt in mainView.cpp? I guess I should:

  1. Create a queue with os_create_queue()
  2. Send this message with os_send_message()
  3. Form mainvView.cpp receive the message os_receive_message()
  4. Update the clock.

Would you please send me an example how to do this right? (Maybe I wrong and there is better solution for this job...)


Thanks,
Louis

MM..1
Chief III
January 21, 2025

HW interrupt is in real world delayd based on priority etc. But counter will show valid value, then simply in screenview tick check 

 

if( (lcdUpdateTick/50) != lastshowedsec ) shownewsec... 

 

and this require be

 

volatile uint32_t lcdUpdateTick = 0;

 

 because is updated in ISR. And in view extern "C" it.

PS> Name it lcdUpdateTick is inappropriate.

Louie88
Louie88Author
Senior
January 24, 2025

Hi MM..1,

Almost all measurements (flow rate, totalizers, temperature, pressure, density,...) happens in interrupts in real world in the flow measurement industry, regardless the interrupt latency, which is almost always the same. Good interrupt priority policy helps a lot.

But I dropped the TIMER 1 interrupt-based clock example. Instead of TIMER 1 interrupt and counting the interrupt counts manually, I moved to STM32H7xxx's Real Time Clock (RTC) Subsystem. It can be initialized to run from 32 768Hz crystal which is assembled on the discovery board, and you do not have manually calculate the elapsed time even you get a leap-year (except 400 years) safe calendar. It works nice. No delay (or just minimal) but you can calibrate the clock of the RTC if the accuracy would be very important. And the main feature: I do not need any interrupt to read RTC. 

However one question raised about RTC: How can I stop and restart time measurement in RTC to write time and calendar registers?

Best regards,

Louis

 

Andrew Neil
Super User
January 24, 2025

@Louie88 wrote:

However one question raised about RTC: How can I stop and restart time measurement in RTC to write time and calendar registers?


That's a separate question - please start a new thread for that, and post a link here so that people can find it.

 

If your original TouchGFX question has been answered, please mark the solution - see:

https://community.st.com/t5/community-guidelines/help-others-to-solve-their-issues/ta-p/575256

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Lead II
January 24, 2025

Just sync it periodically with the RTC.

I call this in my screen view handleTickEvent():

 //clock
 //When every N tick execute C++ code
 //Execute C++ code
 tm const* timeinfoPtr = nullptr;
 static tm timeinfoOld = tm();// {0};
 
 #ifdef SIMULATOR
 	time_t rawtime;	
 
 	time(&rawtime);
 	timeinfoPtr = localtime(&rawtime);
 #else
 	//tm timeinfo={};
 	//timeinfoPtr = &timeinfo;
 
 	timeinfoPtr = getTimeStruct();
 #endif
 
 
 if (timeinfoPtr != nullptr)
 {
 	bool timeChanged = 	timeinfoPtr->tm_sec!= timeinfoOld.tm_sec||
 				timeinfoPtr->tm_min != timeinfoOld.tm_min ||
 				timeinfoPtr->tm_hour != timeinfoOld.tm_hour;
 
 
 	bool dateChanged = 	timeinfoPtr->tm_mday != timeinfoOld.tm_mday ||
 				timeinfoPtr->tm_mon != timeinfoOld.tm_mon ||
 				timeinfoPtr->tm_year != timeinfoOld.tm_year;
 
 	timeinfoOld = *timeinfoPtr;
 
 
 	
 	static bool timeInitialized = false;
 	
 	if (!timeInitialized)
 	{
 		timeInitialized = true;
 		// disable animation the first time to prevent arms from rotating a lot
 		analogClock1.initializeTime24Hour(timeinfoPtr->tm_hour, 
 							timeinfoPtr->tm_min, 
 							timeinfoPtr->tm_sec);
 	}
 	else
 	{
 		// analogClock need to be updated more than 1 time per second to allow animation
 		analogClock1.setTime24Hour(timeinfoPtr->tm_hour, 
 						timeinfoPtr->tm_min, 
 						timeinfoPtr->tm_sec);
 	}
 	
 	if (timeChanged)
 	{
 		digitalClock1.setTime24Hour(	timeinfoPtr->tm_hour, 
 							timeinfoPtr->tm_min, 
 							timeinfoPtr->tm_sec);
 	}
 	
 
 	if(dateChanged)
 	{
 		Unicode::snprintf(dateBuffer, DATE_SIZE, 
 						"%04d-%02d-%02d", 
 						timeinfoPtr->tm_year + TM_YEAR_BASE, 
 						timeinfoPtr->tm_mon - TM_MONTH_OFFSET, 
 						timeinfoPtr->tm_mday);
 			
 		date.resizeToCurrentText();
 		date.invalidate();
 	}
 }

 

 

"Kudo posts if you have the same problem and kudo replies if the solution works.Click ""Accept as Solution"" if a reply solved your problem. If no solution was posted please answer with your own."
Louie88
Louie88Author
Senior
January 25, 2025

Thanks, currently I am already doing what you suggested. (Except I check seconds only. If seconds is changed then I update the screen because any other part of RTC might be changed)

I just was wonder how to stop and restart the RTC? It is hard to belevie that such a complex and nice RTC chip does not have start/stop feature...

Best regards,

Louis

PS: My job was to demonstrate how "easy" to use the TouchGFX and can it be "mixed" with HAL based STM32CubeIDE app. I attached two screenshots of the working demo: the clock-calendar and the col-calendar setup screens.