RTT学习之RTC设备

RTC:可以用单片机的片上rtc,也可以用专用的RTC芯片DS1302,PCF8563等,当然没有的也可以软件模拟。

  • 片上RTC需要使能片上RTC的时钟(LSI或LSE(外接36.768K晶体)
  • 时钟芯片芯片一般都是i2c接口,需要使能。对于时间要求严格,并且没有连接网络无法同步网络时间,则需要选择独立RTC.
  • 在RT-THREAD的组件-驱动里面使能RTC组件驱动。
  • 软件模拟需要使能软件模拟RTC设备驱动。

RTT兼容C库的时间获取函数——“time”,并重写了time函数,通过查找注册的RTC设备“rtc”来设置和获取时间并通过time.h中的接口调用。

驱动PCF8563可以直接用RTT的设备驱动框架调用组件-驱动-rtc.c进行RTC设备的注册,通过设备控制来获取或设置时间。也可以自己按RTT驱动框架下,自己编写PCF8563驱动《RT-THREAD驱动——RTC PCF8563》:RTC是I2C(底层读写寄存器)和RTC(上层应用)的复合设备。通过I2C驱动框架读写对应寄存器进行读写时间,通过RTC给出读写时间的通用接口。

  1 struct pcf8563_device
  2 {
  3     struct rt_device rtc_parent;
  4     struct rt_i2c_bus_device *i2c_device;
  5 };
  6 
  7 rt_uint8_t pcf8563_read_reg(rt_uint8_t reg,rt_uint8_t *data,rt_uint8_t data_size)
  8 {
  9     struct rt_i2c_msg msg[2];
 10 
 11     msg[0].addr  = PCF8563_ARRD;
 12     msg[0].flags = RT_I2C_WR;
 13     msg[0].len   = 1;
 14     msg[0].buf   = ®
 15     msg[1].addr  = PCF8563_ARRD;
 16     msg[1].flags = RT_I2C_RD;
 17     msg[1].len   = data_size;
 18     msg[1].buf   = data;
 19     if(rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) != 2)
 20         return RT_ERROR; 
 21     return RT_EOK;
 22 }
 23 rt_uint8_t pcf8563_write_reg(rt_uint8_t reg, rt_uint8_t *data,rt_uint8_t data_size)
 24 {
 25     struct rt_i2c_msg msg[2];
 26 
 27     msg[0].addr         = PCF8563_ARRD;
 28     msg[0].flags        = RT_I2C_WR;
 29     msg[0].len          = 1;
 30     msg[0].buf          = ®
 31     msg[1].addr         = PCF8563_ARRD;
 32     msg[1].flags        = RT_I2C_WR | RT_I2C_NO_START;
 33     msg[1].len          = data_size;
 34     msg[1].buf          = data;
 35     if(rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) != 2)
 36         return RT_ERROR; 
 37     return RT_EOK;
 38 }
 39 
 40 static unsigned char bcd_to_hex(unsigned char data)   
 41 {//BCD To Byte 
 42     unsigned char temp;
 43 
 44     temp = ((data>>4)*10 + (data&0x0f)); 
 45     return temp;
 46 }
 47 static unsigned char hex_to_bcd(unsigned char data)
 48 {//Byte To BCD 
 49     unsigned char temp;
 50 
 51     temp = (((data/10)<<4) + (data%10)); 
 52     return temp;
 53 }
 54 
 55 static rt_err_t rt_pcf8563_control(rt_device_t dev, rt_uint8_t cmd, void *args)
 56 {
 57     time_t              *time;
 58     struct tm           time_temp;  
 59     rt_uint8_t          buff[7];
 60 
 61     RT_ASSERT(dev != RT_NULL);
 62     rt_memset(&time_temp, 0, sizeof(struct tm));
 63     switch (cmd)
 64     {
 65         case RT_DEVICE_CTRL_RTC_GET_TIME:
 66             time = (time_t *)args;
 67             pcf8563_read_reg(REG_PCF8563_SEC,buff,7);
 68             time_temp.tm_year  = bcd_to_hex(buff[6]&SHIELD_PCF8563_YEAR) + 2000 - 1900;
 69             time_temp.tm_mon   = bcd_to_hex(buff[5]&SHIELD_PCF8563_MON) - 1;
 70             time_temp.tm_mday  = bcd_to_hex(buff[3]&SHIELD_PCF8563_DAY);
 71             time_temp.tm_hour  = bcd_to_hex(buff[2]&SHIELD_PCF8563_HOUR);       
 72             time_temp.tm_min   = bcd_to_hex(buff[1]&SHIELD_PCF8563_MIN);
 73             time_temp.tm_sec   = bcd_to_hex(buff[0]&SHIELD_PCF8563_SEC);
 74             *time = mktime(&time_temp);
 75         break;
 76         case RT_DEVICE_CTRL_RTC_SET_TIME:
 77         {
 78             struct tm *time_new;
 79 
 80             time = (time_t *)args;
 81             time_new = localtime(time);
 82             buff[6] = hex_to_bcd(time_new->tm_year + 1900 - 2000);
 83             buff[5] = hex_to_bcd(time_new->tm_mon + 1);
 84             buff[3] = hex_to_bcd(time_new->tm_mday);
 85             buff[4] = hex_to_bcd(time_new->tm_wday+1);
 86             buff[2] = hex_to_bcd(time_new->tm_hour);
 87             buff[1] = hex_to_bcd(time_new->tm_min);
 88             buff[0] = hex_to_bcd(time_new->tm_sec);
 89             pcf8563_write_reg(REG_PCF8563_SEC,buff,7);
 90         }
 91         break;
 92         default:break;
 93         }
 94     return RT_EOK;
 95 }
 96 
 97 int rt_hw_pcf8563_init(void)
 98 {       
 99     struct rt_i2c_bus_device *i2c_device;
100     uint8_t data;
101 
102     i2c_device = rt_i2c_bus_device_find("i2c1");
103     if (i2c_device == RT_NULL)
104     {
105     #ifdef RT_USE_FINSH_DEBUG
106         rt_kprintf("i2c bus device %s not found!\r\n", "i2c1");
107     #endif
108         return RT_ERROR;
109     }                   
110     pcf8563_dev.i2c_device              = i2c_device;
111     /* register rtc device */
112     pcf8563_dev.rtc_parent.type         = RT_Device_Class_RTC;  
113     pcf8563_dev.rtc_parent.init         = RT_NULL;
114     pcf8563_dev.rtc_parent.open         = rt_pcf8563_open;
115     pcf8563_dev.rtc_parent.close        = RT_NULL;
116     pcf8563_dev.rtc_parent.read         = rt_pcf8563_read;
117     pcf8563_dev.rtc_parent.write        = RT_NULL;
118     pcf8563_dev.rtc_parent.control      = rt_pcf8563_control;
119     pcf8563_dev.rtc_parent.user_data    = RT_NULL;          /* no private */
120     rt_device_register(&pcf8563_dev.rtc_parent, "rtc", RT_DEVICE_FLAG_RDWR);
121 
122     //init pcf8563
123     data = 0x7f;    //close clock out
124     pcf8563_write_reg(REG_PCF8563_CLKOUT,&data,1);
125 
126     return 0;
127 }
128 INIT_DEVICE_EXPORT(rt_hw_pcf8563_init);
View Code

 

 

  •  目前系统内只允许存在一个 RTC 设备,且名称为 "rtc",所以不用查找设备
  • 启用 Soft RTC (软件模拟 RTC),对无硬件RTC

  • 启用 NTP 时间自动同步,需要NTP网络校时功能

  • FISH命名:设置:date 秒;读时间:date

