搜索
bottom↓
回复: 62

九九的STM32笔记(六)发布基于ANSI-C的RTC_Time库,利用UNIX时间戳格式,无中断实现万年历

[复制链接]

出0入0汤圆

发表于 2009-2-18 19:36:47 | 显示全部楼层 |阅读模式
基于STM32处理器
RTC只是个能靠电池维持运行的32位定时器over!
并不像实时时钟芯片,读出来就是年月日。。。
看过些网上的代码,有利用秒中断,在内存中维持一个年月日的日历。
我觉得,这种方法有很多缺点:
1.断电时没有中断可用
2.频繁进中断,消耗资源
3.时间运算复杂,代码需要自己写
4.不与国际接轨。。。。

so,还是用标准的UNIX时间戳来进行时间的操作吧!
什么是UNIX时间戳?
UNIX时间戳,是unix下的计时方式。。。很废话
具体点:他是一个32位的整形数(刚好和STM32的RTC寄存器一样大),表示从UNIX元年(格林尼治时间1970-1-1 0:0:0)开始到某时刻所经历的秒数
听起来很玄幻的,计算下: 32位的数从0-0xFFFFFFFF秒,大概到2038年unix时间戳将会溢出!这就是Y2038bug
不过,事实上的标准,我们还是照这个用吧,还有二十年呢。。。

UNIX时间戳:1229544206 <==> 现实时间:2008-12-17  20:03:26

我们要做的,就是把当前时间的UNIX时间戳放在RTC计数器中让他每秒++,over
然后,设计一套接口函数,实现UNIX时间戳与年月日的日历时间格式转换 这样就可以了

在RTC中实现这个时间算法,有如下好处:
1. 系统无需用中断和程序来维持时钟,断电后只要RTC在走即可
2. 具体的两种计时的换算、星期数计算,有ANSI-C的标准C库函数实现,具体可以看time.h
3. 时间与时间的计算,用UNIX时间戳运算,就变成了两个32bit数的加减法
4. 与国际接轨。。。


幸好是与国际接轨,我们有time.h帮忙,在MDK的ARM编辑器下有,IAR下也有
其中已经定义了两种数据类型:unix时间戳和日历型时间
time_t:       UNIX时间戳(从1970-1-1起到某时间经过的秒数)
     typedef unsigned int time_t;

struct tm:    Calendar格式(年月日形式)

同时有相关操作函数
gmtime,localtime,ctime,mktime等等,方便的实现各种时间类型的转换和计算

于是,基于这个time.h,折腾了一天,搞出了这个STM32下的RTC_Time使用的时间库


这是我的RTC_Time.c中的说明:

本文件实现基于RTC的日期功能,提供年月日的读写。(基于ANSI-C的time.h)

作者:jjldc (九九)
QQ: 77058617

RTC中保存的时间格式,是UNIX时间戳格式的。即一个32bit的time_t变量(实为u32)

ANSI-C的标准库中,提供了两种表示时间的数据  型:
time_t:       UNIX时间戳(从1970-1-1起到某时间经过的秒数)
     typedef unsigned int time_t;

struct tm:    Calendar格式(年月日形式)
   tm结构如下:
   struct tm {
       int tm_sec;   // 秒 seconds after the minute, 0 to 60
                        (0 - 60 allows for the occasional leap second)
       int tm_min;   // 分 minutes after the hour, 0 to 59
        int tm_hour;  // 时 hours since midnight, 0 to 23
        int tm_mday;  // 日 day of the month, 1 to 31
        int tm_mon;   // 月 months since January, 0 to 11
        int tm_year;  // 年 years since 1900
        int tm_wday;  // 星期 days since Sunday, 0 to 6
        int tm_yday;  // 从元旦起的天数 days since January 1, 0 to 365
         int tm_isdst; // 夏令时??Daylight Savings Time flag
         ...
     }
     其中wday,yday可以自动产生,软件直接读取
     mon的取值为0-11
    ***注意***:
    tm_year:在time.h库中定义为1900年起的年份,即2008年应表示为2008-1900=108
     这种表示方法对用户来说不是十分友好,与现实有较大差异。
     所以在本文件中,屏蔽了这种差异。
     即外部调用本文件的函数时,tm结构体类型的日期,tm_year即为2008
     注意:若要调用系统库time.c中的函数,需要自行将tm_year-=1900

成员函数说明:
struct tm Time_ConvUnixToCalendar(time_t t);
     输入一个Unix时间戳(time_t),返回Calendar格式日期
time_t Time_ConvCalendarToUnix(struct tm t);
     输入一个Calendar格式日期,返回Unix时间戳(time_t)
time_t Time_GetUnixTime(void);
     从RTC取当前时间的Unix时间戳值
