ETH' v( p/ }5 c; p; m
* c+ X f' [' W: A' Q' _1 P3 x* @+ `5 S
* G6 m5 e! Z* u& L
* ]3 Y5 I* t" R f
& ]4 ]* }- u1 Y
6 r) H$ ]. q& M9 \4 H* s, j* \# @
6 a" n, [7 L5 R+ P! r配置EHT_RST引脚
6 ~4 K4 F$ {/ `. I# y" v+ X" s8 y7 A) x% O% j; G
3 [* W6 Y! S7 t9 Q: y3 g0 Z( T3 W2 ?4 G. p' f! J
LWIP
! n& J: c- r9 n Y' d$ I i) M& l! k6 W/ ]% [
- T4 l) S% e. M2 l+ w& R" }& g3 ~/ n( j3 l. j9 u. }
; d. F2 p6 j$ X( D
$ q& R, v. ]4 X4 }注意一定要把LWIP_NETIF_LINK_CALLBACK选上,不然连接状态改变不能进入拔下或者插入网线回调函数,里面做一点自己的事情
" ~) n# i% E0 ~, R8 I5 R8 h3 O( ^. R, a
5 t# t+ t c9 F2 s9 M: N E' L, G4 s# n" A& [
LAN8742
: a! m$ ~3 {0 V9 ]/ G
1 ~) p1 y5 l& ~! K5 N* c
2 E8 x- B5 i& i: \" p: LMPU6 F5 ?, i r d2 H6 {
Lwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理??需要使用内存守护单元MPU。
6 l' A/ Q6 D, a; y" i6 i% Z( b$ `$ n! W
使用CubeMX配置MPU,最多可以管理16段。为lwip配置,管理两段即可。
" V! Y& E1 I4 @
; h( S+ `1 u* S w. v3 T8 p
1 V+ Z6 a8 w" i
, Z. R1 z0 H% |! C+ D4 G6 x/ n7 w6 J- `* t) D/ e
+ O3 ]) D# d6 E$ u) [
( @1 j+ C ~! B$ x
, E1 ?! Y4 n* L1 U
7 d. J! J! G$ c0 |# D' ]2 OMPU设定总结(非操作步骤)
! E& d3 _' C$ H! O% t总结一下这样做的原因与目的:! D E2 p3 [. t0 T, A) ]
(1)Lwip不被允许使用cpu专用的高速L1缓存(DTCM),只能用D2 Sram区域;/ s1 c3 m# _8 A( l8 T
(2)cpu可以无序访问cache,为防止这种情况,Lwip的DMA段必须是device类型或者Strongly-ordered类型,保证有序;
6 x# u$ @+ n2 l# \% Y( s4 c(3)通过MPU配置这段cache,其中一段允许share、允许buffer,长度为256Byte,放TXRX交互存取头;另外一段不share,不buffer,不cache;长度32k。
) r3 f3 ~/ z9 r1 |
; j8 w/ m8 b/ c
" O+ w& C# J: S0 S2 p3 J Q1 l2 U$ T$ k
MPU选项含义(非操作步骤)
$ [6 _" N; }& u( XcubeMX里面配置TEX、C、B,三者搭配。
5 P6 V* O3 t: w! d! l7 ^5 z4 Y6 p7 t4 J5 p# Z; I
4 d% \0 \8 V; f+ C1 G6 [) t
! k3 L4 U# n" [; N
5 [$ w8 |6 J5 h+ `% ZAccess permission被定义为3,即Full access。
g5 ?* ^! r/ T2 C5 r# O
1 I) m+ E) n8 O2 G& `- }8 o3 Z3 U% g* i0 ?, G1 X
6 B: u1 Y' j5 h( x; B4 l" z
经过上面的配置,编译下载,不出意外不用写任何代码就可以ping通了。. i: D7 B& T1 v- c) l6 ]
2 a- F* G( y0 Q7 R好了,可以进行下一步!
) l& L- Y, k( h3 `; `. P
. r3 a, X' o) WLwip协议栈TCP保活(KeepAlive)设定
3 g s, |2 s$ E, s% s0 y万事具备了么?no!' F' v- F" T6 m& T
N I' ?; j6 z) G1 T' I+ H以上只解决了异常自动重连的问题,并不等于协议栈具备检测异常的能力。即H7必须知道网线是在什么时候被拔掉。' W' v& b# i. S, {8 J
0 I0 o1 K1 f! x# i5 c' c
有很多博客都提到KeepAlive的开启方法,但都是详细说明怎样打开,打开了该怎么用就没说。。。: ~# n) h) ]; f: Y
. L1 l0 j3 Q5 W) W, F# ^1 {由于tcp是可靠连接,有数据往来的时候能够检测异常,需要解决的是无数据检测。这就要用到TCP协议的KeepAlive功能,原理就是在空闲的时候,以一定的频率发空数据包给服务器,服务器收到后答复一个数据包,说白了又把空闲段给变成有数据往复状态了。TCP协议栈包含KeepAlive,lwip协议栈这部分也没少,启用几个宏即可自动进行收发。设定方法为:在lwipopts.h后面加入
' Q1 V9 Q& T; | e' \4 B5 E F# o* W( }, a$ {. o% `
- #define LWIP_TCP_KEEPALIVE 1 //激活keepalive- Z* ~4 J$ H( y: f- _- {0 [8 ~& A
- #define TCP_KEEPIDLE_DEFAULT 2000UL //2秒内连接双方都无数据,则发起保活探测
- i1 M/ V* z: I! C, ^* Y - #define TCP_KEEPINTVL_DEFAULT 1000UL //1秒发送一次保活探测
1 Z( E: h/ U' y: R: M; T - #define TCP_KEEPCNT_DEFAULT 3UL //3次保活探测无数据则进入错误回调函数
复制代码 9 x/ B' F9 `5 _3 j% s
tcpecho.c中实例化netconn后,为其tcp成员加入SPF_KEEPALIVE属性。- xNetConn->pcb.tcp->so_options |= SOF_KEEPALIVE; //保活设定,实际上是tcp的一个属性选项
复制代码 5 B( p9 J$ j5 P4 L$ b
这样就能实时检测连接状态了。使用wireshark抓取数据是下图的样子,里面可以看到空闲时段交互的keepalive数据包。* ~4 | q, ^% ]
电脑IP :192.168.0.99
% d7 N/ l& V6 D) P% C( y单片机IP :192.168.0.80: ^0 G1 G& g6 w1 M) o7 v b% S; J
. q7 V+ l0 L: p" C' N, X- B6 V2 T6 D) G0 H! `. ?* j
# @7 A S, t! }6 v" s- H7 J3 m4 @自动化流程为:! P1 z V7 k4 X) A. ?
拔掉网线------进入回调函数销毁tcp资源------主程序while循环连接出错------主程序销毁netconn资源------主程序实例化新的netconn资源------再次连接
# a. b: Z2 {! B/ f如此往复…- K1 x8 x+ M2 j$ n; O
, s1 V: f4 R3 x/ z) }- j n用STM32CubeMX 生成了一个包含 EHT ,Lwip 的项目,调试一直不正常,经仔细检查存在以下问题:) k# p0 z! U6 x8 d
1.硬件采用了LAN8720A ,但是STM32CubeMX 生成的是LAN8742A的代码,必须根据硬件连接更改地址。. R. E* g$ g; C; x4 |) I
解决办法: 在 …Src ethernetif.c 修改如下:
& u% X! s3 T$ R, d/ `' A* J" w( J' h2 y5 }& y$ l
- heth.Instance = ETH;* ?) ^" |* s) G' b# u: g+ s
7 l# v1 \. b2 c' D! X/ f- heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;. p: S: D. {) @9 Z) b1 Y/ o2 H/ s
7 r' H: P+ L$ O/ A: b z- // heth.Init.PhyAddress = LAN8742A_PHY_ADDRESS;
, t' S4 ]7 u) P* E" S$ N8 S# H
\. p) R6 S% v5 e- heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS; //modify by kmsmg,LAN8720A_PHY_ADDRESS=0,LAN8742A_PHY_ADDRESS=1
Y! G, w# ]1 {, c, I5 b
复制代码 % y1 X3 r* V+ ?- I q
2.初始化时必须对LAN8720A 进行硬件复位。
: m0 i7 {) @4 U( m3 p解决办法:/ m" z6 H$ Z+ o# o# z/ l4 n; s* h! N
& D% x3 ^: @( f* d
分配一个 GPIO 连接LAN8720A的复位端,在 …Src ethernetif.c 修改如下:
7 F1 A6 u" e1 ^* n6 Z5 n$ g T: S' r) s" m9 }- o, h+ ]- M i6 G) q, q
- heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;( t: r1 T( Z& I" W3 Y7 [" w
- # f0 C6 D: s8 ]( H" M0 R
- heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
! Y' k* S6 L/ o, G
( Q, O. ?! v& M; B' c2 M8 `6 @9 O- /* USER CODE BEGIN MACADDRESS */
: [; H! q. ]; y - " I7 U3 s- m& _7 V: o
- /* USER CODE END MACADDRESS */* i2 C( S& P7 t' g: V( {6 ]
( J" }8 r2 N5 ?8 [) S3 @- LAN8720_RESET();// modify by kmsmg0 O1 ?# N* L7 t3 W; t7 l! }
- . w4 A8 q! D+ m& e! ~9 e3 f- @
- hal_eth_init_status = HAL_ETH_Init(&heth);5 r, c8 T" q+ s/ G
- + N6 q& T& n5 i: K: c8 c! B4 d
- ............) k! ?2 r- n2 G) ^3 K$ ]
- 0 o" t( o# _5 J
- //-----------------------------------------------; R% w7 H0 o6 C+ a0 t% M/ C
" f, g0 C% o- Q! r% F- void LAN8720_RESET(void)
9 A7 Q7 @8 Y, |. F/ t' h - , D. ]6 m, S8 \ p9 I. a3 W
- {# c- B- k9 f; R
- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_RESET);9 W" y8 u% B& J, B
9 S' U1 Z- `( y- q& N+ a6 N- HAL_Delay(55);
' y; ^+ {- i+ ?3 h1 G - ! G! Z, z ^; I. h) G& m% e* {
- HAL_GPIO_WritePin(GPIOD, GP_EHT_REST_Pin, GPIO_PIN_SET);2 o9 _6 A- V8 d
- ; |5 j" w' l1 ?+ e
- }$ F/ ]2 B1 N9 }5 v1 D% x. K! N
- c- J: \, a! L
复制代码
3 `0 U; s: a r1 W5 ]8 t+ H$ }# A+ k" M" q. p5 L' G
! i5 S3 X! V7 A+ v% U9 o |