用TIMER模拟RTC时钟
借用STM32F103里的RTC.c里的两个函数,实现RTC时钟。
优点:可移植性强,计时准确。
缺点:每次上电后需要校时。
适用场合:无外部RTC晶振,可校时。
使用说明:
1:首先确定时间基准:即以什么时刻为计算的起点。我们以2020年1月1日0时0分0秒为起点,相关定义如下:
#define START_YEAR 2000//default 2000, Jan.1, 00:00:00
#define SEC_IN_DAY 86400//1 day includes 86400 seconds
typedef struct
{ /* date and time components */
int16_t sec; //senconds after the minute, 0 to 59
int16_t min; //minutes after the hour, 0 to 59
int16_t hour; //hours since midnight, 0 to 23
int16_t mday; //day of the month, 1 to 31
int16_t month; //months of the year, 1 to 12
int16_t year; //years, START_YEAR to START_YEAR+135
int16_t wday; //days since Sunday, 0 to 6
int16_t yday; //days of the year, 1 to 366
}Calendar_TypeDef;
2:MyMakeTime()把通过校时获得的时间(年月日时分秒)计算为距离START_YEAR的秒数。MyMakeTime函数仅在获取上位机下发的校时时间时调用一次。
把MyMakeTime函数返回的秒数,放在TIMER定时器中断累加,每秒累加一次。用以模拟RTC时钟。
3:MyLocalTime()函数把TIMER定时器累加的秒数转换为当前时间:年月日时分秒。在需要获取时间时,调用该函数即可。
/*******************************************************************************
* Function Name : MyMakeTime
* Description : Form a 32 bit second counting value from calendar.
* Input : pointer to a calendar struct
* Return : 32 bit second counting value
*******************************************************************************/
uint32_t MyMakeTime(Calendar_TypeDef *pCalendar)
{
uint32_t TotalSeconds = pCalendar->sec;
int16_t nYear = pCalendar->year;
int16_t DaysInYear = 365;
int16_t nMonth = pCalendar->month;
int16_t DaysInMonth = 30;
if((nYear < START_YEAR) || (nYear > (START_YEAR + 135)))
return 0;//out of year range
TotalSeconds += (uint32_t)pCalendar->min * 60;//contribution of minutes
TotalSeconds += (uint32_t)pCalendar->hour * 3600;//contribution of hours
//contribution of mdays
TotalSeconds += (uint32_t)(pCalendar->mday - 1) * SEC_IN_DAY;
while(nMonth > 1)//contribution of months
{
nMonth --;
if(nMonth == 1||nMonth == 3||nMonth == 5||nMonth == 7
||nMonth == 8||nMonth == 10||nMonth == 12)
DaysInMonth = 31;
else if(nMonth == 2)
{
if(IsLeapYear(nYear)) DaysInMonth = 29;
else DaysInMonth = 28;
}
else DaysInMonth = 30;
TotalSeconds += (uint32_t)DaysInMonth * SEC_IN_DAY;
}
while(nYear > START_YEAR)//contribution of years
{
nYear --;
if(IsLeapYear(nYear)) DaysInYear = 366;
else DaysInYear = 365;
TotalSeconds += (uint32_t)DaysInYear * SEC_IN_DAY;
}
return TotalSeconds;
}
//end function MyMakeTime
/*******************************************************************************
* Function Name : MyLocalTime
* Description : Form a calendar from 32 bit second counting.
* Input : pointer to a 32 bit second value
* Return : Calendar structure
*******************************************************************************/
Calendar_TypeDef MyLocalTime(const uint32_t *pTotalSeconds)
{
Calendar_TypeDef Calendar;
uint32_t TotalDays, RemainSeconds;
int16_t DaysInYear=365;
int16_t DaysInMonth=30;
Calendar.year = START_YEAR;
Calendar.month = 1; Calendar.mday = 1;
Calendar.yday = 1;
Calendar.wday = Ymd2Wday(START_YEAR, 1, 1);
TotalDays = *pTotalSeconds / SEC_IN_DAY;
RemainSeconds = *pTotalSeconds % SEC_IN_DAY;
Calendar.hour = (int16_t)(RemainSeconds / 3600);//calculate hour
Calendar.min = (int16_t)((RemainSeconds / 60) % 60);//calculate minute
Calendar.sec = (int16_t)(RemainSeconds % 60);//calculate second
Calendar.wday = (int16_t)((TotalDays + Calendar.wday) % 7);//calculate wday
while(1)//calculate year
{
if(IsLeapYear(Calendar.year)) DaysInYear = 366;
else DaysInYear = 365;
if(TotalDays >= DaysInYear)
{
TotalDays -= DaysInYear;
Calendar.year ++;
}//continue while
else
break;//forced to end while
}//finish year calculation
Calendar.yday += TotalDays;//calculate yday
while(1)//calculate month
{
if(Calendar.month == 1||Calendar.month == 3||Calendar.month == 5
||Calendar.month == 7||Calendar.month == 8
||Calendar.month == 10||Calendar.month == 12)
DaysInMonth = 31;
else if(Calendar.month == 2)
{
if(IsLeapYear(Calendar.year)) DaysInMonth = 29;
else DaysInMonth = 28;
}
else DaysInMonth = 30;
if(TotalDays >= DaysInMonth)
{
TotalDays -= DaysInMonth;
Calendar.month ++;
}//continue while
else
break;//forced to end while
}//finish month calculation
Calendar.mday += (int16_t)TotalDays;//calculate mday
return Calendar;
}
//end function MyLocalTime
/*******************************************************************************
* Function Name : Ymd2Wday
* Description : Calculate days in week from year, month, mday.
* Input : year, month, mday
* Return : 0--6, Suanday--Saturday
*******************************************************************************/
static int16_t Ymd2Wday(int16_t nYear, int16_t nMonth, int16_t nMday)
{
uint8_t i;
const uint8_t DaysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
for(i = 0; i < nMonth; i++) nMday += DaysInMonth[i];
if(nMonth > 2)
{
if(IsLeapYear(nYear)) nMday++;
}
nYear--;
return (nYear + nYear/4 - nYear/100 + nYear/400 + nMday)%7;
}
//end function Ymd2Wday
/***********************************************************************
* Function Name : IsLeapYear
* Description : Check whether the past year is leap or not.
* Input : 4 digits year number
* Return : 1: leap year. 0: not leap year
***********************************************************************/
static uint8_t IsLeapYear(int16_t nYear)
{
if(nYear % 4 != 0) return 0;
if(nYear % 100 != 0) return 1;
return (uint8_t)(nYear % 400 == 0);
}
//end function IsLeapYear
总结:
1:用该方法计时时间准确,比内部低速时钟LSI作为RTC时钟源准确吧。
2:可移植性强,只需开一个定时器,用以累加秒数。
3:适用于可校时的应用环境。