struct tm Time_GetCalendarTime(void);
     从RTC取当前时间的日历时间
void Time_SetUnixTime(time_t);
     输入UNIX时间戳格式时间,设置为当前RTC时间
void Time_SetCalendarTime(struct tm t);
     输入Calendar格式时间,设置为当前RTC时间

外部调用实例:
定义一个Calendar格式的日期变量:
struct tm now;
now.tm_year = 2008;
now.tm_mon = 11;        //12月
now.tm_mday = 20;
now.tm_hour = 20;
now.tm_min = 12;
now.tm_sec = 30;

获取当前日期时间:
tm_now = Time_GetCalendarTime();
然后可以直接读tm_now.tm_wday获取星期数

设置时间:
Step1. tm_now.xxx = xxxxxxxxx;
Step2. Time_SetCalendarTime(tm_now);

计算两个时间的差
struct tm t1,t2;
t1_t = Time_ConvCalendarToUnix(t1);
t2_t = Time_ConvCalendarToUnix(t2);
dt = t1_t - t2_t;
dt就是两个时间差的秒数
dt_tm = mktime(dt);    //注意dt的年份匹配,ansi库中函数为相对年份,注意超限
另可以参考相关资料,调用ansi-c库的格式化输出等功能,ctime,strftime等


这是包含了RTC_Time的工程实例,可以用来参考
基于MDK环境
点击此处下载 ourdev_419649.rar(文件大小:179K) (原文件名:STM32-6.rar)


我的博客中本文的地址,内有语法高亮的代码,方便阅读

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2009-2-25 22:32:24 | 显示全部楼层
不错

出0入70汤圆

发表于 2009-2-26 22:58:39 | 显示全部楼层
好!!!

出0入0汤圆

发表于 2009-2-26 23:11:51 | 显示全部楼层
国际接轨,让人想起了很多很多

出0入0汤圆

发表于 2009-2-27 08:33:02 | 显示全部楼层
怎么用你的函数没法设置 RTC 时间呢?

出0入0汤圆

发表于 2009-4-2 17:22:54 | 显示全部楼层
&#8226;        首先请原谅在我的一个自玩的程序中直接拷贝了你的两个文件,RTC_Time.c RTC_Time.h
&#8226;        在使用过程中有一些疑问,还请能解答下
&#8226;        我的环境也是MDK
&#8226;        头文件如此包含:
&#8226;        #include "stm32f10x_lib.h"
&#8226;        #include "RTC_Time.h"
&#8226;        #include “stdio.h”
&#8226;        全局变量:struct tm time_now;
&#8226;        代码中如此引用:Year = GetRealTime_Year();
&#8226;        能获取年份,但不知道这个是不是对的?
&#8226;       
&#8226;        执行过这段代码后有些疑问:
&#8226;        if(GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_8)==0)
&#8226;        {
&#8226;         time_now.tm_year = 2009;
&#8226;         time_now.tm_mon = 4;
&#8226;         time_now.tm_mday = 5;
&#8226;         time_now.tm_hour = 17;
&#8226;         time_now.tm_min = 00;
&#8226;         time_now.tm_sec = 00;  
&#8226;         Time_SetCalendarTime(time_now);
&#8226;        }
&#8226;        在WATCH看结构如下:
&#8226;        tm_sec = 36,
&#8226;        min = 0,
&#8226;        hour = 17.
&#8226;        mday = 1,
&#8226;        mon = 0,
&#8226;        year = 2009,
&#8226;        wday = 4.
&#8226;        yday = 0,
&#8226;        isdst = 0
&#8226;        跟赋值不同,很是奇怪
&#8226;       
&#8226;        另:Time_SetCalendarTime(time_now);执行这句后,程序会跑飞,不知道会在哪 还请指点一二

出0入0汤圆

发表于 2009-4-3 11:25:00 | 显示全部楼层
感觉现在的坛子好冷清啊

出0入0汤圆

发表于 2009-4-11 23:27:31 | 显示全部楼层
这个程序我用了。谢谢。
不过RTC的初始化似乎还有些错误。

出0入0汤圆

发表于 2009-5-11 13:28:44 | 显示全部楼层
这个还是用的中断啊!

出0入0汤圆

发表于 2009-9-17 18:58:26 | 显示全部楼层
强悍啊

出0入0汤圆

发表于 2009-9-18 17:05:13 | 显示全部楼层
问下99,RTC死机了,是怎么回事??  
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3589802&bbs_id=9999

出0入0汤圆

发表于 2009-9-20 09:01:07 | 显示全部楼层
标记

出0入0汤圆

发表于 2009-9-20 11:07:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-3 15:28:32 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-4 14:23:38 | 显示全部楼层
这个得顶

出0入0汤圆

