[嵌入式开发模块]Network Time Protocol(NTP)模块
文章目录
- 前言
- 源码
- NTP.h
- NTP.c
- NetworkLib.h
- NetworkLib.c
- NTPTimeHelper.h
- NTPTimeHelper.c
- 示例
- 更新历史
前言
NTP协议是用于在TCP/IP网络上进行对时的一个协议。
要是你是搜到的这个博客,估计根本也就不需要介绍了,这是运用很广的一个协议,我们windows上的时间对时就是用它实现的。
简单来说,整个对时过程就是客户端发送一个包,向NTP服务器索要当前时间,这个请求包中可选的带上自己发送包时的时间戳,然后服务器在答复包中加上收到请求时以及发出答复时自己时间戳, 然后客户端收到答复包时再记录下时间戳;然后,假定网络延迟在来回时相同,通过一点计算就能消除网络传输延时算出真正的当前时间并更新自己的时间戳。当然,这是完整的NTP过程,对于精度要求不高的场合,直接把客户端答复的时间戳赋值给自己就行,又名SNTP(S即Simple)。
这里给出一个自己写的模块,不依赖于网络协议栈,提供底层的NTP包封装和解析,目前只提供SNTP解析,因为实际上对于我目前的需求也够了。
顺带一提。之所以会弄这个模块是因为在基于W5500进行开发时,其提供的NTP库写得我特别难受,耦合特别严重。就一个很简单的需求愣是要独占一个SOCKET,而且接口用起来很不友好,于是干脆就照着自己搞一个吧,所以代码中会有一小部分的相同部分。
源码
NTP.h
/*
******************************************************************************************
*
* NETWORK TIME PROTOCOL MODULE
* NTP模块
*
* File : NTP.h
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2019/04/01
* Version: V1.0
* History:
* NOTE(s): 1. the module is to provide interfaces for network time protocol.
* 2. the current version only provide the interface for resolving SNTP
* 3. the parameter 'buf' in the signature refers to the original network stream,
* and 'msg' refers to the packet parameters which are compatible with the local
* system. In big-endian system, they are the same.
* 4. To use the module:
* a. prepare a NTPMSG variable or a buffer which is big enough to hold the NTPMSG.
* b. use NtpMsg_initRequest to init the buffer as NTP request message.
* c. initialize a socket as udp and call sendto() to send the whole message to
* NTP server with port NTP_PORT. Then wait for the respond from NTP server.
* d. If got respond, resolve the frame with NtpMsg_resolveCurTimeSNTP, then you
* will get the current (NTP) time stamp.
* e. call NtpTime_toDatetime with the time stamp to get a readable datetime.
******************************************************************************************
*/
#ifndef _NTP_H
#define _NTP_H
/*
*******************************************************************************************
* INCLUDES
*******************************************************************************************
*/
#include <stdint.h>
/*
*******************************************************************************************
* CONSTANT 常量
*******************************************************************************************
*/
// 00 no warning
// 01 last minute has 61 seconds
// 10 last minute has 59 seconds
// 11 alarm condition (clock not synchronized)
#define NTP_LI_NOWARNING 0
#define NTP_LI_61s 1
#define NTP_LI_59s 2
#define NTP_LI_ALARM 3
#define NTP_VERSION 4
#define NTP_MODE_0 0 // reserved
#define NTP_MODE_SYMACTIVE 1 // symmetric active
#define NTP_MODE_SYMPASSIVE 2 // symmetric passive
#define NTP_MODE_CLIENT 3 // client
#define NTP_MODE_SERVER 4 // server
#define NTP_MODE_BROADCAST 5 // broadcast
#define NTP_MODE_6 6 // reserved for NTP control message
#define NTP_MODE_7 7 // reserved for private use
#define DAYS_LEAPYEAR 366
#define DAYS_COMMYEAR 365
#define SECS_PERMIN 60UL
#define SECS_PERHOUR (60 * SECS_PERMIN)
#define SECS_PERDAY (24 * SECS_PERHOUR)
#define SECS_LEAPYEAR (DAYS_LEAPYEAR * SECS_PERDAY)
#define SECS_COMMYEAR (DAYS_COMMYEAR * SECS_PERDAY)
#define SECS_1900to1970 0x83aa7e80
#define SECS_1900to2019 0xdfd52c00
// NTP server Domain name of windows
#define NTPSERVER_NAME_WINDOWS "time.windows.com"
// NTP server IP Address of National Time Service Center Chinese Academy Of Scences
#define NTPSERVER_IP_CHINA 210,72,145,44
#define NTP_PORT 123 // NTP server port number
#define EPOCH 1900 // NTP start year
/*
*******************************************************************************************
* TYPE DEFINITION
*******************************************************************************************
*/
typedef uint32_t NtpSeconds;
typedef struct DATETIME_STRUCT{
uint16_t yy;
uint8_t mo;
uint8_t dd;
uint8_t hh;
uint8_t mm;
uint8_t ss;
} Datetime;
typedef struct NTPTIME_STRUCT{
NtpSeconds sec;
uint32_t frac; // fractions
} NtpTime;
typedef struct NTPMSG_STRUCT{
// Leap Indicator (LI): This is a two-bit code warning of an impending leap second to be inserted/deleted
// in the last minute of the current day, with bit 0 and bit 1, respectively. (see NTP_LI_XXXX)
//unsigned int LI :2;
// Version Number (VN): This is a three-bit integer indicating the NTP version number, currently three (4).
//unsigned int VN :3;
// Mode: This is a three-bit integer indicating the mode. (see NTP_MODE_XXX)
//unsigned int Mode:3;
// fields | LI | VN | Mode |
// bitPos | XX | XXX | XXX |
uint8_t Flag;
// Stratum: This is a eight-bit integer indicating the stratum level of the local clock, with values
// defined as follows:
// 0 unspecified
// 1 primary reference (e.g., radio clock)
// 2-255 secondary reference (via NTP)
// The values that can appear in this field range from zero to NTP.INFIN inclusive
uint8_t Stratum;
// Poll Interval: This is an eight-bit signed integer indicating the maximum interval between successive
// messages, in seconds to the nearest power of two. The values that can appear in this field range from
// NTP.MINPOLL to NTP.MAXPOLL inclusive.
int8_t Poll;
// Precision: This is an eight-bit signed integer indicating the precision of the local clock, in seconds
// to the nearest power of two.
int8_t Precision;
// int8_t Precision;
// Root Delay: This is a 32-bit signed fixed-point number indicating the total roundtrip delay to the
// primary reference source, in seconds with fraction point between bits 15 and 16. Note that this
// variable can take on both positive and negative values, depending on clock precision and skew.
int32_t RootDelay;
// Root Dispersion: This is a 32-bit signed fixed-point number indicating the maximum error relative
// to the primary reference source, in seconds with fraction point between bits 15 and 16. Only
// positive values greater than zero are possible.
int32_t RootDisp;
// Reference Clock Identifier: This is a 32-bit code identifying the particular reference clock.
char ClkID[4];
// Reference Timestamp: This is the local time at which the local clock was last set or corrected, in
// 64-bit timestamp format.
NtpTime RefStamp;
// Originate Timestamp: This is the local time at which the request departed the client host for the
// service host, in 64-bit timestamp format.
NtpTime OrgStamp;
// Receive Timestamp: This is the local time at which the request arrived at the service host, in 64-bit
// timestamp format.
NtpTime RxStamp;
// Transmit Timestamp: This is the local time at which the reply departed the service host for the client
// host, in 64-bit timestamp format.
NtpTime TxStamp;
// Authenticator (optional): When the NTP authentication mechanism is implemented, this contains
// the authenticator information defined in Appendix C.
} NTPMSG;
/*
*******************************************************************************************
* INTERFACES
*******************************************************************************************
*/
// initialize the NTP message frame to request time.
// description: initialize the buffer to the NTP frame which will be sended to request time.
// parameter : buf the buffer to store the frame.
// orgStamp the local time at which the request departed the client host for the
// service host. NULL if not used.
// return : length of the message
// note :
uint16_t NtpMsg_initRequest(uint8_t * buf, NtpTime const * orgStamp);
// resolve the current time from the NTP message just received.(SNTP)
// description: resolve the current time from the NTP message just received.
// parameter : buf the NTP message just received.
// ret return the current time stamp.
// return :
// note : after call this function, you can pass ret->sec to NtpTime_toDatetime to
// get the date time.
void NtpMsg_resolveCurTimeSNTP(uint8_t const * buf, NtpTime * ret);
// set the first byte of NTP message
// description: used to set the first byte of NTP message
// parameter : buf the NTP message buffer.
// LI see NTP_LI_XXX
// VN expect NTP_VERSION
// Mode see NTP_MODE_XXX
// return :
// note :
void NtpMsg_setFlag(NTPMSG * buf,uint8_t LI, uint8_t VN, uint8_t Mode);
// NTP second to Datetime
// description: used to resolve the NTP second in NTP timestamp to the human readable datetime.
// parameter : seconds the NTP seconds.
// ret return the resolve result.
// return :
// note :
void NtpTime_toDatetime(NtpSeconds seconds, Datetime * ret);
#endif // _NTP_H
NTP.c
/*
******************************************************************************************
*
* NETWORK TIME PROTOCOL MODULE
* NTP模块
*
* File : NTP.c
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2019/04/01
* Version: V1.0
* History:
* NOTE(s):
******************************************************************************************
*/
/*
*******************************************************************************************
* INCLUDES
*******************************************************************************************
*/
#include <string.h>
#include "NTP.h"
#include "NetworkLib.h"
/*
*******************************************************************************************
* CONFIGURATION 配置
*******************************************************************************************
*/
/*
00)UTC-12:00 Baker Island, Howland Island (both uninhabited)
01) UTC-11:00 American Samoa, Samoa
02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii)
03) UTC-09:30 Marquesas Islands
04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska)
05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California)
06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state))
07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta)
08) UTC-07:00 Mexico (Chihuahua), United States (Colorado)
09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras
10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas)
11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru
12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec)
13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.)
14) UTC-04:30 Venezuela
15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay,
16) UTC-04:00 Puerto Rico, Trinidad and Tobago
17) UTC-03:30 Canada (Newfoundland)
18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay
19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands
20) UTC-01:00 Portugal (Azores), Cape Verde
21) UTC±00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira)
22) UTC±00:00 Spain (Canary Islands), Morocco, United Kingdom
23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina,
24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo,
25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland
26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia,
27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine
28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia,
29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen
30) UTC+03:30 (Summer)Iran
31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates
32) UTC+04:30 Afghanistan
33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan
34) UTC+05:30 India, Sri Lanka
35) UTC+05:45 Nepal
36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast)
37) UTC+06:30 Cocos Islands, Myanmar
38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam
39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia
40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan
41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia))
42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria)
43) UTC+10:30 Lord Howe Island
44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands
45) UTC+11:30 Norfolk Island
46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand
47) UTC+12:45 (Summer)New Zealand
48) UTC+13:00 Tonga
49) UTC+14:00 Kiribati (Line Islands)
*/
// timezone china
static const uint8_t time_zone = 39;
/*
*******************************************************************************************
* LOCAL FUNCTION DECLARATION
*******************************************************************************************
*/
// get the local ntp timestamp for the standard one, depends on the timezone.
static NtpSeconds _stdTimeSecToLocal(NtpSeconds sec);
/*
*******************************************************************************************
* IMPLEMENTATIONS
*******************************************************************************************
*/
uint16_t NtpMsg_initRequest(uint8_t * buf, NtpTime const * orgStamp){
NTPMSG * pMsg = (NTPMSG *)buf;
(void)memset(buf,0,sizeof(NTPMSG));
NtpMsg_setFlag(pMsg,NTP_LI_NOWARNING, NTP_VERSION, NTP_MODE_CLIENT);
if(orgStamp != NULL){
(void)NetworkStream_putUint32((uint8_t *)&pMsg->OrgStamp.sec, orgStamp->sec);
(void)NetworkStream_putUint32((uint8_t *)&pMsg->OrgStamp.frac, orgStamp->frac);
}
return sizeof(NTPMSG);
}
void NtpMsg_resolveCurTimeSNTP(uint8_t const * buf, NtpTime * ret){
NTPMSG const * pMsg = (NTPMSG const *)buf;
(void)NetworkStream_getUint32((uint8_t *)&pMsg->TxStamp.sec, &ret->sec);
(void)NetworkStream_getUint32((uint8_t *)&pMsg->TxStamp.frac, &ret->frac);
}
void NtpMsg_setFlag(NTPMSG * buf,uint8_t LI, uint8_t VN, uint8_t Mode){
*(uint8_t *)buf = (LI << 6) + ((VN & 0x07) << 3) + (Mode & 0x07);
}
static const uint8_t daytab[2][13] = {
{
0,31,28,31,30,31,30,31,31,30,31,30,31 },
{
0,31,29,31,30,31,30,31,31,30,31,30,31 }
};
void _DayLeftInAYearToMonthAndDay(uint16_t dayLeft,uint8_t isLeapYear,uint8_t * mon,uint8_t * day){
uint8_t monthCur = 1;
while(dayLeft >= daytab[isLeapYear][monthCur]){
dayLeft -= daytab[isLeapYear][monthCur];
++monthCur;
}
*mon = monthCur;
*day = dayLeft + 1;
}
void NtpTime_toDatetime(NtpSeconds sec, Datetime * ret){
uint8_t isLeap;
uint32_t dayLeft;
uint16_t yearCur;
sec = _stdTimeSecToLocal(sec);
if(sec >= SECS_1900to2019){
sec -= SECS_1900to2019;
yearCur = 2019;
}else{
yearCur = EPOCH;
}
dayLeft = sec / (SECS_PERDAY);
for(;;){
isLeap = (yearCur % 400 == 0 || (yearCur % 100 != 0 && yearCur % 4 == 0));
if(dayLeft < (uint32_t)(DAYS_COMMYEAR + isLeap))
break;
dayLeft -= (uint32_t)(DAYS_COMMYEAR + isLeap);
++yearCur;
}
ret->yy = yearCur;
_DayLeftInAYearToMonthAndDay((uint16_t)dayLeft,isLeap,&ret->mo,&ret->dd);
sec %= SECS_PERDAY;
ret->hh = (uint8_t)(sec / SECS_PERHOUR);
sec %= SECS_PERHOUR;
ret->mm = (uint8_t)(sec / SECS_PERMIN);
ret->ss = (uint8_t)(sec % SECS_PERMIN);
}
/*
*******************************************************************************************
* LOCAL FUNCTION IMPLEMENTATION
*******************************************************************************************
*/
static NtpSeconds _stdTimeSecToLocal(NtpSeconds sec){
switch (time_zone){
case 0:
sec -= 12 * SECS_PERHOUR;
break;
case 1:
sec -= 11 * SECS_PERHOUR;
break;
case 2:
sec -= 10 * SECS_PERHOUR;
break;
case 3:
sec -= (9 * SECS_PERHOUR+30 * SECS_PERMIN);
break;
case 4:
sec -= 9 * SECS_PERHOUR;
break;
case 5:
case 6:
sec -= 8 * SECS_PERHOUR;
break;
case 7:
case 8:
sec -= 7 * SECS_PERHOUR;
break;
case 9:
case 10:
sec -= 6 * SECS_PERHOUR;
break;
case 11:
case 12:
case 13:
sec -= 5 * SECS_PERHOUR;
break;
case 14:
sec -= (4 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 15:
case 16:
sec -= 4 * SECS_PERHOUR;
break;
case 17:
sec -= (3 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 18:
sec -= 3 * SECS_PERHOUR;
break;
case 19:
sec -= 2 * SECS_PERHOUR;
break;
case 20:
sec -= 1 * SECS_PERHOUR;
break;
case 21:
case 22:
break;
case 23:
case 24:
case 25:
sec += 1 * SECS_PERHOUR;
break;
case 26:
case 27:
sec += 2 * SECS_PERHOUR;
break;
case 28:
case 29:
sec += 3 * SECS_PERHOUR;
break;
case 30:
sec += (3 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 31:
sec += 4 * SECS_PERHOUR;
break;
case 32:
sec += (4 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 33:
sec += 5 * SECS_PERHOUR;
break;
case 34:
sec += (5 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 35:
sec += (5 * SECS_PERHOUR + 45 * SECS_PERMIN);
break;
case 36:
sec += 6 * SECS_PERHOUR;
break;
case 37:
sec += (6 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 38:
sec += 7 * SECS_PERHOUR;
break;
case 39:
sec += 8 * SECS_PERHOUR;
break;
case 40:
sec += 9 * SECS_PERHOUR;
break;
case 41:
sec += (9 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 42:
sec += 10 * SECS_PERHOUR;
break;
case 43:
sec += (10 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 44:
sec += 11 * SECS_PERHOUR;
break;
case 45:
sec += (11 * SECS_PERHOUR + 30 * SECS_PERMIN);
break;
case 46:
sec += 12 * SECS_PERHOUR;
break;
case 47:
sec += (12 * SECS_PERHOUR + 45 * SECS_PERMIN);
break;
case 48:
sec += 13 * SECS_PERHOUR;
break;
case 49:
sec += 14 * SECS_PERHOUR;
break;
}
return sec;
}
NetworkLib.h
#ifndef _NETWORK_LIB_H
#define _NETWORK_LIB_H
#include <stdint.h>
// to help produce better code
// #define _CPU_BIGENDIAN
// or
// #define _CPU_LITTLEENDIAN
// NetworkStream_getUintXX
// description: get uintXX from the network stream ns and return the pointer to the
// next position in the stream.
// parameter : ns the stream to get uintXX.
// ret return the result through the pointer.
// return : the pointer to the next position in the stream.
// note : the interface will handle the byte order for you.
uint8_t const * NetworkStream_getUint16(uint8_t const * ns,uint16_t * ret);
uint8_t const * NetworkStream_getUint32(uint8_t const * ns,uint32_t * ret);
// NetworkStream_putUintXX
// description: put uintXX to the network stream ns and return the pointer to the
// next position in the stream.
// parameter : ns the stream to get uintXX.
// val the value to be put.
// return : the pointer to the next position in the stream.
// note : the interface will handle the byte order for you.
uint8_t * NetworkStream_putUint16(uint8_t * ns,uint16_t val);
uint8_t * NetworkStream_putUint32(uint8_t * ns,uint32_t val);
// whether the cpu is big endian system.
// return : 1 if is big endian.
// 0 if is little endian.
int isBigEndianSys(void);
#ifdef _CPU_BIGENDIAN
#define htonl(h) (h)
#define htons(h) (h)
#define ntohl(n) (n)
#define ntohs(n) (n)
#else
#define htonl(h) NetworkLib_htonl(h)
#define htons(h) NetworkLib_htons(h)
#define ntohl(n) NetworkLib_ntohl(n)
#define ntohs(n) NetworkLib_ntohs(n)
#endif
uint32_t NetworkLib_htonl(uint32_t hostlong);
uint16_t NetworkLib_htons(uint16_t hostshort);
uint32_t NetworkLib_ntohl(uint32_t netlong);
uint16_t NetworkLib_ntohs(uint16_t netshort);
#endif
NetworkLib.c
#include <stdio.h>
#include "NetworkLib.h"
#ifdef _CPU_BIGENDIAN
#define _isBigEndianSys() 1
#endif
#ifdef _CPU_LITTLEENDIAN
#define _isBigEndianSys() 0
#endif
#define BigLittleSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
(((uint16_t)(A) & 0x00ff) << 8))
#define BigLittleSwap32(A) ((((uint32_t)(A) & 0xff000000) >> 24) | \
(((uint32_t)(A) & 0x00ff0000) >> 8) | \
(((uint32_t)(A) & 0x0000ff00) << 8) | \
(((uint32_t)(A) & 0x000000ff) << 24))
#ifndef _isBigEndianSys
#define _isBigEndianSys() (*(uint8_t *)&_flag == 0)
static const uint16_t _flag = 1;
#endif
int isBigEndianSys(void){
return _isBigEndianSys();
}
uint8_t const * NetworkStream_getUint16(uint8_t const * ns,uint16_t * ret){
if(_isBigEndianSys()){
* ret = *(uint16_t *)ns;
}else{
((uint8_t *)ret)[0] = ns[1];
((uint8_t *)ret)[1] = ns[0];
}
return ns + sizeof(uint16_t);
}
uint8_t const * NetworkStream_getUint32(uint8_t const * ns,uint32_t * ret){
if(_isBigEndianSys()){
* ret = *(uint32_t *)ns;
}else{
((uint8_t *)ret)[0] = ns[3];
((uint8_t *)ret)[1] = ns[2];
((uint8_t *)ret)[2] = ns[1];
((uint8_t *)ret)[3] = ns[0];
}
return ns + sizeof(uint32_t);
}
uint8_t * NetworkStream_putUint16(uint8_t * ns,uint16_t val){
if(_isBigEndianSys()){
*(uint16_t *)ns = val;
}else{
ns[0] = ((uint8_t *)&val)[1];
ns[1] = ((uint8_t *)&val)[0];
}
return ns + sizeof(uint16_t);
}
uint8_t * NetworkStream_putUint32(uint8_t * ns,uint32_t val){
if(_isBigEndianSys()){
*(uint32_t *)ns = val;
}else{
ns[0] = ((uint8_t *)&val)[3];
ns[1] = ((uint8_t *)&val)[2];
ns[2] = ((uint8_t *)&val)[1];
ns[3] = ((uint8_t *)&val)[0];
}
return ns + sizeof(uint32_t);
}
uint32_t NetworkLib_htonl(uint32_t hostlong){
if(_isBigEndianSys()){
return hostlong;
}else{
return BigLittleSwap32(hostlong);
}
}
uint16_t NetworkLib_htons(uint16_t hostshort){
if(_isBigEndianSys()){
return hostshort;
}else{
return BigLittleSwap16(hostshort);
}
}
uint32_t NetworkLib_ntohl(uint32_t netlong){
if(_isBigEndianSys()){
return netlong;
}else{
return BigLittleSwap32(netlong);
}
}
uint16_t NetworkLib_ntohs(uint16_t netshort){
if(_isBigEndianSys()){
return netshort;
}else{
return BigLittleSwap16(netshort);
}
}
NTPTimeHelper.h
/*
*******************************************************************************************
*
* NTP TIME HELPER MODULE
*
* File : NTPTimeHelper.h
* By : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2019/09/26
* version: V1.0
* History:
* note : the convert interfaces for NTP time.
*********************************************************************************************
*/
#ifndef _NTPTimeHelper_H
#define _NTPTimeHelper_H
#include "NTP.h"
#include <time.h>
typedef struct POSIXTIME_STRUCT{
time_t sec; //秒
uint32_t usec; //微秒
} PosixTime;
void NtpTimeToPosixTime(PosixTime *dst, const NtpTime *src);
void PosixTimeToNtpTime(NtpTime *dst, const PosixTime *src);
#endif
NTPTimeHelper.c
/*
*******************************************************************************************
*
* NTP TIME HELPER MODULE
*
* File : NTPTimeHelper.c
* By : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2019/09/26
* version: V1.0
* History:
* note : the convert interfaces for NTP time.
*********************************************************************************************
*/
#include "NTPTimeHelper.h"
#pragma MESSAGE DISABLE C5919
void NtpTimeToPosixTime(PosixTime *dst, const NtpTime *src){
int valid;
if(dst == NULL || src == NULL)
return;
valid = (src->sec >= SECS_1900to1970);
dst->sec = (valid)? src->sec - SECS_1900to1970: 0;
// 1000000/0x100000000 = 0.0002328306436538696...
dst->usec = (valid)? (uint32_t)((double)src->frac * (double)0.0002328306436538696): 0;
}
void PosixTimeToNtpTime(NtpTime *dst, const PosixTime *src){
int valid;
if(dst == NULL || src == NULL)
return;
valid = (src->sec <= (0xFFFFFFFF - SECS_1900to1970));
dst->sec = (valid)? src->sec + SECS_1900to1970: 0;
// 0x100000000/1000000 = 4294.967296
dst->frac = (valid)?(uint32_t)((double)src->usec * 4294.967296): 0;
}
示例
使用起来就是这句话:
初始化一个请求包用UDP发往NTP服务器对应端口,然后等待答复包并解析出结果。
以下示例基于W5500的官方io库+uCOSII,已略去如创建任务、初始化网络等不重要的代码:
#include "NTP.h"
#define UDP_SOCKET 5
static uint8_t NtpServerIP[4] = {
NTPSERVER_IP_CHINA};
static uint8_t udpBuf[200];
static void ConsoleTask (void *p_arg){
……
for(;;){
c = (uint8_t)getchar();
if(c == 'n'){
(void)NtpMsg_initRequest(udpBuf,NULL);
(void)printf("Send Ntp request\r\n");
(void)sendto(UDP_SOCKET,udpBuf,sizeof(NTPMSG),NtpServerIP,NTP_PORT);
}
}
}
static void UdpRxTask(void *p_arg){
uint8_t dip[4];
uint16_t dport;
int32_t len;
static Datetime dt;
NtpTime t;
(void)p_arg;
for(;;){
// 假设网络已经初始化完成
……
(void)printf("UDPRx task initing socket.\r\n");
switch(socket(UDP_SOCKET,Sn_MR_UDP,MDNS_PORT,0)){
case SOCKERR_SOCKINIT:
(void)printf("UDPRx init socket fail, network not ready.\r\n");
(void)OSTimeDlyHMSM(0,0,2,0);
continue;
case UDP_SOCKET:
(void)printf("UDPRx init socket success.\r\n");
break;
default:
(void)printf("UDPRx init socket fail, unknown error.\r\n");
(void)OSTimeDlyHMSM(0,0,2,0);
continue;
}
for(;;){
(void)printf("UDPRx task is wating for data!\r\n");
len = recvfrom(UDP_SOCKET,udpBuf,sizeof(udpBuf),dip,&dport);
if(len > 0){
(void)printf("UDPRx received data from %u.%u.%u.%u:%u\r\n",dip[0], dip[1], dip[2], dip[3],dport);
if(len >= sizeof(NTPMSG) && dport == NTP_PORT){
printf("NTP packet.\r\n");
NtpMsg_resolveCurTimeSNTP((NTPMSG *)udpBuf, &t);
NtpTime_toDatetime(t.sec,&dt);
printf("%4u-%2u-%2u %2u:%2u:%2u\r\n",dt.yy,dt.mo,dt.dd,dt.hh,dt.mm,dt.ss);
}
}else if(len < 0){
(void)printf("UDPRx found socket err.\r\n");
break;
}
}
}
}
因为是独立的模块,你可以根据自己的需求自己决定如何整合NTP功能至项目中,比如,其实可以多个基于UDP的功能(比如DNS,其实我也写了,可能后面会发)都使用同一个UDP SOCKET,通过对方的IP和/或端口号来区分是哪个功能。
测试结果:至于后面那个TimeHelper,则是用于NTP时间戳和Unix时间戳互转的接口。当你需要使用time.h时自然明白它的作用了。就不示例了。
更新历史
2019/05/29 第一版
2019/10/02 增加NTPTimeHelper模块。并更新了NetworkLib,增加了点功能。
还没有评论,来说两句吧...