0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

第十章 W55MH32 SNTP示例

W55MH32 来源:W55MH32 作者:W55MH32 2025-07-24 09:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机

W55MH32是WIZnet重磅推出的高性能以太网单片机,它为用户带来前所未有的集成化体验。这颗芯片将强大的组件集于一身,具体来说,一颗W55MH32内置高性能Arm® Cortex-M3核心,其主频最高可达216MHz;配备1024KB FLASH与96KB SRAM,满足存储与数据处理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP协议栈、内置MAC以及PHY,拥有独立的32KB以太网收发缓存,可供8个独立硬件socket使用。如此配置,真正实现了All-in-One解决方案,为开发者提供极大便利。

在封装规格上,W55MH32提供了两种选择:QFN100和QFN68。

W55MH32L采用QFN100封装版本,尺寸为12x12mm,其资源丰富,专为各种复杂工控场景设计。它拥有66个GPIO、3个ADC、12通道DMA、17个定时器、2个I2C、5个串口、2个SPI接口(其中1个带I2S接口复用)、1个CAN、1个USB2.0以及1个SDIO接口。如此丰富的外设资源,能够轻松应对工业控制中多样化的连接需求,无论是与各类传感器、执行器的通信,还是对复杂工业协议的支持,都能游刃有余,成为复杂工控领域的理想选择。同系列还有QFN68封装的W55MH32Q版本,该版本体积更小,仅为8x8mm,成本低,适合集成度高的网关模组等场景,软件使用方法一致。更多信息和资料请进入http://www.w5500.com/网站或者私信获取。

此外,本W55MH32支持硬件加密算法单元,WIZnet还推出TOE+SSL应用,涵盖TCP SSL、HTTP SSL以及 MQTT SSL等,为网络通信安全再添保障。

为助力开发者快速上手与深入开发,基于W55MH32L这颗芯片,WIZnet精心打造了配套开发板。开发板集成WIZ-Link芯片,借助一根USB C口数据线,就能轻松实现调试、下载以及串口打印日志等功能。开发板将所有外设全部引出,拓展功能也大幅提升,便于开发者全面评估芯片性能。

若您想获取芯片和开发板的更多详细信息,包括产品特性、技术参数以及价格等,欢迎访问官方网页:http://www.w5500.com/,我们期待与您共同探索W55MH32的无限可能。

wKgZO2iBiBmAe3DyAACpGc5mWX8613.png

第十章 W55MH32 SNTP示例

本篇文章我们将详细介绍如何在W55MH32芯片上面实现SNTP授时功能,并通过实战例程,为大家讲解如何让W55MH32从SNTP服务器获取准确的实际时间。

该例程用到的其他网络协议,例如DHCP、DNS请参考相关章节。有关W55MH32的初始化过程,也请参考 Network Install章节,这里将不再赘述。

1 SNTP协议简介

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

2 SNTP与NTP的区别

SNTP与NTP的区别如下表所示:

特性 SNTP NTP
精度 精度较低,通常误差在几十毫秒到几百毫秒 高精度,通常误差在毫秒级甚至微秒级
算法复杂度 简单,适用于精度要求不高的应用 复杂,使用精密算法进行校正
服务器选择 通常依赖单一时间服务器 多服务器选择,避免单点故障
应用场景 家庭、办公室、小型设备、嵌入式设备 精确时钟同步的场景,如金融系统、科学研究等
协议复杂性 简单,易于实现 较为复杂,需要更多计算和状态管理

3 SNTP协议特点

使用UDP通信:SNTP使用UDP协议在端口123进行通信。

支持请求-响应模式:SNTP使用单次请求-响应模式完成时间同步:客户端向时间服务器发送请求,服务器返回当前时间戳。

实现和部署成本低:SNTP的实现非常简单,通常只需要少量的代码,便于在嵌入式系统中集成。由于其资源占用少,适合大规模部署。