发表于 2010-1-8 18:01:26 | 显示全部楼层
不错

出0入0汤圆

发表于 2010-1-11 13:54:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-24 05:04:40 | 显示全部楼层
哈哈,漂亮!

我看了st的官方代码,总觉的这个rtc差点什么。

你的思路相当好,利用c的标准库。呵呵,与国际接轨。


顶!

出0入0汤圆

发表于 2010-1-31 17:53:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-28 19:48:22 | 显示全部楼层
经测试mktime()和localtime()两个函数转换有时出错,不知道为什么。。。。。。。。

出0入0汤圆

发表于 2010-12-1 17:49:59 | 显示全部楼层
我在使用时,出现了2010-11-31  实际应为 2010-12-01  ,时间正确 KEIL 4.12 和IAR 5.50 同样的现象。

出0入0汤圆

发表于 2010-12-29 15:46:21 | 显示全部楼层
调节时间死机

出0入0汤圆

发表于 2010-12-29 17:53:50 | 显示全部楼层
回复【4楼】zf8848
-----------------------------------------------------------------------

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //如果程序要修改RTC时钟,必须打开PWR模块和BKP模块的时钟,并解除BKP模块的写保护!!
        /* Enable PWR and BKP clocks */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

        /* Allow access to BKP Domain */
        PWR_BackupAccessCmd(ENABLE);
       //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

在RTC那里加上这些。。。。打开PWR模块和BKP模块的时钟,并解除BKP模块的写保护

出0入0汤圆

发表于 2011-1-8 17:22:58 | 显示全部楼层
很谢谢 99 的范例,通过调试基本能够用上,嘿嘿 。有一个问题就是写入的时间与读出的时间总是会差1,比如2010-1-25 15:21:20写入,读出总是会出现2010-1-25 14:21:20,就是小时总是会自动减1,我就通过把写入的时间加1来解决这个问题,经过测试没有问题,希望碰到像是情况的朋友说下,有没有找出问题的原因在哪里。。。

出0入0汤圆

发表于 2011-4-21 21:57:36 | 显示全部楼层
/*******************************************************************************
* Function Name  : Time_ConvUnixToCalendar(time_t t)
* Description    : 转换UNIX时间戳为日历时间
* Input                  : u32 t  当前时间的UNIX时间戳
* Output                 : None
* Return                 : struct tm
*******************************************************************************/
struct tm Time_ConvUnixToCalendar(time_t t)
{
        struct tm *t_tm;
        t_tm = localtime(&t);
        t_tm->tm_year += 1900;        //localtime转换结果的tm_year是相对值,需要转成绝对值
        return *t_tm;
}

这个t_tm指向的结构体最终存储在哪,又没有实现malloc内存管理,
localtime返回的只是个指针,指针指向的数据在哪块地址

出0入0汤圆

