Close

Calculating Time

A project log for NTP Clock Based on STM32H735 Discovery Kit

This is an SNTP clock based on the STM32H735 Discovery Kit.

dmoisandmoisan 04/01/2021 at 22:130 Comments

We have a seconds counter in our NTP clock, but now we need to convert this into human-readable information on the LCD display.   The function DisplayClock does this, and it's (mostly) neatly divided between the computation part and the display part.  I'm only going to cover the computation aspect in this project entry.

I got the algorithm for time computation from APPLICATION NOTE 517 DS1371/DS1372/DS1374 32-BIT BINARY COUNTER TIME CONVERSION, from Maxim Integrated.  It's the same code I used in the Twatch. This is the algorithm, starting with a timestamp in seconds:

  1. Divide the timestamp by 60 to get minutes.
  2. Take the modulus of 60 of the timestamp to get seconds.
  3. Divide the timestamp by 3600 to get hours.
  4. Divide hours by 24 to get days.
  5. Adjust the days to include 1969 and 1968 (reference days to a leap year, 1968).
  6. Determine the number of leap year periods since 1968.
  7. If the current date is a leap year, and it is past February 28th, add another leap year period.
  8. Compute the number of years since 1968.
  9. Find the current month using the number of days that have passed for the current year.
  10. Determine the day of the current month.
  11. Determine the current day of the week.
  12. Add the epoch year (1900 for Unix, 1830 for NTP) to get the current year.

Here is the time computation code from DisplayClock:

extern void DisplayClock(int SecondsCount, char* TimeZoneName, int timeZoneOffset, int DSTFlag) {

	static unsigned int DaysToMonth[13] = {
	   		0,31,59,90,120,151,181,212,243,273,304,334,365
			};

	static unsigned int hour, day, minute, second, month, year;

	static unsigned int whole_minutes, whole_hours, whole_days;
	static unsigned long whole_days_since_1968;
	static unsigned long leap_year_periods, days_since_current_lyear, whole_years;

	static unsigned int days_since_first_of_year;
	static unsigned int days_to_month;
	static unsigned int day_of_week;

	whole_minutes = SecondsCounter / 60;
	// second = SecondsCounter - (60 * whole_minutes);		    // leftover seconds
	second = (SecondsCounter % 60);
	whole_hours  = whole_minutes / 60;
	minute = whole_minutes - (60 * whole_hours);            // leftover minutes
	whole_days   = whole_hours / 24;
	hour         = whole_hours - (24 * whole_days);         // leftover hours

	whole_days_since_1968 = whole_days + 365 + 366;
	leap_year_periods = whole_days_since_1968 / ((4 * 365) + 1);

	days_since_current_lyear = whole_days_since_1968 % ((4 * 365) + 1);

	// if days are after a current leap year then add a leap year period

	if ((days_since_current_lyear >= (31 + 29))) {
		leap_year_periods++;
		}
	whole_years = (whole_days_since_1968 - leap_year_periods) / 365;
	// Fixed to display day of year properly so January 1st displays as "Day 1"
	days_since_first_of_year = whole_days_since_1968 - (whole_years * 365) - leap_year_periods + 1;

	if ((days_since_current_lyear <= 365) && (days_since_current_lyear >= 60)) {
		days_since_first_of_year++;
		}
	year = whole_years + 68;

	// 	setup for a search for what month it is based on how many days have passed
	//  within the current year

	month = 13;
	days_to_month = 366;
//		Fixed to eliminate "Feb 0" problem where the last day of a month was displayed as the "0th" day of the next month
//		Changed comparison to be less than or equal
	while (days_since_first_of_year <= days_to_month) {
		month--;
		days_to_month = DaysToMonth[month-1];
		if ((month > 2) && ((year % 4) == 0)) {
		   days_to_month++;
			}
		}

	day = days_since_first_of_year - days_to_month;


	day_of_week = (whole_days  + 1) % 7; // was (whole_days+4) % 7

	year = year + 1830;

Note the last line.  The epoch year is 1830 to match the SNTP timestamp provided by that function.  Unix timestamps use 1900. 

I need to talk about SNTP.  LwIP has an SNTP module that must be integrated into your code.  I had a lot of trouble with this, and so did others.  I'll describe that next time.

Discussions