嵌入式Linux NTP网络时间、RTC时间、系统时间

一、NTP同步系统时间

NTP是网络时间协议(Network Time Protocol)的简称,它是用来同步网络中各个计算机设备的时间的协议。目前有第三方的代码可以支持NTP,ntpclient的下载地址是: ntpclient下载地址

1. 如想上电时执行ntpclient可参考:此链接的方法:自动更新网络时间
备注:添加系统环境变量,让开机后自动使用东八区,在开发板文件系统/etc/profile中添加

export TZ=CST-8

2. NTP移植:
下载ntpclient源代码后,例如我下载的是ntpclient_2015_365.tar.gz,将其中的ntpclient.c,ntpclient.h,phaselock.c这三个文件放入你的项目中,更改ntpclient.c中的main()函数即可。
修改建议如下:
a. ntpclient.h 中新增了一些参数及ntp_operate()函数定义;

//新增的一些参数
#include <stdbool.h>
#define ENABLE_DEBUG //启用debug变量;
extern bool ntpTimeSync;
void ntp_operate(void);

b. ntpclient.c 中用ntp_operate()函数替代main()函数、在set_time()函数中对ntpTimeSync进行设置,set_time()成功则ntpTimeSync=true;

void ntp_operate(void) 
{
	int usd;  /* socket */
	int c;
	/* These parameters are settable from the command line
	   the initializations here provide default behavior */
	short int udp_local_port=0;   /* default of 0 means kernel chooses */
	char *hostname=NULL;          /* must be set */
	int initial_freq;             /* initial freq value to use */
	struct ntp_control ntpc;
	ntpc.live=0;
	ntpc.set_clock=0;
	ntpc.probe_count=0;           /* default of 0 means loop forever */
	ntpc.cycle_time=600;          /* seconds */
	ntpc.goodness=0;
	ntpc.cross_check=1;
    //hostname = "210.72.145.44"; //中国国家授时中心
    //hostname = "ntp.sjtu.edu.cn"; //上海交通大学NTP服务器,202.120.2.101
 
	//自定义参数,参考Configuration:
	//-c probe_count 1
	//-d (debug)     1
	//-g goodness    0
	//-h hostname    202.108.6.95
	//-i interval    5
	//-l live        0
	//-p local_port  0
	//-q min_delay   800.000000
	//-s set_clock   1
	//-x cross_check 1
	ntpc.probe_count = 1;
	debug = 1;
	hostname = "time.nist.gov";
	ntpc.cycle_time = 5;
	(ntpc.set_clock)++;
	
	if (ntpc.set_clock && !ntpc.live && !ntpc.goodness && !ntpc.probe_count) {
		ntpc.probe_count = 1;
	}
 
	/* respect only applicable MUST of RFC-4330 */
	if (ntpc.probe_count != 1 && ntpc.cycle_time < MIN_INTERVAL) {
		ntpc.cycle_time = MIN_INTERVAL;
	}
 
		printf("Configuration:\n"
		"  -c probe_count %d\n"
		"  -d (debug)     %d\n"
		"  -g goodness    %d\n"
		"  -h hostname    %s\n"
		"  -i interval    %d\n"
		"  -l live        %d\n"
		"  -p local_port  %d\n"
		"  -q min_delay   %f\n"
		"  -s set_clock   %d\n"
		"  -x cross_check %d\n",
		ntpc.probe_count, debug, ntpc.goodness,
		hostname, ntpc.cycle_time, ntpc.live, udp_local_port, min_delay,
		ntpc.set_clock, ntpc.cross_check );
 
	/* Startup sequence */
	if ((usd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1)
	{
		printf ("ntp socket error!\n");
	}
 
	setup_receive(usd, INADDR_ANY, udp_local_port);
 
	setup_transmit(usd, hostname, NTP_PORT, &ntpc);
 
	primary_loop(usd, &ntpc);
 
	close(usd);
}

c. 将必要的exit()换成return,从而出错不退出程序,只做返回;

d. NTP代码流程图借鉴下:
在这里插入图片描述

二、RTC时间

1. 启用RTC驱动:
在这里插入图片描述
2. 可通过如下操作进行读写RTC时间

ioctl(fd, RTC_RD_TIME, rtcTm);
ioctl(fd, RTC_SET_TIME, &rtc_tm);

3. 相关变量:

rtc_time 结构体定义如下:
struct rtc_time {
int tm_sec; //秒0-59
int tm_min;//分 0-59
int tm_hour;//时0-23
int tm_mday;//日 1-31
int tm_mon;//月0-11
int tm_year;//从1900年开始算的年
int tm_wday;//0-6一周中的第几天
int tm_yday;//0-365 一年中的第几天
int tm_isdst;//传入的时间是否是 DST(夏令时)
};

三、系统时间

1. Linux内核中表示时间的结构体和数据类型有6种:
struct timeval;
struct timespec;
struct timezone;
struct tm;
time_t;
struct rtc_time:

timeval由秒和微妙组成:
struct timeval {
__kernel_time_t tv_sec; /* seconds /
__kernel_suseconds_t tv_usec; /
microseconds */
};

timespec由秒和纳秒组成;精度要比timeval的要高:
struct timespec {
__kernel_time_t tv_sec; /* seconds /
long tv_nsec; /
nanoseconds */
};

struct timezone {
int tz_minuteswest; /* minutes west of Greenwich /
int tz_dsttime; /
type of dst correction */
};

tm是很直观的表示时间的方法;
struct tm {
/*
* the number of seconds after the minute, normally in the range
* 0 to 59, but can be up to 60 to allow for leap seconds
/
int tm_sec;
/
the number of minutes after the hour, in the range 0 to 59*/
int tm_min;
/* the number of hours past midnight, in the range 0 to 23 /
int tm_hour;
/
the day of the month, in the range 1 to 31 /
int tm_mday;
/
the number of months since January, in the range 0 to 11 /
int tm_mon;
/
the number of years since 1900 /
long tm_year;
/
the number of days since Sunday, in the range 0 to 6 /
int tm_wday;
/
the number of days since January 1, in the range 0 to 365 */
int tm_yday;
};

time_t : 从新纪元至今经过的秒数。新纪元(格林威治时间):1970年1月1日0时
typedef long time_t; //最多能表示新纪元后2147483647秒,大约会在2038年溢出.

rtc_time 结构体定义如下:
struct rtc_time {
int tm_sec; //秒0-59
int tm_min;//分 0-59
int tm_hour;//时0-23
int tm_mday;//日 1-31
int tm_mon;//月0-11
int tm_year;//从1900年开始算的年
int tm_wday;//0-6一周中的第几天
int tm_yday;//0-365 一年中的第几天
int tm_isdst;//传入的时间是否是 DST(夏令时)
};

2. 获取当前时间的函数

#include <time.h>
time_t time(time_t *t); //秒级
//如果参数t是NULL,返回从新纪元以来流逝的秒数。否则将秒数写入到参数t中。

#include <sys/time.h>
int gettimeofday(struct timeval *tv,struct timezone *tz); //微秒级
//成功调用时,当前时间赋给tv指向的结构体,并返回0,timezone已经弃用,传递NULL。
//失败时,返回-1。

#include <time.h>
int clock_gettime(clockid_t clock_id,strutc timespec *ts); //纳秒级
//获取指定时间源的时间,成功调用时,返回0,并将指定时间源的当前时间存储到ts中。
//失败返回-1。

3. 设置当前时间的函数

#include <time.h>
int stime(time_t *t); //秒级
//成功调用,返回0,系统时间被设置为t所指的值。
//失败返回-1.

#include <sys/time.h>
int settimeofday(const struct timeval *tv,const struct timezone *tz); //微秒级
//tz 传递NULL
//成功返回0,失败返回-1

#include <time.h>
int clock_settime(clockid_t clock_id,const struct timespec *ts); //纳秒级
//成功返回0,失败返回-1

4. 时间转换的函数

将秒数转换成字符串形式的本地时间(非UTC国际标准时间)
char *ctime(const time_t *timep); //不可重入函数;
char *ctime_r(const time_t *timep, char *buf); //可重入函数,推荐使用
timep :time_t 时间变量指针。
返回值:成功将返回一个 char *类型指针,指向转换后得到的字符串;失败将返回 NULL。

将struct tm结构体时间转换成固定格式的字符串的本地时间(非UTC国际标准时间)
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
tm :需要进行转换的 struct tm 表示的时间。
buf :可重入版本函数 asctime_r 需要额外提供的参数 buf,指向一个缓冲区,用于存放转换得到的字符串。
返回值:转换失败将返回 NULL;成功将返回一个 char *类型指针,指向转换后得到的时间字符串,对于 asctime_r 函数来说,返回值就等于参数 buf。

将struct tm结构体时间转换成自己喜好的自定义时间的显示格式
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
s :指向一个缓存区的指针,该缓冲区用于存放生成的字符串。
max :字符串的最大字节数。
format: :这是一个用字符串表示的字段,包含了普通字符和特殊格式说明符,可以是这两种字符的任意组合。特殊格式说明符将会被替换为 struct tm 结构体对象所指时间的相应值,这些特殊格式说明符如右截图;
tm :指向 struct tm 结构体对象的指针。
返回值:如果转换得到的目标字符串不超过最大字节数(也就是 max),则返回放置到 s 数组中的字节数;如果超过了最大字节数,则返回 0。

将秒数转换成 struct tm结构体的本地时间
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);//推荐
timep :需要进行转换的 time_t 时间变量对应的指针,可通过 time()或gettimeofday()获取得到。
result :是一个 struct tm 结构体类型指针,稍后给大家介绍 struct tm 结构体,参数 result 是可重入函数localtime_r()需要额外提供的参数。
返回值:对于不可重入版本 localtime()来说,成功则返回一个有效的 struct tm 结构体指针,而对于可重入版本 localtime_r()来说,成功执行情况下,返回值将会等于参数 result;失败则返回 NULL。

