)
簡介這是一份基于STM32F407VE的W5500以太網(wǎng)通信工程采用HAL庫并集成WIZnet最新驅動面向嵌入式開發(fā)者及物聯(lián)網(wǎng)、工業(yè)控制場景已配置好時鐘、GPIO、SPI、中斷與協(xié)議?;A可直接編譯運行以驗證TCP/UDP通信也可作為應用起點。壓縮包共206個文件、約8.22MB以66個頭文件與46個C源文件為主體涵蓋uvprojx/uvoptx工程配置、ioc圖形化設置、hex/axf固件、sct鏈接腳本及清理編譯中間文件的bat腳本目錄按Hardware、Internet、W5500、Sys等模塊劃分并含utility.c/h工具函數(shù)與board_config.h硬件定義。另有README說明、License授權、CMSIS與RTE組件管理及JLink調(diào)試配置已有40人學習適合需快速搭建W5500應用、減少驅動移植與工程配置成本的開發(fā)者。1. 用STM32F407VE硬接W5500到底比其他方案省在哪如果你正在做一個需要聯(lián)網(wǎng)上報數(shù)據(jù)的設備比如工業(yè)采集器、智能網(wǎng)關或者遠程控制板手里又恰好選中了STM32F407VE那么以太網(wǎng)通信大概率會在兩條路線之間糾結用LwIP軟件協(xié)議棧還是用W5500這種硬件協(xié)議棧芯片。F407VE本身不帶PHY要用內(nèi)部MAC就得外掛PHY芯片再配LwIP光回調(diào)、信號量、內(nèi)存池就能折騰掉一大半開發(fā)周期而W5500把TCP/IP協(xié)議棧做進了芯片內(nèi)部MCU端只需要通過HAL庫配一路SPI和幾個GPIO協(xié)議棧的收發(fā)、重傳、分片全交給硬件去干。這篇筆記講的就是這條落地路線用HAL庫把W5500官方最新驅動跑在F407VE上從接線、CubeMX配置到驅動移植和TCP收發(fā)再到那些最容易翻車的現(xiàn)場一次講清楚。適合手里有F407VE開發(fā)板或自繪板、想快速把以太網(wǎng)跑進量產(chǎn)節(jié)奏的工程師。2. W5500和STM32F407VE的連接方式與CubeMX配置SPI、復位、中斷三個關鍵點W5500對外接口很干凈一路SPI從機、一個片選、一個復位、一個中斷輸出。真正決定通信能不能穩(wěn)定的就這三組信號。這一章先講物理接線再講CubeMX里SPI1、復位腳和外部中斷的配置每一步都會說清楚為什么這么設。2.1 引腳連接表五個信號一個都不能錯常見做法是讓W5500掛在SPI1上因為SPI1掛在APB2上時鐘源最高能到84MHz配置后實際SPI時鐘還有上限后續(xù)如果想把SPI時鐘提上去SPI1的裕量比SPI2和SPI3都大。下面這張表是我常用的一組引腳分配W5500信號W5500引腳STM32F407VE引腳說明SCLKSCLKPA5 (SPI1_SCK)SPI時鐘空閑低電平MOSIMOSIPA7 (SPI1_MOSI)主機輸出接W5500的MOSIMISOMISOPA6 (SPI1_MISO)主機輸入接W5500的MISOSCSSCSnPA4片選低有效軟件控制RSTNRSTnPB0復位低有效普通推挽輸出INTNINTnPA0中斷輸出低有效下降沿觸發(fā)接線時有個容易忽略的點W5500的供電和IO電平都是3.3VF407VE的IO雖然部分引腳兼容5V但既然都是3.3V系統(tǒng)不要偷懶把W5500模塊插到5V供電的轉換座上。模塊上如果自帶電平轉換另說裸芯片或最小系統(tǒng)板必須統(tǒng)一3.3V。另外SCSn和INTn這兩個腳在W5500內(nèi)部都有上拉但這不意味著可以不接。SCSn必須由MCU明確控制INTn必須進MCU的外部中斷否則驅動狀態(tài)更新只能靠輪詢實時性和CPU占用都吃虧。2.2 CubeMX里SPI1配置模式0、10MHz以內(nèi)、軟件片選在CubeMX里選擇STM32F407VE后先把SPI1打開Mode設為Full-Duplex MasterNSS信號設為Disable片選完全交給普通GPIO控制。W5500的SPI時序支持模式0和模式3工程里絕大多數(shù)例程用模式0也就是CPOL0、CPHA0。如果配置成模式1或模式2讀出來的寄存器大概率是0xFF后面排查時會非常迷惑。SPI時鐘先別急著拉滿。W5500手冊上給的SCLK上限在不同參考資料里說法不一致保險的做法是先設到10MHz以內(nèi)跑通功能再一點點往上探。CubeMX里SPI1的波特率預設為10.5MHz84MHz除以8分頻這個值作為起步很合適。參數(shù)表如下配置項取值說明ModeFull-Duplex Master全雙工主機NSSDisable軟件控制片選Data Size8 BitsW5500寄存器都是8位寬First BitMSB First與W5500幀格式一致CPOLHighNo設Low空閑時鐘為低CPHA1 Edge模式0第一個邊沿采樣Baud Rate84MHz / 8 10.5MHz調(diào)通后再提速有個隱藏坑點CubeMX生成代碼后SPI的片選引腳雖然在圖形界面里掛到了PA4但如果你勾選了硬件NSSSPI外設會在傳輸過程中自動拉低NSS造成和軟件片選打架。最好把PA4配成普通推挽輸出初始化代碼里默認輸出高電平片選的拉低和抬升全部在驅動移植層手動完成。2.3 復位與中斷引腳的初始化上電時序和EXTI優(yōu)先級PB0作為復位輸出腳CubeMX里配成GPIO_Output默認輸出高電平。W5500在上電后需要一次完整的復位脈沖拉低、保持、拉高、等待穩(wěn)定。很多人只拉低幾微秒就釋放結果芯片還沒完成內(nèi)部初始化后續(xù)讀寄存器全失敗。我一般拉低不少于1ms拉高后延時100ms再訪問寄存器雖然手冊上給的復位時間沒有這么夸張但工程上多等一點換穩(wěn)定性是劃算的。PA0作為外部中斷輸入CubeMX里配成GPIO_EXTI0觸發(fā)方式選Falling Edge因為W5500的INTn是低有效輸出有中斷事件時拉低。外部中斷的優(yōu)先級要根據(jù)你的系統(tǒng)來定常見做法是比SysTick低、比主循環(huán)邏輯高在NVIC里設搶占優(yōu)先級為5、子優(yōu)先級為0這類中段數(shù)值避免中斷里做的輕量處理影響關鍵實時任務。外部中斷回調(diào)里應當只置標志位和處理狀態(tài)記錄不要直接在中斷里操作SPI。原因很直接HAL庫的SPI收發(fā)是阻塞式的如果主循環(huán)同時也在操作SPI總線中斷和主循環(huán)會穿插訪問W5500輕則數(shù)據(jù)錯位重則卡死。真要在中斷里清W5500事件標志也必須通過下面要講的臨界區(qū)保護來做。3. 移植W5500官方最新驅動把VFS層接到HAL庫SPI驅動移植是這條路線里最核心的一步。W5500官方驅動歷史上幾經(jīng)改版但移植思路一直沒變芯片驅動層負責拼地址幀和控制字節(jié)底層IO函數(shù)需要你按實際MCU去實現(xiàn)。HAL庫環(huán)境下這些IO函數(shù)就是包一層HAL_SPI_TransmitReceive再配合片選和臨界區(qū)回調(diào)。3.1 驅動文件怎么拷、接口層要補什么從官方驅動庫里拷貝文件時核心是Ethernet目錄下的那組源文件wizchip適配層、W5500芯片驅動層和socket接口層。這三個部分是一套完整的依賴關系少一個都編譯不過所以直接整個目錄拷進工程再按需保留其他內(nèi)容。socket接口層之上還有Internet目錄里面是DNS、DHCP這些上層應用協(xié)議。手頭工程如果只需要靜態(tài)IP直連先不拷這些減少干擾項。等以太網(wǎng)通信跑通后再按需加入DHCP。拷完后要補的就是底層移植文件通常命名為w5500_port.c之類。它需要向驅動注冊三樣東西片選控制函數(shù)、SPI單字節(jié)讀寫函數(shù)、臨界區(qū)進入退出函數(shù)。新版驅動通過注冊回調(diào)的方式掛接老版本則直接改寫底層宏定義具體接口名因版本而異但要做的事情就這三件。3.2 移植層代碼片選、SPI收發(fā)、臨界區(qū)三個回調(diào)下面這段是移植層的核心邏輯對應W5500芯片對SPI時序的全部要求/* 片選低有效 */ static void w5500_cs_select(void) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); } static void w5500_cs_deselect(void) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } /* 單字節(jié)讀寫SPI是全雙工收和發(fā)要同時進行 */ static uint8_t w5500_spi_readbyte(void) { uint8_t tx 0xFF; uint8_t rx 0; HAL_SPI_TransmitReceive(hspi1, tx, rx, 1, HAL_MAX_DELAY); return rx; } static void w5500_spi_writebyte(uint8_t data) { uint8_t rx 0; HAL_SPI_TransmitReceive(hspi1, data, rx, 1, HAL_MAX_DELAY); } /* 臨界區(qū)保護裸機環(huán)境可以簡單關中斷 */ static void w5500_cris_enter(void) { __disable_irq(); } static void w5500_cris_exit(void) { __enable_irq(); } /* 注冊到驅動 */ void w5500_port_init(void) { reg_wizchip_cs_cbfunc(w5500_cs_select, w5500_cs_deselect); reg_wizchip_cris_cbfunc(w5500_cris_enter, w5500_cris_exit); reg_wizchip_spi_cbfunc(w5500_spi_readbyte, w5500_spi_writebyte); }注意這里用的是HAL_SPI_TransmitReceive而不是分開的Transmit或Receive原因是SPI本質上是移位交換主機發(fā)送一個字節(jié)的同時必然收到一個字節(jié)。如果用HAL_SPI_Receive去收數(shù)據(jù)主機端依然要產(chǎn)生時鐘雖然HAL庫能生成時鐘但多一層封裝不如直接用收發(fā)一體清晰。片選函數(shù)里沒有做延時。有些工程師擔心CS拉低后W5500還沒準備好于是在select和第一個字節(jié)之間插入微秒級延時這是多余的。只要SPI時鐘頻率不過高CS拉低到采樣之間由SPI硬件時序保證足夠。真遇到數(shù)據(jù)錯位先去查SPI模式而不是亂加延時。臨界區(qū)在裸機環(huán)境直接關中斷即可。如果跑RTOS這里應該換成互斥量或調(diào)度鎖否則中斷回調(diào)里操作wizchip相關寄存器時會發(fā)生上下文競爭。3.3 驅動初始化順序先復位芯片再校驗版本和網(wǎng)關拷完驅動、寫完移植層接下來是初始化順序。這個順序錯一步都會讓后續(xù)操作表現(xiàn)得很詭異尤其是先初始化網(wǎng)絡參數(shù)再復位芯片的做法會把W5500內(nèi)部寄存器清掉調(diào)試時以為是代碼問題其實是順序問題。uint8_t ver 0; /* 1. 硬件復位W5500 */ HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET); HAL_Delay(100); /* 2. 注冊IO回調(diào) */ w5500_port_init(); /* 3. 讀取版本寄存器確認SPI鏈路已通 */ if (ctlwizchip(CW_GET_VERSION, ver) ! 0) { /* 讀版本失敗多半是SPI或復位問題 */ } if (ver ! 0x04) { /* W5500版本號固定為0x04讀到別的值說明鏈路不對 */ } /* 4. 初始化wizchip內(nèi)部狀態(tài) */ ctlwizchip(CW_INIT_WIZCHIP); /* 5. 配置網(wǎng)絡參數(shù)靜態(tài)IP起步 */ wiz_NetInfo netinfo { .mac {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}, .ip {192, 168, 1, 20}, .sn {255, 255, 255, 0}, .gw {192, 168, 1, 1}, .dns {192, 168, 1, 1}, .dhcp 0, }; ctlwizchip(CW_SET_NETINFO, netinfo);版本寄存器讀到0x04這一步是整個移植的分水嶺。如果這一步能通過說明SPI時序、片選、復位都正常后面的socket操作都是在調(diào)參數(shù)如果這一步讀不出來先別急著查業(yè)務代碼回去對著第5章的避坑清單查物理層和配置層。DHCP在第一步調(diào)通時不建議開因為辦公室里不一定每臺路由器都有DHCP服務或者DHCP響應慢會干擾你判斷到底是驅動問題還是網(wǎng)絡環(huán)境問題。靜態(tài)IP直連同網(wǎng)段PC是最干凈的最小驗證環(huán)境。4. 跑通第一路TCP客戶端、服務器最小代碼與狀態(tài)機驅動初始化完成后W5500相當于一個具備完整TCP/IP能力的網(wǎng)絡設備剩下的工作就是在8個socket上執(zhí)行open、connect、listen、send、recv。這一章分別寫出TCP客戶端和TCP服務器的標準流程再講清中斷狀態(tài)機怎么處理和主循環(huán)配合。4.1 TCP客戶端連接服務器并收發(fā)數(shù)據(jù)先看看客戶端側的最小代碼。假設目標服務器是192.168.1.100:8080本地端口用6000uint8_t sn 0; int8_t ret; /* 分配socket0的收發(fā)緩沖區(qū)每個2KB */ setSn_RXBUF(sn, 0x02); setSn_TXBUF(sn, 0x02); /* 打開socket指定TCP協(xié)議和本地端口 */ ret socket(sn, Sn_MR_TCP, 6000, 0); if (ret ! sn) { /* socket分配失敗可能是sn已被占用 */ } /* 連接服務器 */ uint8_t server_ip[4] {192, 168, 1, 100}; uint16_t server_port 8080; ret connect(sn, server_ip, server_port); if (ret ! SOCK_OK) { /* 連接失敗檢查服務器是否可達、端口是否開放 */ } /* 連接成功后進入主循環(huán) */ while (1) { uint16_t len getSn_RX_RSR(sn); if (len 0) { uint8_t buf[1460]; int16_t rlen recv(sn, buf, len sizeof(buf) ? sizeof(buf) : len); if (rlen 0) { /* 收多少回多少做回顯測試 */ send(sn, buf, rlen); } } /* 主循環(huán)里還要定期檢查連接狀態(tài)見4.3節(jié) */ }setSn_RXBUF(sn, 0x02)里的0x02表示2KBW5500每個socket的發(fā)送和接收緩沖可獨立設置為1KB到16KB總共加起來32KB。調(diào)大收發(fā)緩沖能提升吞吐但會擠占其他socket的空間。8個socket全部使用默認2KB配置在簡單場景下完全夠用。recv函數(shù)返回的是實際接收字節(jié)數(shù)如果返回值是個負數(shù)去查錯誤碼定義常見的是SOCK_BUSY意思是socket正在忙需要稍后重試。newdriver里有拿返回值當長度用的坑記得先判斷正負再處理緩沖區(qū)。4.2 TCP服務器監(jiān)聽端口并處理新連接服務器側流程差異不大只是把connect換成listen然后查狀態(tài)寄存器確認是否進入監(jiān)聽uint8_t sn 0; setSn_RXBUF(sn, 0x04); setSn_TXBUF(sn, 0x04); if (socket(sn, Sn_MR_TCP, 6000, 0) ! sn) { /* socket打開失敗 */ } listen(sn); if (getSn_SR(sn) ! SOCK_LISTEN) { /* 沒有進入監(jiān)聽狀態(tài)檢查波特率和socket狀態(tài) */ } while (1) { /* 有客戶端連上來時Sn_SR變成SOCK_ESTABLISHED */ if (getSn_SR(sn) SOCK_ESTABLISHED) { uint16_t len getSn_RX_RSR(sn); if (len 0) { /* 與客戶端收發(fā)數(shù)據(jù) */ } } }W5500在服務器模式下沒有顯式的accept函數(shù)。客戶端連接建立后Sn_SR自動從SOCK_LISTEN變成SOCK_ESTABLISHED同時產(chǎn)生Sn_IR_CON中斷。應用層只需要持續(xù)查詢狀態(tài)或者依賴中斷事件來感知新連接。有個邊界要提前知道W5500每個socket在監(jiān)聽模式下同一時刻只能接受一個TCP連接。第二個客戶端連上來時W5500不會主動處理表現(xiàn)為連接不上或超時。需要多連接時要么一個socket只服務一路客戶端要么改用多socket監(jiān)聽不同端口這個坑在第5章還會細說。4.3 中斷處理SIR/Sn_IR按位解析輪到誰就服務誰W5500的中斷源分兩級全局中斷寄存器SIR指示哪個socket有事件Sn_IR指示具體是什么事件。外部中斷回調(diào)里做的事件解析如下void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin INT_Pin) { uint8_t sir getSIR(); for (uint8_t i 0; i 8; i) { if (sir (1 i)) { uint8_t ir getSn_IR(i); if (ir Sn_IR_CON) { g_event[i] | EVT_CONNECT; } if (ir Sn_IR_RECV) { g_event[i] | EVT_RECV; } if (ir Sn_IR_DISCON) { g_event[i] | EVT_DISCON; } setSn_IR(i, ir); /* 寫1清除讀到的ir原樣寫回 */ } } setSIR(sir); } }中斷回調(diào)里只置應用層的g_event標志不直接調(diào)用recv或send。主循環(huán)檢測到標志后再去操作socket這樣避免SPI總線并發(fā)訪問。同時也能保證中斷處理的時間非常短不會拖累系統(tǒng)實時性。很多新手的疑問是初始化時INT引腳已經(jīng)觸發(fā)了大量的中斷事件主循環(huán)還沒來得及處理flags會不會重復累積W5500的中斷標志寄存器是邊沿型事件讀后如果不清除就會一直保持如果清了就不會重觸發(fā)。這里的關鍵是主循環(huán)處理完數(shù)據(jù)后再去清一次Sn_IR。如果主循環(huán)還沒處理完就把中斷清了事件就丟了如果處理完不清下次中斷不會再觸發(fā)。常見做法是中斷回調(diào)里清一次主循環(huán)里處理完業(yè)務后再主動讀一次狀態(tài)寄存器確認沒有新的pending。5. W5500移植最容易翻車的5個現(xiàn)場現(xiàn)象、原因、解決這一章是拿真金白銀換來的經(jīng)驗。以下5個問題在論壇和實際項目里反復出現(xiàn)每條都按現(xiàn)象、原因、解決的順序拆開講排查時可以直接對號入座。5.1 讀版本寄存器返回0xFFSPI模式或時鐘先查讀VERSIONR寄存器一直得到0xFF是最常見的首發(fā)癥狀。出現(xiàn)這個現(xiàn)象時程序往往已經(jīng)正確配置了SPI、也注冊了回調(diào)但就是讀不到芯片的應答。原因分三類一是SPI模式配錯W5500要求模式0或模式3不少人用了模式2CPOL1、CPHA0數(shù)據(jù)在錯誤的邊沿被采樣二是片選控制失效CS引腳沒用軟件GPIO控制、而是被SPI硬件NSS接管導致CS時序錯亂三是SPI時鐘過快超出W5500的實際承受范圍尤其在長走線或模塊供電不穩(wěn)時更容易出錯。解決方法是先降速排查。把SPI波特率降到2MHz左右確認模式為CPOL0、CPHA0片選用普通GPIO拉低不放再讀版本寄存器。如果還是0xFF用示波器抓SCLK和CS的時序看CS是否在傳輸期間穩(wěn)定拉低、SCLk是否有毛刺。這一步能排除九成的問題。5.2 復位后立刻訪問寄存器不成功復位時序不夠現(xiàn)象是代碼流程看著完全正常但第一次讀版本寄存器失敗復位一次后又能成功或者上電后必須手動按一下復位鍵才能通信。原因是W5500內(nèi)部在復位釋放后需要時間完成自檢和PHY初始化。復位拉低時間太短、釋放后等待時間不足芯片還沒準備好就訪問寄存器自然會失敗。解決方法是拉低RSTn至少10ms釋放后延時100ms再執(zhí)行任何SPI訪問。如果板子上W5500的電源濾波電容偏大上電時間會更長延時要相應加大。這個延時只出現(xiàn)在初始化階段不影響運行時的響應速度。5.3 中斷標志清不掉讀到的ir原樣寫回不是寫0現(xiàn)象是調(diào)試時發(fā)現(xiàn)中斷回調(diào)反復被觸發(fā)即使在回調(diào)里執(zhí)行了setSn_IR(sn, 0)中斷依然不斷進來仿佛永遠清不掉。原因W5500的中斷標志寄存器是寫1清除W1C機制。寫0沒有任何效果只有把對應位寫1才會清除該中斷標志。很多從其他芯片轉過來的工程師習慣寫0清標志在W5500這里就失效了。解決方法是把讀出來的ir值原樣寫回setSn_IR(i, ir);比如讀到的ir是0x04Sn_IR_RECV就寫回0x04。如果想清除多個事件把要清除的位按位或起來再寫。全局中斷SIR寄存器同樣是寫1清除不要寫0。5.4 TCP服務器只能接一個連接W5500硬件就這么設計現(xiàn)象開發(fā)板上跑著TCP server第一個手機或PC連上來收發(fā)正常第二個設備再連接時一直連不上抓包發(fā)現(xiàn)服務端完全沒有響應。原因W5500的每個socket在同一時刻只維護一個TCP連接。監(jiān)聽狀態(tài)下一旦有客戶端連入Sn_SR就從SOCK_LISTEN變成SOCK_ESTABLISHED芯片不會再處理新的SYN包。這是硬件TCP/IP協(xié)議棧的邊界不是代碼bug。解決辦法分兩種一是產(chǎn)品本身只需要單連接比如一臺設備對一臺配置終端那就接受這個限制在文檔里寫清楚二是需要多連接就在同一端口開放多個socket輪詢監(jiān)聽或者改走UDP。注意同一個端口號和不同socket綁定是可行的多個socket都執(zhí)行l(wèi)isten同一端口由硬件負載均衡但實現(xiàn)復雜度會上升一般場景不如直接換UDP或加一層應用層轉發(fā)。5.5 收發(fā)一段時間后丟包接收緩沖被讀慢或緩沖區(qū)分配不合理現(xiàn)象TCP通信剛連上時收發(fā)都正常持續(xù)跑幾分鐘后開始丟包或者吞吐量驟降重啟后又恢復過陣子再犯。原因W5500的接收緩沖區(qū)是共享的如果某個socket的接收緩沖太小應用層讀取不及時硬件FIFO滿了之后新的數(shù)據(jù)包會被直接丟棄。TCP協(xié)議棧不會無限制地重傳超過重傳次數(shù)后連接就會被斷開。解決方法是先從軟件上保證recv的調(diào)度頻率。中斷置位后主循環(huán)要盡快讀取數(shù)據(jù)不要在recv之前插入大延時。再把接收緩沖區(qū)調(diào)大比如setSn_RXBUF(sn, 0x08)設為8KB同時觀察getSn_RX_RSR的返回值是否持續(xù)增長。如果增長到接近緩沖區(qū)上限后掉回0說明發(fā)生了溢出丟包。還有一種隱蔽情況recv的len參數(shù)直接用了getSn_RX_RSR的返回值而這個返回值可能大于你定義的buf長度導致緩沖區(qū)溢出。標準寫法是取RSR和buf容量的較小值防止棧被寫穿。6. 收尾技巧用UDP回顯拉通全鏈路再談SPI提速TCP鏈路調(diào)通后我習慣再用一個UDP回顯做交叉驗證因為UDP無連接、無重傳能直接暴露最底層的收發(fā)路徑是否完整。如果UDP回顯都穩(wěn)定那么TCP出現(xiàn)問題時就可以把嫌疑范圍縮小到驅動參數(shù)或網(wǎng)絡環(huán)境而不是SPI物理鏈路。6.1 一個UDP socket測通整條收發(fā)路徑UDP不需要connect和listen打開socket后直接收發(fā)uint8_t sn 1; socket(sn, Sn_MR_UDP, 9000, 0); uint8_t buf[1460]; while (1) { uint16_t len getSn_RX_RSR(sn); if (len 0) { uint8_t peer_ip[4]; uint16_t peer_port; int16_t rlen recvfrom(sn, buf, len, peer_ip, peer_port); if (rlen 0) { sendto(sn, buf, rlen, peer_ip, peer_port); } } }用PC上的網(wǎng)絡調(diào)試助手向板子的IP:9000發(fā)一串數(shù)據(jù)板子原樣回傳能連續(xù)跑幾百個包不丟說明SPI鏈路、W5500緩沖管理、中斷機制都正常。UDP測試的另一個價值是排除TCP狀態(tài)機干擾——如果UDP正常而TCP異常問題多半出在socket狀態(tài)轉換或網(wǎng)絡參數(shù)上而不是底層硬件。6.2 提高SPI時鐘的驗證方法和判斷標準鏈路穩(wěn)定后可以試著提速SPI。每次在CubeMX里把波特率上調(diào)一檔比如從10.5MHz提到16MHz、21MHz重新編譯燒錄然后跑一遍UDP回顯壓力測試。判斷標準是連續(xù)跑10分鐘不出現(xiàn)0xFF、不丟包、中斷響應正常。提速度有個前置條件檢查PCB走線。如果W5500模塊是通過杜邦線連到開發(fā)板的線長超過10cm高速SPI會因反射和串擾變得非常不可靠此時強行提速屬于玄學調(diào)參。自繪板上W5500的SCLK走線要做等長處理盡量短遠離晶振和電感。提速后如果出現(xiàn)偶發(fā)丟包先別急著降速試著在片選函數(shù)里加一個SPI速率相關的空操作延時。雖然我之前說不要亂加延時但在高SPI頻率下CS拉低后立即產(chǎn)生第一個時鐘邊沿如果W5500從機還需要一點建立時間加一個極短的__NOP()可能有幫助。這個要根據(jù)示波器實測決定不要盲調(diào)。以我自己的經(jīng)驗SPI時鐘穩(wěn)定運行在16MHz左右是一個甜點區(qū)功耗不高且CPU占用率遠低于純軟件協(xié)議棧。超過21MHz后收益衰減明顯排查成本和硬件要求反而直線上升。所以日常做的產(chǎn)品我一般鎖死在16MHz省下的時間拿去完善應用協(xié)議更值得。最后說一個習慣W5500工程做完后我會把讀版本寄存器、連接服務器、UDP回顯這三個節(jié)點用串口打日志輸出每個節(jié)點一個固定前綴比如[W5500][VER]、[W5500][TCP]。每次燒錄新板卡不用接調(diào)試器光看串口就知道問題出在物理層、驅動層還是網(wǎng)絡層。這個習慣幫我省掉了大量反復插拔調(diào)試器的時間希望幫到你。本文還有配套的精品資源點擊獲取