与NTP兼容:SNTP是 NTP的子集,客户端可以与NTP服务器通信以获取时间。NTP服务器可以无缝提供时间同步服务,无需额外配置。

支持单向时间同步:在特定场景中(如设备只需同步本地时间,而无需计算网络延迟),SNTP可以仅基于服务器提供的时间戳完成时间同步。

4 SNTP应用场景

SNTP协议通常用于需要时间同步的场景。通过将时间同步到RTC,能够实现以下功能:

协同工作:定期同步时间,使各模块能够按照预定的时间顺序执行任务。

日志与事件管理:确保W55MH32的日志时间和事件记录准确,从而便于后续分析与故障排查。

定时任务:通过时间同步,支持定时任务的准确执行。

5时区介绍

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

/*
00)UTC-12:00 Baker Island, Howland Island (both uninhabited)
01) UTC-11:00 American Samoa, Samoa
02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii)
03) UTC-09:30 Marquesas Islands
04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska)
05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California)
06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state))
07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta)
08) UTC-07:00 Mexico (Chihuahua), United States (Colorado)
09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras
10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas)
11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru
12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec)
13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.)
14) UTC-04:30 Venezuela
15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay,
16) UTC-04:00 Puerto Rico, Trinidad and Tobago
17) UTC-03:30 Canada (Newfoundland)
18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay
19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands
20) UTC-01:00 Portugal (Azores), Cape Verde
21) UTC±00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira)
22) UTC±00:00 Spain (Canary Islands), Morocco, United Kingdom
23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina,
24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo,
25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland
26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia,
27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine
28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia,
29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen
30) UTC+03:30 (Summer)Iran
31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates
32) UTC+04:30 Afghanistan
33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan
34) UTC+05:30 India, Sri Lanka
35) UTC+05:45 Nepal
36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast)
37) UTC+06:30 Cocos Islands, Myanmar
38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam
39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia
40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan
41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia))
42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria)
43) UTC+10:30 Lord Howe Island
44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands
45) UTC+11:30 Norfolk Island
46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand
47) UTC+12:45 (Summer)New Zealand
48) UTC+13:00 Tonga
49) UTC+14:00 Kiribati (Line Islands)
*/

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

1.客户端发送时间请求

客户端向 SNTP服务器发送一个请求数据包,并记录T1时间戳信息。

请求数据包中通常包含客户端的时间戳(请求发送的时间),以便在计算延迟时使用。

2.服务器接收请求并处理

SNTP 服务器接收到请求后,记录下接收请求的时间T2。

服务器生成一个响应数据包,其中包括以下时间戳信息:

T2:服务器接收到请求的时间。

T3:服务器发送响应的时间。

3.客户端接收响应并计算时间

客户端从服务器返回的数据包中提取时间戳信息(T2、T3),并记录接收时间T4。

客户端根据这些时间戳计算本地时间与服务器时间的差异,以及网络延迟:

网络延迟公式: 网络延迟=(T4−T1)−(T3−T2)

本地时间校准公式: 校准时间=T3+(T4−T1)−(T3−T2)/2

4.调整本地时间

客户端根据校准时间调整本地时钟,以同步到服务器的时间。

7 SNTP协议的报文解析

SNTP的发送和接收报文为固定结构,通常为48字节,报文结构如下:

字节偏移 字段名称 长度(字节) 描述
0 Leap Indicator (LI), Version Number, Mode 1 包含闰秒标志、版本号、模式等信息。
1 Stratum 1 服务器层级(0为未同步,1为主时钟)。
2 Poll Interval 1 轮询间隔,表示客户端请求时间的频率。
3 Precision 1 服务器时间精度。
4–7 Root Delay 4 到主时钟的总延迟,单位为秒的2的负16次方。
8–11 Root Dispersion 4 到主时钟的最大误差,单位为秒的 2的负 16次方。
12–15 Reference ID 4 标识时间源(IPv4地址或ASCII标识符)。
16–23 Reference Timestamp 8 参考时间戳,表示最后同步时间的 UTC时间。
24–31 Originate Timestamp 8 客户端请求时间戳 (T1)。
32–39 Receive Timestamp 8 服务器接收此请求的时间戳 (T2)。
40–47 Transmit Timestamp 8 服务器发送响应时间戳 (T3)。