将秒数转换成 struct tm结构体的UTC国际标准时间
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
该函数的参数和返回值,同 localtime()和localtime_r()。

将struct tm 结构体时间转换成秒数,同localtime()函数相反
time_t mktime(struct tm *tm);
tm :需要进行转换的 struct tm 结构体变量对应的指针。
返回值:成功返回转换得到 time_t 时间值;失败返回-1。

5. 备注:clockid_t clk_id 表示时钟类型,Linux提供下面这些时钟类型:

CLOCK_REALTIME: 系统范围的实时时钟。系统范围是指系统的所有用户所有程序都使用。实时时钟是指系统现在的时间(就像Windows右下角的那个时间),这和gettimeofday函数获取的系统时间是相同的。系统时间是可以修改的,所以可以使用clock_settime(CLOCK_REALTIME)修改系统的时间
CLOCK_REALTIME_COARSE: 一个更快但时间粒度更大(即时间精确度没那么高)的CLOCK_REALTIME。这个函数是Linux系统特有的,在2.6.32内核版本中首次出现
CLOCK_MONOTONIC:monotonic时间。monotonic是单调的意思,也就是说它只会单调递增,不能被人手动修改。POSIX只是说明它是一个单调时间,并没有指定从什么时候开始算起的单调时间。所以有些系统取了Epoch时间,而有些系统(比如Linux)就取boot启动的时间。但也并不影响它的本身意义
CLOCK_MONOTONIC_COARSE:一个更快但时间粒度更大(即时间精确度没那么高)的CLOCK_MONOTONIC。这个函数是Linux系统特有的,在2.6.32内核版本中首次出现
CLOCK_BOOTTIME: 同CLOCK_MONOTONIC一样,只是当系统被挂起时,一样会计时(CLOCK_MONOTONIC不会)
CLOCK_PROCESS_CPUTIME_ID: 进程使用了多少CPU时间。该进程的所有线程所使用的CPU时间都会被统计进来
CLOCK_THREAD_CPUTIME_ID: 线程使用了多少CPU时间

