單芯片解決方案,開啟全新體驗(yàn)——W55MH32 高性能以太網(wǎng)單片機(jī)
W55MH32是WIZnet重磅推出的高性能以太網(wǎng)單片機(jī),它為用戶帶來前所未有的集成化體驗(yàn)。這顆芯片將強(qiáng)大的組件集于一身,具體來說,一顆W55MH32內(nèi)置高性能Arm? Cortex-M3核心,其主頻最高可達(dá)216MHz;配備1024KB FLASH與96KB SRAM,滿足存儲(chǔ)與數(shù)據(jù)處理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP協(xié)議棧、內(nèi)置MAC以及PHY,擁有獨(dú)立的32KB以太網(wǎng)收發(fā)緩存,可供8個(gè)獨(dú)立硬件socket使用。如此配置,真正實(shí)現(xiàn)了All-in-One解決方案,為開發(fā)者提供極大便利。
在封裝規(guī)格上,W55MH32提供了兩種選擇:QFN100和QFN68。
W55MH32L采用QFN100封裝版本,尺寸為12x12mm,其資源豐富,專為各種復(fù)雜工控場(chǎng)景設(shè)計(jì)。它擁有66個(gè)GPIO、3個(gè)ADC、12通道DMA、17個(gè)定時(shí)器、2個(gè)I2C、5個(gè)串口、2個(gè)SPI接口(其中1個(gè)帶I2S接口復(fù)用)、1個(gè)CAN、1個(gè)USB2.0以及1個(gè)SDIO接口。如此豐富的外設(shè)資源,能夠輕松應(yīng)對(duì)工業(yè)控制中多樣化的連接需求,無論是與各類傳感器、執(zhí)行器的通信,還是對(duì)復(fù)雜工業(yè)協(xié)議的支持,都能游刃有余,成為復(fù)雜工控領(lǐng)域的理想選擇。同系列還有QFN68封裝的W55MH32Q版本,該版本體積更小,僅為8x8mm,成本低,適合集成度高的網(wǎng)關(guān)模組等場(chǎng)景,軟件使用方法一致。更多信息和資料請(qǐng)進(jìn)入http://www.w5500.com/網(wǎng)站或者私信獲取。
此外,本W(wǎng)55MH32支持硬件加密算法單元,WIZnet還推出TOE+SSL應(yīng)用,涵蓋TCP SSL、HTTP SSL以及 MQTT SSL等,為網(wǎng)絡(luò)通信安全再添保障。
為助力開發(fā)者快速上手與深入開發(fā),基于W55MH32L這顆芯片,WIZnet精心打造了配套開發(fā)板。開發(fā)板集成WIZ-Link芯片,借助一根USB C口數(shù)據(jù)線,就能輕松實(shí)現(xiàn)調(diào)試、下載以及串口打印日志等功能。開發(fā)板將所有外設(shè)全部引出,拓展功能也大幅提升,便于開發(fā)者全面評(píng)估芯片性能。
若您想獲取芯片和開發(fā)板的更多詳細(xì)信息,包括產(chǎn)品特性、技術(shù)參數(shù)以及價(jià)格等,歡迎訪問官方網(wǎng)頁:http://www.w5500.com/,我們期待與您共同探索W55MH32的無限可能。

