1064|1

862

帖子

2

TA的资源

纯净的硅(初级)

楼主
 

【得捷Follow me第4期】W5500-EVB-Pico PIO通过NTP服务器获取网络时间 [复制链接]

  本帖最后由 wo4fisher 于 2024-3-5 13:42 编辑

1. 可用的NTP服务器测试

使用如下的命令可以测试NTP授时服务器是否可用

  • w32tm /stripchart /computer:ntp_server_address

ntp_server_address 可以是服务器主机名称,也可以是IP地址。

可用的NTP服务器地址:

国家授时中心 NTP 服务器      ntp.ntsc.ac.cn        114.118.7.161                114.118.7.163

中国 NTP 快速授时服务         cn.ntp.org.cn           223.113.97.98                119.29.26.206

教育网                                    edu.ntp.org.cn        202.118.1.130                 202.118.1.81

2. 新建基本工程并添加Ethernet3库文件到工程lib目录

3. 代码编辑  

  • /*
  • Udp NTP Client
  • Get the time from a Network Time Protocol (NTP) time server
  • Demonstrates use of UDP sendPacket and ReceivePacket
  • For more on NTP time servers and the messages needed to communicate with them,
  • see http://en.wikipedia.org/wiki/Network_Time_Protocol
  • created 4 Sep 2010
  • by Michael Margolis
  • modified 9 Apr 2012
  • by Tom Igoe
  • modified 02 Sept 2015
  • by Arturo Guadalupi
  • This code is in the public domain.
  • */
  • #include <Arduino.h>
  • #include <SPI.h>
  • #include <Ethernet3.h>
  • #include <EthernetUdp3.h>
  • #define FOURYEARDAY (365 + 365 + 365 + 366) // 4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化)
  • #define TIMEZONE (8) // 北京时区调整
  • typedef struct rtc_time_struct
  • {
  • uint16_t ui8Year; // 1970~2038
  • uint8_t ui8Month; // 1~12
  • uint8_t ui8DayOfMonth; // 1~31
  • uint8_t ui8Week;
  • uint8_t ui8Hour; // 0~23
  • uint8_t ui8Minute; // 0~59
  • uint8_t ui8Second; // 0~59
  • } rtc_time_t;
  • static uint8_t month_day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 平年
  • static uint8_t Leap_month_day[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 闰年
  • // static char weekCHS[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  • // Enter a MAC address for your controller below.
  • // Newer Ethernet shields have a MAC address printed on a sticker on the shield
  • byte mac[] = {
  • 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  • unsigned int localPort = 8888; // local port to listen for UDP packets
  • const char timeServer[] = "ntp.ntsc.ac.cn"; // time.nist.gov NTP server
  • const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
  • byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
  • // A UDP instance to let us send and receive packets over UDP
  • EthernetUDP Udp;
  • // 判断是否是闰年 return:1:闰年, 0: 平年
  • uint8_t isLeapYear(uint16_t year);
  • // 将Unix时间戳转换为北京时间
  • void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing);
  • void setup()
  • {
  • // Open serial communications and wait for port to open:
  • Serial.begin(115200);
  • while (!Serial)
  • {
  • ; // wait for serial port to connect. Needed for native USB port only
  • }
  • Ethernet.setCsPin(17);
  • Ethernet.setRstPin(20);
  • // start Ethernet and UDP
  • if (Ethernet.begin(mac) == 0)
  • {
  • Serial.println("Failed to configure Ethernet using DHCP");
  • // no point in carrying on, so do nothing forevermore:
  • for (;;)
  • ;
  • }
  • Udp.begin(localPort);
  • }
  • void sendNTPpacket(const char *address);
  • void loop()
  • {
  • sendNTPpacket(timeServer); // send an NTP packet to a time server
  • // wait to see if a reply is available
  • delay(1000);
  • if (Udp.parsePacket())
  • {
  • // We've received a packet, read the data from it
  • Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  • // the timestamp starts at byte 40 of the received packet and is four bytes,
  • // or two words, long. First, extract the two words:
  • unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
  • unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
  • // combine the four bytes (two words) into a long integer
  • // this is NTP time (seconds since Jan 1 1900):
  • unsigned long secsSince1900 = highWord << 16 | lowWord;
  • Serial.print("Seconds since Jan 1 1900 = ");
  • Serial.println(secsSince1900);
  • // now convert NTP time into everyday time:
  • Serial.print("Unix time = ");
  • // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
  • const unsigned long seventyYears = 2208988800UL;
  • // subtract seventy years:
  • unsigned long epoch = secsSince1900 - seventyYears;
  • // print Unix time:
  • Serial.println(epoch);
  • // print the date & Time
  • Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
  • rtc_time_t tempBeijing2;
  • char dtBuf[30] = {0};
  • covUnixTimeStp2Beijing(epoch, &tempBeijing2);
  • sprintf(dtBuf, "%d/%02d/%02d-%02d:%02d:%02d",
  • tempBeijing2.ui8Year, tempBeijing2.ui8Month, tempBeijing2.ui8DayOfMonth,
  • tempBeijing2.ui8Hour, tempBeijing2.ui8Minute, tempBeijing2.ui8Second);
  • Serial.println(dtBuf);
  • }
  • // wait ten seconds before asking for the time again
  • delay(6000);
  • Ethernet.maintain();
  • }
  • // send an NTP request to the time server at the given address
  • void sendNTPpacket(const char *address)
  • {
  • // set all bytes in the buffer to 0
  • memset(packetBuffer, 0, NTP_PACKET_SIZE);
  • // Initialize values needed to form NTP request
  • // (see URL above for details on the packets)
  • packetBuffer[0] = 0b11100011; // LI, Version, Mode
  • packetBuffer[1] = 0; // Stratum, or type of clock
  • packetBuffer[2] = 6; // Polling Interval
  • packetBuffer[3] = 0xEC; // Peer Clock Precision
  • // 8 bytes of zero for Root Delay & Root Dispersion
  • packetBuffer[12] = 49;
  • packetBuffer[13] = 0x4E;
  • packetBuffer[14] = 49;
  • packetBuffer[15] = 52;
  • // all NTP fields have been given values, now
  • // you can send a packet requesting a timestamp:
  • Udp.beginPacket(address, 123); // NTP requests are to port 123
  • Udp.write(packetBuffer, NTP_PACKET_SIZE);
  • Udp.endPacket();
  • }
  • // 判断是否是闰年
  • // year: 需要判断的年
  • // return:1:闰年
  • // 0: 平年
  • uint8_t isLeapYear(uint16_t year)
  • {
  • uint8_t res = 0;
  • if (year % 4 == 0) // 能够被4整除
  • {
  • if ((year % 100 == 0) && (year % 400 != 0)) // 能够被100整除,但是不能够被400整除
  • {
  • res = 0;
  • }
  • else
  • {
  • res = 1;
  • }
  • }
  • return res;
  • }
  • // 将Unix时间戳转换为北京时间
  • // unixTime: 需要判断的Unix时间戳
  • // *tempBeijing:返回的北京时间
  • // return:none
  • // note:没对输入参数正确性做判断
  • void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing)
  • {
  • uint32_t totleDayNum = 0, totleSecNum = 0;
  • uint16_t remainDayofYear = 0, tempYear = 0;
  • uint8_t *pr = NULL;
  • totleDayNum = unixTime / (24 * 60 * 60); // 总天数(注意加括号)
  • totleSecNum = unixTime % (24 * 60 * 60); // 当天剩余的秒速
  • memset(tempBeijing, 0x00, sizeof(rtc_time_t));
  • // 1.先计算时间 HH:MM:SS
  • tempBeijing->ui8Hour = totleSecNum / 3600;
  • tempBeijing->ui8Minute = (totleSecNum % 3600) / 60; // error:变量搞错
  • tempBeijing->ui8Second = (totleSecNum % 3600) % 60;
  • // 2.对时间进行时区调整(注意:这里可能造成日期 +1)
  • tempBeijing->ui8Hour += TIMEZONE;
  • if (tempBeijing->ui8Hour > 23)
  • {
  • // printf("modify day..\n");
  • tempBeijing->ui8Hour -= 24;
  • remainDayofYear++; // 日期+1
  • }
  • // 3.计算哪一年
  • tempBeijing->ui8Year = 1970 + (totleDayNum / FOURYEARDAY) * 4; // 4年为一个周期
  • remainDayofYear += totleDayNum % FOURYEARDAY;
  • // printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
  • tempYear = isLeapYear(tempBeijing->ui8Year) ? 366 : 365;
  • while (remainDayofYear >= tempYear) // 计算4年整数倍外的年。
  • {
  • tempBeijing->ui8Year++;
  • remainDayofYear -= tempYear;
  • tempYear = isLeapYear(tempBeijing->ui8Year) ? 366 : 365;
  • }
  • // 4.计算哪一月的哪一天
  • pr = isLeapYear(tempBeijing->ui8Year) ? Leap_month_day : month_day;
  • remainDayofYear++; // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号,所以这里要 +1
  • while (remainDayofYear > *(pr + tempBeijing->ui8Month))
  • {
  • remainDayofYear -= *(pr + tempBeijing->ui8Month);
  • tempBeijing->ui8Month++;
  • }
  • // printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
  • tempBeijing->ui8Month++; // month
  • tempBeijing->ui8DayOfMonth = remainDayofYear; // day
  • // printf("year:%d, day:%d.\n", tempBeijing->ui8Year, tempBeijing->ui8DayOfMonth);
  • }

