用TIMER模拟RTC时钟

用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:适用于可校时的应用环境。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值