注意:时间戳的定义是指从1900年1月1日00:00:00 UTC开始的秒数。

SNTP发送请求实例:

偏移 数据 描述
0 23 Leap Indicator (LI): 0
Version Number: 4
Mode: 3 (客户端请求)
1 00 Stratum: 0 (未同步,客户端请求时该字段为 0)
2 00 Poll Interval: 0 (默认值)
3 00 Precision: 0 (未设置)
4 - 7 00000000 Root Delay: 0 (客户端请求时该字段为 0)
8 - 11 00000000 Root Dispersion: 0 (客户端请求时该字段为 0)
12 - 15 00000000 Reference ID:未设置 (客户端请求时为 0)
16 - 23 0000000000000000 Reference Timestamp:未设置
24 - 31 0000000000000000 Originate Timestamp (T1):未设置
32 - 39 0000000000000000 Receive Timestamp (T2):未设置
40 - 47 0000000000000000 Transmit Timestamp (T3):未设置

SNTP服务器响应实例:

偏移 数据 描述
0 24 Leap Indicator (LI): 0
Version Number: 4
Mode: 4 (服务器响应)
1 03 Stratum: 3 (三级时钟)
2 00 Poll Interval: 0
3 E7 Precision: -25 (表示时间精度为 2^(-25) 秒)
4 - 7 00000755 Root Delay: 0.007324秒
8 - 11 00002d0a Root Dispersion: 0.011017秒
12 - 15 d30804eb Reference ID: IPv4地址 211.8.4.235
16 - 23 2368c6b074d70e00 Reference Timestamp:
秒数部分:0x2368C6B0 (594279216秒)
小数部分:0x74D70E00 (转换为 0.456789秒)
24 - 31 0000000000000000 Originate Timestamp (T1):未设置 (表示客户端未设置请求时间戳)
32 - 39 eb2368fcfb5dfcad Receive Timestamp (T2):
秒数部分:0xEB2368FC (3949187836秒)
小数部分:0xFB5DFCAD (转换为 0.982354秒)
40 - 47 eb2368fcfb618c14 Transmit Timestamp (T3):
秒数部分:0xEB2368FC (3949187836秒)
小数部分:0xFB618C14 (转换为 0.983214秒)

8实现过程

接下来,我们在W55MH32上实现SNTP授时功能。

注意:因为本示例需要访问互联网,请确保 W55MH32的网络环境及配置能够正常访问互联网。

步骤一:初始化RTC

RTC_Init();

RTC_Init()函数具体内容如下:

uint8_t RTC_Init(void)
{
 uint8_t temp = 0;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR和BKP外设时钟
 PWR_BackupAccessCmd(ENABLE);
 RCC_LSEConfig(RCC_LSE_ON);                                        //设置外部低速晶振(LSE),使用外设低速晶振
 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250) //检查指定的RCC标志位设置与否,等待低速晶
振就绪
 {
     temp++;
     delay_ms(10);
 }
 if (temp >= 250) return 1;              //初始化时钟失败,晶振有问题
 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
 RCC_RTCCLKCmd(ENABLE);                  //使能RTC时钟
 RTC_WaitForLastTask();                  //等待最近一次对RTC寄存器的写操作完成
 RTC_WaitForSynchro();                   //等待RTC寄存器同步
 RTC_ITConfig(RTC_IT_SEC, ENABLE);       //使能RTC秒中断
 RTC_WaitForLastTask();                  //等待最近一次对RTC寄存器的写操作完成
 RTC_EnterConfigMode();                  // 允许配置
 RTC_SetPrescaler(32767);                //设置RTC预分频的值,计算方式32768/(32767+1) = 1Hz 周期刚好是1秒。
 RTC_WaitForLastTask();                  //等待最近一次对RTC寄存器的写操作完成
 RTC_Set(1900, 1, 1, 0, 0, 1);           //设置时间
 RTC_ExitConfigMode();                   //退出配置模式
 RTC_WaitForSynchro();                   //等待最近一次对RTC寄存器的写操作完成
 RTC_ITConfig(RTC_IT_SEC, ENABLE);       //使能RTC秒中断
 RTC_WaitForLastTask();                  //等待最近一次对RTC寄存器的写操作完成
 RTC_NVIC_Config();                      //RCT中断分组设置
 RTC_Get();                              //更新时间
 return 0;                               //ok
}