四、相关命令

1. 修改系统时间:

date -s “2022-07-07 15:22:33”

2. 查看系统时间:

[root@Ingenic-uc1_1:mnt]# date
Thu Jul 7 17:53:17 CST 2022

3. 查看系统时区:

[root@Ingenic-uc1_1:mnt]# date -R
Thu, 07 Jul 2022 17:54:17 +0800

4. 修改时区

使用 timedatectl 命令列出系统中所有可用的时区:
$ timedatectl list-timezones
$ timedatectl list-timezones | grep Shanghai
Asia/Shanghai
$ sudo timedatectl set-timezone Asia/Shanghai

5. 读取rtc实时时钟

hwclock -r -f /dev/rtc0

6. 写入rtc实时时钟

hwclock -w -f /dev/rtc0

Linux时间类型、函数和休眠函数:
https://blog.csdn.net/luotuo44/article/details/39374759

嵌入式Linux下ntp移植:
https://blog.csdn.net/qq_29214249/article/details/71107808

struct timespec 和 struct timeval
https://www.cnblogs.com/book-gary/p/3716790.html

NTP命令:
https://www.h3c.com/cn/d_202104/1400310_30005_0.htm

linux rtc驱动分析和测试:
https://blog.csdn.net/qq_31050687/article/details/114419439
https://blog.csdn.net/xiaoma_2018/article/details/85163485

内核函数时间获得:
https://www.likecs.com/show-204690399.html

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值