調(diào)試實(shí)戰(zhàn):從Nucleo-F767ZI原理圖到LWIP ping通)
1. 拿到Nucleo-F767ZI之后先別急著點(diǎn)生成代碼很多人拿到Nucleo-F767ZI這塊板子的第一反應(yīng)是打開STM32CubeMX選好芯片型號然后直奔Connectivity選項卡去勾ETH。我當(dāng)初也是這么干的結(jié)果在生成的工程里折騰了整整兩天PHY死活協(xié)商不上ping都ping不通。后來才發(fā)現(xiàn)問題根本不在軟件配置上而是我從一開始就忽略了硬件層面幾個要命的細(xì)節(jié)。Nucleo-F767ZI這塊板子在以太網(wǎng)這塊的設(shè)計比較特殊。它板載了一顆LAN8742A的PHY芯片通過RMII接口和STM32F767ZIT6的MAC層連接。RMII相比MII少了將近一半的信號線但代價是參考時鐘的要求更嚴(yán)格。板子上給PHY和MAC提供的50MHz時鐘來自同一個源——ST-LINK那側(cè)的MCO輸出經(jīng)過一個時鐘緩沖器分出來的。這意味著如果你在CubeMX里把時鐘樹配錯了RMII的REF_CLK就直接廢了PHY連最基本的鏈路建立都做不到。所以我的建議是在打開CubeMX之前先把板子的原理圖翻出來看一遍。Nucleo-F767ZI的原理圖在ST官網(wǎng)能下到重點(diǎn)看三個地方PHY的地址配置引腳、RMII的信號連接、復(fù)位和電源引腳。這三處任何一處理解錯了后面軟件怎么調(diào)都是白搭。這篇文章我會按照實(shí)際調(diào)試的順序來寫先從原理圖里挖出那些CubeMX界面上看不到的信息再講時鐘樹和引腳配置的坑然后是PHY初始化和LWIP的對接最后是實(shí)測中遇到的各種奇怪現(xiàn)象和排查思路。適合已經(jīng)上手過STM32、但對以太網(wǎng)外設(shè)不太熟悉的開發(fā)者也適合那些CubeMX點(diǎn)了一遍但網(wǎng)絡(luò)死活不通的朋友。2. 原理圖里藏著的三個關(guān)鍵信息CubeMX不會告訴你2.1 PHY地址到底是多少LAN8742A的SMIStation Management Interface也就是MDIO/MDC那套地址是由PHYAD[0]引腳在上電復(fù)位時鎖存的。Nucleo-F767ZI的原理圖上LAN8742A的PHYAD0引腳通過一個電阻拉高或拉低決定了它的SMI地址。我翻原理圖確認(rèn)過這塊板子上LAN8742A的PHY地址是0x00。這個值在CubeMX里配置ETH外設(shè)的時候需要填進(jìn)去但CubeMX的ETH配置界面里并沒有一個叫“PHY Address”的顯眼輸入框——它藏在Parameter Settings選項卡的PHY Address字段里默認(rèn)值就是0。如果你用的是自己畫的板子PHY地址可能不同一定要根據(jù)原理圖上的PHYAD引腳接法來確認(rèn)。提示LAN8742A的SMI地址范圍是0x00到0x1F由PHYAD[4:0]五個引腳決定。Nucleo-F767ZI上只用了PHYAD0其余接地所以地址就是0x00。2.2 RMII的REF_CLK從哪來這是最容易踩坑的地方。RMII接口需要一個50MHz的參考時鐘這個時鐘可以來自MAC側(cè)STM32輸出給PHY也可以來自PHY側(cè)PHY輸出給STM32。Nucleo-F767ZI的設(shè)計是PHY提供REF_CLK給MAC。具體來說板子上有一顆25MHz的晶振連接到LAN8742APHY內(nèi)部PLL倍頻到50MHz然后通過REF_CLK引腳輸出給STM32的PA1引腳。PA1在STM32F767上的復(fù)用功能就是ETH_RMII_REF_CLK。這意味著在CubeMX里配置PA1的時候必須把它設(shè)為ETH_RMII_REF_CLK的復(fù)用模式而不是普通的GPIO輸入。如果你漏了這一步STM32的MAC層收不到參考時鐘ETH外設(shè)初始化會直接失敗HAL_ETH_Init返回HAL_ERROR。2.3 復(fù)位引腳和電源使能LAN8742A有一顆復(fù)位引腳NRSTNucleo-F767ZI上它連接到了STM32的某個GPIO上。具體是哪個引腳不同版本的板子可能不一樣我手上這塊是PG3不同批次可能不同務(wù)必以你手頭板子的原理圖為準(zhǔn)。這個復(fù)位引腳在CubeMX里不需要特殊配置但你在寫PHY初始化代碼的時候需要先拉低再拉高給它一個復(fù)位脈沖否則PHY可能處于不確定狀態(tài)。另外LAN8742A的電源使能引腳也是通過GPIO控制的同樣需要確認(rèn)原理圖上的連接。我一般會在CubeMX里把這兩個GPIO都配成輸出模式初始電平設(shè)成復(fù)位有效低電平然后在代碼里手動控制復(fù)位時序。這樣比依賴PHY的上電復(fù)位更可靠。3. CubeMX里的時鐘樹配置一步錯步步錯3.1 RMII_REF_CLK的時鐘源選擇在CubeMX的Clock Configuration界面里有一個容易被忽略的選項ETH的RMII參考時鐘來源。這個選項在Connectivity → ETH → Parameter Settings里叫做RMII clock source有兩個選項External和Internal。Nucleo-F767ZI必須選External因?yàn)镽EF_CLK是PHY提供的。如果你選了InternalSTM32會嘗試從自己的PLL輸出50MHz給PHY但板子上PA1已經(jīng)硬連到PHY的REF_CLK輸出了兩邊打架結(jié)果就是時鐘信號混亂PHY工作不正常。這個選項在CubeMX的界面上不太顯眼我第一次配的時候完全沒注意到默認(rèn)就是External算是運(yùn)氣好。但如果你用的是自己設(shè)計的板子PHY不提供REF_CLK那就必須選Internal并且確保STM32的PA1配置為ETH_RMII_REF_CLK輸出模式。3.2 主時鐘和ETH時鐘的聯(lián)動STM32F767的ETH外設(shè)掛載在AHB1總線上它的時鐘來自PLL的Q輸出或者外部PHY時鐘。在CubeMX里當(dāng)你選了External RMII clock source之后ETH的時鐘就直接來自PA1引腳輸入的50MHz不需要PLL額外輸出。但這里有個隱藏的坑PA1引腳的時鐘輸入需要使能SYSCFG時鐘。CubeMX在生成代碼的時候會自動加上__HAL_RCC_SYSCFG_CLK_ENABLE()但如果你手動改過初始化順序可能會漏掉。我遇到過一種情況ETH初始化偶爾成功偶爾失敗后來發(fā)現(xiàn)是SYSCFG時鐘使能被放在了ETH初始化之后導(dǎo)致PA1的復(fù)用功能沒有正確映射。正確的順序是先使能SYSCFG時鐘再配置GPIO復(fù)用最后初始化ETH外設(shè)。CubeMX生成的代碼默認(rèn)就是這個順序但如果你在MX_ETH_Init之前插入了自己的GPIO初始化代碼就要特別注意。3.3 時鐘樹參數(shù)的實(shí)際計算假設(shè)你用Nucleo-F767ZI板載的8MHz晶振來自ST-LINK的MCO經(jīng)過PLL配置到216MHz的系統(tǒng)時鐘。ETH的RMII時鐘是50MHz來自PHY不經(jīng)過PLL。但**MDIO的時鐘MDC**是由AHB1時鐘分頻得到的分頻系數(shù)在ETH_MACCR寄存器里配置。MDC的時鐘頻率范圍是1MHz到2.5MHz根據(jù)IEEE 802.3標(biāo)準(zhǔn)。STM32F767的AHB1時鐘是216MHz分頻系數(shù)最小是42對應(yīng)約5.14MHz最大是1024對應(yīng)約210kHz。實(shí)際上HAL庫的HAL_ETH_Init會根據(jù)你傳入的ETH_MACConfigTypeDef結(jié)構(gòu)體里的MDCClockDiv字段來配置。我一般設(shè)成AHB1時鐘/102對應(yīng)約2.1MHz在標(biāo)準(zhǔn)范圍內(nèi)。這個值在CubeMX的ETH配置界面里沒有直接暴露需要在生成的代碼里手動改heth.Init.MDCClockDiv或者調(diào)用HAL_ETH_SetMDIOClockRange。4. 引腳配置的細(xì)節(jié)不只是勾選那么簡單4.1 RMII引腳清單和復(fù)用編號Nucleo-F767ZI的RMII接口用了以下引腳引腳功能復(fù)用編號PA1ETH_RMII_REF_CLKAF11PA2ETH_MDIOAF11PA7ETH_RMII_CRS_DVAF11PC1ETH_MDCAF11PC4ETH_RMII_RXD0AF11PC5ETH_RMII_RXD1AF11PB11ETH_RMII_TX_ENAF11PB12ETH_RMII_TXD0AF11PB13ETH_RMII_TXD1AF11PG3PHY復(fù)位GPIO輸出-CubeMX在你勾選ETH外設(shè)并選擇RMII模式后會自動把這些引腳配好。但PG3不會自動配置因?yàn)樗瞧胀℅PIO需要你手動在GPIO選項卡里設(shè)置。4.2 GPIO速度和輸出類型的講究RMII的TX和REF_CLK信號頻率是50MHzGPIO的輸出速度必須設(shè)成Very High。CubeMX默認(rèn)可能會設(shè)成Low或Medium這會導(dǎo)致信號邊沿變緩PHY采樣出錯。我實(shí)測過用Medium速度時ping的丟包率大概在5%左右改成Very High之后丟包率降到0。MDIO和MDC的頻率低得多MDC最高2.5MHz用默認(rèn)的Low速度就夠了。但為了統(tǒng)一我一般把ETH相關(guān)的所有GPIO都設(shè)成Very High省得漏掉。PHY復(fù)位引腳PG3是普通GPIO輸出速度設(shè)成Low就行初始電平設(shè)成Low復(fù)位有效然后在代碼里延時一段時間后再拉高。4.3 那些CubeMX不會自動配置的引腳除了PG3之外還有幾個引腳需要留意PHY的電源使能引腳Nucleo-F767ZI上LAN8742A的電源是常通的不需要GPIO控制。但如果你用的是自己畫的板子可能需要一個GPIO來控制PHY的電源。PHY的中斷引腳LAN8742A有一顆中斷輸出引腳可以連接到STM32的外部中斷輸入。Nucleo-F767ZI上沒有連接所以CubeMX里不需要配置。但如果你要用中斷方式處理鏈路狀態(tài)變化就需要自己接線并在CubeMX里配置EXTI。5. PHY初始化和LWIP對接的實(shí)戰(zhàn)細(xì)節(jié)5.1 HAL_ETH_Init到底做了什么CubeMX生成的代碼里MX_ETH_Init函數(shù)會調(diào)用HAL_ETH_Init。這個函數(shù)做的事情包括復(fù)位ETH外設(shè)的MAC層配置MAC的工作模式全雙工/半雙工、100M/10M配置MDIO時鐘分頻讀取PHY的ID寄存器確認(rèn)PHY存在配置PHY的工作模式自協(xié)商、速度、雙工模式使能MAC的收發(fā)功能其中第4步和第5步是通過SMI接口讀寫PHY寄存器實(shí)現(xiàn)的。如果PHY地址配錯了或者M(jìn)DIO/MDC引腳沒配好HAL_ETH_Init會返回HAL_ERROR并且heth.Instance-MACMIIAR寄存器的值會異常。我遇到過一次HAL_ETH_Init返回錯誤的情況排查后發(fā)現(xiàn)是MDC時鐘分頻設(shè)得太小導(dǎo)致MDC頻率超過了PHY能接受的上限。LAN8742A的MDC最高頻率是12.5MHz有些資料寫的是2.5MHz但實(shí)測12.5MHz也能工作但為了穩(wěn)定還是按標(biāo)準(zhǔn)來控制在2.5MHz以內(nèi)。5.2 PHY自協(xié)商的等待邏輯HAL_ETH_Init里會調(diào)用HAL_ETH_WritePHYRegister和HAL_ETH_ReadPHYRegister來配置PHY的自協(xié)商。自協(xié)商的過程需要時間通常幾十毫秒到幾百毫秒。HAL庫里的等待邏輯比較簡單就是輪詢PHY的BSRBasic Status Register寄存器的自協(xié)商完成位。但這里有個坑如果PHY的復(fù)位沒有做好自協(xié)商可能永遠(yuǎn)完不成。我遇到過一種情況PHY的復(fù)位引腳在CubeMX里配成了高電平初始結(jié)果PHY一直處于復(fù)位狀態(tài)自協(xié)商自然完不成。后來把初始電平改成低然后在代碼里手動拉高問題解決。正確的復(fù)位時序是// 假設(shè)PHY復(fù)位引腳是PG3 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_3, GPIO_PIN_RESET); // 拉低復(fù)位 HAL_Delay(10); // 保持至少10ms HAL_GPIO_WritePin(GPIOG, GPIO_PIN_3, GPIO_PIN_SET); // 釋放復(fù)位 HAL_Delay(100); // 等待PHY穩(wěn)定這段代碼要放在MX_ETH_Init之前否則PHY還沒準(zhǔn)備好HAL_ETH_Init里的PHY讀寫就會失敗。5.3 LWIP的配置要點(diǎn)CubeMX里可以勾選LWIP中間件然后配置IP地址、子網(wǎng)掩碼、網(wǎng)關(guān)等參數(shù)。這里有幾個容易出問題的地方第一PHY的地址和LWIP里的PHY地址要一致。LWIP的ethernetif.c里會調(diào)用HAL_ETH_Init而HAL_ETH_Init用的PHY地址來自heth.Init.PHYAddress。這個值在CubeMX的ETH配置里設(shè)置默認(rèn)是0。如果你在LWIP里改了PHY地址但ETH配置里沒改兩邊不一致PHY讀寫就會失敗。第二LWIP的DHCP和靜態(tài)IP選擇。如果你用靜態(tài)IP確保IP地址和你的路由器在同一網(wǎng)段。我一開始設(shè)了192.168.1.100但路由器是192.168.0.1網(wǎng)段結(jié)果ping不通排查了半天才發(fā)現(xiàn)是網(wǎng)段不對。第三LWIP的內(nèi)存配置。STM32F767有512KB的RAMLWIP的默認(rèn)內(nèi)存配置比較保守可以適當(dāng)調(diào)大。在lwipopts.h里MEM_SIZE默認(rèn)是1600字節(jié)可以調(diào)到4096或更大。PBUF_POOL_SIZE默認(rèn)是16可以調(diào)到32。這些參數(shù)影響網(wǎng)絡(luò)吞吐量調(diào)大之后TCP傳輸速度明顯提升。5.4 中斷優(yōu)先級和DMA描述符ETH外設(shè)的中斷優(yōu)先級在CubeMX的NVIC配置里設(shè)置。我一般設(shè)成5數(shù)值越小優(yōu)先級越高比SysTick的優(yōu)先級低比普通外設(shè)高。這樣網(wǎng)絡(luò)中斷不會打斷系統(tǒng) tick但又能及時響應(yīng)。DMA描述符的配置在HAL_ETH_Init里自動完成但描述符的內(nèi)存對齊有要求。STM32F767的ETH DMA描述符需要4字節(jié)對齊HAL庫會自動處理。但如果你自己定義了描述符數(shù)組記得加上__attribute__((aligned(4)))。6. 實(shí)測中遇到的奇怪現(xiàn)象和排查思路6.1 ping不通但PHY鏈路顯示已建立這是最常見的問題。PHY的鏈路狀態(tài)寄存器顯示Link Up但ping不通。可能的原因有IP地址配置錯誤檢查IP、子網(wǎng)掩碼、網(wǎng)關(guān)是否和路由器匹配。MAC地址沖突如果局域網(wǎng)里有另一臺設(shè)備用了相同的MAC地址會導(dǎo)致通信異常。STM32的MAC地址默認(rèn)是從UID生成的一般不會沖突但如果你手動設(shè)了一個固定的MAC地址就要注意。防火墻或路由器設(shè)置有些路由器會隔離有線設(shè)備或者開啟了AP隔離功能。我遇到過一次PHY鏈路正常但ping不通最后發(fā)現(xiàn)是網(wǎng)線的問題。那根網(wǎng)線是舊的只有兩對線通100M模式需要四對線全通。換了一根網(wǎng)線就好了。所以排查的時候先換根網(wǎng)線試試成本最低。6.2 丟包嚴(yán)重或傳輸速度慢如果ping能通但丟包嚴(yán)重或者TCP傳輸速度很慢可能的原因有GPIO速度設(shè)置不對RMII的TX和REF_CLK引腳速度必須設(shè)成Very High。MDC時鐘分頻不合適MDC頻率太高會導(dǎo)致PHY寄存器讀寫錯誤。LWIP內(nèi)存配置太小PBUF_POOL_SIZE和MEM_SIZE太小會導(dǎo)致頻繁丟包。中斷優(yōu)先級沖突ETH中斷被其他高優(yōu)先級中斷打斷太頻繁。我實(shí)測下來把GPIO速度改成Very High、MDC分頻設(shè)成102、PBUF_POOL_SIZE調(diào)到32之后ping的丟包率從5%降到了0TCP傳輸速度從幾百KB/s提升到了幾MB/s。6.3 HAL_ETH_Init返回HAL_ERROR這個錯誤的排查思路是檢查PHY地址是否正確對照原理圖。檢查MDIO和MDC引腳是否配置為AF11復(fù)用模式。檢查PHY復(fù)位時序是否正確。檢查MDC時鐘分頻是否在合理范圍內(nèi)。用示波器或邏輯分析儀抓MDC和MDIO的波形看是否有信號。我遇到過一次HAL_ETH_Init返回錯誤用邏輯分析儀抓波形發(fā)現(xiàn)MDC根本沒有輸出。后來發(fā)現(xiàn)是PC1引腳被其他外設(shè)占用了CubeMX里雖然配了ETH_MDC但另一個外設(shè)也用了PC1導(dǎo)致復(fù)用沖突。在CubeMX的引腳視圖里沖突的引腳會顯示黃色警告仔細(xì)看就能發(fā)現(xiàn)。6.4 鏈路狀態(tài)不穩(wěn)定時通時斷如果PHY的鏈路狀態(tài)寄存器顯示Link Up和Link Down交替出現(xiàn)可能的原因有電源不穩(wěn)定PHY的供電電壓波動會導(dǎo)致鏈路不穩(wěn)定。檢查板子上的電源濾波電容是否正常。REF_CLK信號質(zhì)量差50MHz的參考時鐘如果邊沿不夠陡PHY可能無法穩(wěn)定鎖定。檢查PA1引腳的配置和PCB走線。網(wǎng)線接觸不良RJ45接口松動或網(wǎng)線水晶頭氧化。我遇到過一種情況板子放在桌上時鏈路正常一碰就斷。后來發(fā)現(xiàn)是RJ45接口的焊點(diǎn)有虛焊重新焊接后問題解決。所以如果鏈路時通時斷先檢查硬件連接再查軟件配置。7. 幾個能省下大量時間的實(shí)操技巧7.1 用CubeMX的引腳視圖快速排查沖突CubeMX的引腳視圖Pinout View里如果某個引腳被多個外設(shè)占用會顯示黃色或紅色警告。在配置ETH的時候先把所有RMII引腳在引腳視圖里找一遍確認(rèn)沒有沖突。特別是PA1、PA2、PA7、PC1、PC4、PC5、PB11、PB12、PB13這幾個引腳有些可能被其他外設(shè)默認(rèn)占用了。7.2 在main函數(shù)里加PHY寄存器打印在MX_ETH_Init之后加一段代碼讀取PHY的ID寄存器和狀態(tài)寄存器打印出來確認(rèn)PHY工作正常uint32_t phy_id 0; HAL_ETH_ReadPHYRegister(heth, 0, PHY_ID1_REG, phy_id); printf(PHY ID1: 0x%04X\r\n, phy_id); HAL_ETH_ReadPHYRegister(heth, 0, PHY_BSR_REG, phy_id); printf(PHY BSR: 0x%04X\r\n, phy_id);LAN8742A的ID1寄存器值應(yīng)該是0x0007ID2是0xC0F1左右。如果讀出來是0xFFFF或0x0000說明SMI通信有問題。7.3 用LWIP的統(tǒng)計信息定位問題LWIP提供了統(tǒng)計信息接口可以在lwipopts.h里使能LWIP_STATS然后在代碼里調(diào)用stats_display()打印統(tǒng)計信息。能看到收發(fā)包數(shù)量、丟包數(shù)量、內(nèi)存分配失敗次數(shù)等對定位問題很有幫助。7.4 注意CubeMX版本和固件包的匹配STM32CubeMX的版本和STM32CubeF7固件包的版本要匹配。我用過CubeMX 6.5配F7固件包1.17.0生成的代碼里ETH初始化部分有bugHAL_ETH_Init里的PHY地址被硬編碼成了0不管CubeMX里設(shè)的是什么。后來升級到CubeMX 6.8配固件包1.17.2問題解決。所以如果遇到奇怪的初始化問題先檢查一下CubeMX和固件包的版本。8. 從原理圖到ping通我的完整檢查清單每次調(diào)試新的以太網(wǎng)配置我都會按這個清單過一遍原理圖確認(rèn)PHY型號、PHY地址、REF_CLK方向、復(fù)位引腳、電源引腳。CubeMX配置ETH模式選RMII、RMII clock source選External、PHY地址填對、引腳復(fù)用選AF11、GPIO速度設(shè)Very High。時鐘樹配置系統(tǒng)時鐘216MHz、AHB1不分頻、MDC分頻設(shè)102。復(fù)位時序PHY復(fù)位引腳先低后高延時足夠。LWIP配置IP地址和路由器同網(wǎng)段、MAC地址不沖突、內(nèi)存參數(shù)調(diào)大。硬件檢查網(wǎng)線完好、RJ45接口焊接牢固、電源穩(wěn)定。調(diào)試輸出打印PHY ID和BSR寄存器、用LWIP統(tǒng)計信息定位問題。這套流程走下來大部分問題都能在半小時內(nèi)定位。最怕的是跳過原理圖直接配CubeMX那樣出了問題根本不知道從哪查起。以太網(wǎng)這個東西硬件和軟件的耦合太緊密了任何一邊出問題都表現(xiàn)為“ping不通”沒有清晰的排查思路很容易陷入死胡同。最后說一個我踩過的坑有一次調(diào)試了一整天各種配置都檢查了就是ping不通。最后發(fā)現(xiàn)是網(wǎng)線插在了路由器的WAN口上而不是LAN口。WAN口和LAN口在不同的網(wǎng)段當(dāng)然ping不通。換到LAN口秒通。所以排查的時候先從最簡單的物理連接開始別一上來就懷疑代碼。