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

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

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

//=====================================================================
8 Y& M  \" u$ x//TITLE:
& ~! [! v. ?/ P  Z& r//    STM32F2xx的tcp_echoserver例程解说6 `8 w7 J  W1 X
//AUTHOR:8 E% p+ ?2 h! ^) Z3 y
//    norains: @- o  a9 A  p  x, ]
//DATE:2 z" Z3 D, _# |/ K6 L4 J) c; Q
//    Monday  04-July-2011
9 _' j# H! [* u! N2 [//Environment:
; k5 g) u/ {/ g% C' `//    Keil MDK 4.2' Y4 G/ U  P+ W1 i  `0 H
//    STM32F207 核心版
. Z/ v$ Y4 [6 u% z//=====================================================================

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

# Q  C  W  n3 E4 a8 v. a

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

    在tcp_echoserver_init()函数里,主要做了这么几件事情:, m3 t3 ?. t+ g: |" k
     1. 创建一个新的TCP协议控制块
4 [+ b% w" J8 F; G  I     2. 绑定地址和端口号(port)& N2 L" i0 y: R. J  A; Y3 e- L
     3. 开始监听(listen)  D) ~+ D6 \% W9 r$ j3 R
     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  4 z0 \. J" ?0 n8 g3 D/ W
  2. {  
    ( w# a5 O5 s2 E& b
  3.   //创建一个新的TCP控制块  / m3 m' y: J  J! W# w+ J4 g
  4.   tcp_echoserver_pcb = tcp_new();  
    9 R& [2 p; D; }4 y
  5.   2 V8 N& d- h1 ]3 B. a6 J
  6.   if (tcp_echoserver_pcb != NULL)  
    / f$ E1 m- ~. Y' d3 D" Q
  7.   {  / J5 `! X* v9 s3 N& \6 ~
  8.     err_t err;  
    8 F0 g$ }# F% Z7 A6 E* i- Y
  9.       # d* U6 {; P7 f* B
  10.     //绑定到端口7  
    8 N0 J" o& T6 d1 U
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    ! \, r- ^* b5 P
  12.       , x) h: z0 E) {. A
  13.     if (err == ERR_OK)  
    : z5 v+ y' ^' L7 V* h  O" `& m
  14.     {  # b# S" ?* ~/ k1 @- u4 y9 l
  15.       //开始监听  
    8 m  [/ u* _. C- ~/ z( J% x
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  2 t6 p! o: U  h2 S
  17.         
    5 S3 h1 v0 X5 `4 L! }3 \: i/ N
  18.       //设置tcp_echoserver_accept为accept的回调函数  ; g" n, w. a  A* v  I; O5 T* F
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    6 f9 X) N  `% k
  20.      }  4 U( U& w2 U8 }$ I
  21.     else   2 D+ Z$ Z: \$ r8 t9 O0 J# C" x
  22.     {  7 n5 _8 v/ c/ k2 D' \1 F$ B6 b
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  4 x, t" T/ K% l5 Q
  24.     }  / c& n, s+ o1 K4 H
  25.   }  
      H! ?' ?/ L* z1 V9 S# P6 D
  26.   else  5 \6 g, L7 p( p, x4 t' l1 D
  27.   {  
    % I# R( F6 e8 b- n* t
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  
    . W7 X* u* O' X6 W
  29.   }  
    : l3 l0 H: Y* G
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。+ G% U. A4 |; q8 J- Z
  6 e- }0 S$ t2 R0 G$ n2 {: J
  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  
    $ @; S2 `9 {6 Z/ O) w2 B& p: Z3 n1 K
  2. {  
    ) b# T0 Z  P9 [' N! G
  3.   err_t ret_err;  + K" w' v; Q- T$ c$ [. ?$ |
  4.   struct tcp_echoserver_struct *es;  
    ) J1 K2 t- R6 Z3 ~
  5.   ' L. }8 H' F  ]* ^. R: [: z
  6.   LWIP_UNUSED_ARG(arg);  
    " o0 @0 R. F6 p7 ^; ]/ b) H
  7.   LWIP_UNUSED_ARG(err);  
    ; h4 T) c: s  D& S$ f
  8.   
    2 ?1 L( j! T8 m$ V
  9.   ///给新的连接设置优先级  
    * P. t( [3 u: S7 [; e
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  
    $ _6 v) ]* p$ ^, q  O$ L
  11.   8 [: F! ?0 ?* O: i+ G
  12.   //分配一个结构体空间以保持TCP的连接  8 i2 W8 H* x$ ~6 V
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  : e* ]/ {  L( E3 C4 g/ X, {
  14.   if (es != NULL)  ) ^: e; h8 V1 w, {0 j9 [
  15.   {  
    + c( E1 {. a' R: ?( S
  16.     es->state = ES_ACCEPTED;  
    . s4 Q2 n& Q- B  i3 O8 p% P3 M' I
  17.     es->pcb = newpcb;  
    + t# I* y( C. P' Q2 F( B" ^5 j
  18.     es->p = NULL;  # ]4 k- _* @1 G$ O& h- U/ M
  19.       
    ) i0 W$ m0 z2 r; R0 |+ n
  20.     //传递新分配的结构体数据给新的pcb  : c& s% K7 [5 G% H$ I4 \
  21.     tcp_arg(newpcb, es);  
    / k' L0 t/ C9 M2 X2 u* R2 N
  22.       $ s9 u  N0 v3 d: r# W$ o* m
  23.     //为新的连接设置receive回调函数   
    0 s1 p7 D, V" z+ L& ]' r& c
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  4 ]  t, A0 Q* b& U# Y% v* L
  25.       
    % x% J! ~1 o7 c! @; O2 O
  26.     //为新的连接设置error回调函数  $ x4 g/ J5 F! {# P% f" T% \$ D
  27.     tcp_err(newpcb, tcp_echoserver_error);  8 e6 V' A& u9 f; ]8 q" y9 k
  28.         q* T8 k6 x& ~1 m! F
  29.     //为新的连接设置poll回调函数  9 |/ s5 t1 o7 s2 {: `# t! P! @6 a
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  0 x! @- w3 O. N% c9 S% s. c& S" x# T
  31.       
    4 H9 Y* \& V3 q) Q7 y% N
  32.     ret_err = ERR_OK;  
    3 p. @# X& E: z& `' g5 a6 s) u! q
  33.   }  
    ) D- ]) T- J% ~2 E$ ]( e( I
  34.   else  * d( i/ K$ g& \/ i$ v
  35.   {  $ |$ l0 I! Y( T9 V# N- V
  36.     /* return memory error */  
    " x# k' f4 s# Q( e! H  u
  37.     ret_err = ERR_MEM;  4 W+ {% q( e. g6 ^  T. w3 Q
  38.   }  
      E5 J: {0 v: ?! U* u5 N$ P: Q
  39.   return ret_err;   
    : X8 F5 ]6 k; B  D% p9 M0 S
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:
" b; B" a) i8 [9 E0 A' T, R5 {

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  
    % l7 Z0 k5 o% n, ^3 c
  2. {  / c# e: v3 L; l( ?
  3.   struct tcp_echoserver_struct *es;  6 R1 W$ |8 E( s* `$ q3 O3 W3 C
  4.   err_t ret_err;  , g" k& r3 d. K. h/ j- M" ~
  5.   
    ' D5 ^3 z) [5 A9 i$ k
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  ! `2 u' A0 d  P! s. M$ g7 J
  7.    
    $ L% L0 l) e/ ]6 N8 s% C
  8.   es = (struct tcp_echoserver_struct *)arg;  
    3 C# Z6 I. ]3 X  }
  9.   - B* f# K3 c% H, h
  10.   if (p == NULL)  3 N; j! O( R0 o; ~4 k! p8 p( D: Q/ t2 K
  11.   {  7 ?  Z5 g) M8 ^7 x# m$ Z% v# h( D
  12.    //如果接收到空的帧,则释放连接  
      k/ n, u& A/ U, t. @
  13.    ...  7 }2 _! v* d0 `0 p4 D9 Q7 O
  14.   }     
    ; R9 S) M3 z& F. R! B# z
  15.   else if(err != ERR_OK)  ! {6 {; _8 P4 x/ X/ o
  16.   {  
    # n" V1 N& ^) R6 X* V+ e
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  
    * e# u( @) |" G/ {
  18.    ...  ! s) B$ n, S7 |2 H, O* a
  19.   }  9 x$ J2 u# u0 L
  20.   else if(es->state == ES_ACCEPTED)  & s: `9 Z. e7 _  t( T
  21.   {  
    & p$ F& |$ S( I$ y- l; p" }! H
  22.    //连接成功,在这里需要设置sent回调函数  # s, A* J3 d) Y/ Q  P; x
  23.    ...  0 y' k$ J) Q( m, W& d
  24.   }  
    $ W: c% ~: F! v
  25.   else if (es->state == ES_RECEIVED)  
    9 L: y, i/ A- M$ S
  26.   {    Q; w& g9 n* k/ e% G! f2 A0 N" \- k; p0 d
  27.    //从客户端收到数据  % `- g  u: x1 C3 g- @
  28.    ...  
    7 M! D- Y' g0 R) @
  29.   }  - F2 H& V8 ^# _
  30.   else  
      k. D9 G* D- P; Z3 I: C$ s
  31.   {  
    - l7 G2 _0 {5 K/ B* V
  32.    //当连接关闭时,还收到了数据  
    & B! S3 B3 \% r! [' R3 k) G! x6 X2 l
  33.    ...  
    7 u2 A  H  J6 l: ~
  34.   
    . p, ?2 g% `2 f
  35.   }  
    6 c) @2 z: u6 U* E2 H
  36.     7 w1 G3 [0 T& h" s3 c: x
  37.   return ret_err;  
    2 N1 c: k+ q- Q: B4 Z6 u, k0 ^
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:2 P" q) U0 V/ T$ |! g0 `

: s5 H, f1 |6 R& @8 m/ Y
0_1309762874AF1I.png

& u9 O6 D/ F& t7 u  r. h/ g( o- `4 _- S
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:0 q* _: w: W  c
   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
2 O1 Z8 G- x3 |, [+ C  # w5 R: N) Y+ @  t1 C
  如果网络联通的话,测试成功将如下如下的画面,如图:
8 y5 K' t7 E4 j" t; b

# G( ], j! i. F
0_1309762878a9k1.png

- P6 L. a' X! j/ q% x' u( M" Z7 F6 {5 j8 Y" n! }2 j

: b- M; ~* k% Y4 ?, c0 ^" w$ v- `  z* F' @- x1 X
! h4 J( d/ \4 o: g9 L
收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版