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

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

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

//=====================================================================  b$ R0 W1 \7 j3 }# z0 s. U* b
//TITLE:
7 Y5 @9 g5 n3 l//    STM32F2xx的tcp_echoserver例程解说/ i2 N5 o% t/ O" p6 M
//AUTHOR:) Y* ^7 h: W: W! k) x) l
//    norains
: J* c, ^* _: h//DATE:
+ k8 m9 ?: n' f//    Monday  04-July-2011
0 b( G# y2 G# e/ D//Environment:$ ~9 t1 L% l: J( Y
//    Keil MDK 4.2
. b5 A) e& a) r$ f5 t//    STM32F207 核心版
6 I6 |9 m3 A. w: K& r6 c//=====================================================================

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

" j8 t% ^# J5 o) z  x

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

    在tcp_echoserver_init()函数里,主要做了这么几件事情:
/ F9 R' Y; ~1 A9 q+ Y* _7 p# I     1. 创建一个新的TCP协议控制块
& G4 \8 W6 v& i, R0 z1 h     2. 绑定地址和端口号(port): y0 H+ K$ T+ Q+ B6 m- ]
     3. 开始监听(listen)
( F. U4 U8 d$ ?     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  
    , f' J/ @4 Z1 z- \
  2. {  9 y6 E& J6 V% N
  3.   //创建一个新的TCP控制块  
    : F! _8 D  }5 u2 |: @9 q- T
  4.   tcp_echoserver_pcb = tcp_new();  7 u# k+ t0 c8 C  N& u" n2 L
  5.   
    3 ]+ V& v; b) p5 O# v* R( H
  6.   if (tcp_echoserver_pcb != NULL)  , i5 f9 `3 J; A! X' a5 h5 C
  7.   {  
    9 X, v! U/ }# l. D) @! m9 a; B
  8.     err_t err;  
    , M% N  ^" i7 u9 Y  Y2 w
  9.       . @0 `. f6 y* e; V
  10.     //绑定到端口7  
    ) `. I+ D9 m  j: A5 s, l' i
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    7 f# v; f3 h+ t( z' K* _
  12.       $ r5 x6 \$ p* I7 o  v
  13.     if (err == ERR_OK)  
    " q, H( @% _' F5 F' x/ K! E, ]
  14.     {  # t2 X! u4 I* M& w! U! n* M4 z
  15.       //开始监听  2 x" z: d+ p0 M/ a" U- v+ v
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  6 q# b4 k8 b& F1 o! k# T$ d
  17.         " C1 `2 O/ K0 V' h; @
  18.       //设置tcp_echoserver_accept为accept的回调函数  
    # L% i. G0 P- Z' u+ e
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    , E6 g6 A% y8 X$ R5 |2 l
  20.      }  % i3 f! O0 ^% z$ T5 w
  21.     else   
    % N: L9 H% X; @# O7 {( R* L2 O
  22.     {  - Q$ r- I0 e: f) R
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  
    8 \2 {; u$ W6 z1 i$ F; m$ C( N( M
  24.     }  & A7 N7 t) G: c' J3 [3 l
  25.   }  " ^5 Q9 ^7 c4 S* Y) _, Y9 w, h5 v
  26.   else  
      j9 m- w, ?, x' H* Y- n/ c5 s% G
  27.   {  
    & x) r/ K, F( r6 f* |- [
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment    a; Y  \% ]" q( M9 y
  29.   }  
    % c. b3 Y1 Z$ n# ^! E0 ^9 i
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。) N! L% U, g2 j
  
) f0 a9 s: Z3 G  j2 M* B2 m2 m+ z  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  % Q: ~/ j" O. U, n* W, f* u$ D) e
  2. {  8 b) U5 ~  A$ [/ C: w& ^; y
  3.   err_t ret_err;  . w. ]5 u4 H' j& A/ x
  4.   struct tcp_echoserver_struct *es;  ; h' a2 E$ g( W0 i1 w6 e
  5.   6 ^1 i" Q0 c6 L
  6.   LWIP_UNUSED_ARG(arg);  3 G  U3 b$ d) m8 J2 a8 v
  7.   LWIP_UNUSED_ARG(err);  
    ' E  f8 T' M- F9 [' V
  8.   
    6 `+ f5 d0 I! `0 g& O9 F/ f0 D
  9.   ///给新的连接设置优先级  
      T; ?9 q4 o* M1 T! L0 G% `) a; i: W  A( y
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  , w2 ~( R0 R' [8 W3 X4 l
  11.   
    ( J; Y9 W6 Z% t1 ~
  12.   //分配一个结构体空间以保持TCP的连接  
    , w( [3 t+ G* J7 J2 z
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  . V  m, o! U  W1 R
  14.   if (es != NULL)  " b) P1 G* n% |: f! z
  15.   {  8 {) `8 Y! b% w& f# |4 }& b4 Z
  16.     es->state = ES_ACCEPTED;  
    8 [6 ~: q# n6 B2 M) U8 L7 o1 g8 j
  17.     es->pcb = newpcb;  
    ( n% J5 v' _& B8 g" A
  18.     es->p = NULL;  & F3 w: f8 j/ H( B2 X6 s; }2 }
  19.       % H# t1 l* P  u# h9 @
  20.     //传递新分配的结构体数据给新的pcb  
    ! S% t' T6 l1 ]
  21.     tcp_arg(newpcb, es);  
    6 i, d' q4 _, y% }
  22.       
    . ~4 [3 E0 v* F! T" M% H9 J- Z. Q) L
  23.     //为新的连接设置receive回调函数   
    0 n5 ]+ T$ t2 A& a# T# @
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  
    7 L9 \1 J5 G, K6 w
  25.       
    : Q( Y6 J$ [8 w9 `/ ~/ z
  26.     //为新的连接设置error回调函数  
    & M6 A) }; F* n1 ~# ]
  27.     tcp_err(newpcb, tcp_echoserver_error);  ; G  F4 v) ^' ]  F/ i/ i" M! ]
  28.       
    ! O$ L$ Q' m/ V3 Y* D+ Y: }
  29.     //为新的连接设置poll回调函数  
    1 a/ s. [3 x/ f
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  
    ; {3 ~9 o8 t+ b( @: g9 Y$ G7 h
  31.       $ M+ F2 a( I& [" m/ {; d- w' Q
  32.     ret_err = ERR_OK;  
    . E( z3 \% S8 T5 P
  33.   }  
    7 t+ z* N: q6 M8 S8 N1 R0 O+ F8 Z
  34.   else    S- U: m: s" e( Z% ^
  35.   {  
    0 d% u; |) W) x9 `
  36.     /* return memory error */  $ U7 c( {$ F/ [1 k/ d
  37.     ret_err = ERR_MEM;  0 s& `) r5 j9 R
  38.   }  
    ) O! L+ G/ L; f4 U' m; K1 {- `
  39.   return ret_err;   
    : K7 J, Z% F* G' E9 l
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:% Y' I" j. \3 G$ G0 S, Q5 |

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  
    ; W- k6 z- \7 p& F
  2. {  . C% H3 m$ v/ i/ B; J0 j% [
  3.   struct tcp_echoserver_struct *es;  6 ]$ Q. N! p1 j1 N
  4.   err_t ret_err;  - \+ p) r3 o, {% Z% |- j
  5.   . |) `1 w4 `8 Q* g9 u: p& z
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  / S7 D# ?9 y) |4 ^& ^% Y- b( @
  7.     & e" X& c. |% T5 h, P  J$ q8 x5 b
  8.   es = (struct tcp_echoserver_struct *)arg;  * d' |. w3 o" e. H
  9.   
    . ^" F) r1 a/ Q
  10.   if (p == NULL)  
    ! l+ H- |( I" Y& `. h/ A5 L. D
  11.   {  5 B+ B$ v" O2 b6 U3 [
  12.    //如果接收到空的帧,则释放连接  
    9 [% h8 x* x* L! Y5 F: u; F
  13.    ...  6 t7 o; u( A# i0 q" J
  14.   }     2 j6 k2 [) F. Y2 r) f8 ?( k
  15.   else if(err != ERR_OK)  5 J. V2 \9 l' H' e4 h/ H
  16.   {  
    , S' {: e% I! w
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  5 \, Y$ C2 M( H
  18.    ...  
    6 @# S6 C. ~' a+ W6 @" m8 c$ `
  19.   }  4 w& K' x2 u4 g! p* E
  20.   else if(es->state == ES_ACCEPTED)  0 y) X$ ~3 e* g$ J& z5 y
  21.   {  
    & T$ p: h. f/ S7 r, {7 Q- p
  22.    //连接成功,在这里需要设置sent回调函数  
    6 w; d$ p: n% X1 q
  23.    ...  ( M2 k3 I( E# |  r8 p: O  E7 z
  24.   }  4 Q( H* b8 C- H
  25.   else if (es->state == ES_RECEIVED)  
    ) C' A; X2 X2 Z, `
  26.   {  
    * S. ]' L$ h5 G7 v5 o& M4 Y# H. h
  27.    //从客户端收到数据  
      x- L2 G$ ~" _+ T9 t
  28.    ...  
    , G* d% |; m8 [" D4 T
  29.   }  
    * F3 L4 G  m. B. W: a& y# s
  30.   else  5 J) Q+ _7 w+ i; _) F! A4 A
  31.   {  
    3 }/ b8 h5 Q! p/ v& O# `0 P4 W
  32.    //当连接关闭时,还收到了数据  # q7 T$ Y6 l& b
  33.    ...  
    " a6 v9 V4 Z6 u* m  L  L5 f
  34.   * x) A) G0 \, k' ]2 C
  35.   }  
    ; P( Z5 B, g$ p) s8 B# b) ]; l
  36.     3 c% @! d' z- u. c6 z- x
  37.   return ret_err;  
    / ?; @9 d& _. s
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:
  X) B2 d+ \$ X# u1 X# E
' x; b5 K. B$ x' J0 Z+ O4 U
0_1309762874AF1I.png
5 H. _, u( W5 a3 d, `# `8 _8 M
) R8 h, i/ n$ ]$ I8 h% n( @# f
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:/ c9 @) Z2 t6 K5 A1 A/ r
   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server1 k5 N7 B8 v9 }: d+ S6 F
  
  Z1 A1 B9 P/ Y3 \5 p4 u  如果网络联通的话,测试成功将如下如下的画面,如图:$ M0 X2 X! {! T3 q( t( w

) {  r6 T! \4 [7 m- D, h' ?' V
0_1309762878a9k1.png

* x9 H* }1 @: N& U7 Z
6 p- s5 Z! Q* R% ~3 u6 {6 w! c  g, E& _$ }% n7 N% s5 \2 r
# r- B. M  h$ Q0 `

9 z3 a: g& ]; a3 m
赞 收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版