本文为 W55RP20-EVB-MKR模块 MicroPython 教程专项篇,基于官方最新固件编写,代码均经过实际验证,可直接烧录运行。 版权声明:本文为 WIZnet 官方原创技术文章,转载请注明出处。

前言

上一篇实战教程,我们已经完成了 W55RP20 芯片 DNS 域名解析与公网 IP 映射功能开发。

本篇内容我们聚焦嵌入式设备精准计时的核心技术 ——NTP 网络时间同步

NTP(网络时间协议)是物联网领域通用的时间同步协议,默认基于 UDP 123 端口通信,可让设备自动获取全球标准 UTC 时间,解决本地时钟漂移、断电清零、手动校准繁琐等问题,非常适合嵌入式以太网设备的日志打点、定时任务、多设备时序同步场景。

W55RP20 集成硬件 TCP/IP 协议栈,搭配 MicroPython 开发环境,几行代码即可实现稳定 NTP 时间同步,无需关心底层 Socket 细节,自动完成时区转换与定时重同步防漂移。

  • NTP 协议核心原理与时间戳换算机制
  • W55RP20 通过公网 NTP 服务器获取标准时间
  • 实现 UTC 时间转北京时间时区校准
  • 本地走时 + 定时重同步防漂移实现
  • NTP 请求异常处理与常见问题排查
  • 嵌入式设备精准计时的工业级实现方案

系列教程学习路径

本专栏共 15 篇,循序渐进覆盖 W55RP20-EVB-MKR模块 MicroPython 开发全流程:

1.第 1 篇:静态 IP 配置与网络基础

2.第 2 篇:DHCP 自动联网与网络诊断

3.第 3 篇:TCP Client 客户端通信

4.第 4 篇:TCP Server 服务端通信

5.第 5 篇:UDP 单播数据通信

6.第 6 篇:UDP 组播/广播数据通信

7.第 7 篇:DNS 域名解析

8.第 8 篇:NTP 从网络获取时间(本文)

9.第 9 篇:HTTP Client 客户端请求

10.第 10 篇:HTTP Server 服务端搭建

11.第 11 篇:HTTP 协议与 OneNET 平台数据上云

12.第 12 篇:MQTT 协议基础通信验证

13.第 13 篇:MQTT 协议与阿里云平台对接

14.第 14 篇:MQTT 协议与 OneNET 平台对接

15.第 15 篇:MQTT 协议与 ThingSpeak 平台对接

16.第 16 篇:Modbus 工业协议通信

建议收藏本专栏,跟随教程逐步学习,所有代码均会同步更新至官方 Gitee 仓库。

目录

 1. 准备工作

1.1 软件准备

1.2 硬件准备

2. 烧录 W55RP20-EVB-MKR模块专属 MicroPython 固件

3. 硬件连接与开发环境配置

3.1 硬件连接

3.1.1 基础连接(供电+调试)

3.1.2 以太网连接

3.1.3 模块与开发板接线

3.2 Thonny 开发环境配置

4. NTP 协议核心原理

4.1 NTP 协议简介

4.2 时间戳换算规则

4.3 时区说明

4.4 NTP 工作流程

5. 核心代码解析

5.1 完整可运行代码

5.2 代码功能说明

6. 运行结果与测试验证

7. 常见问题一站式排查

8. WIZnet 硬件协议栈核心优势对比

9. 典型应用场景

10. 系列预告与资源获取

10.1 系列预告

10.2 资源获取

 1. 准备工作

1.1 软件准备

所需软件均为免费版本,按要求下载安装即可,无需额外付费。

软件名称

版本要求

下载地址

说明

Thonny

4.0 及以上

Thonny 官方下载

轻量级 MicroPython IDE,支持代码编辑、烧录与串口调试,新手友好

W55RP20-EVB-MKR模块 MicroPython 固件

最新稳定版

WIZnet 官方固件下载

