4688|5

327

帖子

1

TA的资源

纯净的硅(初级)

楼主
 

STM32实时时钟RTC日历算法 [复制链接]

本帖最后由 shipeng 于 2017-9-9 09:29 编辑

最近工作上需要利用STM32的自带RTC编写日历功能。其实对于公历万年历并不是很复杂,但是由于自带RTC断电后只能做32位二进制秒计数不能更新日期信息,要从根本上解决这个问题就需要编写一个算法可以根据计数器RTC_CNT的当前值计算出当前日期。由于32位的RTC_CNT的最大计数值0xFFFFFFFF/秒,即135年左右,因此这个算法只适用于2000年~2099年。由于日历的时间跨度大短时间内很难通过测试发现bug,所以恳请大家帮忙排查一下是否存在bug并跟帖回复
      编写思路是这样的:在100年范围内刚好是4年1闰,也就是每4年1个周期=366+365*3天,当前总天数days=RTC_CNT/(24*3600),年year=days/(366+365*3)*4 + days%(366+365*3)/365,月和日的算法请直接参照下列代码:

const u8 yizhou[]={"六日一二三四五"};

struct RtcReg
{
        u32 days;
        u8 date;
        u8 month;
        u8 year;
};


void TranslateYMD(struct RtcReg *RtcRegs)//在调用此函数前,RtcRegs->days需赋值当前天数值RTC_CNT/(24*3600)
{
        u8 i,j;u16 u16buf=RtcRegs->days;
        RtcRegs->year = u16buf/(366+365*3)*4;
        u16buf %= (366+365*3);//4年为一个周期,当前周期内的天数
        RtcRegs->year += u16buf/365;//加上当前4年周期内的年
        if (u16buf==365)RtcRegs->year--;//4年周期中的第一年为闰年366天,如果天数u16buf等于365则上一句多算了1年//年计算完成,开始月和日计算:
        else if (u16buf>365)u16buf = (u16buf-1)%365;//得到当前年中的天数,如u16buf超过365则经过了1个闰年u16buf减1再对365求余
        i = (RtcRegs->year&3)?0:1;//当前年份为 平年i=0/闰年i=1
        RtcRegs->month = u16buf/(181+i);//1年分为两个181+i天,即以7月1日为界限,分别计算月份
        RtcRegs->month = RtcRegs->month*6;
        if (RtcRegs->month<12)RtcRegs->month++;
        if (RtcRegs->month<7)
        {
                //                 大于59+i即经过了2月份需补3-i天     大于120+i即经过了4月份需补1天
                j = u16buf + ((u16buf>59+i)?(3-i):0)       +         ((u16buf>i+120)?1:0);
                RtcRegs->month += j/31;RtcRegs->date = j%31+1;
        }
        else if (RtcRegs->month==7)
        {
                u16buf -= 181+i;
                //                 大于92即经过了9月需补1天   大于153即经过了11月需补1天
                j = u16buf + ((u16buf>92)?1:0)       +      ((u16buf>153)?1:0);
                RtcRegs->month += j/31;RtcRegs->date = j%31+1;
        }
//12月份减去1-11月的天数(334+i)由于日是从1开始没有0因此需要加1即-(334+i)+1=-(333+i)
        else RtcRegs->date = u16buf - (333+i);
}


void DisplayYMDW(struct RtcReg *RtcRegs)//日期显示子程序
{
        Disp1Char(2,3,yizhou[RtcRegs->days%7*2]);//星期
        LCD_Wdata(yizhou[RtcRegs->days%7*2+1]);//星期,由于一个汉字占用2个字节,因此星期需发送两个字节
        LCD_Wdata('2');
        LCD_Wdata('0');
        LCD_Wdata((RtcRegs->year/10)+'0');
        LCD_Wdata((RtcRegs->year%10)+'0');
        LCD_Wdata('-');
        LCD_Wdata((RtcRegs->month/10)+'0');
        LCD_Wdata((RtcRegs->month%10)+'0');
        LCD_Wdata('-');
        LCD_Wdata((RtcRegs->date/10)+'0');
        LCD_Wdata((RtcRegs->date%10)+'0');
}


此帖出自stm32/stm8论坛

最新回复

