[嵌入式开发模块]Network Time Protocol(NTP)模块

矫情吗;* 2022-01-28 19:47 438阅读 0赞

文章目录

  • 前言
  • 源码
    • 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

  1. /*
  2. ******************************************************************************************
  3. *
  4. * NETWORK TIME PROTOCOL MODULE
  5. * NTP模块
  6. *
  7. * File : NTP.h
  8. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  9. * Date: 2019/04/01
  10. * Version: V1.0
  11. * History:
  12. * NOTE(s): 1. the module is to provide interfaces for network time protocol.
  13. * 2. the current version only provide the interface for resolving SNTP
  14. * 3. the parameter 'buf' in the signature refers to the original network stream,
  15. * and 'msg' refers to the packet parameters which are compatible with the local
  16. * system. In big-endian system, they are the same.
  17. * 4. To use the module:
  18. * a. prepare a NTPMSG variable or a buffer which is big enough to hold the NTPMSG.
  19. * b. use NtpMsg_initRequest to init the buffer as NTP request message.
  20. * c. initialize a socket as udp and call sendto() to send the whole message to
  21. * NTP server with port NTP_PORT. Then wait for the respond from NTP server.
  22. * d. If got respond, resolve the frame with NtpMsg_resolveCurTimeSNTP, then you
  23. * will get the current (NTP) time stamp.
  24. * e. call NtpTime_toDatetime with the time stamp to get a readable datetime.
  25. ******************************************************************************************
  26. */
  27. #ifndef _NTP_H
  28. #define _NTP_H
  29. /*
  30. *******************************************************************************************
  31. * INCLUDES
  32. *******************************************************************************************
  33. */
  34. #include <stdint.h>
  35. /*
  36. *******************************************************************************************
  37. * CONSTANT 常量
  38. *******************************************************************************************
  39. */
  40. // 00 no warning
  41. // 01 last minute has 61 seconds
  42. // 10 last minute has 59 seconds
  43. // 11 alarm condition (clock not synchronized)
  44. #define NTP_LI_NOWARNING 0
  45. #define NTP_LI_61s 1
  46. #define NTP_LI_59s 2
  47. #define NTP_LI_ALARM 3
  48. #define NTP_VERSION 4
  49. #define NTP_MODE_0 0 // reserved
  50. #define NTP_MODE_SYMACTIVE 1 // symmetric active
  51. #define NTP_MODE_SYMPASSIVE 2 // symmetric passive
  52. #define NTP_MODE_CLIENT 3 // client
  53. #define NTP_MODE_SERVER 4 // server
  54. #define NTP_MODE_BROADCAST 5 // broadcast
  55. #define NTP_MODE_6 6 // reserved for NTP control message
  56. #define NTP_MODE_7 7 // reserved for private use
  57. #define DAYS_LEAPYEAR 366
  58. #define DAYS_COMMYEAR 365
  59. #define SECS_PERMIN 60UL
  60. #define SECS_PERHOUR (60 * SECS_PERMIN)
  61. #define SECS_PERDAY (24 * SECS_PERHOUR)
  62. #define SECS_LEAPYEAR (DAYS_LEAPYEAR * SECS_PERDAY)
  63. #define SECS_COMMYEAR (DAYS_COMMYEAR * SECS_PERDAY)
  64. #define SECS_1900to1970 0x83aa7e80
  65. #define SECS_1900to2019 0xdfd52c00
  66. // NTP server Domain name of windows
  67. #define NTPSERVER_NAME_WINDOWS "time.windows.com"
  68. // NTP server IP Address of National Time Service Center Chinese Academy Of Scences
  69. #define NTPSERVER_IP_CHINA 210,72,145,44
  70. #define NTP_PORT 123 // NTP server port number
  71. #define EPOCH 1900 // NTP start year
  72. /*
  73. *******************************************************************************************
  74. * TYPE DEFINITION
  75. *******************************************************************************************
  76. */
  77. typedef uint32_t NtpSeconds;
  78. typedef struct DATETIME_STRUCT{
  79. uint16_t yy;
  80. uint8_t mo;
  81. uint8_t dd;
  82. uint8_t hh;
  83. uint8_t mm;
  84. uint8_t ss;
  85. } Datetime;
  86. typedef struct NTPTIME_STRUCT{
  87. NtpSeconds sec;
  88. uint32_t frac; // fractions
  89. } NtpTime;
  90. typedef struct NTPMSG_STRUCT{
  91. // Leap Indicator (LI): This is a two-bit code warning of an impending leap second to be inserted/deleted
  92. // in the last minute of the current day, with bit 0 and bit 1, respectively. (see NTP_LI_XXXX)
  93. //unsigned int LI :2;
  94. // Version Number (VN): This is a three-bit integer indicating the NTP version number, currently three (4).
  95. //unsigned int VN :3;
  96. // Mode: This is a three-bit integer indicating the mode. (see NTP_MODE_XXX)
  97. //unsigned int Mode:3;
  98. // fields | LI | VN | Mode |
  99. // bitPos | XX | XXX | XXX |
  100. uint8_t Flag;
  101. // Stratum: This is a eight-bit integer indicating the stratum level of the local clock, with values
  102. // defined as follows:
  103. // 0 unspecified
  104. // 1 primary reference (e.g., radio clock)
  105. // 2-255 secondary reference (via NTP)
  106. // The values that can appear in this field range from zero to NTP.INFIN inclusive
  107. uint8_t Stratum;
  108. // Poll Interval: This is an eight-bit signed integer indicating the maximum interval between successive
  109. // messages, in seconds to the nearest power of two. The values that can appear in this field range from
  110. // NTP.MINPOLL to NTP.MAXPOLL inclusive.
  111. int8_t Poll;
  112. // Precision: This is an eight-bit signed integer indicating the precision of the local clock, in seconds
  113. // to the nearest power of two.
  114. int8_t Precision;
  115. // int8_t Precision;
  116. // Root Delay: This is a 32-bit signed fixed-point number indicating the total roundtrip delay to the
  117. // primary reference source, in seconds with fraction point between bits 15 and 16. Note that this
  118. // variable can take on both positive and negative values, depending on clock precision and skew.
  119. int32_t RootDelay;
  120. // Root Dispersion: This is a 32-bit signed fixed-point number indicating the maximum error relative
  121. // to the primary reference source, in seconds with fraction point between bits 15 and 16. Only
  122. // positive values greater than zero are possible.
  123. int32_t RootDisp;
  124. // Reference Clock Identifier: This is a 32-bit code identifying the particular reference clock.
  125. char ClkID[4];
  126. // Reference Timestamp: This is the local time at which the local clock was last set or corrected, in
  127. // 64-bit timestamp format.
  128. NtpTime RefStamp;
  129. // Originate Timestamp: This is the local time at which the request departed the client host for the
  130. // service host, in 64-bit timestamp format.
  131. NtpTime OrgStamp;
  132. // Receive Timestamp: This is the local time at which the request arrived at the service host, in 64-bit
  133. // timestamp format.
  134. NtpTime RxStamp;
  135. // Transmit Timestamp: This is the local time at which the reply departed the service host for the client
  136. // host, in 64-bit timestamp format.
  137. NtpTime TxStamp;
  138. // Authenticator (optional): When the NTP authentication mechanism is implemented, this contains
  139. // the authenticator information defined in Appendix C.
  140. } NTPMSG;
  141. /*
  142. *******************************************************************************************
  143. * INTERFACES
  144. *******************************************************************************************
  145. */
  146. // initialize the NTP message frame to request time.
  147. // description: initialize the buffer to the NTP frame which will be sended to request time.
  148. // parameter : buf the buffer to store the frame.
  149. // orgStamp the local time at which the request departed the client host for the
  150. // service host. NULL if not used.
  151. // return : length of the message
  152. // note :
  153. uint16_t NtpMsg_initRequest(uint8_t * buf, NtpTime const * orgStamp);
  154. // resolve the current time from the NTP message just received.(SNTP)
  155. // description: resolve the current time from the NTP message just received.
  156. // parameter : buf the NTP message just received.
  157. // ret return the current time stamp.
  158. // return :
  159. // note : after call this function, you can pass ret->sec to NtpTime_toDatetime to
  160. // get the date time.
  161. void NtpMsg_resolveCurTimeSNTP(uint8_t const * buf, NtpTime * ret);
  162. // set the first byte of NTP message
  163. // description: used to set the first byte of NTP message
  164. // parameter : buf the NTP message buffer.
  165. // LI see NTP_LI_XXX
  166. // VN expect NTP_VERSION
  167. // Mode see NTP_MODE_XXX
  168. // return :
  169. // note :
  170. void NtpMsg_setFlag(NTPMSG * buf,uint8_t LI, uint8_t VN, uint8_t Mode);
  171. // NTP second to Datetime
  172. // description: used to resolve the NTP second in NTP timestamp to the human readable datetime.
  173. // parameter : seconds the NTP seconds.
  174. // ret return the resolve result.
  175. // return :
  176. // note :
  177. void NtpTime_toDatetime(NtpSeconds seconds, Datetime * ret);
  178. #endif // _NTP_H

