Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Since STM32F103 MCU internal storage time, then through this function, you can do a perpetual calendar. The implementation process is as follows:

typedef struct
{
    vu8 hour;
    vu8 min;
    vu8 sec;
    // Gregorian calendar day month year week
    vu16 w_year;
    vu16 w_month;
    vu8 w_date;
    vu8 week;

} _calendar_obj;
Copy the code

Start by defining a structure to store hours, minutes, seconds, years, months, days, and weeks of data.

// Check whether it is a leap year function
// Enter: year
// Output: is the year a leap year? 1, yes.0, no
u8 IS_Leap_Year( u16 year )
{
    // Divisible by 4 or 400, and not by 100
    if( ( ( year % 4= =0 ) || ( year % 400= =0 ) ) && ( year % 100! =0))return 1;
    else
        return 0;

}
// Set the clock
// Convert the input clock to seconds
// Based on January 1, 1970
// 1970-2099 are legal years
// Return value :0, success; Others: Error code.
// Monthly data table
u8 const table_week[12] = {0.3.3.6.1.4.6.2.5.0.3.5}; // Monthly revision data table
// A list of months and dates for ordinary years
const u8 mon_table[12] = {31.28.31.30.31.30.31.31.30.31.30.31};
u8 RTC_Set( u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec )
{
    u16 t;
    u32 seccount = 0;
    if( syear < 1970 || syear > 2099 )
        return 1;
    for( t = 1970; t < syear; t++ )						// Add the seconds of all years
    {
        if( IS_Leap_Year( t ) )
            seccount += 31622400;						// Number of leap year seconds
        else
            seccount += 31536000;						// The number of seconds in a normal year
    }
    smon -= 1;
    for( t = 0; t < smon; t++ )							// Add the number of seconds in the previous month
    {
        seccount += ( u32 )mon_table[t] * 86400;
        if( IS_Leap_Year( syear ) && t == 1 )				// The number of seconds in a day is increased in February in leap years
            seccount += 86400;
    }
    seccount += ( u32 )( sday - 1 ) * 86400;				// Add the seconds of the previous days
    seccount += ( u32 )hour * 3600;						// Add hours to seconds
    seccount += ( u32 )min * 60;							// Add minutes to seconds
    seccount += sec;									// Add the last seconds

    RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE );
    PWR_BackupAccessCmd( ENABLE );

    RTC_SetCounter( seccount );							// Set the RTC counter value
    RTC_WaitForLastTask();
    return 0;
}
// Initialize the alarm clock
// Based on January 1, 1970
// 1970-2099 are legal years
/ / syear smon, sday, hour, min, SEC: minutes when the alarm clock (date) (month) (year)
// Return value :0, success; Others: Error code.
u8 RTC_Alarm_Set( u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec )
{
    u16 t;
    u32 seccount = 0;
    if( syear < 1970 || syear > 2099 )
        return 1;
    for( t = 1970; t < syear; t++ )						// Add the seconds of all years
    {
        if( IS_Leap_Year( t ) )
            seccount += 31622400;						// Number of leap year seconds
        else
            seccount += 31536000;						// The number of seconds in a normal year
    }
    smon -= 1;
    for( t = 0; t < smon; t++ )							// Add the number of seconds in the previous month
    {
        seccount += ( u32 )mon_table[t] * 86400;
        if( IS_Leap_Year( syear ) && t == 1 )				// The number of seconds in a day is increased in February in leap years
            seccount += 86400;
    }
    seccount += ( u32 )( sday - 1 ) * 86400;				// Add the seconds of the previous days
    seccount += ( u32 )hour * 3600;						// Add hours to seconds
    seccount += ( u32 )min * 60;							// Add minutes to seconds
    seccount += sec;									// Add the last seconds

    RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE );
    PWR_BackupAccessCmd( ENABLE );

    RTC_SetAlarm( seccount );
    RTC_WaitForLastTask();

    return 0;
}
// Get the current time
// Return value :0, success; Others: Error code.
u8 RTC_Get( void )
{
    static u16 daycnt = 0;
    u32 timecount = 0;
    u32 temp = 0;
    u16 temp1 = 0;
    timecount = RTC_GetCounter();
    temp = timecount / 86400;							If the number of days is less than 1 day, the number of seconds is less than 86400

    if( daycnt ! = temp ) { daycnt = temp; temp1 =1970;
        while( temp > 365 )								// More than 1 year
        {
            if( IS_Leap_Year( temp1 ) )						/ / a leap year
            {
                if( temp > 366 )
                {
                    temp -= 366;
                }
                else
                {
                    temp1++;
                    break; }}else										/ / leap year
            {
                temp -= 365;
            }
            temp1++;
        }
        calendar.w_year = temp1;						// Get the year
        temp1 = 0;
        while( temp >= 28 )								// Over 1 month
        {
            if( IS_Leap_Year( calendar.w_year ) && temp1 == 1 )
            {
                if( temp >= 29 )
                    temp -= 29;
                else
                    break;
            }
            else
            {
                if( temp >= mon_table[temp1] )
                    temp -= mon_table[temp1];
                else
                    break;
            }
            temp1++;
        }
        calendar.w_month = temp1 + 1;					/ / month
        calendar.w_date = temp + 1;						/ /,
    }
    temp = timecount % 86400;
    calendar.hour = temp / 3600;
    calendar.min = ( temp % 3600 ) / 60;
    calendar.sec = ( temp % 3600 ) % 60;
    calendar.week = RTC_Get_Week( calendar.w_year, calendar.w_month, calendar.w_date );
    return 0;
}
// Get what day of the week it is
// Enter the calendar date to get the week (1901-2099 only allowed)
// Input parameters: Gregorian calendar year month day
// Return value: week number
u8 RTC_Get_Week( u16 year, u8 month, u8 day )
{
    u16 tem;
    u8 yearH, yearL;
    yearH = year / 100;
    yearL = year % 100;
    if( yearH > 19 )
        yearL += 100;
    tem = yearL + yearL / 4;
    tem = tem % 7;
    tem = tem + day + table_week[month - 1];
    if( yearL % 4= =0 && month < 3 )
        tem--;
    return tem % 7;
}
Copy the code

