ETH0 @" c( z _( p7 w' Z7 V2 d) c
# c9 k: t# V. [) l! z
3 N' S! q/ T4 w6 A6 b' R0 Z3 i
7 q; k. e# |1 g. f3 X7 n8 D' a: A3 F! _
/ x" E4 V L, ]& l7 @
5 D$ H. Y, L7 T1 y
: o1 q( C6 {! |/ n配置EHT_RST引脚
1 @: h2 J) n8 ?( l9 ?. d2 V8 z) {5 U! X% m. P% W' b
8 [& o% ^- f5 r) X
' }1 T4 h/ q& e9 g, r$ @! PLWIP
8 W) ^ I6 W U& _( E7 X) e
6 F9 u! B# w7 e/ w) a+ G
2 I, W# e! V C5 \( o |
( M+ d# V/ Q7 l3 l3 m$ K
' q- i5 \: L) |4 A, b" D, k9 |% `) {# L. ]- W- g
注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情
* H1 \$ d8 U* M8 `3 Z2 [/ i2 j
6 _ z7 H3 k& S n+ w) X O& ~/ I: g% ~ v( w3 H
) E1 w6 d0 N; _4 \LAN8742
2 o5 X# _5 k7 X# C2 ^0 |! n r7 F9 [( M7 D: b8 }
% y" Q4 ?# |0 o# a+ u) E
MPU* B. _, |: R+ A( Y" e
Lwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理??需要使用内存守护单元MPU。
5 L2 I k( d' S' p0 j& P5 h! t6 s2 R4 d+ Z5 |9 [7 j6 R' {
使用CubeMX配置MPU,最多可以管理16段。为lwip配置,管理两段即可。2 R1 Q$ j2 a4 q2 K# c" Y3 p
' [" V- V$ U! q/ g
. L8 ?7 k |! j& W0 @' w
: n6 E$ Q& j+ O0 }) C
: e$ S0 L3 g" w9 D" A9 \( c0 ^( V G" y+ U( ~4 ^% l( a( E. p1 C& D
3 H* N8 g( s* c' Y- z1 D6 O1 j W' i# ?5 }$ |. Z
3 `$ R( _ W3 y) ?' eMPU设定总结(非操作步骤)/ r6 I) G3 [. o/ S( l: J
总结一下这样做的原因与目的:; q- y: w ~' V+ v4 ]* B7 }* Y
(1)Lwip不被允许使用cpu专用的高速L1缓存(DTCM),只能用D2 Sram区域;
+ T2 `6 _' L& U2 r$ Q) Q(2)cpu可以无序访问cache,为防止这种情况,Lwip的DMA段必须是device类型或者Strongly-ordered类型,保证有序;
4 U/ _6 Q! o: B) A) Y: m(3)通过MPU配置这段cache,其中一段允许share、允许buffer,长度为256Byte,放TXRX交互存取头;另外一段不share,不buffer,不cache;长度32k。
* S* d6 b G$ n2 ~ a: T; P, s1 \& _9 S: ^2 v& C$ x& n( v
3 Q$ z& X) f9 f: q' e7 Y6 o
5 F2 \& G$ b0 t: o9 s/ T( s
MPU选项含义(非操作步骤)
$ y1 j% D3 `/ ?' q3 B5 _cubeMX里面配置TEX、C、B,三者搭配。. B4 T0 t( h) p7 w* }( p9 W1 V4 ]& u4 R
/ f# \0 d$ |! G
+ h* o# L- N3 {0 d; r7 q# |2 B# W1 S1 t7 ]. V* K
3 s/ s8 f# y! W6 W/ M- @8 t+ R
Access permission被定义为3,即Full access。# q/ t2 \' A' X0 d# l& Q6 \% M" T" r
' n$ l' p/ F8 H
2 A2 \. p) R7 n, K0 T2 L7 U& K' c0 {, B/ j* @8 n
经过上面的配置,编译下载,不出意外不用写任何代码就可以ping通了。: `9 E! o, E( c
- L2 O/ B: S2 ^6 S好了,可以进行下一步!
: [* Z8 M; u% t$ F1 u; t: d, J( M+ c
Lwip协议栈TCP保活(KeepAlive)设定- S! @- X2 Z4 _! a3 `3 i
万事具备了么?no! k$ W# c& n2 d2 I" J
& f6 P/ @! ^+ B以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即H7必须知道网线是在什么时候被拔掉。
D4 \3 q4 ^% p0 p6 q
r8 L9 D, v+ k! h6 _有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。2 \; |# S8 p! R4 C& M& r
7 E1 ~3 L; n) G4 S由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlive,lwip协议栈这部分也没少,启用几个宏即可自动进行收发。设定方法为:在lwipopts.h后面加入
) r# l& o9 R- `( }* z/ y
( _. P) g$ g. |# L6 m7 o, S4 i" o- #define LWIP_TCP_KEEPALIVE 1 //激活keepalive
M h j& J# [$ O! P6 a S5 [: w - #define TCP_KEEPIDLE_DEFAULT 2000UL //2秒内连接双方都无数据,则发起保活探测
3 y9 T9 r7 c+ o# x, H - #define TCP_KEEPINTVL_DEFAULT 1000UL //1秒发送一次保活探测1 ^. g) F# O+ ^9 Y# v: {* Q
- #define TCP_KEEPCNT_DEFAULT 3UL //3次保活探测无数据则进入错误回调函数
复制代码 ( M! C1 s$ S3 `4 |
tcpecho.c中实例化netconn后,为其tcp成员加入SPF_KEEPALIVE属性。- xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE; //保活设定,实际上是tcp的一个属性选项
复制代码 + a( ^5 B3 Z- H6 s# I" m
这样就能实时检测连接状态了。使用wireshark抓取数据是下图的样子,里面可以看到空闲时段交互的keepalive数据包。
- ~) f# z' X$ P7 ]) J电脑IP :192.168.0.99
$ e" n. u: e+ `1 k8 H _单片机IP :192.168.0.800 w" T! G7 W5 y N' c
7 p) ^ y: W( y& A/ p$ e8 H
: [7 o" R2 ~0 U @+ `
; M' y; H/ Q; q6 B$ f3 {自动化流程为:2 z& @; \/ r( {) T4 q% F5 \# H
拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接
[( A h- ?& E5 {如此往复…
6 [/ A P( B5 ~( |1 y; [5 s$ w9 ~
用STM32CubeMX 生成了一个包含 EHT ,Lwip 的项目,调试一直不正常,经仔细检查存在以下问题:$ X6 E4 L1 B" }4 j2 k
1.硬件采用了LAN8720A ,但是STM32CubeMX 生成的是LAN8742A的代码,必须根据硬件连接更改地址。) T. e+ I5 x$ @! H! V* o% s% e6 T
解决办法: 在 …Src ethernetif.c 修改如下:. {0 v* L# t8 R; G
2 H- d% p! ^% K6 Z- heth.Instance = ETH;7 z; C/ o" g! q. @4 X I
- ' @" x2 R7 o# J+ Z6 x1 r
- heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;! Z% j: G& K$ Q4 j4 S
X9 h4 y( j/ i6 x4 l' j% B- // heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;# O0 @3 F9 y& X9 k5 f
- 8 B) T8 T6 D4 x8 X
- heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS; //modify by kmsmg,LAN8720A_PHY_ADDRESS=0,LAN8742A_PHY_ADDRESS=10 s0 n1 b: v+ |8 s5 w8 Q
复制代码 - c- J; s9 w; h( C
2.初始化时必须对LAN8720A 进行硬件复位。
+ R: b+ F% t2 ?% r$ o解决办法:; D) a* e( I8 c1 Z# Y$ V8 j
1 R7 a) U! [- X/ j分配一个 GPIO 连接LAN8720A的复位端,在 …Src ethernetif.c 修改如下:
6 J( J9 W3 u$ ]; P4 u: v$ X/ k' M& H; S/ I8 Q4 d! `
- heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
1 f7 Z. W' ]* `6 S; o5 {/ Y4 o1 H
2 u8 S: w5 E0 E; }+ ], l/ A* O1 f" K- heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
' ?7 L: S) m: \5 O
' Y4 E" J) {; z) o- /* USER CODE BEGIN MACADDRESS */
$ E6 Z: m# J$ J1 M! M) U1 h6 N
/ y+ a/ W+ V& d1 m; ~; K( u" P. `- /* USER CODE END MACADDRESS */5 d: A6 l7 z; \5 m5 Y+ g
0 a/ `0 O( K) \3 J, s; U- LAN8720_RESET();// modify by kmsmg
9 P, B6 N) N& V* X
/ ~9 H. u- H4 Q2 M9 l: B5 C- hal_eth_init_status = HAL_ETH_Init(&heth);
7 ~! d$ b+ w% c) d% f* Y - 7 Z% p- ~4 y/ l7 U; G
- ............. v& I" }$ H5 a
! G: G2 Z! \ ]+ ]9 @$ l3 n" c- //-----------------------------------------------/ p% {$ r7 ~" {/ Z0 \6 d d
8 y; I& }+ x8 e5 u% d3 s. n+ Y- void LAN8720_RESET(void); I L2 q! C* Z9 f1 V
- , ]# w( ]2 X9 t/ N2 D2 t/ L( b3 `
- {
: Y+ B3 R; [2 Z: y - HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_RESET);. V0 \4 x4 [: ]. ]( f
- & w# A- X6 [& q: N# L: T6 Q" |
- HAL_Delay(55);
4 Q2 ?) @3 ~# k& V
1 G0 R9 x8 T' r- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_SET);
, v6 B. D( M; d4 ~* _! J( j: J - % t' d* X- ]7 O/ \- r" t
- }
! u/ v6 ~ K. {* s& O
. n+ x" m; M% o6 T4 s4 v% Z m+ U% D
复制代码
$ [- l" W2 E* ~5 P6 b5 e
3 b# E2 F1 c, g0 o
& f* Y% `% V! B; ~# D) v# Z |