专为 W55RP20-EVB-MKR模块 编写,已集成 WIZnet 硬件驱动与协议栈

1.2 硬件准备

  • W55RP20-EVB-MKR × 1
  • Micro USB 数据线(必须支持数据传输,不能使用纯充电线)× 1
  • 标准网线 × 1
  • 开启 DHCP 功能的路由器 / 交换机 × 1(用于获取网络参数,实现 DNS 解析)

W55RP20-EVB-MKR模块已集成以太网相关器件,无需额外焊接飞线,配合 RP2040 开发板可快速搭建开发环境,大幅降低接线错误和硬件故障概率。

2. 烧录 W55RP20-EVB-MKR模块专属 MicroPython 固件

W55RP20-EVB-MKR模块 完全兼容树莓派 MKR的 UF2 固件烧录方式,操作简单无需额外烧录器,新手可快速上手:

  1. 按住 RP2040 开发板上的 BOOTSEL 按键不放;
  2. 使用 Micro USB 数据线连接开发板与电脑;
  3. 待电脑识别出名为 RPI-RP2 的 U 盘后,松开 BOOTSEL 按键;
  4. 将下载好的 W5500_RP2040_firmware.uf2 固件文件拖拽到 U 盘中;
  5. 开发板会自动重启,固件烧录完成。

注意:如果电脑没有识别出 RPI-RP2 U 盘,请尝试更换 USB 数据线、重新插拔开发板,或更换电脑 USB 接口(优先使用 USB 2.0 接口)。

3. 硬件连接与开发环境配置

3.1 硬件连接

W55RP20-EVB-MKR模块连接分为两步,分别实现供电/调试和以太网连接,操作简单,无需复杂接线:

3.1.1 基础连接(供电+调试)

使用 Micro USB 数据线连接 RP2040 开发板与电脑,用于开发板供电、代码烧录和串口调试。

3.1.2 以太网连接

使用网线连接 W55RP20-EVB-MKR模块的以太网接口与路由器的 LAN 口(或直接连接电脑网口,需手动配置电脑 IP 与开发板同网段)。

3.1.3 模块与开发板接线

若使用分离式模块与开发板,需按以下引脚对应连接(SPI 通信):

【硬件预留】此处插入硬件连接示意图

3.2 Thonny 开发环境配置

打开 Thonny 软件,按以下步骤配置开发环境,确保代码能正常烧录和运行:

  1. 点击顶部菜单栏「运行」→「配置解释器」;
  2. 切换到「解释器」选项卡;
  3. 在「解释器」下拉列表中选择 MicroPython (通用);
  4. 在「端口」下拉列表中选择开发板对应的串口(通常显示为 Board CDC @ COMx);
  5. 勾选「运行代码前先重启解释器」和「同步设备的实时时钟」;
  6. 点击「确定」完成配置。

如果端口列表中没有出现开发板,请尝试:

  • 重新插拔 USB 数据线;

  • 更换支持数据传输的 USB 数据线;

  • 关闭其他占用串口的软件(如串口助手、Arduino IDE 等);

  • 重新烧录 MicroPython 固件;

  • 安装树莓派 MKRUSB 驱动。

4. NTP 协议核心原理

4.1 NTP 协议简介

NTP(Network Time Protocol)网络时间协议,是嵌入式、物联网领域通用的时间同步协议,默认使用 UDP 123 端口 进行通信。设备通过向公网 NTP 服务器发送请求数据包,获取全球统一标准时间,解决本地时钟不准、时间漂移问题。

4.2 时间戳换算规则

  • NTP 原始时间:起始年份 1900-01-01
  • Unix 标准时间戳:起始年份 1970-01-01
  • 固定时间偏移量:用于两种时间格式相互转换

4.3 时区说明

NTP 服务器默认返回 UTC 零时区 时间,北京时间为 UTC+8,程序内部自动增加 8 小时时差,直接输出本地标准时间。

