你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F2xx的tcp_echoserver例程解说

[复制链接]
STMCU小助手 发布时间:2021-12-2 14:20

//=====================================================================2 f. D7 E( l7 l, x
//TITLE:: j: E2 o# Q7 B/ c
//    STM32F2xx的tcp_echoserver例程解说
7 p% i; L) X3 |) w) s- L; g2 c5 @  z4 i//AUTHOR:, M$ e' Y% m1 k+ @  R: K- P5 B1 e
//    norains6 W) i* M- v! l" a" M3 J4 @3 p
//DATE:
0 i! M6 C8 y0 \//    Monday  04-July-20117 m$ c% c& D- i, h' ^# o/ L/ o% X% ?
//Environment:
2 S0 e+ {& c" F& o! `//    Keil MDK 4.2
2 ^, h0 S  Z& z: L//    STM32F207 核心版
( I: V. |' {+ v$ J3 \2 Z7 r//=====================================================================

     最近拿到STM32F207的核心版,板载有网卡芯片,自然要拿过来捣鼓一番。而对于一个从未接触过网络的菜鸟来说,最好的入门方式就是揣测ST公司的例程,所以今天norains也不例外。那么我们就一起来看看这个官方的例程吧!


( e6 u1 J: A) v$ x6 q0 @0 o, ~

    ST关于TCP的例程分为client和server,根据字面意思,可以知道tcp_echoserver例程是将STM32F2xx作为server来用。而例程的第一步呢,便是初始化,调用的是tcp_echoserver_init()函数。

    在tcp_echoserver_init()函数里,主要做了这么几件事情:8 m; J: A4 |: S+ O7 y  d3 h
     1. 创建一个新的TCP协议控制块8 o% e8 M" l% F- p9 }2 ^. n9 Z
     2. 绑定地址和端口号(port)) I: V: D4 }/ O8 j/ W8 O
     3. 开始监听(listen)