NTP.c

  1. /*
  2. ******************************************************************************************
  3. *
  4. * NETWORK TIME PROTOCOL MODULE
  5. * NTP模块
  6. *
  7. * File : NTP.c
  8. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  9. * Date: 2019/04/01
  10. * Version: V1.0
  11. * History:
  12. * NOTE(s):
  13. ******************************************************************************************
  14. */
  15. /*
  16. *******************************************************************************************
  17. * INCLUDES
  18. *******************************************************************************************
  19. */
  20. #include <string.h>
  21. #include "NTP.h"
  22. #include "NetworkLib.h"
  23. /*
  24. *******************************************************************************************
  25. * CONFIGURATION 配置
  26. *******************************************************************************************
  27. */
  28. /*
  29. 00)UTC-12:00 Baker Island, Howland Island (both uninhabited)
  30. 01) UTC-11:00 American Samoa, Samoa
  31. 02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii)
  32. 03) UTC-09:30 Marquesas Islands
  33. 04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska)
  34. 05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California)
  35. 06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state))
  36. 07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta)
  37. 08) UTC-07:00 Mexico (Chihuahua), United States (Colorado)
  38. 09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras
  39. 10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas)
  40. 11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru
  41. 12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec)
  42. 13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.)
  43. 14) UTC-04:30 Venezuela
  44. 15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay,
  45. 16) UTC-04:00 Puerto Rico, Trinidad and Tobago
  46. 17) UTC-03:30 Canada (Newfoundland)
  47. 18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay
  48. 19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands
  49. 20) UTC-01:00 Portugal (Azores), Cape Verde
  50. 21) UTC±00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira)
  51. 22) UTC±00:00 Spain (Canary Islands), Morocco, United Kingdom
  52. 23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina,
  53. 24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo,
  54. 25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland
  55. 26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia,
  56. 27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine
  57. 28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia,
  58. 29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen
  59. 30) UTC+03:30 (Summer)Iran
  60. 31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates
  61. 32) UTC+04:30 Afghanistan
  62. 33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan
  63. 34) UTC+05:30 India, Sri Lanka
  64. 35) UTC+05:45 Nepal
  65. 36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast)
  66. 37) UTC+06:30 Cocos Islands, Myanmar
  67. 38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam
  68. 39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia
  69. 40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan
  70. 41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia))
  71. 42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria)
  72. 43) UTC+10:30 Lord Howe Island
  73. 44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands
  74. 45) UTC+11:30 Norfolk Island
  75. 46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand
  76. 47) UTC+12:45 (Summer)New Zealand
  77. 48) UTC+13:00 Tonga
  78. 49) UTC+14:00 Kiribati (Line Islands)
  79. */
  80. // timezone china
  81. static const uint8_t time_zone = 39;
  82. /*
  83. *******************************************************************************************
  84. * LOCAL FUNCTION DECLARATION
  85. *******************************************************************************************
  86. */
  87. // get the local ntp timestamp for the standard one, depends on the timezone.
  88. static NtpSeconds _stdTimeSecToLocal(NtpSeconds sec);
  89. /*
  90. *******************************************************************************************
  91. * IMPLEMENTATIONS
  92. *******************************************************************************************
  93. */
  94. uint16_t NtpMsg_initRequest(uint8_t * buf, NtpTime const * orgStamp){
  95. NTPMSG * pMsg = (NTPMSG *)buf;
  96. (void)memset(buf,0,sizeof(NTPMSG));
  97. NtpMsg_setFlag(pMsg,NTP_LI_NOWARNING, NTP_VERSION, NTP_MODE_CLIENT);
  98. if(orgStamp != NULL){
  99. (void)NetworkStream_putUint32((uint8_t *)&pMsg->OrgStamp.sec, orgStamp->sec);
  100. (void)NetworkStream_putUint32((uint8_t *)&pMsg->OrgStamp.frac, orgStamp->frac);
  101. }
  102. return sizeof(NTPMSG);
  103. }
  104. void NtpMsg_resolveCurTimeSNTP(uint8_t const * buf, NtpTime * ret){
  105. NTPMSG const * pMsg = (NTPMSG const *)buf;
  106. (void)NetworkStream_getUint32((uint8_t *)&pMsg->TxStamp.sec, &ret->sec);
  107. (void)NetworkStream_getUint32((uint8_t *)&pMsg->TxStamp.frac, &ret->frac);
  108. }
  109. void NtpMsg_setFlag(NTPMSG * buf,uint8_t LI, uint8_t VN, uint8_t Mode){
  110. *(uint8_t *)buf = (LI << 6) + ((VN & 0x07) << 3) + (Mode & 0x07);
  111. }
  112. static const uint8_t daytab[2][13] = {
  113. {
  114. 0,31,28,31,30,31,30,31,31,30,31,30,31 },
  115. {
  116. 0,31,29,31,30,31,30,31,31,30,31,30,31 }
  117. };
  118. void _DayLeftInAYearToMonthAndDay(uint16_t dayLeft,uint8_t isLeapYear,uint8_t * mon,uint8_t * day){
  119. uint8_t monthCur = 1;
  120. while(dayLeft >= daytab[isLeapYear][monthCur]){
  121. dayLeft -= daytab[isLeapYear][monthCur];
  122. ++monthCur;
  123. }
  124. *mon = monthCur;
  125. *day = dayLeft + 1;
  126. }
  127. void NtpTime_toDatetime(NtpSeconds sec, Datetime * ret){
  128. uint8_t isLeap;
  129. uint32_t dayLeft;
  130. uint16_t yearCur;
  131. sec = _stdTimeSecToLocal(sec);
  132. if(sec >= SECS_1900to2019){
  133. sec -= SECS_1900to2019;
  134. yearCur = 2019;
  135. }else{
  136. yearCur = EPOCH;
  137. }
  138. dayLeft = sec / (SECS_PERDAY);
  139. for(;;){
  140. isLeap = (yearCur % 400 == 0 || (yearCur % 100 != 0 && yearCur % 4 == 0));
  141. if(dayLeft < (uint32_t)(DAYS_COMMYEAR + isLeap))
  142. break;
  143. dayLeft -= (uint32_t)(DAYS_COMMYEAR + isLeap);
  144. ++yearCur;
  145. }
  146. ret->yy = yearCur;
  147. _DayLeftInAYearToMonthAndDay((uint16_t)dayLeft,isLeap,&ret->mo,&ret->dd);
  148. sec %= SECS_PERDAY;
  149. ret->hh = (uint8_t)(sec / SECS_PERHOUR);
  150. sec %= SECS_PERHOUR;
  151. ret->mm = (uint8_t)(sec / SECS_PERMIN);
  152. ret->ss = (uint8_t)(sec % SECS_PERMIN);
  153. }
  154. /*
  155. *******************************************************************************************
  156. * LOCAL FUNCTION IMPLEMENTATION
  157. *******************************************************************************************
  158. */
  159. static NtpSeconds _stdTimeSecToLocal(NtpSeconds sec){
  160. switch (time_zone){
  161. case 0:
  162. sec -= 12 * SECS_PERHOUR;
  163. break;
  164. case 1:
  165. sec -= 11 * SECS_PERHOUR;
  166. break;
  167. case 2:
  168. sec -= 10 * SECS_PERHOUR;
  169. break;
  170. case 3:
  171. sec -= (9 * SECS_PERHOUR+30 * SECS_PERMIN);
  172. break;
  173. case 4:
  174. sec -= 9 * SECS_PERHOUR;
  175. break;
  176. case 5:
  177. case 6:
  178. sec -= 8 * SECS_PERHOUR;
  179. break;
  180. case 7:
  181. case 8:
  182. sec -= 7 * SECS_PERHOUR;
  183. break;
  184. case 9:
  185. case 10:
  186. sec -= 6 * SECS_PERHOUR;
  187. break;
  188. case 11:
  189. case 12:
  190. case 13:
  191. sec -= 5 * SECS_PERHOUR;
  192. break;
  193. case 14:
  194. sec -= (4 * SECS_PERHOUR + 30 * SECS_PERMIN);
  195. break;
  196. case 15:
  197. case 16:
  198. sec -= 4 * SECS_PERHOUR;
  199. break;
  200. case 17:
  201. sec -= (3 * SECS_PERHOUR + 30 * SECS_PERMIN);
  202. break;
  203. case 18:
  204. sec -= 3 * SECS_PERHOUR;
  205. break;
  206. case 19:
  207. sec -= 2 * SECS_PERHOUR;
  208. break;
  209. case 20:
  210. sec -= 1 * SECS_PERHOUR;
  211. break;
  212. case 21:
  213. case 22:
  214. break;
  215. case 23:
  216. case 24:
  217. case 25:
  218. sec += 1 * SECS_PERHOUR;
  219. break;
  220. case 26:
  221. case 27:
  222. sec += 2 * SECS_PERHOUR;
  223. break;
  224. case 28:
  225. case 29:
  226. sec += 3 * SECS_PERHOUR;
  227. break;
  228. case 30:
  229. sec += (3 * SECS_PERHOUR + 30 * SECS_PERMIN);
  230. break;
  231. case 31:
  232. sec += 4 * SECS_PERHOUR;
  233. break;
  234. case 32:
  235. sec += (4 * SECS_PERHOUR + 30 * SECS_PERMIN);
  236. break;
  237. case 33:
  238. sec += 5 * SECS_PERHOUR;
  239. break;
  240. case 34:
  241. sec += (5 * SECS_PERHOUR + 30 * SECS_PERMIN);
  242. break;
  243. case 35:
  244. sec += (5 * SECS_PERHOUR + 45 * SECS_PERMIN);
  245. break;
  246. case 36:
  247. sec += 6 * SECS_PERHOUR;
  248. break;
  249. case 37:
  250. sec += (6 * SECS_PERHOUR + 30 * SECS_PERMIN);
  251. break;
  252. case 38:
  253. sec += 7 * SECS_PERHOUR;
  254. break;
  255. case 39:
  256. sec += 8 * SECS_PERHOUR;
  257. break;
  258. case 40:
  259. sec += 9 * SECS_PERHOUR;
  260. break;
  261. case 41:
  262. sec += (9 * SECS_PERHOUR + 30 * SECS_PERMIN);
  263. break;
  264. case 42:
  265. sec += 10 * SECS_PERHOUR;
  266. break;
  267. case 43:
  268. sec += (10 * SECS_PERHOUR + 30 * SECS_PERMIN);
  269. break;
  270. case 44:
  271. sec += 11 * SECS_PERHOUR;
  272. break;
  273. case 45:
  274. sec += (11 * SECS_PERHOUR + 30 * SECS_PERMIN);
  275. break;
  276. case 46:
  277. sec += 12 * SECS_PERHOUR;
  278. break;
  279. case 47:
  280. sec += (12 * SECS_PERHOUR + 45 * SECS_PERMIN);
  281. break;
  282. case 48:
  283. sec += 13 * SECS_PERHOUR;
  284. break;
  285. case 49:
  286. sec += 14 * SECS_PERHOUR;
  287. break;
  288. }
  289. return sec;
  290. }