4.4 NTP 工作流程

  1. 开发板通过 DHCP 自动获取 IP,完成网络联网;
  2. 构建标准 NTP 请求报文,通过 UDP 发送至 NTP 服务器;
  3. 服务器返回 48 字节标准时间数据包;
  4. 解析数据包提取 NTP 时间戳,转换为 Unix 时间;
  5. 程序计算时区偏差,格式化输出年月日时分秒;
  6. 本地循环走时,每分钟自动重同步,抑制时间漂移。

5. 核心代码解析

5.1 完整可运行代码

from wiznet_init import wiznet
import usocket
import struct
import time

# 初始化网络
nic = wiznet("W55RP20-EVB-Pico", dhcp=True)
print("IP:", nic.ifconfig()[0])

# NTP服务器(阿里云稳定IP)
NTP_IP = "120.25.115.20"
NTP_PORT = 123

# 1900-01-01 到 1970-01-01 的秒数
NTP_TO_UNIX_DELTA = 2208988800

def get_ntp_unix_time():
    """获取标准Unix时间戳(从1970-01-01开始的秒数)"""
    try:
        sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM)
        sock.settimeout(3)
        msg = b'\x1b' + b'\x00' * 47
        sock.sendto(msg, (NTP_IP, NTP_PORT))
        data, addr = sock.recvfrom(48)
        sock.close()
        ntp_timestamp = struct.unpack("!I", data[40:44])[0]
        return ntp_timestamp - NTP_TO_UNIX_DELTA
    except Exception as e:
        print("NTP同步失败:", e)
        return None

def unix_to_datetime(ts):
    """
    把Unix时间戳转为(年,月,日,时,分,秒)
    时区:UTC+8 北京时间
    """
    # 转为北京时间
    t = ts + 8 * 3600
    
    # 先算年份
    year = 1970
    days_in_year = 365
    while True:
        # 判断闰年
        is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
        if is_leap:
            days_in_year = 366
        else:
            days_in_year = 365
        
        year_seconds = days_in_year * 86400
        if t < year_seconds:
            break
        t -= year_seconds
        year += 1
    
    # 再算月份
    if is_leap:
        month_days = [31,29,31,30,31,30,31,31,30,31,30,31]
    else:
        month_days = [31,28,31,30,31,30,31,31,30,31,30,31]
    
    month = 1
    for md in month_days:
        month_seconds = md * 86400
        if t < month_seconds:
            day = t // 86400 + 1
            t %= 86400
            break
        t -= month_seconds
        month += 1
    
    # 时分秒
    hour = t // 3600
    t %= 3600
    minute = t // 60
    second = t % 60
    
    return (year, month, day, hour, minute, second)

# 首次同步
print("正在同步NTP时间...")
unix_ts = get_ntp_unix_time()
if not unix_ts:
    print("首次同步失败,程序退出")
    exit()

print("同步成功!")
last_sync_ts = unix_ts
last_sync_micros = time.time()

# 每秒更新,每分钟重连NTP
while True:
    elapsed = int(time.time() - last_sync_micros)
    current_ts = last_sync_ts + elapsed
    
    dt = unix_to_datetime(current_ts)
    print(f"{dt[0]}年{dt[1]:02d}月{dt[2]:02d}日 {dt[3]:02d}:{dt[4]:02d}:{dt[5]:02d}")
    
    # 每分钟重新同步一次,防止飘移
    if elapsed >= 60:
        print("重新同步NTP...")
        new_ts = get_ntp_unix_time()
        if new_ts:
            last_sync_ts = new_ts
            last_sync_micros = time.time()
            print("重新同步成功!")
    
    time.sleep(1)

5.2 代码功能说明

  1. 依靠 wiznet_init 模块快速完成硬件初始化、DHCP 自动联网;
  2. 配置阿里云高可用 NTP 服务器,保证同步稳定性;
  3. 封装 NTP 请求函数,自动收发、解析 UDP 数据包;
  4. 完成时间戳换算、时区加 8 小时,输出标准北京时间;
  5. 本地软件秒级走时,保证断网也能继续计时;
  6. 每分钟主动重连 NTP 服务器,修正时间漂移;
  7. 增加异常捕获,网络波动时不会卡死程序。