( W2 ~' l" I0 r. t! z     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  
    ! o7 _) L* Q3 q+ d( E! ^8 y
  2. {  
    5 L$ J  A8 \8 _5 ^$ o4 ^
  3.   //创建一个新的TCP控制块  : w& M  u; ~' L* C: a4 `9 A/ v
  4.   tcp_echoserver_pcb = tcp_new();  
    9 j; F0 h- z  r3 t( I9 ^, @
  5.   
    " H- }  o, F( w
  6.   if (tcp_echoserver_pcb != NULL)  ; G5 L3 Q1 u. ?( _& [
  7.   {  " |) S! ?4 X+ z8 ~
  8.     err_t err;  6 V: f: V" z9 b2 F2 }6 j! C
  9.       3 q& u) U) d, T, V: c
  10.     //绑定到端口7  
    ! I3 Z- L: Z& t* ~2 @# v
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    % w$ X/ s4 `1 z8 t
  12.       
    5 q- P) d; T8 I, r# L0 n- s" \
  13.     if (err == ERR_OK)  & Q7 P6 p/ f$ _8 Z) L
  14.     {  
    + q$ ^' ]# H1 j& T* o+ g9 C
  15.       //开始监听  . n, b1 C4 p4 `) a
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  1 J. A1 W1 T( _' s. Z5 m+ c$ O6 r
  17.         
    - S% k+ ~$ `. u% o+ y/ V& a/ b7 R3 M
  18.       //设置tcp_echoserver_accept为accept的回调函数  
    2 _, I$ o- G$ z0 [
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    2 R  Q, X& C' |- F3 I* l, l" z9 [8 w
  20.      }  # ^- {9 ^! s4 Y
  21.     else   
    ! ?) F% C& v% q! Y* f! l
  22.     {  & h  T, I, E* N  g
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  & w1 r; k- u0 }! ^$ W" J4 G
  24.     }  $ j8 ~$ J" d" [% t0 p
  25.   }  0 T" I+ H" m; H% O9 n
  26.   else  0 ?) s" o+ I: x) M0 i
  27.   {  
    4 _- t; \1 W8 ^' u/ P. m
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  / T& T% q6 b" @3 |, d
  29.   }  
    4 U- l$ G+ F+ L5 h' ^
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。( `  R- ]% h, ]: }, n5 W( A1 p
  : f6 x4 ^- c! D3 s
  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  1 g7 \! S7 K, [7 V! _' j
  2. {  
    ( k( ]1 V8 F8 D- A1 c9 U! l
  3.   err_t ret_err;  & R6 |# z0 [+ e4 z
  4.   struct tcp_echoserver_struct *es;  " o, t0 A; ^; q+ O. A$ `
  5.   ' }4 B/ t: S" V  }6 k6 @7 m
  6.   LWIP_UNUSED_ARG(arg);  + A% J8 n) Q  R4 G8 k4 P; Y
  7.   LWIP_UNUSED_ARG(err);  
    # c- A8 j. F0 {. ?. u
  8.   
    : ?; Y5 C5 b& K6 _& ]
  9.   ///给新的连接设置优先级  4 r- c: v) _) H, w7 R
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  
    3 Q+ j+ o0 f! ~) F' s
  11.   3 \0 c' L+ Q7 N8 k
  12.   //分配一个结构体空间以保持TCP的连接  
    7 ]5 o, r, U! q. m( T. @
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  2 i/ R; C8 j9 l, H
  14.   if (es != NULL)  
    2 C9 Y" k2 y% T( C5 J" i" ~
  15.   {  
    6 @/ Y3 x- i  u3 \
  16.     es->state = ES_ACCEPTED;  
    , L4 }, _/ c1 |$ h
  17.     es->pcb = newpcb;  
      f3 r9 `, ~' v' B$ y
  18.     es->p = NULL;  ' V9 l  U' a' Y- [, g8 Y; p
  19.       3 Y# N( {! ~: u& O4 O0 z
  20.     //传递新分配的结构体数据给新的pcb  
    ( V9 c4 d6 f# \+ S
  21.     tcp_arg(newpcb, es);  % C& i$ Z8 P1 @1 ~& A2 D. g
  22.       + r) j" p- j: o% [
  23.     //为新的连接设置receive回调函数   
    6 U$ W' Z  U# W+ z" c# N8 s
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  
    # k% G4 A: l# s+ M8 R. n# ^3 q
  25.       
    , k/ ?* r5 A) o  I$ S
  26.     //为新的连接设置error回调函数  5 w7 `$ N1 \, P( g2 i4 p
  27.     tcp_err(newpcb, tcp_echoserver_error);  
    ; y" q4 ~% S! ~, d6 s  Z
  28.       7 [3 T( h3 B, c( H$ P$ Z1 |0 B: N4 i
  29.     //为新的连接设置poll回调函数  
    6 [& t# X4 k# E
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  - s$ v/ {; P: f/ M( p+ K4 z, B4 @
  31.       
    ( D4 k) |  L0 _* ?& e; O6 e0 a" v
  32.     ret_err = ERR_OK;  
    - v* F* }' p* _6 k5 k
  33.   }  : B; M8 m5 Q0 s/ ?
  34.   else    e) B/ O6 w( f6 A
  35.   {  ) ?" u; Z; H# S4 p+ R, N
  36.     /* return memory error */  
    ) o9 ?  D$ N! O, x. a; ~
  37.     ret_err = ERR_MEM;  ; _$ j! t3 I9 t+ H
  38.   }  
    % A5 P+ y! t1 u' j/ `5 O( ?, f
  39.   return ret_err;   
    ' Q0 p/ {1 T2 b9 f
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:8 f) V. n4 Q, }$ o, ~( R1 g

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  
    " k, V% H) D5 d" U! c8 N
  2. {  , ~0 K- R  C( F  M& x" P
  3.   struct tcp_echoserver_struct *es;  
    8 N7 |3 O- x* s2 R# K
  4.   err_t ret_err;  
    3 {$ J  g* Z4 w
  5.   + ]* \8 ]: V' Q' U( @
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  5 F2 _0 O" ~% P( O, e# C5 A
  7.    
    8 i/ g4 N) m, Q$ m* I1 q; q
  8.   es = (struct tcp_echoserver_struct *)arg;  / k( \  a* P* o, y- G) d" K
  9.   : q8 v& [9 p( }' v6 o
  10.   if (p == NULL)  
    4 K$ s# K! V1 u+ \; l9 A
  11.   {  , L" l- w# u0 P: z
  12.    //如果接收到空的帧,则释放连接  
    ) n9 X' `' y. g$ t
  13.    ...  5 K$ U3 w6 z9 l; ^1 W  P8 ]  Z& ]1 I
  14.   }     
    ' u. m6 c3 o' }1 y
  15.   else if(err != ERR_OK)  
    6 m% K- g, ?. {, `' L  Z& S% s
  16.   {  
    ! _- D3 P; p& B0 Z4 |2 W( ~! `
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  ) K2 S5 U4 h$ V+ m
  18.    ...  
    8 G- H7 c0 [2 x$ C; o/ F% k2 c# h
  19.   }  0 t5 u. @6 ~0 W2 n9 w2 D5 R/ P, R
  20.   else if(es->state == ES_ACCEPTED)  
    ) p( S6 T5 H+ T# Q
  21.   {  
    ; q; }6 I) v% a  [3 [  @
  22.    //连接成功,在这里需要设置sent回调函数  . z0 A& v, j5 v: F' ?) x2 ]
  23.    ...  ) f7 G- f9 I' z& V& f! K
  24.   }  
    % m/ W, m* x' o% D9 V# t% p
  25.   else if (es->state == ES_RECEIVED)  
    + D1 k+ k  U/ g8 b. Y* B& _& @
  26.   {  
    % L( {. z3 G: H1 C
  27.    //从客户端收到数据  . \8 D- H2 ?3 t; y1 G
  28.    ...  0 q8 e) w' a3 T$ g! Z- e& D
  29.   }  / y$ r7 q  P  m, |0 a! W
  30.   else  
    $ r7 ~* f/ a( O" u
  31.   {  ( x0 h1 G) r3 T1 u8 n
  32.    //当连接关闭时,还收到了数据  ' x, O' ]$ f5 _. g5 m
  33.    ...  ; ?7 l; h6 s8 ^; ~( j
  34.   
    ' J7 L: Q2 |( a) @
  35.   }  
    6 n) w: @: c. M. c0 Y) r& }/ {: m% Q
  36.     . l' D7 P1 w/ o' K8 p* {
  37.   return ret_err;  3 X3 u8 W: ?# j& l7 c% v$ ^
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:
3 e  H5 G6 i' d$ t
# {* k  W% K) R* J4 i! s# k+ z
0_1309762874AF1I.png

0 K" a9 m4 b  m4 \. h
5 [4 p3 U3 Q7 X& v
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:) z8 ]6 P) Z) b8 k/ x( [0 t
   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server6 b! X. n1 M: H7 a" S, {' R5 p$ c8 z
  4 `1 B* I0 N; _4 b
  如果网络联通的话,测试成功将如下如下的画面,如图:
5 V' K6 M, z' k4 C4 G0 g) h0 {
: ?7 s: ~5 e6 M4 A, b% u  G; o
0_1309762878a9k1.png
8 q. L% a+ c" V0 d. a  _3 c
# d% p0 G* R6 e0 t0 c$ B2 X/ ^0 y
$ x; C) l# w0 o+ p/ G/ B: o& J

2 v: e' {* P* r( s+ F: \& H
# g1 e6 e1 [, L& y4 r/ `! J
收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版