NetworkLib.h

  1. #ifndef _NETWORK_LIB_H
  2. #define _NETWORK_LIB_H
  3. #include <stdint.h>
  4. // to help produce better code
  5. // #define _CPU_BIGENDIAN
  6. // or
  7. // #define _CPU_LITTLEENDIAN
  8. // NetworkStream_getUintXX
  9. // description: get uintXX from the network stream ns and return the pointer to the
  10. // next position in the stream.
  11. // parameter : ns the stream to get uintXX.
  12. // ret return the result through the pointer.
  13. // return : the pointer to the next position in the stream.
  14. // note : the interface will handle the byte order for you.
  15. uint8_t const * NetworkStream_getUint16(uint8_t const * ns,uint16_t * ret);
  16. uint8_t const * NetworkStream_getUint32(uint8_t const * ns,uint32_t * ret);
  17. // NetworkStream_putUintXX
  18. // description: put uintXX to the network stream ns and return the pointer to the
  19. // next position in the stream.
  20. // parameter : ns the stream to get uintXX.
  21. // val the value to be put.
  22. // return : the pointer to the next position in the stream.
  23. // note : the interface will handle the byte order for you.
  24. uint8_t * NetworkStream_putUint16(uint8_t * ns,uint16_t val);
  25. uint8_t * NetworkStream_putUint32(uint8_t * ns,uint32_t val);
  26. // whether the cpu is big endian system.
  27. // return : 1 if is big endian.
  28. // 0 if is little endian.
  29. int isBigEndianSys(void);
  30. #ifdef _CPU_BIGENDIAN
  31. #define htonl(h) (h)
  32. #define htons(h) (h)
  33. #define ntohl(n) (n)
  34. #define ntohs(n) (n)
  35. #else
  36. #define htonl(h) NetworkLib_htonl(h)
  37. #define htons(h) NetworkLib_htons(h)
  38. #define ntohl(n) NetworkLib_ntohl(n)
  39. #define ntohs(n) NetworkLib_ntohs(n)
  40. #endif
  41. uint32_t NetworkLib_htonl(uint32_t hostlong);
  42. uint16_t NetworkLib_htons(uint16_t hostshort);
  43. uint32_t NetworkLib_ntohl(uint32_t netlong);
  44. uint16_t NetworkLib_ntohs(uint16_t netshort);
  45. #endif