步骤二:通过DNS解析SNTP服务器的IP地址

if (do_dns(ethernet_buf, sntp_server_name, sntp_server_ip))
{
   while (1)
   {
   }
}

步骤三:SNTP初始化

SNTP_init(SOCKET_ID, sntp_server_ip, timezone, ethernet_buf);

SNTP_init()函数需要传入四个参数,分别是使用的socket通道号,sntp服务器地址,时区,socket缓存,在这个函数中,我们会进行SNTP报文组包操作,并将传入的socket通道号,时区,socket缓存注册到库中,具体内容如下:

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);
}

步骤四:发送SNTP请求报文获取时间

while (1) //上电自动获取时间
{
   if (SNTP_run(&date))
   {
       RTC_Set(date.yy, date.mo, date.dd, date.hh, date.mm, date.ss);
       break;
   }
}

接着,我们需要运行SNTP_run()函数来执行发送报文以及解析报文的操作,当成功获取到时间后,我们设置到RTC中。

SNTP_run()函数需要传入一个时间结构体date,它的定义如下:

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

SNTP_run()函数定义如下:

 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: %drn", ntp_retry_cnt);
#endif
       ntp_retry_cnt++;
     }
   }
 }
 else //ntp retry fail
 {
   ntp_retry_cnt=0;
#ifdef _SNTP_DEBUG_
   printf("ntp retry failed!rn");
#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;
}

在这里会执行一个UDP的状态机,当socket处于SOCK_UDP状态时,首先会执行sendto发送初始化SNTP时组装的SNTP请求报文,然后是监听服务器响应。

步骤五:解析SNTP响应报文

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: %drn", ntp_retry_cnt);
#endif
       ntp_retry_cnt++;
     }
   }
 }
 else //ntp retry fail
 {
   ntp_retry_cnt=0;
#ifdef _SNTP_DEBUG_
   printf("ntp retry failed!rn");
#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;
}

当Sn_RX_RSR (Socket n空闲接收缓存寄存器)的值大于0时,说明服务器给W55MH32返回了响应。首先会通过recvfrom()函数读取响应报文,然后执行get_seconds_from_ntp_server解析时间。

注意:get_seconds_from_ntp_server()函数目前只解析了服务器响应时间戳(即最后8个字节),没有减去网络延迟。

get_seconds_from_ntp_server()函数定义如下:

