921|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
点赞 关注
个人签名水不撩不知深浅 人不拼怎知输赢
 
 

回复
举报

6841

帖子

11

TA的资源

版主

沙发
 

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

恭喜完成任务。

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

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

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