NetworkLib.c

  1. #include <stdio.h>
  2. #include "NetworkLib.h"
  3. #ifdef _CPU_BIGENDIAN
  4. #define _isBigEndianSys() 1
  5. #endif
  6. #ifdef _CPU_LITTLEENDIAN
  7. #define _isBigEndianSys() 0
  8. #endif
  9. #define BigLittleSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
  10. (((uint16_t)(A) & 0x00ff) << 8))
  11. #define BigLittleSwap32(A) ((((uint32_t)(A) & 0xff000000) >> 24) | \
  12. (((uint32_t)(A) & 0x00ff0000) >> 8) | \
  13. (((uint32_t)(A) & 0x0000ff00) << 8) | \
  14. (((uint32_t)(A) & 0x000000ff) << 24))
  15. #ifndef _isBigEndianSys
  16. #define _isBigEndianSys() (*(uint8_t *)&_flag == 0)
  17. static const uint16_t _flag = 1;
  18. #endif
  19. int isBigEndianSys(void){
  20. return _isBigEndianSys();
  21. }
  22. uint8_t const * NetworkStream_getUint16(uint8_t const * ns,uint16_t * ret){
  23. if(_isBigEndianSys()){
  24. * ret = *(uint16_t *)ns;
  25. }else{
  26. ((uint8_t *)ret)[0] = ns[1];
  27. ((uint8_t *)ret)[1] = ns[0];
  28. }
  29. return ns + sizeof(uint16_t);
  30. }
  31. uint8_t const * NetworkStream_getUint32(uint8_t const * ns,uint32_t * ret){
  32. if(_isBigEndianSys()){
  33. * ret = *(uint32_t *)ns;
  34. }else{
  35. ((uint8_t *)ret)[0] = ns[3];
  36. ((uint8_t *)ret)[1] = ns[2];
  37. ((uint8_t *)ret)[2] = ns[1];
  38. ((uint8_t *)ret)[3] = ns[0];
  39. }
  40. return ns + sizeof(uint32_t);
  41. }
  42. uint8_t * NetworkStream_putUint16(uint8_t * ns,uint16_t val){
  43. if(_isBigEndianSys()){
  44. *(uint16_t *)ns = val;
  45. }else{
  46. ns[0] = ((uint8_t *)&val)[1];
  47. ns[1] = ((uint8_t *)&val)[0];
  48. }
  49. return ns + sizeof(uint16_t);
  50. }
  51. uint8_t * NetworkStream_putUint32(uint8_t * ns,uint32_t val){
  52. if(_isBigEndianSys()){
  53. *(uint32_t *)ns = val;
  54. }else{
  55. ns[0] = ((uint8_t *)&val)[3];
  56. ns[1] = ((uint8_t *)&val)[2];
  57. ns[2] = ((uint8_t *)&val)[1];
  58. ns[3] = ((uint8_t *)&val)[0];
  59. }
  60. return ns + sizeof(uint32_t);
  61. }
  62. uint32_t NetworkLib_htonl(uint32_t hostlong){
  63. if(_isBigEndianSys()){
  64. return hostlong;
  65. }else{
  66. return BigLittleSwap32(hostlong);
  67. }
  68. }
  69. uint16_t NetworkLib_htons(uint16_t hostshort){
  70. if(_isBigEndianSys()){
  71. return hostshort;
  72. }else{
  73. return BigLittleSwap16(hostshort);
  74. }
  75. }
  76. uint32_t NetworkLib_ntohl(uint32_t netlong){
  77. if(_isBigEndianSys()){
  78. return netlong;
  79. }else{
  80. return BigLittleSwap32(netlong);
  81. }
  82. }
  83. uint16_t NetworkLib_ntohs(uint16_t netshort){
  84. if(_isBigEndianSys()){
  85. return netshort;
  86. }else{
  87. return BigLittleSwap16(netshort);
  88. }
  89. }

