ETH* l- x3 f+ J1 }2 i
6 R9 o- T4 [/ c0 Q: y7 [( S6 x! O4 I+ z) U/ n
" |; L, o2 A1 ~3 Q0 e5 c& i5 P
& | N* \3 |. X4 k$ x2 p' k. k: `+ g
7 `- m! D+ q7 c' j
- ~6 R! [3 S7 o; a# T9 Z1 o( j7 L: b/ G6 t% S+ E* _
配置EHT_RST引脚
4 @+ n; w# R: ?8 s' {/ R$ X
) U5 v9 E9 [ c- |' U- k: H. `4 ^
$ H) S- x" U+ O. x- ]5 Q
; {3 B5 V) t6 d% r/ }8 DLWIP
$ t. ~" Y1 X: n2 v7 u! V# N/ W
3 V+ S2 \- p; X# [5 \* p- }4 r9 x7 ]6 S! X
# Y9 }" u: }/ T! A! _& q4 c6 y
注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情
! o( ]0 _1 p- r- g
( q. P+ E% e! Z' M4 t' w1 s# T1 S0 p
5 V+ [" `, H; k+ z! w
LAN8742
6 i- E5 c: S0 j6 _( B( R6 @9 P) t$ N7 ~8 e; n
2 p0 ]- H R! F e' U
MPU8 o, T D+ {; q5 M
Lwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理??需要使用内存守护单元MPU。
8 w* \ P% T, h0 o& y8 M8 U& ~ B" n4 v- m7 e
使用CubeMX配置MPU,最多可以管理16段。为lwip配置,管理两段即可。
O7 [7 Q% s6 V: A4 r
+ r" B5 U8 X2 G9 x( [, I; Z4 X) N; i+ y2 ~. D$ r9 l! g
% Z' L& R, x8 }* l
$ {$ E8 G" {8 Z' G* a! i1 o
* {3 n0 V4 n/ D& S9 w1 [
2 n5 ]+ X! e o1 r
3 u7 k R% i; m$ _2 x% J
9 U9 J3 H$ O+ k
MPU设定总结(非操作步骤)
0 A6 F4 C C1 N( K8 }. A" ^, |总结一下这样做的原因与目的:
# e; u' m& g. Q0 D% D" `(1)Lwip不被允许使用cpu专用的高速L1缓存(DTCM),只能用D2 Sram区域;' o+ X0 V0 c9 Q
(2)cpu可以无序访问cache,为防止这种情况,Lwip的DMA段必须是device类型或者Strongly-ordered类型,保证有序;
& ^, Z) {& N& ^- O8 c5 [(3)通过MPU配置这段cache,其中一段允许share、允许buffer,长度为256Byte,放TXRX交互存取头;另外一段不share,不buffer,不cache;长度32k。
6 R& r; M3 K5 M7 C* }6 {
( f' \% o7 w, ?# f
9 {& U. U7 C9 ]9 M+ W# ^5 H$ g6 g' ~! @: R+ k
MPU选项含义(非操作步骤)# [3 w2 }8 j, T# C( `
cubeMX里面配置TEX、C、B,三者搭配。
) R: r f2 d' I) k) a" j
- |- q+ |0 I+ {3 B( ^' ?8 n; x5 M* a1 ]- P: F
& k& [/ R+ z2 b; d1 `; x+ M# y% P1 e, f( ]- U5 v- |" u: m
Access permission被定义为3,即Full access。
4 p2 n! B$ @7 [4 U2 S1 ]8 Z. I, K* D, ]) V' a* M M1 @
& {0 E. D1 f: `1 l% W( q* Q3 X E4 K: o# M: n
经过上面的配置,编译下载,不出意外不用写任何代码就可以ping通了。! z. V" R% R5 G$ b/ ~. K S
) P2 P# o/ J1 s9 F& _& U7 V, Y好了,可以进行下一步!! @! L/ F% G" [8 H8 d
. h; g2 {2 f, `& `$ h0 W
Lwip协议栈TCP保活(KeepAlive)设定$ o* y" F. O. m' P
万事具备了么?no!* W& k2 O6 \ i0 ^ o2 o& X
6 ~& T' I& f3 O t i/ c, T; z) d3 p
以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即H7必须知道网线是在什么时候被拔掉。
# q6 _5 _& x+ k9 U6 A( A+ t
' s$ o$ ~ V) X有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。
N9 q# H" |, c
3 _0 C% E/ U- ?; l* p2 Y3 b# q由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlive,lwip协议栈这部分也没少,启用几个宏即可自动进行收发。设定方法为:在lwipopts.h后面加入
$ l5 O! s& k2 s1 G4 ] ?) B+ C
1 t: S3 R0 c6 S9 U4 [0 N/ T; @- #define LWIP_TCP_KEEPALIVE 1 //激活keepalive
8 @2 p( ]/ x. X2 `1 y" u - #define TCP_KEEPIDLE_DEFAULT 2000UL //2秒内连接双方都无数据,则发起保活探测2 O) _* y& {( c
- #define TCP_KEEPINTVL_DEFAULT 1000UL //1秒发送一次保活探测. n, F) u! ] r/ u$ \. b3 I
- #define TCP_KEEPCNT_DEFAULT 3UL //3次保活探测无数据则进入错误回调函数
复制代码
' O& Q" p+ A4 G6 s' rtcpecho.c中实例化netconn后,为其tcp成员加入SPF_KEEPALIVE属性。- xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE; //保活设定,实际上是tcp的一个属性选项
复制代码
( m) f# |0 w! R" B( K这样就能实时检测连接状态了。使用wireshark抓取数据是下图的样子,里面可以看到空闲时段交互的keepalive数据包。- _& P" T# b) ]2 L- F9 O' O' p m
电脑IP :192.168.0.99% n: A% V* {, ]0 Y' E
单片机IP :192.168.0.807 I2 @) {2 y$ k' ~
) i u3 O: t( }1 a" p; m
/ A- J1 ^9 t6 |5 \! O9 _$ R4 B- h
2 e; W. S, M5 r# |+ f自动化流程为:. k0 b- a& E: Y5 [0 H) V/ i5 g
拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接
8 g) z# ^# P# E+ F6 V" u {如此往复…1 X+ A. M) ~, i8 Z9 v- s4 u
2 g; M U0 t8 q9 o) L G6 J用STM32CubeMX 生成了一个包含 EHT ,Lwip 的项目,调试一直不正常,经仔细检查存在以下问题:# d" r! N8 ^& t2 n- ?
1.硬件采用了LAN8720A ,但是STM32CubeMX 生成的是LAN8742A的代码,必须根据硬件连接更改地址。
- X$ L) _0 Z, |; y5 Q解决办法: 在 …Src ethernetif.c 修改如下:
5 E- s) g y: b0 w7 }8 _
0 B9 J: G1 `! U. O% h4 X; h' ?4 U0 j- heth.Instance = ETH;
7 j( ^2 {$ Y: R% J9 Z* r6 c) r
+ F7 Y" t5 ? w& _* f; P. f/ `0 d/ C- heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
& T3 g& P w$ G
6 ], [; r" ? }9 Z+ q7 F1 w5 |* l9 F- // heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;/ o% I8 K" A" \7 L
- 5 Z1 U. F) u8 U
- heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS; //modify by kmsmg,LAN8720A_PHY_ADDRESS=0,LAN8742A_PHY_ADDRESS=1
8 ]+ j& Y. ?) @* X/ b' P+ s
复制代码
2 z0 e' J* j$ T! _2 S2.初始化时必须对LAN8720A 进行硬件复位。
4 V6 @+ P1 s( W/ b7 l' \解决办法:
$ [' U) B' t; Q
1 k8 p! L9 L- w1 w' z) M分配一个 GPIO 连接LAN8720A的复位端,在 …Src ethernetif.c 修改如下:9 f5 S2 {( k' {
2 J0 I! T: |) A3 b
- heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
2 u3 K! b0 M4 P5 j
6 X+ o' h2 e% C4 o. H) F, W* h* G+ ?- heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
2 n5 w+ A8 y+ C& N; Y1 r
8 ]: H5 ]5 c: |3 O7 B' ~" l- /* USER CODE BEGIN MACADDRESS */& N( I% L/ `+ V y% b# e
6 M h5 f" _0 o5 q1 c- /* USER CODE END MACADDRESS */
5 ~) n: x4 [' U5 B" O. o- g% N
3 V0 C2 f6 P( P- LAN8720_RESET();// modify by kmsmg
6 P3 Z# y! p) g+ J% k E$ l
4 {& J' B. d' A- hal_eth_init_status = HAL_ETH_Init(&heth);
1 j2 V3 @4 e) N! M$ @' P/ c - f! d8 p5 E$ ]% Q' f0 n, \
- ............
( ~7 `) e4 H. f
) g3 J$ Y. w$ O* p; P5 O; x- //------------------------------------------------ ]/ _. C- y& N
5 t' ]( Z0 m: o2 H- void LAN8720_RESET(void)1 q: M3 w, d; `% x2 L2 K. k- ^( O. a
4 R6 q: A! h6 K9 G# }6 Y- {! D _ V5 L% A# T3 N; G3 a( k
- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_RESET);- r7 M1 ]2 o; Z4 R. I1 W9 x* o
4 m0 P' E `0 M* E5 l; P- HAL_Delay(55);; R9 P3 K- C! A7 F# g& D
- ' e6 E& K" Q" l
- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_SET);2 m- G" c0 ~7 g7 z$ c: ]' c
- 3 \, _0 x9 Q8 o* Y8 Y8 m' K$ M
- }
' ~# T: W5 E) k5 J1 I4 f0 z/ E - 1 e( N0 h6 M4 h l3 q7 h
复制代码
) F) b7 w. U9 }4 g0 T/ n- |' r9 G1 J% f* i- E+ x3 g
7 {2 O2 p, ?6 B3 i- @/ {( i8 B8 P! P( l |