文章目錄
10 RTC應用開發
10.1 RTC的作用及時間表示
10.2 RTC的操作命令
10.2.1 系統時間和硬件時間
10.2.2 系統時間操作命令
10.2.3 硬件時間操作命令
10.3 RTC的數據結構和函數
10.3.1 時間相關的數據結構
1.time_t 類型:長整型
2.struct timeb 結構
3.struct timeval 和struct timezone結構
4.struct tm 結構
10.3.2時間相關的函數
1.時間格式化函數
2.localtime函數
3.mktime函數
4.asctime函數
5.ctime函數
10.4 RTC時間的編程實例
10 RTC應用開發
10.1 RTC的作用及時間表示
? “RTC”的英文全稱是Real-Time Clock,翻譯過來是實時時鐘芯片。實時時鐘芯片是日常生活中應用最為廣泛的電子器件之一,它為人們或者電子系統提供精確的實時時間。實時時鐘芯片通過引腳對外提供時間讀寫接口,通常內部帶有電池,保證在外部系統關電時,內部電路正常工作,時間正常運行。不同的時鐘芯片內部機制不一樣,時間數據存儲格式、讀寫操作方式也不一樣,Linux系統和驅動封裝了不同時鐘芯片的操作細節,為應用程序提供了統一的時間操作接口。
? 那么在Linux世界里,時間是怎么表示的呢?是不是與人們一樣用年月日+時分秒來表示時間呢?聰明的程序員自然不會讓計算機這么做,正所謂越簡單越科學,直接用一個整數表示時間,這個整數代表當前與Epoch Time的時間差(以秒為單位)。Epoch Time 是指一個特定的時間:1970年1月1日0時0分0秒。假設現在距離1970年1月1日0時0分0秒走了N秒,在Linuxx系統里,時間數值就是N。
? 那么問題來了,為什么要從1970年1月1日0時0分0秒開始呢?那年發生了什么,以至于Unix系統以它作“紀元”。原來Unix就是在那個時代產生的,1969年發布的雛形,最早是基于硬件60Hz的時間計數。1971年底出版的《Unix Programmer’s Manual》里定義的Unix Time是以1971年1月1日00:00:00作為起始時間,每秒增長60。后來考慮到如果每秒60個數字,則1.1年后時間值就到達最大,于是改成以秒為計數單位,時間能表示到68.1年之長,就不在乎起始時間是1970還是1971年,遂改成人工記憶、計算比較方便的1970年。于是Unix的世界開啟了“紀元”,Unix時間戳也就成為了一個專有名稱。后Linux系統沿用了這種定義時間的方式。
? 當時計算機操作系統是32位,時間也是用一個32位的有符號數來表示,數據取值范圍為-2147483648~2147483647,也就是說時間最大值只能取到2147483647(秒),換算成年也即2147483647÷365÷24÷60÷60=68.1年,也就是說32位能表示的最長時間是1970+68.1=2038年。精確點講,2038年01月19日03時14分07秒,時間便會達到最大值,為0x7FFFFFFF。過了這個時間點,下一秒時間值便會變為0x80000000,算下來也就是1901年12月13日20時45分52秒,這樣便會出現時間回歸的現象,很多系統便會運行異常。
? 上邊說過了,那是Unix系統“元年”時候的事情,32位的時間已經足以解決當時的問題了。現在主流CPU都是64位的,使用64位的數據表示時間也是順其自然的事,用64位的有符號數來表示時間,可以表示到292,277,026,596年12月4日15時30分08秒,相信我們再也不用愁時間回歸的問題了。
10.2 RTC的操作命令
? 弄清楚了時間怎么表示之后,Linux是怎么使用和維護時間的呢?我們怎么通過Linux操作時間呢?
10.2.1 系統時間和硬件時間
? 在Linux中有系統時鐘與硬件時鐘兩種時鐘。系統時間是由CPU主芯片的定時器進行維護的時間,一般情況下都會選擇芯片上精度最高的定時器作為系統時間的定時基準,以避免在系統運行較長時間后出現大的時間偏移。特點是系統掉電后,系統時間將丟失。硬件時鐘是指系統中包含的RTC芯片內部所維護的時間。RTC芯片都有電池+系統電源的雙重供電機制,在系統正常工作時由系統供電,在系統掉電后由電池進行供電。因此系統電源掉電后RTC時間仍然能夠正常運行,Linux系統中硬件時鐘的基本目的是在Linux不運行時保持時間。
? 在Linux啟動時,將系統時間從硬件時鐘初始化,然后不再使用硬件時鐘。在系統開機時,由Linux操作系統從RTC芯片讀取硬件時間后,由CPU內部定時器維護時間運行。此后操作系統使用的時間都是系統時間,如果沒有顯式的通過命令去控制RTC的讀寫操作,系統將不會再從RTC中去獲取或者同步設置時間。
10.2.2 系統時間操作命令
? 查看系統時間:
date Sat May 1 08:11:19 EDT 2020
? 格式化輸出:
date +"%Y-%m-%d" 2020-05-01
? 2秒后輸出:
date -d "2 second" +"%Y-%m-%d %H:%M.%S" 2020-05-01 14:21.31
? 顯示1234567890秒的時間:
date -d "1970-01-01 1234567890 seconds" +"%Y-%m-%d %H:%m:%S" 2009-02-13 23:02:30
? 普通轉格式:
date -d "2009-05-01" +"%Y/%m/%d %H:%M.%S" 2020/05/01 00:00.00
? 輸出其他日期:
date -d "+1 day" +%Y%m%d #顯示后一天的日期 date -d "-1 day" +%Y%m%d #顯示前一天的日期 date -d "-1 month" +%Y%m%d #顯示上一月的日期 date -d "+1 month" +%Y%m%d #顯示下一月的日期 date -d "-1 year" +%Y%m%d #顯示前一年的日期 date -d "+1 year" +%Y%m%d #顯示下一年的日期
? 設置系統時間:
date -s 20200501 #設置成20200501,這樣會把具體時間設置成空00:00:00 date -s 01:01:01 #設置具體時間,不會對日期做更改 date -s "01:01:01 2020-05-01" #這樣可以設置全部時間 date -s "01:01:01 20200501" #這樣可以設置全部時間 date -s "2020-05-01 01:01:01" #這樣可以設置全部時間 date -s "20200501 01:01:01" #這樣可以設置全部時間
? 命令更多參數使用方法可訪問:https://www.cnblogs.com/machangwei-8/p/10352546.html
10.2.3 硬件時間操作命令
? 顯示硬件時間:
hwclock或 hwclock -r 或 hwclock --show 2000年04月11日 星期二 13時24分35秒 -0.109687 seconds
? 設置硬件時鐘:
hwclock --set --date '2015-04-11 13:36:11'11
? 將系統時鐘同步到硬件時鐘:
hwclock -w
? 將硬件時鐘同步到系統時鐘:
hwclock -s
? 命令更多參數使用方法可訪問:https://www.cnblogs.com/wj78080458/p/9806774.html
10.3 RTC的數據結構和函數
? 在Linux環境中,我們學會了使用命令,修改系統時間和硬件時間。在編程時我們當然可以直接使用system系統調用來操作時間,但是這樣既顯得不夠專業,也不能滿足大部分需求,因為很多情況下我們不只是要修改時間,而是要對時間進行運算處理。
? RTC編程,重點是學習時間相關的結構體和相關操作函數。
10.3.1 時間相關的數據結構
? 在C語言涉及中經常需要定時觸發事件,涉及到獲取系統時間,其結構體類型有多種。Linux系統下,與時間有關的數據類型定義在頭文件 /usr/include/sys/time.h 中:
只要有以下幾種時間相關的數據類型:
1.time_t 類型:長整型
? 一般用來表示從Epoch Time(1970年1月1日午夜(00:00:00))以來的秒數,單位為秒。
#define _TIME_T typedef long time_t; #endif
? 由函數time_t time(time_t* lpt)來獲取time_t 數據,函數返回自Epoch Time(1970年1月1日午夜(00:00:00))起經過的時間,以秒為單位。如果 lpt不為空,則返回值也存儲在lpt指向的變量中。
? 示例:
time_t t = time(NULL);
2.struct timeb 結構
? 它有四個成員,一個是秒,另一個是毫秒。
struct timeb{ time_t time; unsigned short millitm; short timezone; short dstflag; };
? time是從Epoch Time(1970年1月1日午夜(00:00:00))起累計的秒數。
? millitm是一秒內的毫秒數。
? dstflag不為0,說明這是夏令時時間。
? timezone是UTC時間和本地時間的相差分鐘數。
? 由函數int ftime(struct timeb *tp) 來獲取timeb,調用成功返回0,調用失敗返回-1。
示例:
struct timeb tp; ftime(&tp);
3.struct timeval 和struct timezone結構
? timeval 有兩個成員,一個是秒,另一個表示微秒。
struct timeval{ long tv_sec; /*秒*/ long tv_usec;/*微秒*/ };
? tv_sec為Epoch Time到創建struct timeval時的秒數,tv_usec為微秒數,即秒后面的零頭。比如當tv_sec為1234567890,tv_usec為1234,即當前時間距Epoch時間1234567890秒,1234微秒。
struct timezone{ int tz_minuteswest;/*和greenwich 時間差了多少分鐘*/ int tz_dsttime; /*type of DST correction*/ }; tz_minuteswest表示當前系統所在時區和UTC的時間差,tz_minuteswest以分鐘計算。比如北京GMT+8區,tz_minuteswest為-480。tz_dsttime的定義為日光節約時間(DST,也就是夏令時。
? 由函數int gettimeofday(struct timeval*tv,struct timezone *tz )來獲取timeval和timezone,在gettimeofday()函數中tv或者tz都可以為空。如果為空則就不返回其對應的結構體。函數執行成功后返回0,失敗后返回-1,錯誤代碼存于errno中。
示例:
struct timeval tv; gettimeofday(&tv, NULL);
4.struct tm 結構
struct tm { int tm_sec; /* 秒–取值區間為[0,59] */ int tm_min; /* 分 - 取值區間為[0,59] */ int tm_hour; /* 時 - 取值區間為[0,23] */ int tm_mday; /* 一個月中的日期 - 取值區間為[1,31] */ 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()為負。*/ }; int tm_sec 代表目前秒數,正常范圍為0-59 int tm_min 代表目前分數,范圍0-59 int tm_hour 從午夜算起的時數,范圍為0-23 int tm_mday 目前月份的日數,范圍01-31 int tm_mon 代表目前月份,從一月算起,范圍從0-11 int tm_year 從1900 年算起至今的年數 int tm_wday 一星期的日數,從星期一算起,范圍為0-6 int tm_yday 從今年1月1日算起至今的天數,范圍為0-365 int tm_isdst 日光節約時間的旗標
? 由函數struct tm* gmtime(const time_t*timep)解析得到tm,gmtime()將參數timep 所指的time_t 數據類型中的信息轉換成真實世界所使用的時間日期表示方法,然后將結果由結構tm的指針返回。
? 示例:
struct tm* tm =NULL ; time_t t = time(NULL); tm = gmtime(&t);
10.3.2時間相關的函數
1.時間格式化函數
? strftime()函數原型:size_t strftime(char *str,size_t max,char *fmt,struct tm *tp);函數識別以百分號(%)開始的格式命令集合,其格式由fmt來指定,可以使用strftime 函數將時間格式轉化為我們想要的格式(其輸出結果為字符串),strftime有點像sprintf。
? str 表示返回的時間字符串
? count要寫入的字節的最大數量
? format 格式字符串由零個或多個轉換符和普通字符(除%)
? tm 輸入時間
? 格式命令,是區分大小寫的:
%a 星期幾的簡寫 %A 星期幾的全稱 %b 月分的簡寫 %B 月份的全稱 %c 標準的日期的時間串 %C 年份的后兩位數字 %d 十進制表示的每月的第幾天 %D 月/天/年 %e 在兩字符域中,十進制表示的每月的第幾天 %F 年-月-日 %g 年份的后兩位數字,使用基于周的年 %G 年分,使用基于周的年 %h 簡寫的月份名 %H 24小時制的小時 %I 12小時制的小時 %j 十進制表示的每年的第幾天 %m 十進制表示的月份 %M 十時制表示的分鐘數 %n 新行符 %p 本地的AM或PM的等價顯示 %r 12小時的時間 %R 顯示小時和分鐘:hh:mm %S 十進制的秒數 %t 水平制表符 %T 顯示時分秒:hh:mm:ss %u 每周的第幾天,星期一為第一天 (值從0到6,星期一為0) %U 第年的第幾周,把星期日做為第一天(值從0到53) %V 每年的第幾周,使用基于周的年 %w 十進制表示的星期幾(值從0到6,星期天為0) %W 每年的第幾周,把星期一做為第一天(值從0到53) %x 標準的日期串 %X 標準的時間串 %y 不帶世紀的十進制年份(值從0到99) %Y 帶世紀部分的十進制年份 %z,%Z 時區名稱,如果不能得到時區名稱則返回空字符。 %% 百分號
? 示例:
time_t t = time(NULL); struct tm *info; info = gmtime(&t); strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info); printf("格式化的日期 & 時間 : |%s|n", buffer );
2.localtime函數
? localtime()函數原型:struct tm *localtime(const time_t *timer) 使用 timer 的值來填充 tm 結構,timer 的值被解析為 tm 結構,并用本地時區表示
? 示例:
time_t rawtime; struct tm *info; time( &rawtime ); info = localtime( &rawtime );
3.mktime函數
? mktime()函數原型: time_t mktime(struct tm *timeptr) 把 timeptr 所指向的結構轉換為一個依據本地時區的 time_t 值。函數將參數timeptr所指的tm結構數據轉換成從公元1970年1月1日0時0分0 秒算起至今的UTC時間所經過的秒數。該函數返回一個 time_t 值,該值對應于以參數傳遞的日歷時間。如果發生錯誤,則返回 -1 值。
? 示例:
time_t rawtime; struct tm * timeinfo; int year, month ,day; /* 獲取當前時間信息,并修改用戶輸入的輸入信息 */ time ( &rawtime ); timeinfo = localtime ( &rawtime ); timeinfo->tm_year-= 1;//去年的今天 rawtime = mktime ( timeinfo );
4.asctime函數
? asctime()函數原型: char *asctime(const struct tm *timeptr) ;函數返回一個指向字符串的指針,它代表了結構 struct timeptr 的日期和時間。包含了可讀格式的日期和時間信息 Www Mmm dd hh:mm:ss yyyy,其中,Www 表示星期幾,Mmm 是以字母表示的月份,dd 表示一月中的第幾天,hh:mm:ss 表示時間,yyyy 表示年份。
? 示例:
struct tm t; t.tm_sec = 10; t.tm_min = 10; t.tm_hour = 6; t.tm_mday = 25; t.tm_mon = 2; t.tm_year = 89; t.tm_wday = 6; puts(asctime(&t));
5.ctime函數
? ctime()函數原型:char *ctime(const time_t *timer); 可以把time函數得到的結果轉換成一個時間字符串, 返回一個表示當地時間的字符串,當地時間是基于參數 timer。返回的字符串格式如下: Www Mmm dd hh:mm:ss yyyy 其中,Www 表示星期幾,Mmm 是以字母表示的月份,dd 表示一月中的第幾天,hh:mm:ss 表示時間,yyyy 表示年份。調用ctime(t)等價于asctime(localtime(t))。
? 示例:
time_t curtime; time(&curtime); printf("當前時間 = %s", ctime(&curtime));
10.4 RTC時間的編程實例
? 嵌入式Linux環境下,RTC時間編程與桌面Linux環境下的編程是一樣的。本文在桌面Linux下編程,實現時間顯示、計算等功能。編譯后的程序名為“mytime”,進入程序后,使用不同命令完成不同功能,主要命令見下表:
序號 | 命令 | 功能 | 示例 |
---|---|---|---|
1 | p | 在終端顯示當前時間 | p |
2 | y | 在終端顯示昨天日期 | y |
3 | n | 在終端顯示現在距新年的天數 | n |
4 | a | 根據輸入的出生年代,在終端顯示年紀 | a 2001 |
5 | e | 退出程序 | e |
? 在main函數中,首先打印函數的使用手冊,然后循環接收用戶輸入的命令,根據命令調用相應的函數:
32 //打印使用手冊 33 printf( "nn" 34 "Usage:npn" 35 "yn" 36 "nn" 37 "a 2001n" 38 "en" 39 "p:在終端顯示當前時間ny:查看昨天日期nn:查看現在距新年的天數n" 40 "a:根據輸入的出生年計算出年紀ne:退出程序n" 41 ); 42 43 //主程序中循環接收輸入的命令,根據不同命令執行不同函數 44 while (1){ 45 if (c !='n') 46 printf("n請輸入命令:"); 47 scanf("%c",&c); 48 switch(c){ 49 case 'p'://在終端顯示當前時間 50 displaydate(); 51 break; 52 case 'y'://顯示昨天日期 53 displayyesterday(); 54 break; 55 case 'n'://顯示現在距新年的天數 56 displaynewyear(); 57 break; 58 case 'a'://根據輸入的出生年計算出年紀 59 scanf("%d",&age); 60 displayage(age); 61 break; 62 case 'e'://退出程序 63 exit(0); 64 break; 65 66 default : /* 可選的 */ 67 break; 68 } 69 }
? 實現在終端顯示當前時間的函數:
/********************************************************** 72 * 函數名稱: displaydate 73 * 功能描述: 在終端打印當前時間信息 74 * 輸入參數: 無 75 * 輸出參數: 無 76 * 返 回 值: 無 77 * 2020/05/10 V1.0 yanxni 創建 78 ***********************************************************/ 79 void displaydate(){ 80 struct tm *ptr; 81 time_t lt; 82 83 /*獲取日歷時間*/ 84 lt = time(NULL); 85 86 /*轉化為本地時間*/ 87 ptr = localtime(<); 88 89 /*以本地時間的字符串方式打印*/ 90 printf("%sn",ctime(<)); 91 92 /*以本地時間的字符串方式打印*/ 93 printf("%sn",asctime(ptr)); 94 95 }
? 實現在終端顯示昨天日期的函數:
/********************************************************** 97 * 函數名稱: displayyesterday 98 * 功能描述: 在終端打印昨天的日期 99 * 輸入參數: 無 100 * 輸出參數: 無 101 * 返 回 值: 無 102 * 2020/05/10 V1.0 yanxni 創建 103 ***********************************************************/ 104 void displayyesterday(void){ 105 struct tm *ptr; 106 time_t lt; 107 108 /*獲取日歷時間*/ 109 lt = time(NULL); 110 lt -= 24*60*60; 111 112 /*轉化為本地時間*/ 113 ptr = localtime(<); 114 115 /*以本地時間的字符串方式打印*/ 116 printf("昨天是%d年%d月%d日n",ptr->tm_year + 1900,ptr->tm_mon + 1,ptr->tm_mday); 117 }
? 實現在終端顯示現在距新年的天數的函數:
118 /********************************************************** 119 * 函數名稱: displaynewyear 120 * 功能描述: 在終端打印距離新年的天數 121 * 輸入參數: 無 122 * 輸出參數: 無 123 * 返 回 值: 無 124 * 2020/05/10 V1.0 yanxni 創建 125 ***********************************************************/ 126 void displaynewyear(void){ 127 struct tm *ptr; 128 time_t lt,lt2; 129 int date; 130 131 /*獲取日歷時間*/ 132 lt = time(NULL); 133 134 /*轉化為本地時間*/ 135 ptr = localtime(<); 136 /*構造新年的本地時間*/ 137 ptr->tm_year += 1; 138 ptr->tm_mon = 0; 139 ptr->tm_mday =1; 140 ptr->tm_hour =0; 141 ptr->tm_min =0; 142 ptr->tm_sec =0; 143 144 lt2 = mktime(ptr); 145 date = (lt2-lt)/(24*60*60); 146 147 printf("距離新年還有%d天n",date); 148 }
? 實現在根據輸入的出生年代在終端顯示年紀的函數:
/********************************************************** 151 * 函數名稱: displayage 152 * 功能描述: 在終端打印年紀 153 * 輸入參數: 出生年代 154 * 輸出參數: 無 155 * 返 回 值: 無 156 * 2020/05/10 V1.0 yanxni 創建 157 ***********************************************************/ 158 void displayage(int year){ 159 struct tm *ptr; 160 time_t lt; 161 162 /*獲取日歷時間*/ 163 lt = time(NULL); 164 165 /*轉化為本地時間*/ 166 ptr = localtime(<); 167 168 printf("你的年齡是:%d歲n",ptr->tm_year +1900 - year ); 審核編輯 黃昊宇
-
Linux
+關注
關注
87文章
11345瀏覽量
210406 -
RTC
+關注
關注
2文章
544瀏覽量
67032
發布評論請先 登錄
相關推薦
評論