全部例程

W6300从网络获取时间

W6300

更新于 2026年10月9日

本篇总结

在 W6300 上实现 SNTP 授时功能,从 SNTP 服务器获取准确时间并打印北京时间。

🔑 MH2457、QSPI、UART调试、硬件TOE引擎、SNTP、DNS、DHCP

硬件平台:本例程基于 W6300-EVB-M4 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

本示例演示如何在 W6300 上实现 SNTP 授时功能,通过实战例程讲解如何让 W6300 从 SNTP 服务器获取准确的实际时间。该例程用到的其他网络协议(例如 DHCP、DNS),请参考相关章节。有关 W6300 的初始化过程,请参考 Network install 章节,这里不再赘述。

前置工具准备

开始本例程前,请确认以下工具已安装:

SNTP 协议简介

SNTP(Simple Network Time Protocol)是一种基于 UDP 协议的网络时间协议,主要用于在计算机网络中同步设备的时间。SNTP 旨在提供简单的时间校准服务,相比 NTP(Network Time Protocol),SNTP 功能更为简单,精度相对较低。

SNTP 协议特点:

  • 使用 UDP 通信:SNTP 使用 UDP 协议在端口 123 进行通信
  • 支持请求-响应模式:单次请求-响应完成时间同步——客户端发送请求,服务器返回当前时间戳
  • 实现和部署成本低:实现简单,通常只需要少量代码,便于嵌入式系统集成
  • 与 NTP 兼容:SNTP 是 NTP 的子集,客户端可与 NTP 服务器通信以获取时间
  • 支持单向时间同步:特定场景下可仅基于服务器时间戳完成时间同步,无需计算网络延迟

SNTP 应用场景

通过将时间同步到本地时钟,实现:协同工作(各模块按预定时间顺序执行任务)、日志与事件管理(确保日志时间和事件记录准确)、定时任务(支持定时任务准确执行)。

时区介绍

通过 SNTP 获取世界标准时间(UTC)后,需要根据所在时区进行加减运算以计算当地时间。例如中国位于 UTC+8 时区,在示例代码中定义为 39。因此获取 UTC 后需加 8 小时才能转换为中国时间。

SNTP 协议同步时间的基本流程

  1. 客户端发送时间请求:客户端向 SNTP 服务器发送请求数据包,并记录 T1 时间戳信息
  2. 服务器接收请求并处理:服务器记录接收时间 T2,生成响应数据包,包含 T2(接收时间)和 T3(发送时间)
  3. 客户端接收响应并计算时间:客户端提取 T2、T3,记录接收时间 T4,计算网络延迟和本地时间校准值
  4. 调整本地时间:客户端根据校准时间调整本地时钟,同步到服务器时间

时间戳定义

时间戳的定义是指从 1900 年 1 月 1 日 00:00:00 UTC 开始的秒数。SNTP 的发送和接收报文为固定结构,通常为 48 字节。

硬件连接与拓扑

请注意:

本示例需要访问互联网,请确保 W6300 的网络环境及配置能够正常访问互联网。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口,路由器需连接互联网
  3. 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
┌───────────┐ ┌─────────────────┐ ┌──────────────────┐
│ W6300 │──网线──│ 路由器/网关 │──互联网──│ SNTP 服务器 │
│ (SNTP │ │ (DHCP/DNS) │ │ cn.pool.ntp.org │
│ Client) │ │ 192.168.1.1 │ │ UDP :123 │
└────┬──────┘ └─────────────────┘ └──────────────────┘
│ QSPI
┌────┴──────┐
│ MH2457 │──────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:W6300 通过路由器接入互联网,从 SNTP 服务器获取时间,MH2457 通过 QSPI 驱动 W6300

工程结构

工程核心文件如下:

Project/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c           ← 网络地址定义 + SNTP 服务器 + user_run() 主流程
    ├── sntp.c                ← SNTP 协议实现(SNTP_init / SNTP_run / calcdatetime)
    ├── do_dns.c              ← DNS 域名解析
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    └── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器

