stm32f1xx - нет аппаратного календаря, UNIX-time
http://we.easyelectronics.ru/Soft/funkcii-kalendarya-i-vremeni-na-odnom-registre.html
Следует учитывать, что RTC в STM32F10x в отличии от внешних микросхем (DS1307) или часов в MCU NXP не имеют аппаратного календаря и представляют всего лишь 32х битный счётчик. С другой стороны это позволяет легко использовать кванты времени отличные от секунды.
stm32 для часов предоставляет нам 32 разрядный регистр, который можно настроить на увеличение каждую секунду. Вроде бы всё хорошо, берем количество секунд, делим на 60 получаем минуты, еще раз — часы… сутки, месяцы, года… Но есть одно НО. Это високосные годы и различное количество дней в месяцах. Посмотрев на различные реализации, решил именно тут изобрести велосипед.
Итак:
Дано:
32 разрядный регистр, инкремент ежесекундно.
Задача:
Обеспечить функции отсчета времени и календаря основываясь только на данных этого регистра.
Решение:
Сразу приходит на ум UNIX-time. Но вот в плане реализации информации маловато. Ну да ладно. Идем в википедию и читаем про преобразование дат. Так. Значит формулы есть — уже хорошо. Осталось их привести в удобоваримый вид и немного дополнить. После мозговых преобразований имеем:
#define JD0 2451911 // дней до 01 янв 2001 ПН
typedef struct{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} ftime_t;
// функция преобразования григорианской даты и времени в значение счетчика
uint32_t FtimeToCounter(ftime_t * ftime)
{
uint8_t a;
uint16_t y;
uint8_t m;
uint32_t JDN;
// Вычисление необходимых коэффициентов
a=(14-ftime->month)/12;
y=ftime->year+4800-a;
m=ftime->month+(12*a)-3;
// Вычисляем значение текущего Юлианского дня
JDN=ftime->day;
JDN+=(153*m+2)/5;
JDN+=365*y;
JDN+=y/4;
JDN+=-y/100;
JDN+=y/400;
JDN+=-32045;
JDN+=-JD0; // так как счетчик у нас нерезиновый, уберем дни которые прошли до 01 янв 2001
JDN*=86400; // переводим дни в секунды
JDN+=(ftime->hour*3600); // и дополняем его скундами текущего дня
JDN+=(ftime->minute*60);
JDN+=(ftime->second);
// итого имеем количество секунд с 00-00 01 янв 2001
return JDN;
}
// функция преобразования значение счетчика в григорианскую дату и время
void CounterToFtime(uint32_t counter,ftime_t * ftime)
{
uint32_t ace;
uint8_t b;
uint8_t d;
uint8_t m;
ace=(counter/86400)+32044+JD0;
b=(4*ace+3)/146097; // может ли произойти потеря точности из-за переполнения 4*ace ??
ace=ace-((146097*b)/4);
d=(4*ace+3)/1461;
ace=ace-((1461*d)/4);
m=(5*ace+2)/153;
ftime->day=ace-((153*m+2)/5)+1;
ftime->month=m+3-(12*(m/10));
ftime->year=100*b+d-4800+(m/10);
ftime->hour=(counter/3600)%24;
ftime->minute=(counter/60)%60;
ftime->second=(counter%60);
}
Теперь про сами функции. Первая преобразует дату и время из структуры ftime_t в значение счетчика секунд, прошедших с 01.01.2001. В принципе может быть выбрана любая другая дата, указывается в смещении JD0 в Юлианских днях, прошедших с 24 ноября 4714 г. до н. э. Если выбрать 1 января 1970, то будет показывать UNIX-time и можно будет сделать часы для гиков и подарить на новый год. Вторая, собственно, антипод первой.
Вариант конвертации из википедии:
Конвертация времени, заголовочный файл xtime.h
typedef unsigned short u16_t;
typedef unsigned long u32_t;
typedef signed short s16_t;
typedef signed long s32_t;
// DEF: standard signed format
// UNDEF: non-standard unsigned format
#define _XT_SIGNED
#ifdef _XT_SIGNED
typedef s32_t xtime_t;
#else
typedef u32_t xtime_t;
#endif
struct tm
{ /* date and time components */
BYTE tm_sec;
BYTE tm_min;
BYTE tm_hour;
BYTE tm_mday;
BYTE tm_mon;
u16_t tm_year;
};
void xttotm(struct tm *t, xtime_t secs);
xtime_t xtmtot(struct tm *t);
Конвертация времени: xtime.c
#include "xtime.h" /* Приводится пример реализации на языке Си функций конвертации между UNIX-временем и обычным представлением в виде даты и времени суток. Пример приведен в стандартном знаковом 32-бит формате. Однако, закомментировав определение _XT_SIGNED, пример соберется в беззнаковом варианте. */ #define _TBIAS_DAYS ((70 * (u32_t)365) + 17) #define _TBIAS_SECS (_TBIAS_DAYS * (xtime_t)86400) #define _TBIAS_YEAR 1900 #define MONTAB(year) ((((year) & 03) || ((year) == 0)) ? mos : lmos) const s16_t lmos[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; const s16_t mos[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; #define Daysto32(year, mon) (((year - 1) / 4) + MONTAB(year)[mon]) ///////////////////////////////////////////////////////////////////// xtime_t xtmtot(struct tm *t) { /* convert time structure to scalar time */ s32_t days; xtime_t secs; s32_t mon, year; /* Calculate number of days. */ mon = t->tm_mon - 1; year = t->tm_year - _TBIAS_YEAR; days = Daysto32(year, mon) - 1; days += 365 * year; days += t->tm_mday; days -= _TBIAS_DAYS; /* Calculate number of seconds. */ secs = 3600 * t->tm_hour; secs += 60 * t->tm_min; secs += t->tm_sec; secs += (days * (xtime_t)86400); return (secs); } ///////////////////////////////////////////////////////////////////// void xttotm(struct tm *t, xtime_t secsarg) { u32_t secs; s32_t days; s32_t mon; s32_t year; s32_t i; const s16_t* pm; #ifdef _XT_SIGNED if (secsarg >= 0) { secs = (u32_t)secsarg; days = _TBIAS_DAYS; } else { secs = (u32_t)secsarg + _TBIAS_SECS; days = 0; } #else secs = secsarg; days = _TBIAS_DAYS; #endif /* days, hour, min, sec */ days += secs / 86400; secs = secs % 86400; t->tm_hour = secs / 3600; secs %= 3600; t->tm_min = secs / 60; t->tm_sec = secs % 60; /* determine year */ for (year = days / 365; days < (i = Daysto32(year, 0) + 365*year); ) { --year; } days -= i; t->tm_year = year + _TBIAS_YEAR; /* determine month */ pm = MONTAB(year); for (mon = 12; days < pm[--mon]; ); t->tm_mon = mon + 1; t->tm_mday = days - pm[mon] + 1; }
Обратная связь
Интересуют вопросы реализации алгоритмов, программирования, выбора электроники и прочая информация, постараюсь осветить в отдельных статьях
пишите мне на netdm@mail.ru