有些嵌入式设备中为了节省成本或者体积原因都没有配备掉电保存电池,每次上电之后就需要人为手动的更新系统时间,而且系统时间也不准确。有些需要得到实时的比较准确的时间,以和服务器或是设备之间进行时间同步,但是很多嵌入式设备又不能通过人工设置时间的方式来同步时间,需要自动从网络上获取时间,这就需要用到NTP。NTP是网络时间协议(Network Time Protocol)的简称,它是用来同步网络中各个计算机设备的时间的协议。目前有第三方的代码可以支持NTP,本文讲诉ntpclient的用法。

ntpclient is an NTP (RFC-1305) client for unix-alike computers. Its functionality is a small subset of xntpd, but IMHO performs better (or at least has the potential to function better) within that limited scope. Since it is much smaller than xntpd, it is also more relevant for embedded computers.
ntpclient is Copyright © 1997-2015 Larry Doolittle, and may be freely copied and modified according to the terms of the GNU General Public License, version 2.
ntpclient的下载地址是: ntpclient下载地址

下载好后,解压,进入解压后的目录进行交叉编译

首先需要修改Makefile

# To cross-compile
    CC = arm-linux-gcc
将上面这行的注释去掉,进行交叉编译

然后之需要make,后会得到ntpclient文件,这个就是嵌入式设备上的可执行文件
将他拷贝到开发板中,我使用的nfs文件系统,所以拷贝到nfs目录中

linux@ubuntu:~/arm/ntp/ntpclient-2015$ cp ntpclient /nfsroot/rootfs/root/

然后我们需要一个网络授时服务器网址,

http://www.ntp.org.cn/

这个是授时中心网页,在这个上面可以找到需要的首饰中心网址
然后我们在开发板上运行程序

root@EasyARM-iMX28x ~# ./ntpclient -s -d -c 1 -i 5 -h 202.108.6.95

设备会返回如下

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
Listening...
Sending ...
packet of length 48 received
Source: INET Port 123 host 202.108.6.95
LI=0  VN=3  Mode=4  Stratum=2  Poll=4  Precision=-23
Delay=1144.4  Dispersion=43899.5  Refid=10.69.2.34
Reference 3689931396.917562
(sent)    3689931541.832492
Originate 3689931541.832492
Receive   3689931541.824087
Transmit  3689931541.824131
Our recv  3689931541.838811
Total elapsed:   6750.00
Server stall:      42.14
Slop:            6707.86
Skew:          -11369.57
Frequency:             0
 day   second     elapsed    stall     skew  dispersion  freq
set time to 1480942741.824131000
42707 46741.839    6750.0     42.1  -11369.6  43899.5         0

我为什么没有使用域名呢?因为我使用域名的时候出现无法连接的情况,具体原因我也不知道,然后就是换几个ip尝试,
其中那些参数可以阅读解压后的目录下的README文件,里面有详细的说明,需要提示的是-g不能使用,可能是嵌入式设备不支持。

这时候我们使用date来查看一下系统时间

root@EasyARM-iMX28x ~# date
Mon Dec  5 12:59:05 UTC 2016

其中时区不是中国的东八区,需要使用一个环境变量

export TZ=CST-8

设置后就可以显示中国时间了

root@EasyARM-iMX28x ~# date
Mon Dec  5 21:06:30 CST 2016

然后我根据我的开发板将这个命令写到启动脚本中,当开发板上电后自动通过网络更新系统时间。

我的开发板启动脚本是/etc/rc.d/init.d/start_userapp

修改下面几句话

#date -s “2015-09-01 00:00:00” &> /dev/null
/root/ntpclient -s -d -c 1 -i 5 -h 202.108.6.95 >/dev/null &

然后是添加系统环境变量,让开机后自动使用东八区

开发板文件系统/etc/profile中添加

export TZ=CST-8

然后就可以当开发板上电自动同步时间了,但是必须确认开发板上电时可以访问网络,否则无法从服务器获取系统时间。

但是如果你需要将ntpclient整合到你自己的代码里面中,需要怎么做呢,其实也很简单,下载源代码后,例如我下载的是ntpclient_2015_365.tar.gz,解压tar -zxvf ntpclient_2015_365.tar.gz,解压后将ntpclient.c,ntpclient.h,phaselock.c这三个文件放入你的项目中,更改ntpclient.c中的main()函数即可。

void ntp_test(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
 
 
	(ntpc.set_clock)++;
	ntpc.probe_count = 1;
	hostname = "time.nist.gov";
	
	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);
}

编译运行,在需要的地方调用就可以了。