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

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

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

//=====================================================================* d. s+ V" u3 c6 d# Y+ p! n" i) D
//TITLE:
6 L. d% o7 V  j, \3 N//    STM32F2xx的tcp_echoserver例程解说
( ~' V" Q& c- N- X" q/ n* ?//AUTHOR:0 ~: y/ @5 B3 _$ r, S# i0 }
//    norains
2 W. b3 _( n1 d! A. O+ ~//DATE:
, W( g1 T5 k  b. b- t2 e//    Monday  04-July-2011( I+ v4 {+ \5 O  w; _3 O/ I  f
//Environment:
' a0 A) Z# Y& t$ p# \//    Keil MDK 4.22 w# X* x; t$ l2 a8 ?- J
//    STM32F207 核心版
) J1 S+ Y. j6 ~% H) c6 w//=====================================================================

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

) t0 n' q& g% |

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

    在tcp_echoserver_init()函数里,主要做了这么几件事情:9 X: F" Y8 d2 H. ?, V! k  {/ s
     1. 创建一个新的TCP协议控制块
* P# W8 W! A0 |3 [7 t     2. 绑定地址和端口号(port)
1 w( j( }1 v7 l- e$ N" z     3. 开始监听(listen). c3 G- }! R& @5 @5 ~  D7 U
     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  
    ( Q' a  T& |: @9 S% |
  2. {  
    * Y, W$ a5 L9 Q  `2 z
  3.   //创建一个新的TCP控制块  2 ~8 I3 `, P' w, L
  4.   tcp_echoserver_pcb = tcp_new();  
    - K1 J- h- b# h; P: ^1 ?/ B
  5.   & h9 D0 i- J* x) i
  6.   if (tcp_echoserver_pcb != NULL)  
    ( C- l/ P( n9 L4 d# U
  7.   {  % E! W% i0 ]* i7 w
  8.     err_t err;  
    7 G5 m% E! ]" \- _# v( _. ?- T7 A
  9.       
    : ]9 t  E4 s- i4 [6 `
  10.     //绑定到端口7  & Z0 e/ F/ _$ G: y% ~# @
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    ) ]! Z; D  f/ O8 I* A9 M) i" J  f# Q
  12.       8 l7 g, [- o0 R9 b/ X
  13.     if (err == ERR_OK)  5 x' l- r6 ^: _( h
  14.     {  
    / C4 x- j  `; ^
  15.       //开始监听  ' Z* C% z* a9 I2 Z; ?
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  - W- i+ u1 y/ L# u/ a/ ]* U
  17.         5 P( u7 Y# Y( J
  18.       //设置tcp_echoserver_accept为accept的回调函数  ; O- O$ d( E  x8 G
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    4 m' o3 K( U! K# \' T( R# w
  20.      }  
    ( G% n5 e' R+ z: [# J( n
  21.     else   9 z6 c% B2 @& F7 x4 j& t( K
  22.     {  ( e5 ^1 L9 b# q3 o9 R% F& c! L/ s
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  . U( j4 ?' t( }/ E* N% A! }; q, Z: b, L
  24.     }  ; W; S3 [4 ?) a% b
  25.   }  
    $ I( j2 L" p9 O, T6 w; S6 }
  26.   else  3 P" L( t- c. i0 q$ C
  27.   {  
    , W' t! y7 d5 `# r; \+ R
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  
    ' Q8 q1 g" t% M
  29.   }  & q, N6 g; B9 i1 o! ~3 o
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。8 u( a% e  Q! Z
  
4 p6 P- I0 `8 ?1 e8 G& O  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  
      b. j2 M$ i7 |+ N7 f- t  V! z: E
  2. {  0 O) _+ ^- @9 ~. ~7 ?+ i% X, }
  3.   err_t ret_err;  
    ( ~! b! S7 p; f
  4.   struct tcp_echoserver_struct *es;  
    8 [! T: o, s2 Q% W% S" A$ o
  5.   , h$ F* x3 T8 N! b, L& }4 D
  6.   LWIP_UNUSED_ARG(arg);  
    " s# o! l, C+ a# U
  7.   LWIP_UNUSED_ARG(err);  + @1 Z3 {, K& L6 |
  8.   5 K% s7 f% Z6 D- h
  9.   ///给新的连接设置优先级  8 m9 q8 V: K# U. N  v& L' H
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  6 y4 K$ j, G% [  K9 B( ^
  11.   
    # j" @1 K, H! f8 Y2 k7 A
  12.   //分配一个结构体空间以保持TCP的连接  
    % Y2 O6 m* m' ?9 m4 _
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  
    8 t& g. i6 {0 E  S
  14.   if (es != NULL)  * k6 F5 P1 c; V3 l, o
  15.   {  
    & u# ]8 N2 x' A1 Y( I$ N, ~' A2 K
  16.     es->state = ES_ACCEPTED;  
    7 x, A2 p* b+ e# w' Q
  17.     es->pcb = newpcb;  # _# W  e. T' T+ z- @. b$ l, {# Y) u
  18.     es->p = NULL;  " C/ |/ i6 O/ b! E6 ^
  19.       
    , Q# t" ^/ J7 p+ o$ m) U4 k
  20.     //传递新分配的结构体数据给新的pcb  
    * Y/ w2 t( q- O* ~/ w) o
  21.     tcp_arg(newpcb, es);  
    7 \! W" I) k$ K# ^% \3 E& q8 i
  22.       
    6 l+ x4 x9 p. `. ~; ^$ R
  23.     //为新的连接设置receive回调函数   
      l5 c( N7 a8 P$ n% \3 I* @
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  
    ) a$ |" y) Y, v' k) j4 Y
  25.       
    $ [6 H, y( k; N7 b; H) {
  26.     //为新的连接设置error回调函数  3 S0 O: j8 J8 C
  27.     tcp_err(newpcb, tcp_echoserver_error);  
    9 D" `5 T6 t5 o3 s! j
  28.       7 \7 E* S; M2 i# q
  29.     //为新的连接设置poll回调函数  
    ) N1 t* ?; S) i: w7 T: v6 M2 N
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  3 z" D. T& B5 x/ U$ @, W
  31.       " \8 u/ j5 O9 h1 U- f+ ^/ c
  32.     ret_err = ERR_OK;  / X& V  m4 j" f3 g6 I3 X
  33.   }  
    , y! Q: q# I6 L# c0 E" K
  34.   else  : ^1 I7 [; R5 F  U5 H: d. f7 C: z
  35.   {  9 X  I, \1 g5 P& o# c
  36.     /* return memory error */  ; I8 H; w' _9 A9 u* U4 h9 _/ k& }* ?
  37.     ret_err = ERR_MEM;  
    / |8 ^) \1 L7 l( h) h7 L4 m8 B
  38.   }  
    - z. ^* E  P0 y4 p! K: C" |
  39.   return ret_err;   
    4 r; h! q3 q6 `9 z2 \
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:
: H4 W6 r! P8 N" H

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  1 i6 I1 s+ q1 _$ k
  2. {  
    # Q2 E' C% w9 y) r8 v5 r6 X' X
  3.   struct tcp_echoserver_struct *es;  
    : p- Y& J1 ^6 h
  4.   err_t ret_err;  
    , K+ J' ?& T. U
  5.   4 i9 o0 F  w7 X
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  
    : B3 c2 }9 P( N* O$ W, h, B0 |
  7.    
    9 f5 ^6 q  I/ v& Z/ n& M5 F. s1 p  j
  8.   es = (struct tcp_echoserver_struct *)arg;  
    ( R7 N. D. t: [7 K, }
  9.   
    , k$ i1 p9 j, u0 Y7 J
  10.   if (p == NULL)  
    2 f  Q4 `' `0 M' N
  11.   {    h; e" l+ o" G% F4 `
  12.    //如果接收到空的帧,则释放连接  ) X. ^9 {1 u* E& P, h0 ^
  13.    ...  - d* p8 [/ \. t0 {5 k" e% ^8 K6 R8 r
  14.   }     5 J. t) `  j# P# m. J/ @
  15.   else if(err != ERR_OK)  , f. G1 a, J3 ^1 a$ w9 e& z3 c, Z
  16.   {  ! B1 N3 z* z' E  [* b% ^1 e% r% m" H
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  
    0 E8 S/ I( d( {9 y, O
  18.    ...  5 H1 P/ @0 s+ F' d
  19.   }  
    3 _" E+ \% M/ R+ E: S4 R; S
  20.   else if(es->state == ES_ACCEPTED)  
    1 m. |* G; x* B, C3 c8 c% s. W$ q
  21.   {  : W3 B9 R4 d. f" [6 Y
  22.    //连接成功,在这里需要设置sent回调函数  5 J, Q% ]1 ]2 w2 C
  23.    ...  & E  B+ u, f% B' r, W
  24.   }  & v' @% U1 L, I! |6 ^. P+ ~
  25.   else if (es->state == ES_RECEIVED)  # }, n* y( S7 _9 A" p. \3 _
  26.   {  
    ! Y1 N, G$ U5 H+ R! U
  27.    //从客户端收到数据  % l& _! j  W1 {9 O1 p
  28.    ...  
    ) b  ]. i$ f) ]
  29.   }  
    " x# w) F( \. D3 h
  30.   else  3 ^9 O- ?6 y& g( e3 K/ B* z
  31.   {  
    * T8 U3 ?% ^7 W( _, `7 B- J
  32.    //当连接关闭时,还收到了数据  . g# J% ~% O- h
  33.    ...  
    $ s$ b$ L$ z& |0 X- u7 u" x
  34.   
    . [2 L: @" [0 L# I/ U, \
  35.   }  - x2 X4 |! c* t
  36.    
    : e: q+ T" I6 N$ w% Q
  37.   return ret_err;  * @' @1 I; z! W# e7 T
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:
+ I8 {* I  v' z) }
$ [* V. _5 ^) ?) j
0_1309762874AF1I.png

4 Z# E  ?5 v0 w0 `2 ]1 d9 E
. A6 M9 k* D7 {4 m0 F9 M
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:
, V% C- Q4 q1 X: D9 `) K   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
1 s& {& J+ i' x; Z2 I* [  b* }  
2 C& g9 l( a5 C! m/ X2 j  如果网络联通的话,测试成功将如下如下的画面,如图:' O4 }0 M( Q1 L) f7 ?$ X. y

3 _3 o2 _. Q5 J" A1 G: g0 W8 _
0_1309762878a9k1.png
& a* Z$ i  }) M

: a9 d" ^# z5 l6 l$ m. |% n# y( v  y. z- t/ \. `. O) `
6 p9 W3 m9 K4 `. T
& `* o+ |8 P" \" L* F& U9 G
收藏 评论0 发布时间:2021-12-2 14:20

举报

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