6. 运行结果与测试验证

将代码复制到 Thonny,点击运行,串口输出如下:

IP Address: ('192.168.1.123', '255.255.255.0', '192.168.1.1', '202.96.134.33')
IP: 192.168.1.123
正在同步NTP时间...
同步成功!
2026年04月30日 10:45:49
2026年04月30日 10:45:50
2026年04月30日 10:45:51

设备正常联网、时间同步成功,秒级自动刷新,时间精准无误。

7. 常见问题一站式排查

        NTP 同步超时 / 同步失败

  • 检查网线连接,确认开发板可以正常访问外网;
  • 路由器禁止 UDP 123 端口会导致失败,更换手机热点测试;
  • 更换其他公共 NTP 服务器地址。

        时间相差 8 小时

  • 代码未做 UTC+8 时区补偿,检查时间转换函数。

        每隔一段时间时间偏差变大

  • 正常现象,本程序每分钟自动重同步,可自行修改同步周期。

        Thonny 无法识别开发板

  • 更换 USB 线、安装 MKR串口驱动、关闭占用串口软件。

8. WIZnet 硬件协议栈核心优势对比

为了更直观地了解 W5500 硬件协议栈芯片在 UDP 组播/广播通信中的价值,我们对比了目前主流的三种嵌入式以太网方案:

对比维度

W5500 硬件协议栈方案

外接 PHY 芯片方案

BOM 成本

中(MCU + 网络模块,无需额外器件)

中高(MCU + PHY 芯片 + 外围器件)

PCB 面积

小(模块集成度高,仅需预留模块安装空间)

大(需预留芯片、布线空间及外围电路)

开发难度

低(MicroPython 固件已封装底层,少量代码实现 UDP 组播/广播)

中高(需调试协议栈、编写底层驱动,对研发能力要求高)

网络稳定性

极高(WIZnet 专注硬件 TCP/IP 协议栈 25 年,抗干扰能力强,UDP 丢包率低)

不定(依赖研发人员对协议栈和网络开发的掌握程度,UDP 易丢包)

CPU 资源占用

0%(协议栈完全由硬件处理,不占用 MCU 资源,不影响数据发送频率)

50%以上(协议栈运行在 MCU 上,占用大量 CPU 和内存,影响 UDP 发送效率)

硬件 Socket 数量

W5500 8个独立硬件 Socket,支持多组播/广播同时进行

视 MCU 能力而定,理论支持多路拓展,但实际受 CPU 资源限制

网络吞吐量

W5500 最高 15Mbps,UDP 数据传输流畅,无明显延迟

视 MCU 能力而定,普遍低于硬件协议栈方案,多设备通信易卡顿

接口易用性

SPI 接口,接线简单,适配大多数 MCU,支持高速通信

需 MCU 带有 MII/RMII 等专用接口,适配性有限

部署难度

低(MicroPython 成熟固件,应用层协议均有库文件,多设备组网可快速部署)

高(应用层协议需要手动移植开源库适配,调试成本高)

9. 典型应用场景

  • 物联网设备日志时间戳记录;
  • 工业传感器定时采集、定时上报数据;
  • 智能家居设备定时开关、时序控制;
  • 多设备组网场景下统一时间基准;
  • 嵌入式教学、NTP 协议原理实验;
  • 无 RTC 硬件低成本精准计时方案。

10. 系列预告与资源获取

 10.1 系列预告

下一篇将带来 HTTP Client 客户端请求 实战教程,讲解基于 W55RP20-EVB-MKR硬件协议栈实现网页请求、API 数据获取、JSON 数据解析,为后续设备上云、网络数据交互打下基础。

 10.2 资源获取

更多推荐