步骤1:通过 DNS 解析 SNTP 服务器域名

文件:USER/user_main.c — 使用 do_dns() 将 SNTP 服务器域名 cn.pool.ntp.org 解析为 IP 地址。

uint8_t sntp_server_name[] = "cn.pool.ntp.org";
uint8_t sntp_server_ip[4]  = {0, 0, 0, 0};

if (do_dns(ethernet_buf, sntp_server_name, sntp_server_ip))
{
    if (sntp_server_ip[0] != 0U)
    {
        printf("Use Default Server Internet Protocol Address:%d.%d.%d.%d\r\n",
               sntp_server_ip[0], sntp_server_ip[1], sntp_server_ip[2], sntp_server_ip[3]);
    }
    else
    {
        printf("No default server address, unable to connect to the server\r\n");
        while (1) { }
    }
}

do_dns() 将域名解析为 IP 写入 sntp_server_ip。若返回非 0 表示解析失败或未获取到有效 IP,此时程序进入死循环等待,防止后续使用空地址发起 SNTP 请求。W6300 平台的 do_dns() 内部通过软件定时器链表注册 DNS_time_handler() 提供时基。

步骤2:SNTP 初始化

文件:USER/sntp.c | 函数:SNTP_init() — 传入 Socket 通道号、SNTP 服务器地址、时区和缓存,完成 SNTP 报文组包并注册。

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;
    NTPformat.poll      = 0;
    NTPformat.precision = 0;
    NTPformat.rootdelay = 0;
    NTPformat.rootdisp  = 0;
    NTPformat.refid     = 0;
    NTPformat.reftime   = 0;
    NTPformat.org       = 0;
    NTPformat.rec       = 0;
    NTPformat.xmt       = 1;   /* transmit timestamp */

    Flag = (NTPformat.leap<<6)+(NTPformat.version<<3)+NTPformat.mode;
    memcpy(ntpmessage,(void const*)(&Flag),1);
}

在主程序中调用 SNTP_init(SOCKET_ID, sntp_server_ip, timezone, ethernet_buf),其中 timezone = 39U 对应中国 UTC+8 时区,ethernet_buf 兼作 SNTP 收发缓存。

关键参数

version=4(NTP v4)、mode=3(客户端模式)、Flag 字节由 leap、version、mode 拼接而成。

步骤3:发送 SNTP 请求报文并解析响应

文件:USER/sntp.c | 函数:SNTP_run() — 运行 UDP 状态机:SOCK_CLOSED 时打开 Socket,SOCK_UDP 时发送请求并监听响应,收到响应后解析时间戳并转换为日期。

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
    uint8_t  addr_len;

    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;
            recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport, &addr_len);

            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)
            {
                sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage),
                       NTPformat.dstaddr, ntp_port, 4);
                ntp_retry_cnt++;
            }
            else
            {
                if((ntp_retry_cnt % 0xFFF) == 0)
                {
                    sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage),
                           NTPformat.dstaddr, ntp_port, 4);
                    ntp_retry_cnt++;
                }
            }
        }
        else
        {
            ntp_retry_cnt = 0;
            close(NTP_SOCKET);
        }
        break;
    case SOCK_CLOSED:
        socket(NTP_SOCKET, Sn_MR_UDP, ntp_port, 0);
        break;
    }
    return 0;
}

关键点:SNTP 响应报文 48 字节,最后 8 字节是服务器发送时间戳(xmt),因此 startindex = 40。get_seconds_from_ntp_server() 解析时间戳并按时区偏移,calcdatetime() 转换为年/月/日/时/分/秒。W6300 平台的 recvfrom() / sendto() 需要额外传入地址长度参数(IPv4 为 4)。

时区处理

