You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
5.6 KiB
272 lines
5.6 KiB
6 years ago
|
/*
|
||
|
|
||
|
summer.c
|
||
|
|
||
|
stm32l0 daylight savings time (DST) calculation
|
||
|
|
||
|
US:
|
||
|
Der Energy Policy Act of 2005 (Public Law 109-58) bestimmt in Sec. 110 mit dem Titel Daylight Savings,
|
||
|
dass ab 2007 die Sommerzeit am zweiten Sonntag im März beginnt und am ersten Sonntag im November endet.
|
||
|
|
||
|
|
||
|
EU:
|
||
|
Central European Summer Time (CEST, britisch) oder Central European Daylight Saving Time
|
||
|
(applies to almost all european countries)
|
||
|
|
||
|
Die mitteleuropäische Sommerzeit beginnt jeweils am letzten Sonntag im März um 2:00 Uhr MEZ,
|
||
|
indem die Stundenzählung um eine Stunde von 2:00 Uhr auf 3:00 Uhr vorgestellt wird. Sie endet jeweils am letzten Sonntag im Oktober um
|
||
|
3:00 Uhr MESZ, indem die Stundenzählung um eine Stunde von 3:00 Uhr auf 2:00 Uhr zurückgestellt wird. Die Stunde von 2:00 Uhr bis 3:00 Uhr erscheint im Herbst also zweimal.
|
||
|
*/
|
||
|
|
||
|
#ifdef __unix
|
||
|
#include <stdio.h>
|
||
|
#include <stdint.h>
|
||
|
|
||
|
static uint8_t is_leap_year(uint16_t y)
|
||
|
{
|
||
|
if (
|
||
|
((y % 4 == 0) && (y % 100 != 0)) ||
|
||
|
(y % 400 == 0)
|
||
|
)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d)
|
||
|
{
|
||
|
uint8_t tmp1;
|
||
|
uint16_t tmp2;
|
||
|
tmp1 = 0;
|
||
|
if ( m >= 3 )
|
||
|
tmp1++;
|
||
|
tmp2 = m;
|
||
|
tmp2 +=2;
|
||
|
tmp2 *=611;
|
||
|
tmp2 /= 20;
|
||
|
tmp2 += d;
|
||
|
tmp2 -= 91;
|
||
|
tmp1 <<=1;
|
||
|
tmp2 -= tmp1;
|
||
|
if ( tmp1 != 0 )
|
||
|
tmp2 += is_leap_year(y);
|
||
|
return tmp2;
|
||
|
}
|
||
|
|
||
|
uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn)
|
||
|
{
|
||
|
uint8_t j, c, tmp8;
|
||
|
uint16_t tmp16;
|
||
|
y--;
|
||
|
j = y % 100;
|
||
|
c = y / 100;
|
||
|
tmp16 = c;
|
||
|
tmp16 *= 5;
|
||
|
tmp16 += ydn;
|
||
|
tmp8 = j;
|
||
|
j >>= 2;
|
||
|
c >>= 2;
|
||
|
tmp8 += j;
|
||
|
tmp8 += c;
|
||
|
tmp8 += 28;
|
||
|
tmp16 += tmp8;
|
||
|
tmp16 %= 7;
|
||
|
return tmp16;
|
||
|
}
|
||
|
|
||
|
uint16_t year;
|
||
|
uint16_t month;
|
||
|
uint16_t day;
|
||
|
uint16_t hour;
|
||
|
|
||
|
#else
|
||
|
#include "datecalc.h"
|
||
|
#include "stm32l031xx.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
this function does not require gui_date_adjust() to be called
|
||
|
0: no DST (Wintertime)
|
||
|
1: DST (Summertime)
|
||
|
-1: unknown (last sunday in oct, between 2am and 3am )
|
||
|
|
||
|
region
|
||
|
0: do nothing
|
||
|
1: EU
|
||
|
2: US
|
||
|
|
||
|
*/
|
||
|
int is_dst_by_date(uint8_t region)
|
||
|
{
|
||
|
uint16_t ydn;
|
||
|
uint16_t ydn_change_to_wintertime;
|
||
|
uint16_t ydn_change_to_summertime;
|
||
|
|
||
|
|
||
|
#ifndef __unix
|
||
|
|
||
|
uint16_t year;
|
||
|
uint16_t month;
|
||
|
uint16_t day;
|
||
|
uint16_t hour;
|
||
|
|
||
|
|
||
|
/* read date from date register */
|
||
|
|
||
|
year = ((RTC->DR >> 20) & 15);
|
||
|
year *= 10;
|
||
|
year += ((RTC->DR >> 16) & 15);
|
||
|
year += 2000;
|
||
|
|
||
|
month = ((RTC->DR >> 12) & 1);
|
||
|
month *= 10;
|
||
|
month += ((RTC->DR >> 8) & 15);
|
||
|
|
||
|
day = ((RTC->DR >> 4) & 3);
|
||
|
day *= 10;
|
||
|
day += ((RTC->DR) & 15);
|
||
|
|
||
|
/* also get the hour for later comparison */
|
||
|
|
||
|
hour = ((RTC->TR >> 20) & 3); /* assuming 24-hour clock, not sure about PM flag */
|
||
|
hour *= 10;
|
||
|
hour += ((RTC->TR >> 16) & 15);
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/* convert all dates to date numbers of the current year*/
|
||
|
|
||
|
if ( region == 1 ) /* EU */
|
||
|
{
|
||
|
/* European Summer Time: Last Sunday in March and last Sunday in October */
|
||
|
ydn_change_to_summertime = get_year_day_number(year, 3, 31);
|
||
|
ydn_change_to_wintertime = get_year_day_number(year, 10, 31);
|
||
|
}
|
||
|
else if ( region == 2 ) /* US */
|
||
|
{
|
||
|
/* US DST rules: Second Sunday in March and First Sunday in November */
|
||
|
ydn_change_to_summertime = get_year_day_number(year, 2, 28+is_leap_year(year));
|
||
|
ydn_change_to_summertime += 14;
|
||
|
|
||
|
ydn_change_to_wintertime = get_year_day_number(year, 10, 31);
|
||
|
ydn_change_to_wintertime += 7;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ydn_change_to_summertime -= get_weekday_by_year_day_number(year, ydn_change_to_summertime);
|
||
|
ydn_change_to_wintertime -= get_weekday_by_year_day_number(year, ydn_change_to_wintertime);
|
||
|
|
||
|
ydn = get_year_day_number(year, month, day);
|
||
|
|
||
|
if ( ydn == ydn_change_to_summertime )
|
||
|
if ( hour >= 2 )
|
||
|
return 1; /* yes, it is DST */
|
||
|
|
||
|
if ( ydn == ydn_change_to_wintertime )
|
||
|
{
|
||
|
if ( hour < 2 )
|
||
|
return 1; /* still DST */
|
||
|
if ( hour > 3 )
|
||
|
return 0; /* not DST any more */
|
||
|
return -1; /* not sure whether DST or not */
|
||
|
}
|
||
|
|
||
|
if ( ydn > ydn_change_to_summertime && ydn < ydn_change_to_wintertime )
|
||
|
return 1; /* within DST */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef __unix
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
int dd;
|
||
|
int d = -2;
|
||
|
for( year = 2000; year < 2030; year++ )
|
||
|
{
|
||
|
printf("%d: ", year);
|
||
|
for( month = 3; month <= 11; month ++ )
|
||
|
{
|
||
|
for( day = 1; day<= 31; day++ )
|
||
|
{
|
||
|
hour = 2;
|
||
|
dd = is_dst_by_date(1);
|
||
|
if ( d == 0 && dd == 1 )
|
||
|
{
|
||
|
printf("%d.%d.%d", day, month, year);
|
||
|
}
|
||
|
switch(dd)
|
||
|
{
|
||
|
//case -1: printf("%d.%d.%d", day, month, year); break;
|
||
|
//case 0: printf("w"); break;
|
||
|
//case 1: printf("s"); break;
|
||
|
|
||
|
}
|
||
|
d = dd;
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
/*
|
||
|
region
|
||
|
0: do nothing
|
||
|
1: EU
|
||
|
2: US
|
||
|
|
||
|
*/
|
||
|
|
||
|
void adjustDST(uint8_t region)
|
||
|
{
|
||
|
int is_dst;
|
||
|
int dst_state;
|
||
|
|
||
|
is_dst = is_dst_by_date(region);
|
||
|
|
||
|
if ( is_dst >= 0 )
|
||
|
{
|
||
|
dst_state = 0;
|
||
|
if ( RTC->CR & RTC_CR_BCK ) /* BKP flag in the CR register */
|
||
|
dst_state = 1;
|
||
|
|
||
|
if ( is_dst != dst_state )
|
||
|
{
|
||
|
|
||
|
__disable_irq();
|
||
|
|
||
|
PWR->CR |= PWR_CR_DBP; /* disable write protection (step 1) */
|
||
|
|
||
|
RTC->WPR = 0x0ca; /* disable RTC write protection (step 2) */
|
||
|
RTC->WPR = 0x053;
|
||
|
|
||
|
if ( is_dst != 0 )
|
||
|
{
|
||
|
RTC->CR |= RTC_CR_BCK; /* the RTC will now run in summer time (set DST flag) */
|
||
|
RTC->CR |= RTC_CR_ADD1H; /* change to DST (Summertime): add one hour */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RTC->CR &= ~RTC_CR_BCK; /* the RTC will now run in winter time (turn off DST flag) */
|
||
|
RTC->CR |= RTC_CR_SUB1H; /* change back to none DST (Wintertime): subtract one hour */
|
||
|
}
|
||
|
|
||
|
RTC->WPR = 0; /* enable RTC write protection (step 2) */
|
||
|
RTC->WPR = 0;
|
||
|
|
||
|
__enable_irq();
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|