OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5064|回复: 14

使用了正点原子RTC.C实时时钟这个文件后会显示2020年13月1日,今天已经24号了,还有几天我的产品就要暴雷了,大家有没有发现

[复制链接]

1

主题

1

帖子

0

精华

新手上路

积分
29
金钱
29
注册时间
2019-6-9
在线时间
7 小时
发表于 2020-12-24 11:40:47 | 显示全部楼层 |阅读模式
1金钱
F103C8T6的板子,编译用到了开发板带的rtc.c这个库,里面的函数RTC_Set(),RTC_Get(),   设置到 2020年12月31号时,读出来似乎2020年13月1号。
不知道其他人有没有遇到。还有几天我的产品就要暴雷了。

最佳答案

查看完整内容[请看2#楼]

我的也是,闰年计算那里,应该把temp1++去掉 temp1=1970; //从1970年开始 while(temp>=365) { if(Is_Leap_Year(temp1))//是闰年 { if(temp>=366) temp-=366;//闰年的秒钟数 else { //temp1++; break; } } else temp-=365; //平年 temp1++; }
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

27

主题

118

帖子

0

精华

高级会员

Rank: 4

积分
847
金钱
847
注册时间
2015-12-8
在线时间
176 小时
发表于 2020-12-24 11:40:48 | 显示全部楼层
我的也是,闰年计算那里,应该把temp1++去掉
temp1=1970; //从1970年开始
                while(temp>=365)
                {                                 
                        if(Is_Leap_Year(temp1))//是闰年
                        {
                                if(temp>=366)
                                        temp-=366;//闰年的秒钟数
                                else
                                {
                                        //temp1++;
                                        break;
                                }  
                        }
                        else
                                temp-=365;   //平年       
                        temp1++;  
                }       
为自己掌握新的知识感到快乐
回复

使用道具 举报

16

主题

78

帖子

0

精华

高级会员

Rank: 4

积分
873
金钱
873
注册时间
2017-7-5
在线时间
130 小时
发表于 2020-12-31 09:45:15 | 显示全部楼层
兄弟,我的暴雷了
回复

使用道具 举报

4

主题

87

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
250
金钱
250
注册时间
2019-5-11
在线时间
39 小时
发表于 2020-12-31 09:57:19 | 显示全部楼层
正在抓紧修复中...
回复

使用道具 举报

0

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
179
金钱
179
注册时间
2019-2-25
在线时间
53 小时
发表于 2020-12-31 10:03:33 | 显示全部楼层
我也发现了。这个坑够深的
回复

使用道具 举报

0

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
179
金钱
179
注册时间
2019-2-25
在线时间
53 小时
发表于 2020-12-31 10:06:49 | 显示全部楼层
我测试了下,明天可以恢复正常,正常跳转到2021年1月1日
回复

使用道具 举报

27

主题

118

帖子

0

精华

高级会员

Rank: 4

积分
847
金钱
847
注册时间
2015-12-8
在线时间
176 小时
发表于 2020-12-31 10:56:59 | 显示全部楼层
我就是亮 发表于 2020-12-31 10:06
我测试了下,明天可以恢复正常,正常跳转到2021年1月1日

是RTC自己计算跳转吗?还是要去对时重新设置?
为自己掌握新的知识感到快乐
回复

使用道具 举报

4

主题

87

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
250
金钱
250
注册时间
2019-5-11
在线时间
39 小时
发表于 2020-12-31 11:07:18 | 显示全部楼层
测试已经修复,但是不知道有没有其他问题。
原理:单片机内容计算的世纪秒,我们写和读都是time_t。取得时候需要转换一下。
首先提供一个年月日转time_t的高斯牛逼算法。需要的可以自己替换,测试原子写入时正常的。不需要替换。
/***************************************************************/
//linux struct localtime to time_t
/***************************************************************/
static inline unsigned long mktime (unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec){
if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}

return (((
(unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
以下是需要替换和修复的地方,我没有在原子的算法上直接修改,而是移植了glic库,但是这个库有个问题月份是0-11,我测试后再最后加上了1.测试了几个年份都是没有问题的
照例先贴。源代码共勉。
#define SECS_PER_HOUR        (60 * 60)
#define SECS_PER_DAY         (SECS_PER_HOUR * 24)
#define DIV(a, b)            ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))

#define __isleap(year) \
    ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
   
/***************************************************************************//**
* \brief       Convert UTC seconds to tm struct.
* \param       t : seconds from 1970-1-1 0:0:0
* \param       tp : tm struct pointer
* \return      0 : overflow error
* \return      1 : success
* \author      glic __offtime
* \note        timezone is ignored
*               struct tm
*               {
*                       int tm_sec;            Seconds. [0-60] (1 leap second)
*                       int tm_min;            Minutes. [0-59]
*                       int tm_hour;           Hours.   [0-23]
*                       int tm_mday;           Day.     [1-31]
*                       int tm_mon;            Month.   [0-11]
*                       int tm_year;           Year - 1900.  
*                       int tm_wday;           Day of week. [0-6]
*                       int tm_yday;           Days in year.[0-365]
*               }
******************************************************************************/
int gmtime64 (const long long *t, struct tm *tp)
{
    const unsigned short int __mon_yday[2][13] =
    {
        /* Normal years.  */
        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
        /* Leap years.  */
        { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
    };
    long int days, rem, y;
    const unsigned short int *ip;

    days = *t / SECS_PER_DAY;
    rem = *t % SECS_PER_DAY;
    while (rem < 0)
    {
        rem += SECS_PER_DAY;
        --days;
    }
    while (rem >= SECS_PER_DAY)
    {
        rem -= SECS_PER_DAY;
        ++days;
    }

    tp->tm_hour = rem / SECS_PER_HOUR;
    rem %= SECS_PER_HOUR;
    tp->tm_min = rem / 60;
    tp->tm_sec = rem % 60;
    /* January 1, 1970 was a Thursday.  */
    tp->tm_wday = (4 + days) % 7;
    if (tp->tm_wday < 0)
    tp->tm_wday += 7;
    y = 1970;
   
    while (days < 0 || days >= (__isleap (y) ? 366 : 365))
    {
        /* Guess a corrected year, assuming 365 days per year.  */
        long int yg = y + days / 365 - (days % 365 < 0);
        /* Adjust DAYS and Y to match the guessed year.  */
        days -= ((yg - y) * 365
               + LEAPS_THRU_END_OF (yg - 1)
               - LEAPS_THRU_END_OF (y - 1));
        y = yg;
    }

    tp->tm_year = y - 1900;
    if (tp->tm_year != y - 1900)
    {
        /* The year cannot be represented due to overflow.  */
        //__set_errno (EOVERFLOW);
        return 0;
    }

    tp->tm_yday = days;
    ip = __mon_yday[__isleap(y)];
    for (y = 11; days < (long int) ip[y]; --y)
        continue;
    days -= ip[y];
    tp->tm_mon = y;
    tp->tm_mday = days + 1;
    return 1;
}
/*************************************************************以下是修改代码*****************************************/
//define add
#define SECS_PER_HOUR        (60 * 60)
#define SECS_PER_DAY         (SECS_PER_HOUR * 24)
#define DIV(a, b)            ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))

#define __isleap(year) \
    ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))

//add void

int gmtime64 (long long t)  //fix to 20201231
{
    const unsigned short int __mon_yday[2][13] =
    {
        /* Normal years.  */
        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
        /* Leap years.  */
        { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
    };
    long int days, rem, y;
    const unsigned short int *ip;

    days = t / SECS_PER_DAY;
    rem = t % SECS_PER_DAY;
    while (rem < 0)
    {
        rem += SECS_PER_DAY;
        --days;
    }
    while (rem >= SECS_PER_DAY)
    {
        rem -= SECS_PER_DAY;
        ++days;
    }

    calendar.hour = rem / SECS_PER_HOUR;
    rem %= SECS_PER_HOUR;
    calendar.min= rem / 60;
   calendar.sec = rem % 60;
    /* January 1, 1970 was a Thursday.  */
    calendar.week = (4 + days) % 7;
    if (calendar.week < 0)
    calendar.week += 7;
    y = 1970;
   
    while (days < 0 || days >= (__isleap (y) ? 366 : 365))
    {
        /* Guess a corrected year, assuming 365 days per year.  */
        long int yg = y + days / 365 - (days % 365 < 0);
        /* Adjust DAYS and Y to match the guessed year.  */
        days -= ((yg - y) * 365
               + LEAPS_THRU_END_OF (yg - 1)
               - LEAPS_THRU_END_OF (y - 1));
        y = yg;
    }

   calendar.w_year = y;
    if (calendar.w_year != y)
    {
        /* The year cannot be represented due to overflow.  */
        //__set_errno (EOVERFLOW);
        return 0;
    }

   calendar.w_date= days;
    ip = __mon_yday[__isleap(y)];
    for (y = 11; days < (long int) ip[y]; --y)
        continue;
    days -= ip[y];
    calendar.w_month = y+1;   //20201231 add "+1" fix  to month max 11
    calendar.w_date = days + 1;
    return 1;
}

//原子函数修改
//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
//        static u16 daycnt=0;
        long long timecount=0;
//        u32 temp=0;
//        u16 temp1=0;          
   // timecount=RTC_GetCounter();
    gmtime64(RTC_GetCounter());   
//         temp=timecount/86400;   //得到天数(秒钟数对应的)
//        if(daycnt!=temp)//超过一天了
//        {          
//                daycnt=temp;
//                temp1=1970;        //从1970年开始
//                while(temp>=365)
//                {                                 
//                        if(Is_Leap_Year(temp1))//是闰年
//                        {
//                                if(temp>=366)temp-=366;//闰年的秒钟数
//                                else {temp1++;break;}  
//                        }
//                        else temp-=365;          //平年
//                        temp1++;  
//                }   
//                calendar.w_year=temp1;//得到年份
//                temp1=0;
//                while(temp>=28)//超过了一个月
//                {
//                        if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
//                        {
//                                if(temp>=29)temp-=29;//闰年的秒钟数
//                                else break;
//                        }
//                        else
//                        {
//                                if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
//                                else break;
//                        }
//                        temp1++;  
//                }
//                calendar.w_month=temp1+1;        //得到月份
//                calendar.w_date=temp+1;          //得到日期
//        }
//        temp=timecount%86400;                     //得到秒钟数             
//        calendar.hour=temp/3600;             //小时
//        calendar.min=(temp%3600)/60;         //分钟       
//        calendar.sec=(temp%3600)%60;         //秒钟
//        calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
        return 0;
}         
/**************************************************************************************************************
修改仓促,如有错误,欢迎斧正!

回复

使用道具 举报

27

主题

118

帖子

0

精华

高级会员

Rank: 4

积分
847
金钱
847
注册时间
2015-12-8
在线时间
176 小时
发表于 2020-12-31 11:12:07 | 显示全部楼层
Jundodo 发表于 2020-12-31 10:56
是RTC自己计算跳转吗?还是要去对时重新设置?

测试了,RTC自己运行计算不会回复正常,只能再次对时计算,重新设置才能恢复正常
为自己掌握新的知识感到快乐
回复

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
123
金钱
123
注册时间
2013-12-9
在线时间
14 小时
发表于 2020-12-31 13:56:18 | 显示全部楼层
这个就是闰年的时候才出现这个BUG,
  1. void RTC_Get(void)
  2. {
  3.         static uint32_t daycnt = 0;
  4.         uint32_t timecount = 0;
  5.         uint32_t temp = 0;
  6.         uint16_t temp1 = 0;
  7.         timecount = rtc_counter_get();
  8.         temp = timecount / 86400;   //得到天数(秒钟数对应的)
  9.         if (daycnt != temp)   //超过一天了
  10.         {
  11.                 daycnt = temp;
  12.                 temp1 = 2000;        //从2000年开始
  13.                 while (temp >= 365)
  14.                 {
  15.                         if (Is_Leap_Year(temp1))        //是闰年
  16.                         {
  17.                                 if (temp >= 366)
  18.                                         temp -= 366;        //闰年的秒钟数
  19.                                 else
  20.                                 {
  21.                                         [color=Red]//temp1++;[/color]
  22.                                         break;
  23.                                 }
  24.                         }
  25.                         else
  26.                                 temp -= 365;          //平年
  27.                         temp1++;
  28.                 }
  29.                 calendar.w_year = temp1;          //得到年份
  30.                 temp1 = 0;
  31.                 while (temp >= 28)          //超过了一个月
  32.                 {
  33.                         if (Is_Leap_Year(calendar.w_year) && temp1 == 1)          //当年是不是闰年/2月份
  34.                         {
  35.                                 if (temp >= 29)
  36.                                         temp -= 29;          //闰年的秒钟数
  37.                                 else
  38.                                         break;
  39.                         }
  40.                         else
  41.                         {
  42.                                 if (temp >= mon_table[temp1])
  43.                                         temp -= mon_table[temp1];          //平年
  44.                                 else
  45.                                         break;
  46.                         }
  47.                         temp1++;
  48.                 }
  49.                 calendar.w_month = temp1 + 1;        //得到月份
  50.                 calendar.w_date = temp + 1;          //得到日期
  51.         }
  52.         temp = timecount % 86400;                     //得到秒钟数
  53.         calendar.hour = temp / 3600;             //小时
  54.         calendar.min = (temp % 3600) / 60;         //分钟
  55.         calendar.sec = (temp % 3600) % 60;         //秒钟
  56.         calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month,
  57.                         calendar.w_date);         //获取星期

  58. }
复制代码

把红色的注销掉就行了!
这个获取可以自己定义起始年,要是用mktime那个都是1970年开始的。有的编译器u64计算有问题
回复

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
123
金钱
123
注册时间
2013-12-9
在线时间
14 小时
发表于 2020-12-31 13:58:16 | 显示全部楼层
xunyiaiqiao 发表于 2020-12-31 13:56
这个就是闰年的时候才出现这个BUG,
把红色的注销掉就行了!
这个获取可以自己定义起始年,要是用mktime ...

为啥没有标红!!
while (temp >= 365)
                {
                        if (Is_Leap_Year(temp1))        //是闰年
                        {
                                if (temp >= 366)
                                        temp -= 366;        //闰年的秒钟数
                                else
                                {
                                        temp1++;
                                        break;
                                }
                        }
                        else
                                temp -= 365;          //平年
                        temp1++;
                }

把if (temp >= 366)
                                        temp -= 366;        //闰年的秒钟数
                                else
                                {
                                        temp1++;
                                        break;
                                }

temp1++注释掉!
回复

使用道具 举报

1

主题

77

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
388
金钱
388
注册时间
2020-11-7
在线时间
44 小时
发表于 2020-12-31 15:48:10 | 显示全部楼层
回复

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
10
金钱
10
注册时间
2020-8-5
在线时间
2 小时
发表于 2021-1-5 12:08:30 | 显示全部楼层
我也遇到这个问题,好在是在测试阶段,然后换了个其他方案
回复

使用道具 举报

0

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
60
金钱
60
注册时间
2020-8-14
在线时间
14 小时
发表于 2021-1-9 16:00:18 | 显示全部楼层
应该是设置为2020/12/31,读出来是2021/13/1,原因是
闰年是366天,按这个代码,当12/31号时前面有365天,他判断是闰年但是已经过的天数是365天不足366天,
这个时候他到else语句应该直接break就好,结果他给temp1++,就是年份变成了21年,到下边计算月份的时候
,按照21年的计算,21年的366天就是13月1号了,所以会导致2020/12/31变成2021/13/1号这个错误的结果
,删掉那个temp1++就好了

回复

使用道具 举报

10

主题

561

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1817
金钱
1817
注册时间
2014-6-27
在线时间
975 小时
发表于 2021-1-11 17:07:31 | 显示全部楼层
有好方法不会用:
uint32_t RTCtoUnixTime(void)
{
        RTC_TimeTypeDef sTime;
        RTC_DateTypeDef sDate;
        struct tm tTime;
        time_t unix_time;
        uint32_t return_value = 0;
        if(HAL_OK == HAL_RTC_GetTime_Date(&sRtcHandle, &sTime, &sDate))
        {
                tTime.tm_year = sDate.Year - 1900;
                tTime.tm_mon = sDate.Month -1 ;
                tTime.tm_mday = sDate.Date;
                tTime.tm_hour = sTime.Hours;
                tTime.tm_min = sTime.Minutes;
                tTime.tm_sec = sTime.Seconds;
                unix_time = mktime(&tTime) - 28800;
                if(unix_time != -1)
                {
                        return_value = (uint32_t)unix_time;
                }
        }
        //localtime(&unix_time);
        return return_value;
}

uTime_in_seconds=RTCtoUnixTime();
                                                                        uTime_in_seconds+=28800;//时区加8小时
tTime=localtime(&uTime_in_seconds);        /*系统函数,localtime是 把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间*/
                                                                        rtc_set_data(tTime->tm_year+1900, tTime->tm_mon+1, tTime->tm_mday, tTime->tm_wday);
                                                                        rtc_set_time(tTime->tm_hour, tTime->tm_min, tTime->tm_sec);
只要设置一次对的就OK了。
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-14 18:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表