软件生成的代码,关于RTC部分有这么两个函数。具体的我也没验证过。  详情 回复 发表于 2017-9-10 23:18
点赞 关注(2)
个人签名模电临时工
 

回复
举报

327

帖子

1

TA的资源

纯净的硅(初级)

来自 2楼
 
顶楼发现一个严重bug,在小月转到大月的第一天会计算错误,例如12月1日会计算成11月31日,以下代码已更正:
void TranslateYMDW(struct RtcReg *RtcRegs)
{
        u8 i,j;u16 u16buf=RtcRegs->days;
        RtcRegs->year = u16buf/(366+365*3)*4;
        u16buf %= (366+365*3);
        RtcRegs->year += u16buf/365;
        if (u16buf==365)RtcRegs->year--;
        else if (u16buf>365)u16buf = (u16buf-1)%365;
        i = (RtcRegs->year&3)!=0?0:1;
        RtcRegs->month = u16buf/(181+i);
        RtcRegs->month = RtcRegs->month*6;
        if (RtcRegs->month<12)RtcRegs->month++;
        if (RtcRegs->month<7)
        {
                j = u16buf + ((u16buf<59+i)?0:(3-i))+((u16buf59+i)?(3-i):0)+((u16buf>i+120)?1:0);
                RtcRegs->month += j/31;RtcRegs->date = j%31+1;
        }
        else if (RtcRegs->month==7)
        {
                u16buf -= 181+i;
                j = u16buf + ((u16buf<92)?0:1)+((u16buf<153)?0:1);// + ((u16buf>92)?1:0)+((u16buf>153)?1:0);
                RtcRegs->month += j/31;RtcRegs->date = j%31+1;
        }
        else RtcRegs->date = u16buf - (333+i);//RtcRegs->date = u16buf - (334+i)+1;
}
此帖出自stm32/stm8论坛
 
个人签名模电临时工
 

回复

4008

帖子

0

TA的资源

版主

板凳
 
/*
** ===================================================================
**     Method      :  RTC1_GetTime (component RTC_LDD)
*/
/*!
**     @brief
**         Returns actual time and date.
**         Note: Fields not supported by HW are calculated in software.
**     @param
**         DeviceDataPtr   - Pointer to device data
**                           structure pointer returned by [Init] method.
**     @param
**         TimePtr         - Pointer to the time structure to
**                           fill with current time.
*/
/* ===================================================================*/
void RTC1_GetTime(LDD_TDeviceData *DeviceDataPtr, LDD_RTC_TTime *TimePtr)
{
  uint32_t x;
  uint32_t Seconds, Days;

  (void)DeviceDataPtr;                 /* Parameter is not used, suppress unused argument warning */
  Seconds = RTC_PDD_ReadTimeSecondsReg(RTC_BASE_PTR); /* Seconds since 2000-01-01 */
  Seconds--;
  Days = Seconds / 86400U;             /* Days */
  Seconds = Seconds % 86400U;          /* Seconds left */
  TimePtr->Hour = Seconds / 3600U;     /* Hours */
  Seconds = Seconds % 3600u;           /* Seconds left */
  TimePtr->Minute = Seconds / 60U;     /* Minutes */
  TimePtr->Second = Seconds % 60U;     /* Seconds */
  TimePtr->DayOfWeek = (Days + 6U) % 7U; /* Day of week */
  TimePtr->Year = (4U * (Days / ((4U * 365U) + 1U))) + 2000U; /* Year */
  Days = Days % ((4U * 365U) + 1U);
  if (Days == ((0U * 365U) + 59U)) { /* 59 */
    TimePtr->Day = 29U;
    TimePtr->Month = 2U;
    return;
  } else if (Days > ((0U * 365U) + 59U)) {
    Days--;
  } else {
  }
  x =  Days / 365U;
  TimePtr->Year += x;
  Days -= x * 365U;
  for (x=1U; x <= 12U; x++) {
    if (Days < ULY[x]) {
      TimePtr->Month = x;
      break;
    } else {
      Days -= ULY[x];
    }
  }
  TimePtr->Day = Days + 1U;
}