void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx)
{
 tstamp seconds = 0;
 uint8_t i=0;
 for (i = 0; i < 4; i++)
 {
   seconds = (seconds < < 8) | buf[idx + i];
 }
 switch (time_zone)
 {
 case 0:
   seconds -=  12*3600;
   break;
 case 1:
   seconds -=  11*3600;
   break;
 case 2:
   seconds -=  10*3600;
   break;
 case 3:
   seconds -=  (9*3600+30*60);
   break;
 case 4:
   seconds -=  9*3600;
   break;
 case 5:
 case 6:
   seconds -=  8*3600;
   break;
 case 7:
 case 8:
   seconds -=  7*3600;
   break;
 case 9:
 case 10:
   seconds -=  6*3600;
   break;
 case 11:
 case 12:
 case 13:
   seconds -= 5*3600;
   break;
 case 14:
   seconds -=  (4*3600+30*60);
   break;
 case 15:
 case 16:
   seconds -=  4*3600;
   break;
 case 17:
   seconds -=  (3*3600+30*60);
   break;
 case 18:
   seconds -=  3*3600;
   break;
 case 19:
   seconds -=  2*3600;
   break;
 case 20:
   seconds -=  1*3600;
   break;
 case 21:                            
 case 22:
   break;
 case 23:
 case 24:
 case 25:
   seconds +=  1*3600;
   break;
 case 26:
 case 27:
   seconds +=  2*3600;
   break;
 case 28:
 case 29:
   seconds +=  3*3600;
   break;
 case 30:
   seconds +=  (3*3600+30*60);
   break;
 case 31:
   seconds +=  4*3600;
   break;
 case 32:
   seconds +=  (4*3600+30*60);
   break;
 case 33:
   seconds +=  5*3600;
   break;
 case 34:
   seconds +=  (5*3600+30*60);
   break;
 case 35:
   seconds +=  (5*3600+45*60);
   break;
 case 36:
   seconds +=  6*3600;
   break;
 case 37:
   seconds +=  (6*3600+30*60);
   break;
 case 38:
   seconds +=  7*3600;
   break;
 case 39:
   seconds +=  8*3600;
   break;
 case 40:
   seconds +=  9*3600;
   break;
 case 41:
   seconds +=  (9*3600+30*60);
   break;
 case 42:
   seconds +=  10*3600;
   break;
 case 43:
   seconds +=  (10*3600+30*60);
   break;
 case 44:
   seconds +=  11*3600;
   break;
 case 45:
   seconds +=  (11*3600+30*60);
   break;
 case 46:
   seconds +=  12*3600;
   break;
 case 47:
   seconds +=  (12*3600+45*60);
   break;
 case 48:
   seconds +=  13*3600;
   break;
 case 49:
   seconds +=  14*3600;
   break;
 
 }
 
 //calculation for date
 calcdatetime(seconds);
}

然后调用calcdatetime()函数将调整后的秒数转换为年、月、日、时、分、秒的具体时间格式:

void calcdatetime(tstamp seconds)
{
 uint8_t yf=0;
 tstamp n=0,d=0,total_d=0,rz=0;
 uint16_t y=0,r=0,yr=0;
 signed long long yd=0;
 
 n = seconds;
 total_d = seconds/(SECS_PERDAY);
 d=0;
 uint32_t p_year_total_sec=SECS_PERDAY*365;
 uint32_t r_year_total_sec=SECS_PERDAY*366;
 while(n>=p_year_total_sec)
 {
   if((EPOCH+r)%400==0 || ((EPOCH+r)%100!=0 && (EPOCH+r)%4==0))
   {
     if(n< r_year_total_sec)
       break;
     n = n -(r_year_total_sec);
     d = d + 366;
     
   }
   else
   {
     n = n - (p_year_total_sec);
     d = d + 365;
   }
   r+=1;
   y+=1;
 
 }
 
 y += EPOCH;
 
 Nowdatetime.yy = y;
 
 yd=0;
 yd = total_d - d;
 
 yf=1;
 while(yd >=28)
 {
 
   if(yf==1 || yf==3 || yf==5 || yf==7 || yf==8 || yf==10 || yf==12)
   {
     yd -= 31;
     if(yd< 0)break;
     rz += 31;
   }
 
   if (yf==2)
   {
     if (y%400==0 || (y%100!=0 && y%4==0))
     {
       yd -= 29;
       if(yd< 0)break;
       rz += 29;
     }
     else
     {
       yd -= 28;
       if(yd< 0)break;
       rz += 28;
     }
   }
   if(yf==4 || yf==6 || yf==9 || yf==11 )
   {
     yd -= 30;
     if(yd< 0)break;
     rz += 30;
   }
   yf += 1;
 
 }
 Nowdatetime.mo=yf;
 yr = total_d-d-rz;
 
 yr += 1;
 
 Nowdatetime.dd=yr;
 
 //calculation for time
 seconds = seconds%SECS_PERDAY;
 Nowdatetime.hh = seconds/3600;
 Nowdatetime.mm = (seconds%3600)/60;
 Nowdatetime.ss = (seconds%3600)%60;
 
}