第十章 W55MH32 SNTP示例
本篇文章我們將詳細(xì)介紹如何在W55MH32芯片上面實(shí)現(xiàn)SNTP授時(shí)功能,并通過實(shí)戰(zhàn)例程,為大家講解如何讓W(xué)55MH32從SNTP服務(wù)器獲取準(zhǔn)確的實(shí)際時(shí)間。
該例程用到的其他網(wǎng)絡(luò)協(xié)議,例如DHCP、DNS請(qǐng)參考相關(guān)章節(jié)。有關(guān)W55MH32的初始化過程,也請(qǐng)參考 Network Install章節(jié),這里將不再贅述。
1 SNTP協(xié)議簡介
SNTP(Simple Network Time Protocol)是一種基于UDP協(xié)議的網(wǎng)絡(luò)時(shí)間協(xié)議,主要用于在計(jì)算機(jī)網(wǎng)絡(luò)中同步設(shè)備的時(shí)間。SNTP旨在提供簡單的時(shí)間校準(zhǔn)服務(wù),比較NTP(Network Time Protocol)而言,SNTP功能更為簡單,精度相對(duì)較低。
2 SNTP與NTP的區(qū)別
SNTP與NTP的區(qū)別如下表所示:
| 特性 | SNTP | NTP |
| 精度 | 精度較低,通常誤差在幾十毫秒到幾百毫秒 | 高精度,通常誤差在毫秒級(jí)甚至微秒級(jí) |
| 算法復(fù)雜度 | 簡單,適用于精度要求不高的應(yīng)用 | 復(fù)雜,使用精密算法進(jìn)行校正 |
| 服務(wù)器選擇 | 通常依賴單一時(shí)間服務(wù)器 | 多服務(wù)器選擇,避免單點(diǎn)故障 |
| 應(yīng)用場(chǎng)景 | 家庭、辦公室、小型設(shè)備、嵌入式設(shè)備 | 精確時(shí)鐘同步的場(chǎng)景,如金融系統(tǒng)、科學(xué)研究等 |
| 協(xié)議復(fù)雜性 | 簡單,易于實(shí)現(xiàn) | 較為復(fù)雜,需要更多計(jì)算和狀態(tài)管理 |
3 SNTP協(xié)議特點(diǎn)
使用UDP通信:SNTP使用UDP協(xié)議在端口123進(jìn)行通信。
支持請(qǐng)求-響應(yīng)模式:SNTP使用單次請(qǐng)求-響應(yīng)模式完成時(shí)間同步:客戶端向時(shí)間服務(wù)器發(fā)送請(qǐng)求,服務(wù)器返回當(dāng)前時(shí)間戳。
實(shí)現(xiàn)和部署成本低:SNTP的實(shí)現(xiàn)非常簡單,通常只需要少量的代碼,便于在嵌入式系統(tǒng)中集成。由于其資源占用少,適合大規(guī)模部署。
與NTP兼容:SNTP是 NTP的子集,客戶端可以與NTP服務(wù)器通信以獲取時(shí)間。NTP服務(wù)器可以無縫提供時(shí)間同步服務(wù),無需額外配置。
支持單向時(shí)間同步:在特定場(chǎng)景中(如設(shè)備只需同步本地時(shí)間,而無需計(jì)算網(wǎng)絡(luò)延遲),SNTP可以僅基于服務(wù)器提供的時(shí)間戳完成時(shí)間同步。
4 SNTP應(yīng)用場(chǎng)景
SNTP協(xié)議通常用于需要時(shí)間同步的場(chǎng)景。通過將時(shí)間同步到RTC,能夠?qū)崿F(xiàn)以下功能:
協(xié)同工作:定期同步時(shí)間,使各模塊能夠按照預(yù)定的時(shí)間順序執(zhí)行任務(wù)。
日志與事件管理:確保W55MH32的日志時(shí)間和事件記錄準(zhǔn)確,從而便于后續(xù)分析與故障排查。
定時(shí)任務(wù):通過時(shí)間同步,支持定時(shí)任務(wù)的準(zhǔn)確執(zhí)行。
5時(shí)區(qū)介紹
通過 SNTP獲取世界標(biāo)準(zhǔn)時(shí)間 (UTC)后,需要根據(jù)所在時(shí)區(qū)進(jìn)行加減運(yùn)算以計(jì)算當(dāng)?shù)貢r(shí)間。例如,中國位于 UTC+8時(shí)區(qū),在示例代碼中定義為 39。因此,獲取 UTC后需加 8小時(shí)才能轉(zhuǎn)換為中國時(shí)間。
/* 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協(xié)議同步時(shí)間的基本流程
1.客戶端發(fā)送時(shí)間請(qǐng)求
客戶端向 SNTP服務(wù)器發(fā)送一個(gè)請(qǐng)求數(shù)據(jù)包,并記錄T1時(shí)間戳信息。
請(qǐng)求數(shù)據(jù)包中通常包含客戶端的時(shí)間戳(請(qǐng)求發(fā)送的時(shí)間),以便在計(jì)算延遲時(shí)使用。
2.服務(wù)器接收請(qǐng)求并處理
SNTP 服務(wù)器接收到請(qǐng)求后,記錄下接收請(qǐng)求的時(shí)間T2。
服務(wù)器生成一個(gè)響應(yīng)數(shù)據(jù)包,其中包括以下時(shí)間戳信息:
T2:服務(wù)器接收到請(qǐng)求的時(shí)間。
T3:服務(wù)器發(fā)送響應(yīng)的時(shí)間。
3.客戶端接收響應(yīng)并計(jì)算時(shí)間
客戶端從服務(wù)器返回的數(shù)據(jù)包中提取時(shí)間戳信息(T2、T3),并記錄接收時(shí)間T4。
客戶端根據(jù)這些時(shí)間戳計(jì)算本地時(shí)間與服務(wù)器時(shí)間的差異,以及網(wǎng)絡(luò)延遲:
網(wǎng)絡(luò)延遲公式: 網(wǎng)絡(luò)延遲=(T4?T1)?(T3?T2)
本地時(shí)間校準(zhǔn)公式: 校準(zhǔn)時(shí)間=T3+(T4?T1)?(T3?T2)/2
4.調(diào)整本地時(shí)間
客戶端根據(jù)校準(zhǔn)時(shí)間調(diào)整本地時(shí)鐘,以同步到服務(wù)器的時(shí)間。
7 SNTP協(xié)議的報(bào)文解析
SNTP的發(fā)送和接收?qǐng)?bào)文為固定結(jié)構(gòu),通常為48字節(jié),報(bào)文結(jié)構(gòu)如下:
| 字節(jié)偏移 | 字段名稱 | 長度(字節(jié)) | 描述 |
| 0 | Leap Indicator (LI), Version Number, Mode | 1 | 包含閏秒標(biāo)志、版本號(hào)、模式等信息。 |
| 1 | Stratum | 1 | 服務(wù)器層級(jí)(0為未同步,1為主時(shí)鐘)。 |
| 2 | Poll Interval | 1 | 輪詢間隔,表示客戶端請(qǐng)求時(shí)間的頻率。 |
| 3 | Precision | 1 | 服務(wù)器時(shí)間精度。 |
| 4–7 | Root Delay | 4 | 到主時(shí)鐘的總延遲,單位為秒的2的負(fù)16次方。 |
| 8–11 | Root Dispersion | 4 | 到主時(shí)鐘的最大誤差,單位為秒的 2的負(fù) 16次方。 |
| 12–15 | Reference ID | 4 | 標(biāo)識(shí)時(shí)間源(IPv4地址或ASCII標(biāo)識(shí)符)。 |
| 16–23 | Reference Timestamp | 8 | 參考時(shí)間戳,表示最后同步時(shí)間的 UTC時(shí)間。 |
| 24–31 | Originate Timestamp | 8 | 客戶端請(qǐng)求時(shí)間戳 (T1)。 |
| 32–39 | Receive Timestamp | 8 | 服務(wù)器接收此請(qǐng)求的時(shí)間戳 (T2)。 |
| 40–47 | Transmit Timestamp | 8 | 服務(wù)器發(fā)送響應(yīng)時(shí)間戳 (T3)。 |
注意:時(shí)間戳的定義是指從1900年1月1日00:00:00 UTC開始的秒數(shù)。
SNTP發(fā)送請(qǐng)求實(shí)例:
| 偏移 | 數(shù)據(jù) | 描述 |
| 0 | 23 |
Leap Indicator (LI): 0 Version Number: 4 Mode: 3 (客戶端請(qǐng)求) |
| 1 | 00 | Stratum: 0 (未同步,客戶端請(qǐng)求時(shí)該字段為 0) |
| 2 | 00 | Poll Interval: 0 (默認(rèn)值) |
| 3 | 00 | Precision: 0 (未設(shè)置) |
| 4 - 7 | 00000000 | Root Delay: 0 (客戶端請(qǐng)求時(shí)該字段為 0) |
| 8 - 11 | 00000000 | Root Dispersion: 0 (客戶端請(qǐng)求時(shí)該字段為 0) |
| 12 - 15 | 00000000 | Reference ID:未設(shè)置 (客戶端請(qǐng)求時(shí)為 0) |
| 16 - 23 | 0000000000000000 | Reference Timestamp:未設(shè)置 |
| 24 - 31 | 0000000000000000 | Originate Timestamp (T1):未設(shè)置 |
| 32 - 39 | 0000000000000000 | Receive Timestamp (T2):未設(shè)置 |
| 40 - 47 | 0000000000000000 | Transmit Timestamp (T3):未設(shè)置 |
SNTP服務(wù)器響應(yīng)實(shí)例:
| 偏移 | 數(shù)據(jù) | 描述 |
| 0 | 24 |
Leap Indicator (LI): 0 Version Number: 4 Mode: 4 (服務(wù)器響應(yīng)) |
| 1 | 03 | Stratum: 3 (三級(jí)時(shí)鐘) |
| 2 | 00 | Poll Interval: 0 |
| 3 | E7 | Precision: -25 (表示時(shí)間精度為 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: 秒數(shù)部分:0x2368C6B0 (594279216秒) 小數(shù)部分:0x74D70E00 (轉(zhuǎn)換為 0.456789秒) |
| 24 - 31 | 0000000000000000 | Originate Timestamp (T1):未設(shè)置 (表示客戶端未設(shè)置請(qǐng)求時(shí)間戳) |
| 32 - 39 | eb2368fcfb5dfcad |
Receive Timestamp (T2): 秒數(shù)部分:0xEB2368FC (3949187836秒) 小數(shù)部分:0xFB5DFCAD (轉(zhuǎn)換為 0.982354秒) |
| 40 - 47 | eb2368fcfb618c14 |
Transmit Timestamp (T3): 秒數(shù)部分:0xEB2368FC (3949187836秒) 小數(shù)部分:0xFB618C14 (轉(zhuǎn)換為 0.983214秒) |
8實(shí)現(xiàn)過程
接下來,我們?cè)赪55MH32上實(shí)現(xiàn)SNTP授時(shí)功能。
注意:因?yàn)楸臼纠枰L問互聯(lián)網(wǎng),請(qǐng)確保 W55MH32的網(wǎng)絡(luò)環(huán)境及配置能夠正常訪問互聯(lián)網(wǎng)。
步驟一:初始化RTC
RTC_Init();
RTC_Init()函數(shù)具體內(nèi)容如下:
uint8_t RTC_Init(void)
{
uint8_t temp = 0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR和BKP外設(shè)時(shí)鐘
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON); //設(shè)置外部低速晶振(LSE),使用外設(shè)低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250) //檢查指定的RCC標(biāo)志位設(shè)置與否,等待低速晶
振就緒
{
temp++;
delay_ms(10);
}
if (temp >= 250) return 1; //初始化時(shí)鐘失敗,晶振有問題
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //設(shè)置RTC時(shí)鐘(RTCCLK),選擇LSE作為RTC時(shí)鐘
RCC_RTCCLKCmd(ENABLE); //使能RTC時(shí)鐘
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_EnterConfigMode(); // 允許配置
RTC_SetPrescaler(32767); //設(shè)置RTC預(yù)分頻的值,計(jì)算方式32768/(32767+1) = 1Hz 周期剛好是1秒。
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_Set(1900, 1, 1, 0, 0, 1); //設(shè)置時(shí)間
RTC_ExitConfigMode(); //退出配置模式
RTC_WaitForSynchro(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中斷
RTC_WaitForLastTask(); //等待最近一次對(duì)RTC寄存器的寫操作完成
RTC_NVIC_Config(); //RCT中斷分組設(shè)置
RTC_Get(); //更新時(shí)間
return 0; //ok
}
步驟二:通過DNS解析SNTP服務(wù)器的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()函數(shù)需要傳入四個(gè)參數(shù),分別是使用的socket通道號(hào),sntp服務(wù)器地址,時(shí)區(qū),socket緩存,在這個(gè)函數(shù)中,我們會(huì)進(jìn)行SNTP報(bào)文組包操作,并將傳入的socket通道號(hào),時(shí)區(qū),socket緩存注冊(cè)到庫中,具體內(nèi)容如下:
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);
}
步驟四:發(fā)送SNTP請(qǐng)求報(bào)文獲取時(shí)間
while (1) //上電自動(dòng)獲取時(shí)間
{
if (SNTP_run(&date))
{
RTC_Set(date.yy, date.mo, date.dd, date.hh, date.mm, date.ss);
break;
}
}
接著,我們需要運(yùn)行SNTP_run()函數(shù)來執(zhí)行發(fā)送報(bào)文以及解析報(bào)文的操作,當(dāng)成功獲取到時(shí)間后,我們?cè)O(shè)置到RTC中。
SNTP_run()函數(shù)需要傳入一個(gè)時(shí)間結(jié)構(gòu)體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()函數(shù)定義如下:
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;
}
在這里會(huì)執(zhí)行一個(gè)UDP的狀態(tài)機(jī),當(dāng)socket處于SOCK_UDP狀態(tài)時(shí),首先會(huì)執(zhí)行sendto發(fā)送初始化SNTP時(shí)組裝的SNTP請(qǐng)求報(bào)文,然后是監(jiān)聽服務(wù)器響應(yīng)。
步驟五:解析SNTP響應(yīng)報(bào)文
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;
}
當(dāng)Sn_RX_RSR (Socket n空閑接收緩存寄存器)的值大于0時(shí),說明服務(wù)器給W55MH32返回了響應(yīng)。首先會(huì)通過recvfrom()函數(shù)讀取響應(yīng)報(bào)文,然后執(zhí)行g(shù)et_seconds_from_ntp_server解析時(shí)間。
注意:get_seconds_from_ntp_server()函數(shù)目前只解析了服務(wù)器響應(yīng)時(shí)間戳(即最后8個(gè)字節(jié)),沒有減去網(wǎng)絡(luò)延遲。
get_seconds_from_ntp_server()函數(shù)定義如下:
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);
}
然后調(diào)用calcdatetime()函數(shù)將調(diào)整后的秒數(shù)轉(zhuǎn)換為年、月、日、時(shí)、分、秒的具體時(shí)間格式:
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;
}
最后在主循環(huán)1秒打印一次當(dāng)前時(shí)間:
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運(yùn)行結(jié)果
燒錄例程運(yùn)行后,首先進(jìn)行了PHY鏈路檢測(cè),然后是通過DHCP獲取網(wǎng)絡(luò)地址并打印網(wǎng)絡(luò)地址信息,最后,通過SNTP獲取到時(shí)間后賦值給RTC,然后主循環(huán)1秒打印一次當(dāng)前時(shí)間。如下圖所示:

10總結(jié)
本文講解了如何在W55MH32芯片上實(shí)現(xiàn)SNTP授時(shí)功能,通過實(shí)例詳細(xì)展示了從SNTP服務(wù)器同步時(shí)間的實(shí)現(xiàn)流程,包括時(shí)間請(qǐng)求、響應(yīng)解析和本地時(shí)間校準(zhǔn)等核心步驟。文章還對(duì)SNTP的應(yīng)用場(chǎng)景進(jìn)行了分析,幫助讀者理解其在時(shí)間同步中的實(shí)際應(yīng)用價(jià)值。
下一篇文章我們將講解SMTP協(xié)議的原理及在郵件通信中的應(yīng)用,同時(shí)講解如何在W55MH32芯片上實(shí)現(xiàn)SMTP功能,敬請(qǐng)期待!
WIZnet是一家無晶圓廠半導(dǎo)體公司,成立于 1998年。產(chǎn)品包括互聯(lián)網(wǎng)處理器 iMCU?,它采用 TOE(TCP/IP卸載引擎)技術(shù),基于獨(dú)特的專利全硬連線 TCP/IP。iMCU?面向各種應(yīng)用中的嵌入式互聯(lián)網(wǎng)設(shè)備。
WIZnet在全球擁有 70多家分銷商,在香港、韓國、美國設(shè)有辦事處,提供技術(shù)支持和產(chǎn)品營銷。
香港辦事處管理的區(qū)域包括:澳大利亞、印度、土耳其、亞洲(韓國和日本除外)。
審核編輯 黃宇
-
以太網(wǎng)
+關(guān)注
關(guān)注
41文章
5994瀏覽量
180752 -
sntp
+關(guān)注
關(guān)注
0文章
6瀏覽量
3918
發(fā)布評(píng)論請(qǐng)先 登錄
第十章 W55MH32中斷應(yīng)用概覽
【PDF】C++ GUI Programming with Qt 4 中文版(第一章至第十章)目錄版
自己編的信號(hào)與系統(tǒng)(西安電子科大版)第十章課后答案...
明德?lián)P視頻分享點(diǎn)撥FPGA課程--第十章 ?GVIM的使用
第二章 W55MH32 DHCP示例
第五章 W55MH32 UDP示例
第九章 W55MH32 HTTP Server示例
第十四章 W55MH32 TFTP示例
第十五章 W55MH32 SNMP示例
第十六章 W55MH32 PING示例
第十八章 W55MH32 FTP_Server示例
第三十章 W55MH32 HTTP_Server&NetBIOS示例
第十章 W55MH32 SNTP示例
評(píng)論