ETH9 H1 {# f S$ e9 Z
: p9 a% E6 D% W0 ?) {9 w
$ ]% U. J: H& Z6 h1 p" F: ?
/ P! D- _, h* t0 B6 z3 r
" `0 h- A# {' V7 }* q5 O* l- [4 S2 A( a1 O
) K$ r; c0 b ?/ D( l
9 s3 @4 O( ], W; ^; S配置EHT_RST引脚
4 M5 F+ r* F% E: F
3 y0 o6 `% P: K3 M. ?4 o3 M
; F L& V$ ~- w; ~1 l2 j9 ?7 ]% T7 G+ b9 Z
LWIP
8 ]- f0 J* f3 D) c8 W- ~. q* E/ g2 t: V; E
$ c+ u6 `$ h2 Z9 h
$ O' ~0 A7 n& \+ D
+ p( c0 F" \4 q6 k9 Y7 u
, a: T a! F: V" i; u/ ?注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情
7 ^; c! d$ L2 x8 b
$ U, c& K& U% H; n# F
% Q& M: d0 p; ^ h9 E$ f4 T) q" h. r u3 I( i' g: N7 x% n
LAN8742
. F, \9 {& E. M# |' L8 I
4 T, \( m+ k+ j+ `& i
% F* n8 j) k9 A$ D+ V" s7 v& }% F% iMPU
8 r8 k- U" z6 I- y) dLwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理??需要使用内存守护单元MPU。1 z6 `2 l/ B. }0 m
/ U' Q2 l9 O& v% z使用CubeMX配置MPU,最多可以管理16段。为lwip配置,管理两段即可。 }: Q% l" ]7 |" i% @- Y+ d: o8 }: x% h
# b# M4 h6 s0 x8 h. u: A9 E1 t1 [5 Y; }! X( g2 U' x0 M( i
1 x* `& n3 e! i* [- Z, @
& j+ ]; g0 A( E" K
: J$ z& E( N1 A: S9 t: t2 l1 n1 G" @% Q
! E- l+ c) i F3 k% Z5 u; c
% _8 j- x) Q: W- f# Q' l# E
MPU设定总结(非操作步骤)
6 o4 P% y2 Q. p: m# D( b5 q. }总结一下这样做的原因与目的:
0 S8 T1 e5 c, u+ t, g/ E/ T8 o(1)Lwip不被允许使用cpu专用的高速L1缓存(DTCM),只能用D2 Sram区域;. G5 Y5 M! c5 h$ a: x
(2)cpu可以无序访问cache,为防止这种情况,Lwip的DMA段必须是device类型或者Strongly-ordered类型,保证有序;4 u' y6 N! J& x |
(3)通过MPU配置这段cache,其中一段允许share、允许buffer,长度为256Byte,放TXRX交互存取头;另外一段不share,不buffer,不cache;长度32k。' [7 [, w& w, r# _- i6 }4 t3 I7 H
# }1 O6 M: a" K/ U( S) I
: M* G) _1 {/ \7 t1 u0 o# s% g' K/ N. C7 d3 R e# R& U
MPU选项含义(非操作步骤)5 N: p" I8 K4 S+ W: P
cubeMX里面配置TEX、C、B,三者搭配。4 I" i- N' a n: t
3 T6 t, |/ V) z/ w6 ]: ~. _! \% |. f4 x4 n) r! e
0 [7 b" A( ]0 O) i# | U! K
6 ^ k9 u$ v8 c$ @# U- K
Access permission被定义为3,即Full access。* N3 z+ |/ W* }7 q1 I$ E
& {/ |, e, ^/ ^9 C- s
, y; R( H4 F K" H# j7 `- Y! n5 ]
7 `7 N% |& j" t经过上面的配置,编译下载,不出意外不用写任何代码就可以ping通了。; }2 u3 \8 c. x- t$ a: h
c1 z9 X4 x- d* ?
好了,可以进行下一步!
+ l# M- G, ?! h* p, j. ?, [3 D# s
# X& ^5 \6 E% j- bLwip协议栈TCP保活(KeepAlive)设定
4 E P9 @: O( m万事具备了么?no!
! g: Y6 d0 @% `
/ ?. ^! Q- i- P/ d! N以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即H7必须知道网线是在什么时候被拔掉。
8 F$ Z9 W e r2 a% w: U7 T
) c) d/ y0 x( H) r5 ^2 @有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。- r/ [# D9 P1 w! e& L
4 l1 C) D. {: T# [) x, i F由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlive,lwip协议栈这部分也没少,启用几个宏即可自动进行收发。设定方法为:在lwipopts.h后面加入
/ p& K# D7 F( x- ?
8 k4 R6 l; h- q4 ]8 t6 S- #define LWIP_TCP_KEEPALIVE 1 //激活keepalive
: s! k/ g% Q$ S) L6 L2 b% E2 _' }: s - #define TCP_KEEPIDLE_DEFAULT 2000UL //2秒内连接双方都无数据,则发起保活探测
, e- `8 e" a0 h; S - #define TCP_KEEPINTVL_DEFAULT 1000UL //1秒发送一次保活探测
) n, b$ q" ^4 L3 A3 C- C - #define TCP_KEEPCNT_DEFAULT 3UL //3次保活探测无数据则进入错误回调函数
复制代码
' b; g" B( h0 z+ B$ Gtcpecho.c中实例化netconn后,为其tcp成员加入SPF_KEEPALIVE属性。- xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE; //保活设定,实际上是tcp的一个属性选项
复制代码
' u" T3 t& r+ G1 X7 B8 T9 m这样就能实时检测连接状态了。使用wireshark抓取数据是下图的样子,里面可以看到空闲时段交互的keepalive数据包。1 G) Q- j5 d5 e# l3 R' e
电脑IP :192.168.0.995 w9 L1 E; k% Z5 U. U) d
单片机IP :192.168.0.80) s) [1 x: m/ Z! G( _2 y, z
& f, [# M6 u4 |: f! b4 B4 f6 K( N- V0 N. u/ t) C3 q. p: _0 w8 t W
7 X {& W+ p9 L
自动化流程为:9 L2 c7 u6 t1 m2 u' p
拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接; u3 d1 i0 L: R% _2 |. N# R* Q! }
如此往复…
1 u7 c! v# Z L' i: x9 F6 S6 d! Z3 J' N' Z7 M4 D
用STM32CubeMX 生成了一个包含 EHT ,Lwip 的项目,调试一直不正常,经仔细检查存在以下问题:
+ |4 b1 @; i1 X5 o ?( N8 q1.硬件采用了LAN8720A ,但是STM32CubeMX 生成的是LAN8742A的代码,必须根据硬件连接更改地址。
\0 N$ [/ U' K0 Z2 C% t c解决办法: 在 …Src ethernetif.c 修改如下:
7 F; |& \7 Q' m7 m: t+ E5 w
5 S3 A2 E& j/ _- heth.Instance = ETH;
; N5 i2 @) i, Y. S" d& U
{9 @( {* [3 v9 d# H+ v- heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;# Z9 [' k8 T% ~6 h- j7 n
! @. c! \9 f8 F" Q3 j- // heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;
& \. \" k7 ?% Q3 p3 ^1 o - 1 x( h A/ {2 c, O" Q ~% ^- y
- heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS; //modify by kmsmg,LAN8720A_PHY_ADDRESS=0,LAN8742A_PHY_ADDRESS=1. n' d+ B# l+ r2 f; G2 e
复制代码 ' [: u& K2 B! l7 ^
2.初始化时必须对LAN8720A 进行硬件复位。
3 ?1 C0 s; d+ `, ^0 O解决办法:* Z0 Y& E3 x: `) P1 [ B
0 h# T1 v5 _! H' s) Z; s分配一个 GPIO 连接LAN8720A的复位端,在 …Src ethernetif.c 修改如下:1 b9 M' D+ D. U
6 O6 L* S! ~# t- heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
& M9 b; x( i/ W8 L6 m6 k - : E& _' Y7 {8 {& X
- heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;5 {- F' J+ F, M& @: w( i
- . @, i7 j9 e' m$ @! B2 t- z% K0 R5 @! t' g
- /* USER CODE BEGIN MACADDRESS */9 T3 L: ?+ `8 k$ P) \9 I
: R* ^% H* ]% K* I- P: R) Z1 d- /* USER CODE END MACADDRESS */
( K' P6 @6 A+ U8 c0 U6 \- V3 k( T - / a7 ^" L4 y+ p2 P
- LAN8720_RESET();// modify by kmsmg
1 i4 y+ k$ C! K4 q1 \/ } - 4 `2 W/ A& ?, n3 p- `, W/ J5 ~' J& Y
- hal_eth_init_status = HAL_ETH_Init(&heth);
8 P# T# t6 ^; t- e: W( N
8 H+ Q5 C9 d; S7 E- ............8 `. L. P' ]/ y: s$ H
- 9 d& x5 [! ?1 v$ s. k
- //-----------------------------------------------
: `" D6 T8 d4 Y7 D; n# U
! u) _3 C3 ^" q1 T5 f- void LAN8720_RESET(void)
, T' @% m3 O& a3 c& h) u
, r3 I) ?8 f4 W( Q4 c- s- {
/ m6 z3 f( w, @* s$ K8 D - HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_RESET);4 u/ I7 L/ s5 |# W3 p+ G
5 D; P- J0 l" p# ]- HAL_Delay(55); }6 E* i3 v: H. J% t
9 B. p. W# y. Y- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_SET);
5 b% g8 }& l) s9 F% K8 R7 l3 j - 1 W1 l8 j0 f, `4 c* F2 Q4 |% N/ T; \8 R
- }7 ]- O- r; z8 E* C0 ^- U9 r+ |
- $ \9 R/ _' g* @+ Y* M! u
复制代码
5 f8 V7 Y% n8 y! a' t, e0 m5 w1 [
; \% ?: p5 b$ r2 D2 d: j( T. n% d+ @% W
|