單芯片解決方案,開啟全新體驗——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的無限可能。
第十章 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_cnt0xFFFF) { 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_cnt0xFFFF) { 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=28) { if(yf==1 || yf==3 || yf==5 || yf==7 || yf==8 || yf==10 || yf==12) { yd -= 31; if(yd0)break; rz += 31; } if (yf==2) { if (y%400==0 || (y%100!=0 && y%4==0)) { yd -= 29; if(yd0)break; rz += 29; } else { yd -= 28; if(yd0)break; rz += 28; } } if(yf==4 || yf==6 || yf==9 || yf==11 ) { yd -= 30; if(yd0)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秒打印一次當前時間。如下圖所示:
10總結
本文講解了如何在W55MH32芯片上實現SNTP授時功能,通過實例詳細展示了從SNTP服務器同步時間的實現流程,包括時間請求、響應解析和本地時間校準等核心步驟。文章還對SNTP的應用場景進行了分析,幫助讀者理解其在時間同步中的實際應用價值。
下一篇文章我們將講解SMTP協議的原理及在郵件通信中的應用,同時講解如何在W55MH32芯片上實現SMTP功能,敬請期待!
WIZnet是一家無晶圓廠半導體公司,成立于 1998年。產品包括互聯網處理器 iMCU?,它采用 TOE(TCP/IP卸載引擎)技術,基于獨特的專利全硬連線 TCP/IP。iMCU?面向各種應用中的嵌入式互聯網設備。
WIZnet在全球擁有 70多家分銷商,在香港、韓國、美國設有辦事處,提供技術支持和產品營銷。
香港辦事處管理的區域包括:澳大利亞、印度、土耳其、亞洲(韓國和日本除外)。
審核編輯 黃宇
-
以太網
+關注
關注
41文章
5684瀏覽量
176251 -
sntp
+關注
關注
0文章
6瀏覽量
3821
發布評論請先 登錄
第十章 W55MH32中斷應用概覽

【PDF】C++ GUI Programming with Qt 4 中文版(第一章至第十章)目錄版
明德揚視頻分享點撥FPGA課程--第十章 ?GVIM的使用
第二章 W55MH32 DHCP示例

第五章 W55MH32 UDP示例

第九章 W55MH32 HTTP Server示例

第十四章 W55MH32 TFTP示例

第十五章 W55MH32 SNMP示例

第十六章 W55MH32 PING示例

第十八章 W55MH32 FTP_Server示例

第三十章 W55MH32 HTTP_Server&NetBIOS示例

評論