最后在主循环1秒打印一次当前时间:

while (1)
{
   printf("Beijing time now: %04d-%02d-%02d  %s  %02d:%02d:%02drn", calendar.w_year, calendar.w_month, calendar.w_date, week_name[calendar.week], calendar.hour, calendar.min, calendar.sec);
   delay_ms(1000);
}

9运行结果

烧录例程运行后,首先进行了PHY链路检测,然后是通过DHCP获取网络地址并打印网络地址信息,最后,通过SNTP获取到时间后赋值给RTC,然后主循环1秒打印一次当前时间。如下图所示:

wKgZPGiBjfSAKMcUAAB9zivMNxw981.png

10总结

本文讲解了如何在W55MH32芯片上实现SNTP授时功能,通过实例详细展示了从SNTP服务器同步时间的实现流程,包括时间请求、响应解析和本地时间校准等核心步骤。文章还对SNTP的应用场景进行了分析,帮助读者理解其在时间同步中的实际应用价值。

下一篇文章我们将讲解SMTP协议的原理及在邮件通信中的应用,同时讲解如何在W55MH32芯片上实现SMTP功能,敬请期待!

WIZnet是一家无晶圆厂半导体公司,成立于 1998年。产品包括互联网处理器 iMCU™,它采用 TOE(TCP/IP卸载引擎)技术,基于独特的专利全硬连线 TCP/IP。iMCU™面向各种应用中的嵌入式互联网设备。

WIZnet在全球拥有 70多家分销商,在香港、韩国、美国设有办事处,提供技术支持和产品营销。

