Appearance
NTP获取时间
NTP报文格式如上图所示,它的字段含义参考如下:
LI 闰秒标识器,占用2个bit
VN 版本号,占用3个bits,表示NTP的版本号,现在为3
Mode 模式,占用3个bits,表示模式
stratum(层),占用8个bits
Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
Precision 精度,占用8个bits,,表示本地时钟精度
Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源
参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
原始时间戳,客户端发送的时间,64bits。
接受时间戳,服务端接受到的时间,64bits。
传送时间戳,服务端送出应答的时间,64bits。
实现
- 以 UDP 协议连接阿里云 NTP 服务器。
- 发送 NTP 报文到阿里云 NTP 服务器。
- 获取阿里云 NTP 服务器返回的数据,取第 40 位到 43 位的十六进制数值。
- 把 40 位到 43 位的十六进制数值转成十进制。
- 把十进制数值减去 1900-1970 的时间差(2208988800 秒)。
- 数值转成年月日时分秒。
c
/**
*@brief 计算日期时间
*@param seconds UUTC 世界标准时间
*@retval 无
*/
void lwip_calc_date_time(unsigned long long time)
{
unsigned int Pass4year;
int hours_per_year;
if (time <= 0)
{
time = 0;
}
g_nowdate.second = (int)(time % 60); /* 取秒时间 */
time /= 60;
g_nowdate.minute = (int)(time % 60); /* 取分钟时间 */
time /= 60;
g_nowdate.hour = (int)(time % 24); /* 小时数 */
Pass4year = ((unsigned int)time / (1461L * 24L));/*取过去多少个四年,每四年有 1461*24 小时 */
g_nowdate.year = (Pass4year << 2) + 1970; /* 计算年份 */
time %= 1461 * 24; /* 四年中剩下的小时数 */
for (;;) /* 校正闰年影响的年份,计算一年中剩下的小时数 */
{
hours_per_year = 365 * 24; /* 一年的小时数 */
if ((g_nowdate.year & 3) == 0) /* 判断闰年 */
{
hours_per_year += 24; /* 是闰年,一年则多24小时,即一天 */
}
if (time < hours_per_year)
{
break;
}
g_nowdate.year++;
time -= hours_per_year;
}
time /= 24; /* 一年中剩下的天数 */
time++; /* 假定为闰年 */
if ((g_nowdate.year & 3) == 0) /* 校正闰年的误差,计算月份,日期 */
{
if (time > 60)
{
time--;
}
else
{
if (time == 60)
{
g_nowdate.month = 1;
g_nowdate.day = 29;
return ;
}
}
}
for (g_nowdate.month = 0; g_days[g_nowdate.month] < time; g_nowdate.month++) /* 计算月日 */
{
time -= g_days[g_nowdate.month];
}
g_nowdate.day = (int)(time);
return;
}
c
typedef struct _NPTformat
{
char version; /* 版本号 */
char leap; /* 时钟同步 */
char mode; /* 模式 */
char stratum; /* 系统时钟的层数 */
char poll; /* 更新间隔 */
signed char precision; /* 精密度 */
unsigned int rootdelay; /* 本地到主参考时钟源的往返时间 */
unsigned int rootdisp; /* 统时钟相对于主参考时钟的最大误差 */
char refid; /* 参考识别码 */
unsigned long long reftime;/* 参考时间 */
unsigned long long org; /* 开始的时间戳 */
unsigned long long rec; /* 收到的时间戳 */
unsigned long long xmt; /* 传输时间戳 */
} NPTformat;
c
/**
*@brief 初始化请求报文
*@param 无
*@retval 无
*/
void lwip_ntp_client_init(void)
{
uint8_t flag;
g_ntpformat.leap = 0; /* leap indicator */
g_ntpformat.version = 3; /* version number */
g_ntpformat.mode = 3; /* mode */
g_ntpformat.stratum = 0; /* stratum */
g_ntpformat.poll = 0; /* poll interval */
g_ntpformat.precision = 0; /* precision */
g_ntpformat.rootdelay = 0; /* root delay */
g_ntpformat.rootdisp = 0; /* root dispersion */
g_ntpformat.refid = 0; /* reference ID */
g_ntpformat.reftime = 0; /* reference time */
g_ntpformat.org = 0; /* origin timestamp */
g_ntpformat.rec = 0; /* receive timestamp */
g_ntpformat.xmt = 0; /* transmit timestamp */
flag = (g_ntpformat.version << 3) + g_ntpformat.mode; /* one byte Flag */
memcpy(g_ntp_message, (void const *)(&flag), 1);//第一位是0x1b
}