RTC的应用:

#include <time.h>
/*
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 */
    union {       /* ABI-required extra fields, in a variety of types */
        struct {
            int __extra_1, __extra_2;
        };
        struct {
            long __extra_1_long, __extra_2_long;
        };
        struct {
            char *__extra_1_cptr, *__extra_2_cptr;
        };
        struct {
            void *__extra_1_vptr, *__extra_2_vptr;
        };
    };
};

*/


time_t now;
struct tm *timeinfo;

now =time(NULL);
timeinfo =local_time(&now);
View Code

文件应用:生成带日期时间戳的文件如log-date-time.txt;数据库应用:日期-时间 数据;其中重要的UTC与时间的换算,中间还涉及到闰年天数问题,千年虫问题(struct tm结构体中的“tm_year”(是从1900年开始的)和Y2038的bug问题(对于Linux世界来说这个时间的起点是1970年1月1日0时(UTC))。

Y2038的bug问题:解决办法见《单片机的UTC时间时区转换

  View Code

  1 typedef struct
  2 {
  3     uint8_t     tm_sec;      
  4     uint8_t     tm_min;   
  5     uint8_t     tm_hour;  
  6     uint8_t     tm_mday;  
  7     uint8_t     tm_mon;   
  8   uint8_t     tm_wday;      
  9     uint16_t     tm_year;  
 10   //uint16_t     tm_yday;  
 11 }TimeType;
 12 
 13  
 14 //平年累积月分天数表
 15 static const uint16_t NonleapYearMonth[12] = {
 16     31,//1
 17     31 + 28, //2
 18     31 + 28 + 31, //3
 19     31 + 28 + 31 + 30, //4
 20     31 + 28 + 31 + 30 + 31, //5
 21     31 + 28 + 31 + 30 + 31 + 30, //6
 22     31 + 28 + 31 + 30 + 31 + 30 + 31, //7
 23     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, //8
 24     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, //9
 25     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, //10
 26     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, //11
 27     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, //12
 28 };
 29 
 30 //闰年累积月分天数表
 31 static const uint16_t LeapYearMonth[12] = {
 32     31,//1
 33     31 + 29, //2
 34     31 + 29 + 31, //3
 35     31 + 29 + 31 + 30, //4
 36     31 + 29 + 31 + 30 + 31, //5
 37     31 + 29 + 31 + 30 + 31 + 30, //6
 38     31 + 29 + 31 + 30 + 31 + 30 + 31, //7
 39     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31, //8
 40     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30, //9
 41     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, //10
 42     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, //11
 43     31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, //12
 44 };
 45 
 46 uint8_t alg_IsLeapYear(uint32_t year)
 47 {
 48     if((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) //能被4整除,不能被百整除,能被400整除。
 49     {
 50         return 1;                //闰年
 51     }
 52     else
 53     {
 54         return 0;                //平年
 55     }
 56 }
 57 
 58 TimeType alg_Utc2LocalTime(uint32_t UtcVal, int8_t TimeZone)
 59 {
 60     uint32_t i = 0;
 61     TimeType LocalTime;
 62     uint32_t Hour,Days,Year;
 63 
 64     
 65     LocalTime.tm_sec    =    UtcVal%60;                        //得到秒余数
 66     LocalTime.tm_min    =    (UtcVal/60)%60;                //得到整数分钟数
 67     
 68     Hour    =    (UtcVal/60)/60;                                        //得到整数小时数
 69     LocalTime.tm_hour    =    Hour%24+TimeZone;            //得到小时余数+时区
 70     if(LocalTime.tm_hour>23)
 71     {
 72         LocalTime.tm_hour-=24;
 73         Days=Hour/24+1;
 74     }
 75     else
 76     {
 77         Days=Hour/24;
 78     }
 79     LocalTime.tm_wday=(Days+4)%7;                            //计算星期,0-表示星期天        注:1970-1-1 是星期4
 80 
 81                                                     //注:400年=146097天,100年=36524天,4年=1461天
 82     Year = 1970;                                                            //utc时间从1970开始
 83     Year += (Days/146097)*400;
 84 
 85     Days %= 146097;                                                        //计算400年内的剩余天数
 86     Year += (Days/36525)*100;
 87 
 88     Days %= 36525; 
 89     Year += (Days/1461)*4;
 90 
 91     Days %= 1461;                                                            //计算4年内剩余天数,1970平1972闰年
 92     while( Days > 365)
 93     {
 94         if(alg_IsLeapYear(Year))
 95         {
 96             Days--;
 97         }
 98         Days -= 365;
 99         Year++;
100     }
101     if (!alg_IsLeapYear(Year) && (Days == 365) )
102     {
103         Year++;
104         LocalTime.tm_mday    =1;        
105         LocalTime.tm_mon    =1;
106         LocalTime.tm_year    =Year;
107         return LocalTime;
108     }
109     LocalTime.tm_year    =Year;
110     LocalTime.tm_mon=0;
111     LocalTime.tm_mday=0;
112     if (alg_IsLeapYear(Year))                                    //本年是闰年
113     {
114         for (i = 0; i < 12; i++)
115         {
116             if (Days < LeapYearMonth[i])
117             {
118                 LocalTime.tm_mon = i + 1;
119                 if (i == 0)
120                 {
121                     LocalTime.tm_mday = Days;
122                 }
123                 else
124                 {
125                     LocalTime.tm_mday = Days - LeapYearMonth[i - 1];
126                 }
127                 LocalTime.tm_mday++;
128                 return LocalTime;
129             }
130         }
131     }
132     else                                                                            //本年是平年
133     {
134         for (i = 0; i < 12; i++)
135         {
136             if (Days < NonleapYearMonth[i])
137             {
138                 LocalTime.tm_mon  = i + 1;
139                 if (i == 0)
140                 {
141                     LocalTime.tm_mday = Days;
142                 }
143                 else
144                 {
145                     LocalTime.tm_mday = Days - NonleapYearMonth[i - 1];
146                 }
147                 LocalTime.tm_mday++;
148                 return LocalTime;
149             }
150         }
151     }
152     return LocalTime;
153 }
154 
155 
156 uint32_t  alg_LocalTime2Utc(TimeType LocalTime, int8_t TimeZone)
157 {
158     uint32_t y = LocalTime.tm_year -1970;                            //看一下有几个400年,几个100年,几个4年
159     uint32_t dy = (y / 400);
160     uint32_t days = dy *  (400 * 365 + 97);                        //400年的天数
161     
162     dy = (y % 400) / 100;
163     days += dy * (100 * 365 + 25);                                        //100年的天数
164     
165     dy = (y % 100) / 4;
166     days += dy * (4 * 365 + 1);                                                //4年的天数
167     
168     dy = y % 4;                                                                                //注意:这里1972是闰年,与1970只差2年
169     days += dy * 365 ;
170 
171     if(dy == 3)                                                                                //这个4年里,有没有闰年就差1天    
172     {
173         days++;    //只有这个是要手动加天数的,因为1973年计算时前面的天数按365天算,1972少算了一天
174     }
175     
176     if (LocalTime.tm_mon != 1)
177     {
178         if(alg_IsLeapYear(LocalTime.tm_year))                                                //看看今年是闰年还是平年
179         {
180             days += LeapYearMonth[(LocalTime.tm_mon - 1) - 1];
181         } 
182         else 
183         {
184             days += NonleapYearMonth[(LocalTime.tm_mon  - 1) - 1];         //如果给定的月份数为x则,只有x-1个整数月
185         }
186     }
187     days += LocalTime.tm_mday - 1;
188     
189     return (days * 24 * 3600 + ((uint32_t)LocalTime.tm_hour - TimeZone)* 3600 + (uint32_t)LocalTime.tm_min * 60 + (uint32_t)LocalTime.tm_sec);  
190 }
View Code

 

posted on 2019-11-06 11:42  杰瑞鼠  阅读(997)  评论(0编辑  收藏  举报