NTPTimeHelper.h

  1. /*
  2. *******************************************************************************************
  3. *
  4. * NTP TIME HELPER MODULE
  5. *
  6. * File : NTPTimeHelper.h
  7. * By : Lin Shijun(https://blog.csdn.net/lin_strong)
  8. * Date: 2019/09/26
  9. * version: V1.0
  10. * History:
  11. * note : the convert interfaces for NTP time.
  12. *********************************************************************************************
  13. */
  14. #ifndef _NTPTimeHelper_H
  15. #define _NTPTimeHelper_H
  16. #include "NTP.h"
  17. #include <time.h>
  18. typedef struct POSIXTIME_STRUCT{
  19. time_t sec; //秒
  20. uint32_t usec; //微秒
  21. } PosixTime;
  22. void NtpTimeToPosixTime(PosixTime *dst, const NtpTime *src);
  23. void PosixTimeToNtpTime(NtpTime *dst, const PosixTime *src);
  24. #endif

NTPTimeHelper.c

  1. /*
  2. *******************************************************************************************
  3. *
  4. * NTP TIME HELPER MODULE
  5. *
  6. * File : NTPTimeHelper.c
  7. * By : Lin Shijun(https://blog.csdn.net/lin_strong)
  8. * Date: 2019/09/26
  9. * version: V1.0
  10. * History:
  11. * note : the convert interfaces for NTP time.
  12. *********************************************************************************************
  13. */
  14. #include "NTPTimeHelper.h"
  15. #pragma MESSAGE DISABLE C5919
  16. void NtpTimeToPosixTime(PosixTime *dst, const NtpTime *src){
  17. int valid;
  18. if(dst == NULL || src == NULL)
  19. return;
  20. valid = (src->sec >= SECS_1900to1970);
  21. dst->sec = (valid)? src->sec - SECS_1900to1970: 0;
  22. // 1000000/0x100000000 = 0.0002328306436538696...
  23. dst->usec = (valid)? (uint32_t)((double)src->frac * (double)0.0002328306436538696): 0;
  24. }
  25. void PosixTimeToNtpTime(NtpTime *dst, const PosixTime *src){
  26. int valid;
  27. if(dst == NULL || src == NULL)
  28. return;
  29. valid = (src->sec <= (0xFFFFFFFF - SECS_1900to1970));
  30. dst->sec = (valid)? src->sec + SECS_1900to1970: 0;
  31. // 0x100000000/1000000 = 4294.967296
  32. dst->frac = (valid)?(uint32_t)((double)src->usec * 4294.967296): 0;
  33. }