get_seconds_from_ntp_server() 里通过 time_zone 变量对 UTC 秒数进行偏移。中国 UTC+8 对应 time_zone = 39,此时 seconds += 8*3600。

步骤4:主循环获取时间并打印

文件:USER/user_main.c — 循环调用 SNTP_run() 直到获取时间成功,然后打印北京时间并退出循环。

SNTP_init(SOCKET_ID, sntp_server_ip, timezone, ethernet_buf);

while (1)
{
    if (SNTP_run(&date))
    {
        printf("Beijing time now: %04d-%02d-%02d  %02d:%02d:%02d\r\n",
               date.yy, date.mo, date.dd, date.hh, date.mm, date.ss);
        break;
    }
}

与 W55MH32 例程不同,W6300 版本没有 RTC 硬件,获取到时间后直接打印,然后退出循环。如果需要周期更新时间或持续打印,可以去掉 break 并加入定时逻辑。

运行验证

打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先进行 PHY 链路检测和 DHCP 获取网络地址,然后 DNS 解析 SNTP 服务器,获取时间并打印。预期串口输出如下:

MH2457 W6300 QSPI example start
W6300 SNTP example
PHY link

The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.8
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================

> Translated cn.pool.ntp.org to 139.199.215.251
Beijing time now: 2026-10-09  10:58:41

SNTP 获取成功后,串口打印 Beijing time now: 2026-10-09 10:58:41,表示已成功从 SNTP 服务器获取当前时间并转换为北京时间。

WIZ UartTool 串口输出 — SNTP 授时成功,打印北京时间

常见问题

Q: SNTP 和 NTP 有什么区别

SNTP 是 NTP 的简化版本,功能更简单、精度相对较低,但实现和部署成本低,适合嵌入式系统。NTP 精度更高,但协议复杂,需要更多计算资源。SNTP 是 NTP 的子集,可以与 NTP 服务器通信获取时间。

Q: 为什么 SNTP 使用 UDP 而不是 TCP

SNTP 采用单次请求-响应模式,报文小(48 字节),使用 UDP 可以降低协议开销,避免 TCP 三次握手的延迟。SNTP 只需要一次请求和一次响应即可完成时间同步,UDP 的无连接特性恰好满足这种需求。

Q: 时区参数 time_zone 为什么是 39 而不是 8

time_zone 不是时区小时数,而是代码中定义的时区编号。代码里维护了一张从 UTC-12 到 UTC+14 的完整时区表,中国 UTC+8 对应表中第 39 项,因此传入 39。这个设计便于覆盖全球所有时区(包括半小时、45 分钟时区)。

Q: SNTP 获取时间失败怎么办

排查步骤:① 确认开发板能访问互联网;② 确认 DNS 解析成功(串口打印 > Translated cn.pool.ntp.org to ...);③ 检查 123 端口是否被防火墙拦截;④ 更换其他 SNTP 服务器(如 time.windows.com);⑤ 检查 QSPI 连线是否牢固、W6300 是否正常初始化。

总结

本文讲解了如何在 W6300 芯片上实现 SNTP 授时功能:

  • SNTP 协议:基于 UDP 123 端口的简化网络时间协议,单次请求-响应完成时间同步,实现简单、资源占用少
  • 时区处理:SNTP 返回 UTC 时间,需按时区偏移计算当地时间,中国 UTC+8 对应 time_zone = 39
  • 初始化流程:DNS 解析 SNTP 服务器 → SNTP_init 组包 → SNTP_run 循环获取 → 打印北京时间
  • 关键 API:SNTP_init() 初始化、SNTP_run() 循环获取、calcdatetime() 时间戳转日期
  • W6300 平台特有:无 RTC 硬件,获取时间后直接打印;recvfrom/sendto 需传地址长度;DNS 时基通过软件定时器链表提供
  • 应用价值:为日志时间戳、定时任务、多模块协同提供准确的本地时间基准

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5

Andy

W5500 技术支持专家