Next, write the function related to the time setting and alarm setting. Because the time stored in RTC is in seconds, it needs to be converted to seconds when reading the time and setting the time. The number of seconds stored is not from the moment the time is set, but from 00 hours 00 minutes 00 seconds on 01 January 1970. Why this time? This starts with the birth of the computer.

The birth of time stamps

In August 1969, Ken Thompson, a programmer at Bell LABS, took advantage of his wife and children’s month-long absence to set out to create a revolutionary new operating system. He developed a version of Unix using the B-compiled language on the aging PDP-7 machine.

Thompson and his colleague Dennis Leach then improved ON B and developed C, rewriting Unix and releasing the new version in 1971.

After Unix was invented, to represent time on Unix, it was necessary to find a way to define a complete, verifiable representation of time that could represent the existence of a piece of data prior to a certain time.

Thus, Unix timestamps were defined by comparing the current time with an “epoch time”, with the number of seconds between them as the timestamp.

To make Unix timestamps last as long as possible, 1971-1-1, the birth date of Unix, was originally defined as “epoch time.”

Wasn’t it 1970? How did it change to 1971? That’s because the timestamp was changed.

Timestamp modification

In addition to starting at 1971-1-1 instead of 1970-1-1, the original timestamp also changed every 1/60 of a second instead of every 1 second.

In addition, Unix was invented in 1971 when computer systems were 32 bits, and if there were integers in 32, the maximum would be 2147483647(2^31-1).

So, a simple mathematical calculation, if using the timestamp calculation method at the time, the Unix timestamp can use 4294967296/ (606024) /60 = 828.5 days (a day has 606024 seconds, every 1/60 seconds will occupy a timestamp).

Imagine designing a computer system that only represents 828.5 days in time. But that was true of the original Unix.

Later, Unix developers realized that this was not sustainable and began to make changes.

At first, they changed the timestamp every 1/60 of a second to change the timestamp every second. This amplifies the time stamp by another 60 times. That’s 828.5*60/365 = 136 years.

At this point, on the one hand, 136 years is long enough, and a slight change in the epoch would not make much difference. On the other hand to facilitate memory and use.

So the epoch time was adjusted from 1971-01-01 to 1970-01-01. As a result, the subsequent development languages have continued to use 1970-1-1.

So when we talk about time stamps, we usually refer to the total number of seconds in Greenwich Mean Time (GMT) from 00, 00 minutes, 00 seconds on January 01, 1970 to the present.

When setting the time or reading the time, we need to convert the time to the number of seconds from 00:00:00:00 on January 1, 1970 to the current time, so we need a special function to convert the time. Remember to determine the leap year in the conversion process.

You can set the time and alarm directly by using the following two functions: year, month, day, hour, minute, and second.

   RTC_Set( 2021.10.1.08.00.00 );				// Set the time
   RTC_Alarm_Set(2021.10.7.08.00.00 );
Copy the code

Just call RTC_Get() when you need to read the time.

    RTC_Get();
Copy the code

This function stores the read time information into the structure, which reads the current time directly from the structure variable.