发表于 2011-6-10 15:28:26 | 显示全部楼层
  在标准C/C++中,我们可通过tm结构来获得日期和时间,tm结构在time.h中的定义如下:
  #ifndef _TM_DEFINED
  struct tm {
  int tm_sec; /* 秒–取值区间为[0,59] */
  int tm_min; /* 分 - 取值区间为[0,59] */
  int tm_hour; /* 时 - 取值区间为[0,23] */
  int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */

    //注意月份这里是0-11 而不是 1-12  ,所以会出现差1现象,最后显示的时候加上就可以了。
  int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */



  int tm_year; /* 年份,其值等于实际年份减去1900 */
  int tm_wday; /* 星期–取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
  int tm_yday; /* 从每年的1月1日开始的天数–取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
  int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
  };
  #define _TM_DEFINED
  #endif
  ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。
编辑本段
程序举例

  #include <stdio.h>
  #include <time.h>
  int main(void)
  {
  struct tm *ptr;
  time_t lt;
  lt =time(NULL);
  ptr=localtime(&lt);
  printf("second:%d\n",ptr->tm_sec);
  printf("minute:%d\n",ptr->tm_min);
  printf("hour:%d\n",ptr->tm_hour);
  printf("mday:%d\n",ptr->tm_mday);
  printf("month:%d\n",ptr->tm_mon+1);
  printf("year:%d\n",ptr->tm_year+1900);
  return 0;
  }

出0入0汤圆

发表于 2011-6-10 16:23:11 | 显示全部楼层
localtime

出0入0汤圆

发表于 2011-6-10 18:46:41 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-8 15:19:57 | 显示全部楼层
MARK   正好在做RTC项目

出0入0汤圆

发表于 2011-7-10 09:06:12 | 显示全部楼层
项目需要中

出0入0汤圆

发表于 2011-7-12 15:03:35 | 显示全部楼层
mark   改改自己用

出0入0汤圆

发表于 2011-7-26 15:30:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-26 15:35:48 | 显示全部楼层
挖坟真可怕……

出0入0汤圆

发表于 2011-7-27 15:49:43 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-8-20 19:50:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-20 22:11:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-21 02:52:00 | 显示全部楼层
VERY GOOD!

出0入0汤圆

发表于 2011-8-21 10:05:31 | 显示全部楼层
支持一下。

出0入0汤圆

发表于 2012-4-12 21:48:19 | 显示全部楼层
谢谢楼主奉献

出0入8汤圆

发表于 2012-5-31 23:40:22 | 显示全部楼层
好文章,思路很好,可惜论坛下不了附件,附搜到的99原版
http://space.ednchina.com/Upload ... 8e-66c7064d715d.rar

出0入0汤圆

发表于 2012-6-1 00:11:13 来自手机 | 显示全部楼层
不错         。

出0入0汤圆

发表于 2012-6-14 18:33:42 | 显示全部楼层
好用,用在107上跑了

出0入0汤圆

发表于 2012-6-14 19:18:43 | 显示全部楼层
本帖最后由 chengying 于 2012-6-14 19:20 编辑

应该是到2110才溢出把

出0入0汤圆

发表于 2012-7-28 07:24:26 | 显示全部楼层
为什么 我用楼主的函数 秒中断却是 30多秒 无从下手

出0入0汤圆

发表于 2012-8-3 21:02:49 | 显示全部楼层
好 ,借鉴一下,哈哈

出0入0汤圆

发表于 2012-12-24 19:29:20 | 显示全部楼层
IAR 下需要如何设置,才能使用到time.h??

出0入0汤圆

发表于 2012-12-24 20:16:11 | 显示全部楼层
楼主,你的博客还更新吗?觉得写得非常好~~~

出0入0汤圆

发表于 2015-1-23 09:07:51 | 显示全部楼层
下载了看看,非常感谢!

出0入0汤圆

发表于 2015-1-23 09:19:16 | 显示全部楼层
这个必须顶啊

出0入42汤圆

发表于 2015-1-23 09:58:46 | 显示全部楼层
先收藏了

出0入0汤圆

发表于 2015-1-23 12:48:47 | 显示全部楼层
收藏学习。谢谢楼主!

出0入8汤圆

发表于 2015-1-23 15:36:44 | 显示全部楼层
一年的秒数365*24*3600=31536000;0xFFFFFFFF(4294967295)对应可用的年数=4294967295/31536000 = 136.2;从1970年开始的话,应该可以用到2106年吧。Y2038bug?

出0入0汤圆

发表于 2015-1-24 10:33:45 | 显示全部楼层
好,学习了……

出0入0汤圆

发表于 2015-3-3 11:47:09 | 显示全部楼层
谢谢楼主

出0入0汤圆

发表于 2015-3-3 13:35:04 | 显示全部楼层
好东西,收藏了学习学习

出0入0汤圆

发表于 2015-3-3 16:16:05 | 显示全部楼层
好好看一下。。。

出0入0汤圆

发表于 2015-3-3 23:56:02 | 显示全部楼层
mark,stm32 unix 时间

出0入0汤圆

发表于 2015-3-4 09:52:29 | 显示全部楼层
本帖最后由 1125526801 于 2015-3-4 11:53 编辑

STM32 RTC 库函数 RTC_SetCounter 设置时间后一直死机等待完成,后来百度一下解决了。不知道大家有没有遇到这问题。
参考资料http://blog.csdn.net/dengchonglin/article/details/6223859
修改后的函数:
  1. /*******************************************************************************
  2. * Function Name  : Time_SetUnixTime()
  3. * Description    : ½«¸ø¶¨µÄUnixʱ¼ä´ÁдÈëRTC
  4. * Input                  : time_t t
  5. * Output                 : None
  6. * Return                 : None
  7. *******************************************************************************/
  8. void Time_SetUnixTime(time_t t)
  9. {
  10.        
  11.         // Check if the Power On Reset flag is set
  12.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);

  13.         /* Allow access to BKP Domain */
  14.         PWR_BackupAccessCmd(ENABLE);
  15.         /* Wait until last write operation on RTC registers has finished */
  16.         RTC_WaitForLastTask();
  17.         RTC_SetCounter((u32)t);
  18.         RTC_WaitForLastTask();
  19.         return;
  20. }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2015-3-4 10:29:20 | 显示全部楼层
想法好                                       

出0入0汤圆

发表于 2015-3-4 10:35:29 | 显示全部楼层
这种方法太好了,不需要外部供电和唤醒

出0入0汤圆

发表于 2016-5-8 19:05:59 | 显示全部楼层
太感谢楼主了。。。。。和60楼的兄弟。。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 18:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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