W5500-EVB-PICO通过SNTP获取网络时间(十一)

前言

        上一章我们用W5500_EVB_PICO 开发板做Ping数据测试IP检测连通性,那么本章我们进行W5500_EVB_PICO SNTP的测试。

什么是NTP?

        NTP(Network Time Protocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。提供NTP对时的服务器有很多,比如微软的NTP对时服务器,利用NTP服务器提供的对时功能,可以使我们的设备时钟系统能够正确运行。

NTP报文格式

W5500-EVB-PICO通过SNTP获取网络时间(十一)_第1张图片

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。

认证符(可选项)

连接方式

开发板和主机都接在路由器LAN口

获取网络时间测试

1.相关代码

        我们打开例程中库文件的sntp_client.c文件,可以看到两个有关于ntp的两个函数,分别是SNTP_init函数和SNRTP_run函数,SNTP_init函数第一个参数是socket号,第二参数是获取时间的服务器ip,第三个参数是所要获取时间的时区(中国是东八区,sntp.c文件中能找到对应编号),第四个参数是缓存buf;然后SNTP_run的函数是传入一个datetime结构类型的参数,结构体里面有相应的参数。

void SNTP_init(uint8_t s, uint8_t *ntp_server, uint8_t tz, uint8_t *buf)
{
    NTP_SOCKET = s;

    NTPformat.dstaddr[0] = ntp_server[0];
    NTPformat.dstaddr[1] = ntp_server[1];
    NTPformat.dstaddr[2] = ntp_server[2];
    NTPformat.dstaddr[3] = ntp_server[3];
    time_zone = tz;
    data_buf = buf;
    uint8_t Flag;
    NTPformat.leap = 0;           /* leap indicator */
    NTPformat.version = 4;        /* version number */
    NTPformat.mode = 3;           /* mode */
    NTPformat.stratum = 0;        /* stratum */
    NTPformat.poll = 0;           /* poll interval */
    NTPformat.precision = 0;      /* precision */
    NTPformat.rootdelay = 0;      /* root delay */
    NTPformat.rootdisp = 0;       /* root dispersion */
    NTPformat.refid = 0;          /* reference ID */
    NTPformat.reftime = 0;        /* reference time */
    NTPformat.org = 0;            /* origin timestamp */
    NTPformat.rec = 0;            /* receive timestamp */
    NTPformat.xmt = 1;            /* transmit timestamp */
    Flag = (NTPformat.leap<<6)+(NTPformat.version<<3)+NTPformat.mode; //one byte Flag
    memcpy(ntpmessage,(void const*)(&Flag),1);
}
int8_t SNTP_run(datetime *time)
{
    uint16_t RSR_len;
    uint32_t destip = 0;
    uint16_t destport;
    uint16_t startindex = 40; //last 8-byte of data_buf[size is 48 byte] is xmt, so the startindex should be 40
    switch(getSn_SR(NTP_SOCKET))
    {
    case SOCK_UDP:
        if ((RSR_len = getSn_RX_RSR(NTP_SOCKET)) > 0)
        {
            if (RSR_len > MAX_SNTP_BUF_SIZE) RSR_len = MAX_SNTP_BUF_SIZE;   // if Rx data size is lager than TX_RX_MAX_BUF_SIZE
            recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport);
            get_seconds_from_ntp_server(data_buf,startindex);
            time->yy = Nowdatetime.yy;
            time->mo = Nowdatetime.mo;
            time->dd = Nowdatetime.dd;
            time->hh = Nowdatetime.hh;
            time->mm = Nowdatetime.mm;
            time->ss = Nowdatetime.ss;
            ntp_retry_cnt=0;
            close(NTP_SOCKET);
            return 1;
        }
        if(ntp_retry_cnt<0xFFFF)
        {
            if(ntp_retry_cnt==0)//first send request, no need to wait
            {
                sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
                ntp_retry_cnt++;
            }
            else // send request again? it should wait for a while
            {
                if((ntp_retry_cnt % 0xFFF) == 0) //wait time
                {
                    sendto(NTP_SOCKET,ntpmessage,sizeof(ntpmessage),NTPformat.dstaddr,ntp_port);
#ifdef _SNTP_DEBUG_
                    printf("ntp retry: %d\r\n", ntp_retry_cnt);
#endif
                    ntp_retry_cnt++;
                }
            }
        }
        else //ntp retry fail
        {
            ntp_retry_cnt=0;
#ifdef _SNTP_DEBUG_
            printf("ntp retry failed!\r\n");
#endif
            close(NTP_SOCKET);
        }
        break;
    case SOCK_CLOSED:
        socket(NTP_SOCKET,Sn_MR_UDP,ntp_port,0);
        break;
    }
    // Return value
    // 0 - failed / 1 - success
    return 0;
}

         datetime类型结构体

typedef struct _datetime
{
    uint16_t yy;
    uint8_t mo;
    uint8_t dd;
    uint8_t hh;
    uint8_t mm;
    uint8_t ss;
} datetime;

2.测试现象

编译烧录完成之后打开串行监视器,复位一下W5500_EVB_PICO,然后串口上就会开始打印网络配置信息,然后开始获取时间,看到串口上一秒打印一次时间,表示成功。

W5500-EVB-PICO通过SNTP获取网络时间(十一)_第2张图片

相关连接:

本章例程链接

你可能感兴趣的:(WIZnet,W5500-EVB-Pico,C/C++教程,w5500,以太网芯片,树莓派RP2040,wiznet,以太网,嵌入式设备入网方案)