示例

使用起来就是这句话:
初始化一个请求包用UDP发往NTP服务器对应端口,然后等待答复包并解析出结果。

以下示例基于W5500的官方io库+uCOSII,已略去如创建任务、初始化网络等不重要的代码:

  1. #include "NTP.h"
  2. #define UDP_SOCKET 5
  3. static uint8_t NtpServerIP[4] = {
  4. NTPSERVER_IP_CHINA};
  5. static uint8_t udpBuf[200];
  6. static void ConsoleTask (void *p_arg){
  7. ……
  8. for(;;){
  9. c = (uint8_t)getchar();
  10. if(c == 'n'){
  11. (void)NtpMsg_initRequest(udpBuf,NULL);
  12. (void)printf("Send Ntp request\r\n");
  13. (void)sendto(UDP_SOCKET,udpBuf,sizeof(NTPMSG),NtpServerIP,NTP_PORT);
  14. }
  15. }
  16. }
  17. static void UdpRxTask(void *p_arg){
  18. uint8_t dip[4];
  19. uint16_t dport;
  20. int32_t len;
  21. static Datetime dt;
  22. NtpTime t;
  23. (void)p_arg;
  24. for(;;){
  25. // 假设网络已经初始化完成
  26. ……
  27. (void)printf("UDPRx task initing socket.\r\n");
  28. switch(socket(UDP_SOCKET,Sn_MR_UDP,MDNS_PORT,0)){
  29. case SOCKERR_SOCKINIT:
  30. (void)printf("UDPRx init socket fail, network not ready.\r\n");
  31. (void)OSTimeDlyHMSM(0,0,2,0);
  32. continue;
  33. case UDP_SOCKET:
  34. (void)printf("UDPRx init socket success.\r\n");
  35. break;
  36. default:
  37. (void)printf("UDPRx init socket fail, unknown error.\r\n");
  38. (void)OSTimeDlyHMSM(0,0,2,0);
  39. continue;
  40. }
  41. for(;;){
  42. (void)printf("UDPRx task is wating for data!\r\n");
  43. len = recvfrom(UDP_SOCKET,udpBuf,sizeof(udpBuf),dip,&dport);
  44. if(len > 0){
  45. (void)printf("UDPRx received data from %u.%u.%u.%u:%u\r\n",dip[0], dip[1], dip[2], dip[3],dport);
  46. if(len >= sizeof(NTPMSG) && dport == NTP_PORT){
  47. printf("NTP packet.\r\n");
  48. NtpMsg_resolveCurTimeSNTP((NTPMSG *)udpBuf, &t);
  49. NtpTime_toDatetime(t.sec,&dt);
  50. printf("%4u-%2u-%2u %2u:%2u:%2u\r\n",dt.yy,dt.mo,dt.dd,dt.hh,dt.mm,dt.ss);
  51. }
  52. }else if(len < 0){
  53. (void)printf("UDPRx found socket err.\r\n");
  54. break;
  55. }
  56. }
  57. }
  58. }

因为是独立的模块,你可以根据自己的需求自己决定如何整合NTP功能至项目中,比如,其实可以多个基于UDP的功能(比如DNS,其实我也写了,可能后面会发)都使用同一个UDP SOCKET,通过对方的IP和/或端口号来区分是哪个功能。

测试结果:
在这里插入图片描述至于后面那个TimeHelper,则是用于NTP时间戳和Unix时间戳互转的接口。当你需要使用time.h时自然明白它的作用了。就不示例了。

更新历史

2019/05/29 第一版
2019/10/02 增加NTPTimeHelper模块。并更新了NetworkLib,增加了点功能。

发表评论

表情:
评论列表 (有 0 条评论,438人围观)

还没有评论,来说两句吧...

相关阅读