ETH
: }0 P [5 b% i" G0 P% G, x6 R9 @' }$ D% Y/ g
5 y% O, j! z: B. {
: e% n1 j( _, X7 \; v9 V% O
( R! t. `" t% Y9 ]4 H8 f( _. N+ C# s& w
. w3 b& X# H3 p; w9 Q
2 u3 e; m( A0 F- ? M7 O G4 M配置EHT_RST引脚
$ Q! E2 z. n- E. E1 j7 u4 s5 }, G W* o; }6 W5 ~1 i( j( o1 c
* s( P% ~* c' ]2 Z
2 L! v: o6 f- ~) [) }LWIP7 W1 [9 G2 _) U) p
# b' T" Y. b2 e& u3 Q3 W1 F, z5 Q/ u& }- _- g7 j- M
5 T' M9 L1 } M* W6 H" R3 T# ^! e- p) U* ~
3 f5 Q6 ~/ X% C; a
, n7 `4 F4 x8 f, L注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情% A2 s2 o- }6 a. Y. o, f$ O; q
& ~& U& o1 @) o i) ]+ @
( y3 g" L( C0 _2 M( K' {
% J! C0 ]- a" M0 M: {7 vLAN8742
6 c4 O2 F# L3 G2 @8 q* i% i6 L, d. z6 }
- m$ ^: ?; u3 Y! q
MPU: G3 a- F5 J( g. a1 q9 `" O
Lwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理??需要使用内存守护单元MPU。
( s. U; G8 h" U, C8 }' J% B
; H' H" H- n& X) c. o使用CubeMX配置MPU,最多可以管理16段。为lwip配置,管理两段即可。" M6 I' p+ c9 Y' j- s+ L
' T( g5 \+ i/ r6 r; n1 r- A
5 z5 m- a8 p* f( |9 d
/ G7 T( ~+ Q/ t) c/ r- k, @% S
+ }1 ^9 [4 j7 y: V. b: L1 N/ ]
0 q; b# Q8 t6 h) ~+ Q2 j5 N8 y& e, W7 k/ P7 Q
- o9 i5 R i b
3 q' C; x: P7 J5 q
MPU设定总结(非操作步骤)# L& I+ e, t, |
总结一下这样做的原因与目的:, R* \' G, L- K4 ?6 q6 L0 R, p
(1)Lwip不被允许使用cpu专用的高速L1缓存(DTCM),只能用D2 Sram区域;
7 }$ {! _" D1 `(2)cpu可以无序访问cache,为防止这种情况,Lwip的DMA段必须是device类型或者Strongly-ordered类型,保证有序;
: w, ]0 C% D9 I G. j(3)通过MPU配置这段cache,其中一段允许share、允许buffer,长度为256Byte,放TXRX交互存取头;另外一段不share,不buffer,不cache;长度32k。2 B9 H9 m+ L5 z% e
* |+ h2 S1 l+ x% R/ V7 x' h& q# x
2 w; }- r' {4 A0 u& r' P' P9 e
5 E% f" S4 W yMPU选项含义(非操作步骤)
) G% U1 ?* J4 g8 s: ^cubeMX里面配置TEX、C、B,三者搭配。! n, Q ~* [6 J% f
5 G/ I5 ~. h8 j: D8 x: U! | |9 p7 Q
$ k; ?3 ]! @: j) l% y! W
% b$ F8 e: s- P4 k( x- B! o
Access permission被定义为3,即Full access。
( f6 v2 {) s, y2 `
/ P; K: ^3 S0 R* n
9 u2 [. r$ `/ e" B, S9 Q( A- v7 l$ u5 H- c
经过上面的配置,编译下载,不出意外不用写任何代码就可以ping通了。5 T; ]! }1 r Q' G4 |* H
! j0 V- b; u% B( C
好了,可以进行下一步!
# o- K9 F0 B( C$ }# F5 C; \# N( n* G" {, [# u" K
Lwip协议栈TCP保活(KeepAlive)设定
$ b& m# u c( E6 s* u( }万事具备了么?no!
) d/ V- s; p7 K& Z# c# H% w* s" s% I' C& s
以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即H7必须知道网线是在什么时候被拔掉。
% ^& f3 ]! Q4 Q* V1 w2 {% {
0 m; y) d8 P( Z/ h1 m有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。
1 ?' N# s. M7 {2 G0 [8 u
5 H1 J3 z# ]/ A" w由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlive,lwip协议栈这部分也没少,启用几个宏即可自动进行收发。设定方法为:在lwipopts.h后面加入
r+ v% P3 h4 ?& I" z# ^$ E7 A, C; G. ^" k' j) \: p+ n G
- #define LWIP_TCP_KEEPALIVE 1 //激活keepalive& e& w) M$ c8 @" B5 X
- #define TCP_KEEPIDLE_DEFAULT 2000UL //2秒内连接双方都无数据,则发起保活探测
' o0 O% Q) i$ J* s; C( M4 R - #define TCP_KEEPINTVL_DEFAULT 1000UL //1秒发送一次保活探测9 r3 d! ^+ O& O8 O; w# |
- #define TCP_KEEPCNT_DEFAULT 3UL //3次保活探测无数据则进入错误回调函数
复制代码
& J' D$ g- f- V' v6 C# htcpecho.c中实例化netconn后,为其tcp成员加入SPF_KEEPALIVE属性。- xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE; //保活设定,实际上是tcp的一个属性选项
复制代码 / o" N- R' {! e
这样就能实时检测连接状态了。使用wireshark抓取数据是下图的样子,里面可以看到空闲时段交互的keepalive数据包。+ N! H/ k( U. \) B: ~5 I
电脑IP :192.168.0.996 v u* _, F9 Z* O( I0 O9 K J
单片机IP :192.168.0.80
5 f6 l9 ^. R& `4 n
% H/ p/ r; O+ X( I* i, ~# Q& T* B+ [: O0 j) ?, u7 ^1 w8 j
& X/ x$ ~' C3 [/ F/ E! o ]自动化流程为:
. r- Y$ J" E d' y4 z+ Y拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接
# G2 K" g0 s3 A5 o; A! E如此往复…* V4 M. z# g- M& @* }5 V+ e
$ `0 f0 D, @% s7 {' ]) Z9 V用STM32CubeMX 生成了一个包含 EHT ,Lwip 的项目,调试一直不正常,经仔细检查存在以下问题:
/ w+ E; a0 \4 }; b9 F7 q) U/ U- _1.硬件采用了LAN8720A ,但是STM32CubeMX 生成的是LAN8742A的代码,必须根据硬件连接更改地址。
+ @# L0 y# C$ s, \( o3 k: N6 b5 v解决办法: 在 …Src ethernetif.c 修改如下:
- D( @" _; B- Q; ~, G
' n! R! J X/ U2 D U" {- heth.Instance = ETH;
/ p6 y0 X9 `5 f; s6 ?# { - & `* v5 _1 N5 I8 B3 y: v- C
- heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
9 C2 a* p7 X0 I) ]% D% C - . ~" @, E+ l# J R' C* d
- // heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;5 H6 {( }+ K ~7 O$ d
, s0 {# r, Q5 | r- heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS; //modify by kmsmg,LAN8720A_PHY_ADDRESS=0,LAN8742A_PHY_ADDRESS=1+ \: Y4 ?6 n+ d) q( R( k$ E
复制代码 ! N) }# ^. D8 M/ T
2.初始化时必须对LAN8720A 进行硬件复位。& u6 ^0 v8 h' Q- k
解决办法:* d, v: p& _# s2 E* n8 X9 a: J
, p4 b) C7 [7 s; M8 p7 _( H
分配一个 GPIO 连接LAN8720A的复位端,在 …Src ethernetif.c 修改如下:
9 v Y) T6 S0 ^! d
0 F0 p6 F3 w; n7 o* Q* Z- heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;* ^' U& `/ p. n" J2 v
3 l, |: E9 m( f) [4 J' U: }- heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;/ G5 e" {' r; I. z9 m" e
5 E/ [3 S- p& b J* w: H- /* USER CODE BEGIN MACADDRESS */0 N! a- \$ K' h, T# {8 N/ V
5 N$ k- v9 i$ D+ G1 x& j- /* USER CODE END MACADDRESS */
. {# `$ t2 d8 `7 X: ?+ o0 q" g - - a' ~2 I, A- E6 B$ @
- LAN8720_RESET();// modify by kmsmg
( [2 c6 r# [0 ?0 m$ R7 S
* c! w$ i: o: J! B5 k* s- hal_eth_init_status = HAL_ETH_Init(&heth);
# J3 _4 }0 n+ ^% u) i - . l: b8 E! e/ Z5 o4 V' w
- ............! l* v! b O5 t" G1 h; ~
0 j; g4 G5 {( j6 N# i9 O; i6 `- //-----------------------------------------------
1 |$ h/ X5 {' B( i9 u - ! ^- t5 q, W+ ~0 r$ I" Y
- void LAN8720_RESET(void)
1 T9 B2 Y: j0 c$ Z- z - 0 S+ [( j( z- r$ t# A+ x \" m3 `
- {
1 c7 y6 S5 W/ w8 f - HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_RESET);
3 v7 m( Z& M' L5 h% A) r8 ? - 2 I. K% q/ @0 d8 w7 K
- HAL_Delay(55);6 f' s, X' Q4 O7 D. N7 p' U- ^
) L+ q8 a8 b5 U% ^# o; H- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_SET);2 y0 i2 B" B8 @' z
- $ A$ ]1 m% o, `0 W; g1 C1 r& @
- }' Z. ?' i# S: T3 O* X {8 I
' A4 Q/ g( _" Z% h) b
复制代码 : a" V; ^- m7 r8 z
6 A' C2 g; e# B3 `) @. `4 x
1 B6 @5 s% V4 ^( X9 a
|