主要步骤:

第一步:定义mac地址、本地端口、ntp服务器,并且定义UDP客户端EthernetUDP实例。

第二步:在初始化函数setup中使用Ethernet.begin函数初始化W5500网络参数,然后启动udp客户端: Udp.begin(localPort)。

第三步:在Loop函数中 首先向NTP服务器发送请求包sendNTPpacket(),然后解析接收到的应答数据包,并且分别显示自1900年开始的秒数、UNIX时间(即从1970年1月1日开始的秒数)和NTC时间的时分秒。

备注:中间有增加unix时间戳计算当前日期和时间的函数,没有计算星期几。

 

 结果,NTP时间和右下角显示时间一致

最新回复

结果,NTP时间和右下角显示时间一致 恭喜完成任务。   详情 回复 发表于 2024-3-9 16:21
点赞 关注
个人签名水不撩不知深浅 人不拼怎知输赢
 
 

回复
举报

7143

帖子

11

TA的资源

版主

沙发
 

结果,NTP时间和右下角显示时间一致

恭喜完成任务。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
福禄克有奖直播:高精度测温赋能电子制造 报名中!
直播时间:2025年2月28日(周五)上午10:00
直播主题:高精度测温赋能电子制造
小伙伴们儿快来报名直播吧~好礼等你拿!

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表