香港办事处管理的区域包括:澳大利亚、印度、土耳其、亚洲(韩国和日本除外)。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 以太网
    +关注

    关注

    41

    文章

    5923

    浏览量

    179537
  • sntp
    +关注

    关注

    0

    文章

    6

    浏览量

    3885
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    第十章 W55MH32中断应用概览

    本章讲述了W55MH32中断应用,涵盖异常类型、NVIC介绍、优先级定义与分组,阐述中断编程三要点(使能中断、配置 NVIC、编写服务函数),并强调优先级分组设置注意事项。
    的头像 发表于 05-22 17:07 1592次阅读
    <b class='flag-5'>第十章</b> <b class='flag-5'>W55MH32</b>中断应用概览

    【PDF】C++ GUI Programming with Qt 4 中文版(第一第十章)目录版

    C++ GUI Programming with Qt 4 中文版(第一第十章)目录版附件:
    发表于 02-28 15:46

    自己编的信号与系统(西安电子科大版)第十章课后答案...

    分享 一下 自己编写的信号与系统(西安电子科大版)的第十章课后习题的Matlab程序。
    发表于 11-21 13:06

    明德扬视频分享点拨FPGA课程--第十章  GVIM的使用

    第十章GVIM的使用1. GVIM使用2http://yunpan.cn/cjZTiDA9pY56x访问密码 c359
    发表于 11-10 08:59

    《测控电路》习题完整参考答案(第十章

    《测控电路》习题完整参考答案(第十章).pdf
    发表于 05-07 11:43

    HCIE复习资料第十章 特性

    HCIE复习资料第十章 特性,HCIE全套复习资料
    发表于 05-10 17:22 0次下载

    《测控电路》习题完整参考答案(第十章

    《测控电路》习题完整参考答案(第十章
    发表于 02-14 17:01 0次下载

    第二 W55MH32 DHCP示例

    本文介绍 DHCP 协议,包括其在 IP 网络自动分配参数的功能、便捷配置等特点、工作原理、报文格式和应用场景。通过 W55MH32 实战例程展示动态获取网络地址信息过程,含注册定时器中断、启用模式和获取信息等步骤,烧录后可完成检测与信息打印,PC 端能 PING 通设备。
    的头像 发表于 07-24 09:02 582次阅读
    第二<b class='flag-5'>章</b> <b class='flag-5'>W55MH32</b> DHCP<b class='flag-5'>示例</b>

    第五 W55MH32 UDP示例

    本文介绍了在 W55MH32 芯片上实现 UDP 通信及数据回环测试的方法。阐述了 UDP 协议的概念、特点、应用场景、报文传输流程和报文结构,展示了实现过程,借助网络调试工具完成测试。
    的头像 发表于 07-24 09:13 648次阅读
    第五<b class='flag-5'>章</b> <b class='flag-5'>W55MH32</b> UDP<b class='flag-5'>示例</b>

    第九 W55MH32 HTTP Server示例

    本文介绍了在 W55MH32 芯片上实现 HTTP Server 功能,并通过浏览器修改其网络地址信息的方法。阐述了 HTTP 协议的概念、特点、应用场景、工作流程、请求方法、响应内容,以及 Web 页面构成和交互方式。展示了在W55MH32上实现的过程。
    的头像 发表于 07-24 09:35 641次阅读
    第九<b class='flag-5'>章</b> <b class='flag-5'>W55MH32</b> HTTP Server<b class='flag-5'>示例</b>

    第十四章 W55MH32 TFTP示例

    本文讲解了如何在 W55MH32 芯片上实现 TFTP 协议,通过实战例程详细展示了使用 TFTP 客户端模式从服务器获取文本文件的过程,涵盖 TFTP 初始化、发送读请求、运行协议并处理结果等核心
    的头像 发表于 07-24 10:37 631次阅读
    <b class='flag-5'>第十</b>四章 <b class='flag-5'>W55MH32</b> TFTP<b class='flag-5'>示例</b>

    第十五章 W55MH32 SNMP示例

    本文讲解了如何在 W55MH32 芯片上实现 SNMP 功能,通过实战例程展示了使用 MIB Browser 管理 W55MH32 的具体过程,涵盖在 MIB Browser 中创建分支、添加叶子
    的头像 发表于 07-24 10:43 705次阅读
    <b class='flag-5'>第十五章</b> <b class='flag-5'>W55MH32</b> SNMP<b class='flag-5'>示例</b>

    第十六章 W55MH32 PING示例

    本文讲解了如何在 W55MH32 芯片上通过 IPRAW 模式实现 ICMP 协议中的 PING 命令,以进行网络连通性测试,通过实战例程展示了从发送 PING 请求、接收并解析回复到统计结果的完整
    的头像 发表于 07-24 11:41 502次阅读
    <b class='flag-5'>第十</b>六章 <b class='flag-5'>W55MH32</b> PING<b class='flag-5'>示例</b>

    第十八章 W55MH32 FTP_Server示例

    本文讲解了如何在 W55MH32 芯片上实现 FTP 协议的服务器模式,通过实战例程展示了使用 W55MH32 作为 FTP 服务器与 PC 端进行文件传输、目录操作等功能的过程,涵盖获取网络配置
    的头像 发表于 07-24 11:55 450次阅读
    <b class='flag-5'>第十</b>八章 <b class='flag-5'>W55MH32</b> FTP_Server<b class='flag-5'>示例</b>

    第三十章 W55MH32 HTTP_Server&amp;NetBIOS示例

    本文讲解了如何在 W55MH32 芯片上实现 HTTP_Server 与 NetBIOS 功能,并通过 NetBIOS 访问 HTTP 服务器网页内容,通过实战例程展示了在主循环中并行处理 HTTP 与 NetBIOS 相关事务的过程。
    的头像 发表于 07-24 16:21 896次阅读
    第三<b class='flag-5'>十章</b> <b class='flag-5'>W55MH32</b> HTTP_Server&amp;NetBIOS<b class='flag-5'>示例</b>