/*
** ===================================================================
**     Method      :  RTC1_SetTime (component RTC_LDD)
*/
/*!
**     @brief
**         Sets new time and date.
**         Note: All LDD_RTC_TTime structure members must be correctly
**         filled in.
**     @param
**         DeviceDataPtr   - Pointer to device data
**                           structure pointer returned by [Init] method.
**     @param
**         TimePtr         - Pointer to the time structure with
**                           new time to set.
**     @return
**                         - Error code, possible codes:
**                           - ERR_OK - OK.
**                           - ERR_DISABLED - Component is disabled.
**                           - ERR_SPEED - Component does not work in
**                           the active clock configuration.
**                           - ERR_RANGE - Parameter out of range.
*/
/* ===================================================================*/
LDD_TError RTC1_SetTime(LDD_TDeviceData *DeviceDataPtr, LDD_RTC_TTime *TimePtr)
{
  uint32_t Seconds;

  (void)DeviceDataPtr;                 /* Parameter is not used, suppress unused argument warning */
  if ((TimePtr->Year < 2000U) || (TimePtr->Year > 2099U) || (TimePtr->Month > 12U) || (TimePtr->Month == 0U) || (TimePtr->Day > 31U) || (TimePtr->Day == 0U)) { /* Test correctness of given parameters */
    return ERR_RANGE;                  /* If not correct then error */
  }
  if (TimePtr->Year & 3U) {            /* Is given year non-leap-one? */
    if (ULY[TimePtr->Month] < TimePtr->Day) { /* Does the obtained number of days exceed number of days in the appropriate month & year? */
      return ERR_RANGE;                /* If yes (incorrect date inserted) then error */
    }
  } else {                             /* Is given year leap-one? */
    if (LY[TimePtr->Month] < TimePtr->Day) { /* Does the obtained number of days exceed number of days in the appropriate month & year? */
      return ERR_RANGE;                /* If yes (incorrect date inserted) then error */
    }
  }
  Seconds = ((TimePtr->Year - 2000U) * 365U) + (((TimePtr->Year - 2000U) + 3U) / 4U); /* Compute number of days from 2000 till given year */
  Seconds += MONTH_DAYS[TimePtr->Month]; /* Add number of days till given month */
  Seconds += TimePtr->Day;             /* Add days in given month */
  if ((TimePtr->Year & 3U) || (TimePtr->Month <= 2U)) { /* For non-leap year or month <= 2, decrement day counter */
    Seconds--;
  }
  Seconds = (Seconds * 86400U) + (TimePtr->Hour * 3600U) + (TimePtr->Minute * 60U) + TimePtr->Second;
  Seconds++;
  RTC_PDD_EnableCounter(RTC_BASE_PTR, PDD_DISABLE); /* Disable counter */
  RTC_PDD_WriteTimePrescalerReg(RTC_BASE_PTR, 0x00U); /* Clear prescaler */
  RTC_PDD_WriteTimeSecondsReg(RTC_BASE_PTR, Seconds); /* Set seconds counter */
  RTC_PDD_EnableCounter(RTC_BASE_PTR, PDD_ENABLE); /* Enable counter */
  return ERR_OK;
}
此帖出自stm32/stm8论坛
 
 

回复

4008

帖子

0

TA的资源

版主

4
 
这个是菲斯卡的
此帖出自stm32/stm8论坛

点评

百度菲斯卡没有相关结果,兄弟有没有更详细的介绍?  详情 回复 发表于 2017-9-9 09:35
 
 
 

回复

327

帖子

1

TA的资源

纯净的硅(初级)

5
 
huo_hu 发表于 2017-9-8 17:53
这个是菲斯卡的

百度菲斯卡没有相关结果,兄弟有没有更详细的介绍?
此帖出自stm32/stm8论坛

点评

软件生成的代码,关于RTC部分有这么两个函数。具体的我也没验证过。  详情 回复 发表于 2017-9-10 23:18
 
个人签名模电临时工
 
 

回复

4008

帖子

0

TA的资源

版主

6
 
shipeng 发表于 2017-9-9 09:35
百度菲斯卡没有相关结果,兄弟有没有更详细的介绍?

软件生成的代码,关于RTC部分有这么两个函数。具体的我也没验证过。
此帖出自stm32/stm8论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表