请选择 进入手机版 | 继续访问电脑版

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

【STM32以太网在线培训】手把手搭建TCP服务器及TFTP服务器  

[复制链接]
群星闪烁 发布时间:2017-7-23 19:40
本帖最后由 15616384352 于 2017-7-24 09:38 编辑
! Y+ p4 W  a8 y1 s4 z
9 z6 P2 m0 ~$ c$ o; ]  非常感谢STM32以太网在线培训,真的让我学到非常多,没培训前很想搞以太网但有种无从下手的感觉,经过这次培训让我从这个架构上有个从上到下的了解,再借助官方神器STM32CubeMX,开发和学习起来还是非常快的!我一直是STM32的粉丝,一直想去现场培训,但无奈没有机会,不过好希望能申请一个板子, E: k( w3 ?3 _( }) x% M
  看培训直播的照片我就不发了,当时看这个直播喝水都是跑着去的。
% o3 C: e0 b  A  我手上没有官方的板子,带以太网的只有原子的F407的板子,所以也是在这个平台上做的,板子上的PHY为LAN8720A,没有用到显示屏全为串口输出调试信息。
; b3 v, Y* y# T4 Z1 [" ~2 _1 Z实现功能:通过STM32CubeMX在原子F407板子上搭建TCP Server 及 TFTP Server ,实现这些功能非常简单。- I- b6 J4 G# t7 ?0 y$ D
一、功能介绍: d3 J7 M! Z0 u( ^
  1、TCP Server 实现显示连接上的客户端IP及对客户端发来的数据回传
' L, S: Z& K) s1 C  2、TFTP Server 实现IAP功能及读取MCU内部FLASH数据。这里用到的是文件传输协议,主要是参考官方文档UM1709,及官方STM324xG_EVAL的LWIP IAP例子。9 ~; K8 R, Q! t! o8 d  n, m: I
二、工程搭建2 q7 E+ {: p6 q! B5 r
1、外设配置打开STM32CubeMX,点NEW PROJECT,在左侧的输入框中输入407ZG,在右侧会显示STM32F407ZGT6的MCU列表,选择此列表,双击。/ Q; p% @6 d5 O2 m9 O5 i( |
设置时钟源为外部时钟. D! w4 ]+ Y% U6 |2 ^

, G) a6 v$ y, Q7 ~6 W开启SWD调试接口,开启ETH外设,选择RMII接口(LAN8720A为RMII接口),中间件里时能LWIP。使用RMII接口的时候软件会自动配置对应的引脚,但是一定要仔细对比软件配置的引脚是否为板子上硬件所连接的。; n& D) b4 w" c$ F% ]6 l4 p0 k

( W6 w0 s9 H& O: I  N- @原子F407板子上的LAN8720A的RMII接口引脚如下图,
* ?& c1 C0 a6 w8 D- b, C QQ截图20170723165345.png 7 S1 d4 {- A1 M5 b3 ^. a& U
而软件配置的引脚如下:
4 x" p4 Q8 k4 v QQ截图20170723173555.png ; m% H4 P- e% x: l# `  T, R
显然与实际板子上的硬件连接不同,这里需要手动调整3个引脚,分别为:
, P7 l* B7 D7 C3 h7 ]  ETH_TX_EN -> PG11
6 L: t- V2 f) z/ E  ETH_TXD0  ->  PG13( z. u- g& t; X
  ETH_TXD1  ->  PG14
+ [# y& ^, m2 O% S. I, M  PD3配置成GPIO输出(LAN8720A的硬件复位引脚)' \3 y: b5 C& y0 l& C. u! m. `2 Q
时能串口1,用于调试+ @  X  A" A9 s7 o" s" i! }  B' o5 c; v
QQ截图20170723174013.png
/ U2 ?( o# z" {! Q/ g& f2、配置时钟* }: g; a$ ?$ G: y9 V
外部高速晶振选择为8M,PLL SORCE MUX 选择为HSE,在HCLK处输入168点回车,软件会自动配置好。
1 l9 ?+ ~# ^6 k* N( G
5 r, r' A0 `+ Z: g$ i; s QQ截图20170723174313.png : p" B+ c! B6 W( ~' {* H9 }' d
' T* j+ w( K2 ?# J$ q% f1 ~0 }* z% K
5 T1 |9 i/ i9 x' L+ C0 t
3、中间件配置. z' r2 u$ s; E  |
这里我们主要配置,ETH、LWIP、串口1、GPIO, i- D! k5 r8 o& l6 N; h

# k, P' s7 B3 v2 O5 o1 f) v" n8 h$ R( [4 o, E4 A: x4 M
因为PD3为PHY的输入引脚,所有我们这里直接配置它为输出高,这样就为正常工作状态
& L0 A9 M  D( m( V0 J1 H 3LILTE8NF%MCM0PHZ5YQO%O.png . @! K5 e* D( U6 k
串口1我们设置它的波特率为115200,添加发送DMA,注意!如果添加了发送或者接收DMA则必须开启串口中断。3 J1 L! i2 L+ _) F
QQ截图20170723175035.png QQ截图20170723175136.png
, O" Z/ @7 a# @- V1 K) yETH设置,这里主要是注意PHY的地址,原子407的PHY地址引脚为悬空,地址为0。
9 X- T' R9 b8 I QQ截图20170723175259.png * T0 D5 ?! ]9 F
ETH设置的 Advanced Parameters 选项里面,我们选择PHY为USER PHY,名字我们取 LAN7820A,其它的设置全用默认即可。要改的寄存器基本就只有ExternalPHY Configuration,但是我看了下官方的例子,基本没有用到这寄存器里面的值,只有在使用操作系统并且时能了连接状态变更回调或者其它检测的时候才用到,所有这里我们也不管,都用默认值。
& u* K, v8 d* V7 s
! j" U7 v- h9 q! d QQ截图20170723175559.png
9 v5 {2 E* t! I( dLWIP设置 ,我们关闭DHCP,采用静态IP,因为我所在的网关为192.168.0.XXX,所以IP必须为192.168.0.XXX。这里我设置IP为192.168.0.120。因为要用到TCP 和 UDP(TFTP就是用的UDP),所以这两个都是时能的.其它设置选项我们先不管,直接点OK。 QQ截图20170723180028.png / Y' M4 o) ]  p

4 [! Y- H, l9 y* w% t2 W4 x! H( d4、生成工程代码7 w/ z4 \( ?( J1 [3 ~+ u1 b9 }
点左上角的Project,选择Settings ...(不建议直接点黄色的齿轮)
* ?& I; r8 K  N1 p/ u7 }
1 w# h7 T7 E4 O+ c$ W4 o, E9 O$ l' D5 B$ R
取个非中文的工程名,选择IDE为KEIL MDK V5
$ Q$ J# L( k8 P/ c- o! K" c! T/ { QQ截图20170723180818.png   I, T0 ]2 S$ U( l
& z% ?0 D0 ?! F
点击OK。再点右上方的黄色齿轮即可生成工程代码。
: u# T5 R" M  A% K三、工程代码; I3 m3 Y- z  F
生成的代码的试图如下:  x& S) R* b5 G- h
QQ截图20170723181459.png
" g, G5 q/ T# A6 o( r1、TCP Server 的实现
+ S# _% o) C1 p在math.h中 加入
: ]. |0 Y# E6 H; ]6 |, E+ a8 f#include "stm32f4xx_hal.h"
- a+ c/ R2 q0 W  ^. K#include "stdio.h"
/ V$ c" J1 s' N; r" e
7 r6 J8 r6 n4 s* L: K用于支持printf 及 一些 HAL 定义的 数据结构
3 f- _3 O: X# ]
- o4 |, r  Z1 ]! m. e$ `在uart.c 文件中加入如下代码,用于把printf输出到串口1
6 h/ c& Q. r1 Z+ d3 M# J, w' C, F0 p
  1. /* USER CODE BEGIN 0 */7 ?& O! Y' E* X) k& s, w: Z
  2. int fputc(int c,FILE * f)
    : u' k6 Y, L2 _  G: W2 O
  3. {5 k0 Y1 w9 S6 ~% L, E- \/ v3 Q
  4. HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,20);
    2 G& O4 i+ G2 D! Y  T& j2 H
  5. return c;5 X0 v) V" Z* Z3 L0 O/ n
  6. }7 l; A, @" `5 R/ l4 w+ d
  7. /* USER CODE END 0 */
复制代码
注意一定要在 /* USER CODE BEGIN X */ 与 /* USER CODE END X */ 中间添加代码,不然重新用STM32CubeMX生成代码后就会被覆盖。- P/ J( V9 J% j* p
在LWIP.C 中添加如下代码:+ X6 y! f1 p9 i7 |
  1. void User_notification(void) 8 J* Q+ E! W' q( W
  2. {0 U4 z  x1 j: g4 b5 p, E/ B$ l+ \
  3. if (netif_is_up(&gnetif))7 Q# y- P) `, B8 M$ N) ?) X8 B. s
  4. {
    # }5 g/ k0 g) H5 ~, M- i! D! e4 C
  5.     uint8_t iptxt[20];
    2 g6 E3 ?5 k' ~6 W; ]
  6.     sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&gnetif.ip_addr));: i: M" A) g, _7 x
  7.     printf ("Static IP address: %s\r\n", iptxt);
    6 h# c; w5 F  w2 n
  8. }
    : c; f; T9 ]* M, B! k5 ^4 E
  9. }
复制代码
这个代码的功能是把网卡的IP地址通过串口输出,这个是从官方例子里面COPY出来的,例子里面是输出到LCD上,这个是通过串口输出
! R! H. i1 l+ M& v接下来重点:
# T. A; y- A* N新建 tcp_echoserver.c 文件,并在文件中添加如下代码:; z/ _" d& y3 k/ ?) l
  1. /**
    ! B* Z. p6 [8 d; ~3 `/ o: y
  2. * Copyright (c) 2001-2004 Swedish Institute of Computer Science.( R& f+ i- i8 U
  3. * All rights reserved.
    ) ^; }' f* H7 f7 O
  4. * . B; g2 [* T* b4 j! S1 I
  5. * Redistribution and use in source and binary forms, with or without modification,
    * N& `% t& c+ k$ N# r
  6. * are permitted provided that the following conditions are met:
    5 @! o, M' b  O' a* f: R* j( d
  7. *9 s' S. U1 A. o7 N: [! o/ Y
  8. * 1. Redistributions of source code must retain the above copyright notice,( N$ F0 O0 B6 s5 {; d1 U
  9. *    this list of conditions and the following disclaimer.& y5 z" p& L* j$ j% b1 e( @
  10. * 2. Redistributions in binary form must reproduce the above copyright notice,  w( {7 [. D' d2 s- o: P$ j/ G
  11. *    this list of conditions and the following disclaimer in the documentation
    9 x3 a, n$ ]6 d) f- R! R4 h8 ]. M
  12. *    and/or other materials provided with the distribution.
    1 E  v4 O  K. m  O5 F9 ^, v5 ?
  13. * 3. The name of the author may not be used to endorse or promote products4 L, r9 U) L: R7 [1 U3 u6 R
  14. *    derived from this software without specific prior written permission.
    8 a9 U* i( t8 g7 ?1 _
  15. *9 H# c/ x! j) x- l
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
    / i; [+ ^0 y, [) W. n
  17. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * R3 G: j7 r# `6 t4 H& l1 z
  18. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 9 s, F! b2 @$ Z6 n: V/ q6 ?
  19. * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    0 B; ~) {; r  j& D4 X( e+ s, r& x7 z
  20. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 1 `$ ?2 q, p* e# h5 w
  21. * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    & M- x$ d: F. [6 ^% S+ s% t2 ?. h
  22. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    ' W' F- t# U- C
  23. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    8 N% Z6 b; j, d2 I" S1 V' H0 N; H" h
  24. * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
    ' D5 s: w# j# ?. n- `* E( |
  25. * OF SUCH DAMAGE.- [; h2 L5 W1 L8 M9 c
  26. *
    % ]6 `2 V: ~9 \+ N: t" n
  27. * This file is part of and a contribution to the lwIP TCP/IP stack.
    + }. m( M( ~4 X* T( O' b
  28. *
    ( ~" H" y/ E$ K/ J+ |9 t7 f% A" E
  29. * Credits go to Adam Dunkels (and the current maintainers) of this software./ q5 X; l$ U# n! u
  30. *; g7 [7 }, ^8 m6 H- W5 v- R. k
  31. * Christiaan Simons rewrote this file to get a more stable echo application.4 O* Y" R9 q! X5 A; H6 M3 j
  32. *
    & e% ~! i! d4 [2 c
  33. **/' H7 ^! G- W: N1 K1 B1 P
  34. 2 c7 o4 p0 e& d; k
  35. /* This file was modified by ST */% f9 j2 i7 h$ f2 `, r7 ^2 |

  36. % }6 H$ P5 e5 }; G4 N
  37. 2 l- O( i  \- f" s! G- p) K% l
  38. #include "lwip/debug.h"! ^: Z' a  x) Z
  39. #include "lwip/stats.h"+ Z( [& f+ A' m( J
  40. #include "lwip/tcp.h"
    6 Y  c* v/ Q/ {# F( V8 I
  41. #include "usart.h"0 w7 [! s" u: B8 m
  42. ; I  \, H1 ^# n" u# R; Q- i
  43. #if LWIP_TCP
    8 e' p& j2 d6 c% Y

  44. - I( ]2 j4 Q9 ^) m
  45. static struct tcp_pcb *tcp_echoserver_pcb;2 e! |8 s2 b! M+ a9 c" P( q. l: @

  46. ( L% k/ i# d1 @
  47. /* ECHO protocol states */
    8 k9 ~" ?8 l$ ]$ \
  48. enum tcp_echoserver_states
    . M- @1 |) @0 `9 a' x% y1 }
  49. {
    ; j8 S0 W6 I6 R
  50.   ES_NONE = 0,
    ) }, k% N6 G2 G2 L+ Q1 A
  51.   ES_ACCEPTED,
    5 B5 u. o" p" u1 z/ ~* {* j0 a: |
  52.   ES_RECEIVED,
    ) s2 i1 s, p& Y
  53.   ES_CLOSING6 y3 s: H& U$ _$ W2 @. D
  54. };4 M3 p: d( g: i8 l
  55. 6 O5 b. C9 O5 N# g+ [/ b
  56. /* structure for maintaing connection infos to be passed as argument
    ; o1 V5 m; T  O) }; W) Z' S
  57.    to LwIP callbacks*/
    5 {% i/ M9 J, V5 z3 k% d8 s& r1 r
  58. struct tcp_echoserver_struct  A& P) K. G, A, a( B8 ~6 p: \4 u2 ^
  59. {
    + m1 A  A8 Z% |: Y
  60.   u8_t state;             /* current connection state */
    3 K/ E) S/ U9 A$ e3 X/ j
  61.   u8_t retries;
    # m8 P" E- Z# \5 k3 z- z8 f1 D
  62.   struct tcp_pcb *pcb;    /* pointer on the current tcp_pcb */' W. x6 }$ B) t' ~
  63.   struct pbuf *p;         /* pointer on the received/to be transmitted pbuf */
    1 L7 U( b0 o( G( X
  64. };
    . [/ K' w0 H) |, `6 Q  U

  65. 0 p2 O4 j- |- y( O! z) R
  66. % m9 ]  K) S$ x# k, @8 h
  67. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
      |! x# h, a* z1 I
  68. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
    $ X' j% a( g2 n1 q* @
  69. static void tcp_echoserver_error(void *arg, err_t err);
    * \% j2 {+ ^8 F
  70. static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb);
    # o6 o: }6 y7 N! A1 y
  71. static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
    . U% ~5 B4 H8 W0 A
  72. static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);/ }  a" A! ~. _- ^  c2 H7 |" y" v
  73. static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);5 r2 _4 ~* d. i- E* n1 [1 [* x1 q
  74. 0 ?2 h$ ~* R  n; Q

  75.   X9 E# P7 H. c7 f
  76. /**7 f/ l+ T, m; n. @7 H% X
  77.   * @brief  Initializes the tcp echo server
    9 {# z5 }: @( R+ ~
  78.   * @param  None3 f, m; s' ~7 `$ I& m# j# D6 p! ?8 b
  79.   * @retval None( }: D5 ~: r0 a( Q9 e% u8 |
  80.   */" h. d( ]8 C" j5 l% ^% E' p
  81. void tcp_echoserver_init(void)
      d4 b4 P* Z: `. o9 i3 x$ Q
  82. {, R. d! C& K' h9 ~- A
  83.   /* create new tcp pcb */- M4 t+ Y/ h' e! p: w$ b+ P! v- C4 ^
  84.   tcp_echoserver_pcb = tcp_new();
    $ K( L5 D+ g3 j" g2 \
  85. 1 J2 o0 u$ r0 I$ c: A) ~
  86.   if (tcp_echoserver_pcb != NULL)
    4 j8 h) i& I7 \
  87.   {
    / {8 @( P4 }, Z& i& h& ?
  88.     err_t err;
    + C: y2 @+ C+ x% M# K. t: o, g, H
  89.     / L* S& L( y: e2 \5 f$ p
  90.     /* bind echo_pcb to port 7 (ECHO protocol) */
    8 s% b, e* f2 B0 Q, [: O
  91.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);! r8 R7 }  O, K( }# z' z
  92.     / h0 b& }7 A5 z
  93.     if (err == ERR_OK)
    6 ~- `* ^& `! s% C
  94.     {8 G2 {, P$ r8 ?$ S! ?3 @2 z
  95.       /* start tcp listening for echo_pcb */
    + ]; g: w0 l5 k6 e3 O
  96.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
    7 C; D: `/ _2 P' @, l
  97.       printf("开始监听 \r\n");
    - n: _  f! [% z; o3 o$ f2 h! e
  98.       /* initialize LwIP tcp_accept callback function */) D" L- a: N% a$ _
  99.       tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
    & A3 r; L7 G4 a- g: ?
  100.       printf("挂载客户端连接回调函数 \r\n");
    2 Y) b. l# s; }0 L3 t  q' v
  101.     }
    $ n# k& \3 ^8 K3 i
  102.     else   b- H$ ~- S7 g+ E0 f2 i
  103.     {
    " G$ q. n8 }* S, l- S4 [" k% X
  104.       /* deallocate the pcb */4 \7 o0 X2 R8 z5 P( ]
  105.       memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);+ o) q- ~: y7 J, X* h- O
  106.       printf("TCP PCB 内存申请失败 \r\n");
    0 c3 u# I& X+ i0 v8 ~
  107.     }
    # U6 \! q3 }# t+ B
  108.   }
    + \' R+ n0 Q+ y
  109. }7 `  x& K: N( E3 _4 @* R
  110. - E2 g0 ]8 r% j( m! _
  111. /**3 j) q! T+ `0 u/ k8 k
  112.   * @brief  This function is the implementation of tcp_accept LwIP callback
    0 J" P3 C5 `' i9 F. A, q& U6 S
  113.   * @param  arg: not used$ a" k2 R& B* C8 |# b/ m
  114.   * @param  newpcb: pointer on tcp_pcb struct for the newly created tcp connection
    & E2 V3 M5 u& B" W8 u
  115.   * @param  err: not used
    * j* ?0 X; o- \- B
  116.   * @retval err_t: error status
    . d1 I0 ^; R+ f1 u7 ]* F1 Q
  117.   */3 y$ {- z6 F% M+ |/ h' W5 b
  118. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err). l2 ?. r6 l# [; R1 @+ F; \2 K( g
  119. {* S6 B, ?  g' v3 W. e- r( V
  120.   err_t ret_err;
    6 |8 ^4 H. |0 ]* Q! i& c. D
  121.   struct tcp_echoserver_struct *es;4 O. _  {7 k2 d8 A1 b8 I2 `! ~# G
  122. * Z' r8 X% ^6 c
  123.   LWIP_UNUSED_ARG(arg);
    ' h* ?7 j& j9 u( A0 u7 O
  124.   LWIP_UNUSED_ARG(err);  g# U' D2 v2 z" N
  125. # |7 h) Q1 T; X3 w' u3 u! O
  126.   /* set priority for the newly accepted tcp connection newpcb */
    2 Y2 Q- s; q- q5 A
  127.   tcp_setprio(newpcb, TCP_PRIO_MIN);
    ! x! t* R( M; Z* g* G2 {' L) A3 U
  128.   printf("收到客户端连接请求,设置刚连接的客户端为最低优先级 \r\n");
    * o6 G/ T# x) t- g1 i
  129.    
    % n" N& M$ I  N$ `- G
  130.   uint8_t iptxt[20];4 |" S% a% n6 G7 p
  131.   sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&newpcb->remote_ip));
    / x6 W2 J" D7 J0 a+ L
  132.   printf ("客户端 IP address: %s\r\n", iptxt); $ n! n3 C6 @6 f$ l( @) D
  133.     . e+ m0 V0 ]: ?9 a9 m. H! K
  134.   /* allocate structure es to maintain tcp connection informations */
    $ i  ^+ Z/ j" I9 o
  135.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
    9 D$ c6 J/ l4 U$ r' K4 A$ l
  136.   if (es != NULL)% N# w  R7 [% u5 e
  137.   {
    0 H% {. C) _' ?+ Q; m! b- ]
  138.     es->state = ES_ACCEPTED;% d, j; W7 a3 u" O! L! N
  139.     es->pcb = newpcb;- s; w& q4 S! p' `9 z" ]* a1 t
  140.     es->retries = 0;
    2 o' [9 ?' L3 h* o4 P4 q; u
  141.     es->p = NULL;, U+ D' t8 x3 W; o! L
  142.       
    ; L- _% A1 |2 b0 y0 i
  143.     printf("为新连接的客户端挂载需要的回调函数及 调用参数 \r\n");
    ! l" o) I, i( B- E0 o# B1 H! A, |/ d
  144.       . p; ^1 d) J' F
  145.     /* pass newly allocated es structure as argument to newpcb */
    6 [" Q- E2 O( P& T4 d" q' o1 c
  146.     tcp_arg(newpcb, es);8 r) f+ I& \( |; [: J2 V! {# e
  147.    
    / I- L2 y" U7 V! U8 R
  148.     /* initialize lwip tcp_recv callback function for newpcb  */
    , c) d0 y  M0 f6 y6 h' `
  149.     tcp_recv(newpcb, tcp_echoserver_recv);
    & g% r. U! G' s+ p
  150.    
    ; P. ?  p1 g4 O  {( C1 f
  151.     /* initialize lwip tcp_err callback function for newpcb  */! f' l, t+ ?5 g* b8 ^9 T
  152.     tcp_err(newpcb, tcp_echoserver_error);: O* D  R9 _0 \4 R6 M
  153.     ( \2 z+ `; p1 p& M: T
  154.     /* initialize lwip tcp_poll callback function for newpcb */
    3 E' s1 a9 E* \7 E7 ]! t/ Q7 |4 H
  155.     tcp_poll(newpcb, tcp_echoserver_poll, 0);    U* N9 f1 j3 x4 d* |
  156.     ret_err = ERR_OK;, Q" |+ I! y0 h3 M; h
  157.   }6 @4 D: Y" ~7 A
  158.   else3 I3 \3 I: |/ }! `0 W, Y8 Q8 I
  159.   {7 {8 i( i& X& P$ X) A" e
  160.     /*  close tcp connection */
    : t/ C6 d% x8 s$ L9 H7 M" H2 }
  161.     tcp_echoserver_connection_close(newpcb, es);" X4 G$ y8 p( B
  162.     printf("tcp_echoserver_struct 内存申请失败 关闭连接 \r\n");6 J% a  @1 y  Q  B
  163.     /* return memory error */  R# \/ ~# P* {; S: X: Z1 f0 P
  164.     ret_err = ERR_MEM;
    ( P) B1 D8 Y1 h
  165.   }
    ' ?' ]# v! O3 L9 p# S5 X5 n; i$ H
  166.   return ret_err;  $ w. ~# l% W0 v( J  o6 u
  167. }
    7 W3 S8 T3 J- g0 ]: ]* |
  168. 8 U# [+ e9 G% c& t3 j8 j

  169. 1 u% ^. W# m4 ^7 _# G& \! S* m3 x
  170. /**
    2 r; _" Y$ N' U
  171.   * @brief  This function is the implementation for tcp_recv LwIP callback
    1 U% m4 K4 o  I. c( [! U+ e
  172.   * @param  arg: pointer on a argument for the tcp_pcb connection, n/ N4 k* j2 }! C
  173.   * @param  tpcb: pointer on the tcp_pcb connection/ h) ~2 s* V; g; d
  174.   * @param  pbuf: pointer on the received pbuf. t7 X7 ?  l7 E1 o
  175.   * @param  err: error information regarding the reveived pbuf, ~- K3 ^7 G, \* Q4 B& a+ r0 G- E
  176.   * @retval err_t: error code, {  [$ j1 L2 q, Z, f+ L
  177.   */
    " c9 k% }! Y- m. v% U' d
  178. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)8 M/ {3 a  e+ m! v6 l4 _5 B0 A) r
  179. {
    ) i# e5 D3 x9 j% s
  180.   struct tcp_echoserver_struct *es;
    ( B, H1 L- R. Z* m
  181.   err_t ret_err;
    % I  d! E6 D* V: z! K

  182. ' O0 r3 o8 d+ K4 _
  183.   LWIP_ASSERT("arg != NULL",arg != NULL);
    ' b  k* _/ f0 B; \  n5 V$ b: h
  184.   printf("收到客户端数据\r\n");
      D2 @7 `4 j( G& b! |
  185.   es = (struct tcp_echoserver_struct *)arg;- P/ |; N3 y. h$ _4 n! `
  186.   
    % ~6 n( i+ f, F& w* N+ F
  187.   /* if we receive an empty tcp frame from client => close connection */, M  R# \7 b) j+ D
  188.   if (p == NULL)- {/ {( v9 Z, _* i
  189.   {1 @6 O3 \2 u% x$ ?3 g1 m; t
  190.     printf("收到断开连接请求 \r\n");( P, m: a! j9 r9 J, p1 x' Y- B1 H
  191.     /* remote host closed connection */  E+ A" Y( r+ N
  192.     es->state = ES_CLOSING;
    6 P6 g9 a' V3 A0 J
  193.     if(es->p == NULL), V; r: g8 L4 q! j- M/ M
  194.     {
      X+ M. x7 w! k" v
  195.        /* we're done sending, close connection */& n7 A, _* x. {! p" F# J1 }
  196.        tcp_echoserver_connection_close(tpcb, es);
    & f+ k# B) b0 l$ u4 T# B
  197.     }3 J* ~4 {4 f# d9 V! k) B# y. M
  198.     else) I2 h  y7 n% S! l0 `& c/ x7 \: |$ A
  199.     {
    : ~3 J8 H5 F- T' s
  200.        printf("发送的数据还未发送完成 \r\n");
    1 J1 g6 o: u7 `& J. h% L
  201.       /* we're not done yet */2 ^) S4 S' O" L1 E7 [8 M
  202.       /* acknowledge received packet */
    ; D# [# g) M* a! x. F2 Z  n
  203.       tcp_sent(tpcb, tcp_echoserver_sent);/ P+ w! _* C  Q, ?. ?" e
  204.       printf("装载发送完成回调函数 \r\n");% W! f; k# ]7 e% Q
  205.       /* send remaining data*/4 }0 {" Q2 L, t: r$ O
  206.       tcp_echoserver_send(tpcb, es);  f2 q6 L2 }! a" X. I
  207.     }: _  V( P9 N4 `" H+ H+ v
  208.     ret_err = ERR_OK;2 V" |5 I0 x4 h+ B& P' I
  209.   }   
    ; a# |) s, ~+ \% c% m
  210.   /* else : a non empty frame was received from client but for some reason err != ERR_OK */# I& a1 q5 t2 Y$ q2 X# [: c/ ^; j
  211.   else if(err != ERR_OK)1 Y" A- C& H7 s* S
  212.   {' E; `% `6 @% W4 y
  213.     /* free received pbuf*/' r" a2 `1 l. Z( M7 c
  214.     if (p != NULL), o( f0 K0 ~! ?' E+ f  A5 X: j
  215.     {5 K5 i1 U$ _' S- ]( ]
  216.       es->p = NULL;
    ) k2 l7 h4 Y2 y/ Z+ F7 O
  217.       pbuf_free(p);( p8 f- @6 `3 I0 b( Y8 e
  218.     }' g+ J+ f1 ^6 [9 X; i
  219.     ret_err = err;0 M- q$ U# E; Z/ Z7 x
  220.   }
    3 `$ y5 f: j6 ^' _; o/ ~1 W! l$ H+ ^
  221.   else if(es->state == ES_ACCEPTED)4 F9 G$ \7 {0 Q" o. S+ n
  222.   {" H8 D. g! O* H+ I0 d' |
  223.     /* first data chunk in p->payload */  Y" r, ^, ?4 s6 [2 S
  224.     es->state = ES_RECEIVED;
    * C7 {1 o$ a* b; B; B
  225.     : \: u4 i9 g4 h  G; E
  226.     /* store reference to incoming pbuf (chain) */" j; d& i  i3 s" o+ S
  227.     es->p = p;0 j6 w/ K$ E% U* [0 T2 w) o1 u: X1 j  I- t
  228.     6 Q8 Z% Q4 O* d0 A
  229.     /* initialize LwIP tcp_sent callback function */
    ; i( K* z* A5 o9 r! s
  230.     tcp_sent(tpcb, tcp_echoserver_sent);/ z4 j# a1 c+ y1 W6 Q$ q) D+ G; Y% i/ |
  231.     printf("设备刚连接,挂载刚才接收到的数据,设置发送完成回调 \r\n");0 d0 }4 c7 J, B. m- e
  232.     /* send back the received data (echo) */% [6 D, c9 L& O
  233.     tcp_echoserver_send(tpcb, es);
    . _/ ~/ r3 V* U& r* F
  234.    
    & E& K+ w6 U4 S
  235.     ret_err = ERR_OK;6 i, z1 A& k2 {9 Y9 r/ N2 D8 i# ^  V
  236.   }
    5 q& B' ?$ W  f; \7 \
  237.   else if (es->state == ES_RECEIVED)9 z% G. `) D+ ?3 r8 b$ w
  238.   {1 L) T. A8 H1 h1 @/ d, V! [
  239.     /* more data received from client and previous data has been already sent*/% b. w  p7 n& o, y: H8 m( i
  240.     if(es->p == NULL)
    . ~; n- X  V/ d- C4 x
  241.     {$ l: R( ]# f0 y6 m2 k* Z
  242.       es->p = p;" @9 `, o+ c/ @2 V8 j
  243.       printf("接收到的数据直接回传 \r\n");
    # a7 A9 x' S% S
  244.       /* send back received data */7 e* R) n: [# q' y# |
  245.       tcp_echoserver_send(tpcb, es);1 h, d1 j5 k) U/ a1 e1 l( \
  246.     }$ v+ {2 s- Y! t) d7 l: x
  247.     else5 e# L% k! n" f+ ^2 i" D6 e
  248.     {. c' d1 M$ D4 D- f6 K8 n6 N. T/ R
  249.       struct pbuf *ptr;
    ; Q- @& q. s" a5 B: B) p0 u
  250.       printf("上次的数据还未发送完成,把新数据拼接在后面 \r\n");: N* ^1 |/ t8 n6 Z6 g/ ^) N3 c
  251.       /* chain pbufs to the end of what we recv'ed previously  */
    ( Q, e5 b; }) P6 _  G
  252.       ptr = es->p;( B9 k4 K; s* x1 F
  253.       pbuf_chain(ptr,p);
    & k2 ^3 q. ?6 k- q. Q
  254.     }
    ! J; K- G7 G3 L5 ~4 ]! o
  255.     ret_err = ERR_OK;7 z0 T2 I3 S( U% L( x! `6 r
  256.   }8 p. F! g+ A0 C2 v2 ^$ F. V
  257.   else if(es->state == ES_CLOSING)6 r4 q; k- }4 N* e
  258.   {4 d8 v5 g& n8 G9 Y7 ~4 K- z9 m( V
  259.     printf("当前已经是关闭连接了,但还是受到数据 \r\n");9 O1 P& r) w% c, ^8 c3 x
  260.     /* odd case, remote side closing twice, trash data */
    2 F' D' v) p" J" b9 l
  261.     tcp_recved(tpcb, p->tot_len);
    / Y/ G7 {5 `5 Y: V
  262.     es->p = NULL;2 V. ]5 N! ]! ?& ~2 F" |
  263.     pbuf_free(p);
    2 l& m. N( d, h' e* M$ u# V5 L1 [
  264.     ret_err = ERR_OK;
    + Y; [& d, ?3 j* F
  265.   }2 O* w* z9 x% k& O8 Y9 Y& n
  266.   else" o# u  h# f0 y8 P
  267.   {
    ' |; K- i8 {( [, x7 [- h
  268.     /* unkown es->state, trash data  */
    4 R9 ^5 h$ |, [  y
  269.     tcp_recved(tpcb, p->tot_len);
    * H6 c% @# i1 H) x; U; M5 J- w8 H& J' Y
  270.     es->p = NULL;( I1 ?! C, @3 b9 {0 u) M
  271.     pbuf_free(p);" ]! [; t( A3 q& o4 E
  272.     ret_err = ERR_OK;! x5 T! X/ k5 ~: n! [- M- j2 N/ ^4 x
  273.   }, W: |# @9 p! r* H
  274.   return ret_err;
    3 C' d' r. Y2 L+ U
  275. }7 V$ H4 P# L6 |4 U

  276. # y4 ]/ q* H. g- h, M3 n% X2 m
  277. /**
    , H5 M+ a% I, I/ K) G  G3 o7 D
  278.   * @brief  This function implements the tcp_err callback function (called
      P6 h4 ]0 [# @/ P9 ~0 n9 C
  279.   *         when a fatal tcp_connection error occurs. $ F( D) e0 i! w# O$ ]6 H
  280.   * @param  arg: pointer on argument parameter
    . `( d! f: \0 C# x" e
  281.   * @param  err: not used- t. A2 g/ p/ h2 k! h
  282.   * @retval None3 I; v  T& b- U* @+ C. h/ H3 h) i& p; A
  283.   */" {# z" a& C' O. j+ E3 I/ \6 k
  284. static void tcp_echoserver_error(void *arg, err_t err)
    : P) s( s3 Y! S  k' b
  285. {) B& i9 ^; q3 q: n0 |, j2 `
  286.   struct tcp_echoserver_struct *es;$ q: e) @9 g6 L8 V
  287. ; A. J$ e- Z  p: s( i& \
  288.   LWIP_UNUSED_ARG(err);- I% o. a- ?/ X4 Q
  289.   printf("错误 : %d \r\n",err);
    % |2 D6 L  k: a& J$ h
  290.   es = (struct tcp_echoserver_struct *)arg;
    " ?" h7 l" I9 O# i' b
  291.   if (es != NULL)" T  v9 t5 T. W; P  i' w% k3 V/ ]
  292.   {
    : D1 p$ g0 ^% ]8 g
  293.     /*  free es structure */
    # C- @+ k- n& D+ r* N% P3 U, @: }
  294.     mem_free(es);& T, i4 r& S) f, f# a7 x4 _1 h
  295.   }4 Z) L) q% |5 _% Q
  296. }& v: M* Z  Z" }( s3 S

  297. " N8 Y* R9 e6 e. ]* B
  298. /**
    * w# {2 B1 E) D* P. c; `' M
  299.   * @brief  This function implements the tcp_poll LwIP callback function
    , v+ n) U/ Q  v% O1 B- R9 l! a8 g9 x
  300.   * @param  arg: pointer on argument passed to callback& ?) T! K: q$ h
  301.   * @param  tpcb: pointer on the tcp_pcb for the current tcp connection: ]9 s( y5 H2 ]; n* N2 E
  302.   * @retval err_t: error code
    ; s, o3 t2 i1 A* u' S
  303.   */' J$ Y* e: I. k2 M$ ^* M$ _
  304. static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb)3 W( D7 [; f2 k2 G- A% [8 ]2 u! K; U
  305. {
    , \) X9 _. `& z8 l) }. i
  306.   err_t ret_err;
    4 y/ a9 @; _/ \  y( H5 \9 z
  307.   struct tcp_echoserver_struct *es;" h! [( E- q# c1 y7 a5 m- ~
  308. ! o7 Q1 ~* A! L' P( _" y+ _
  309.   es = (struct tcp_echoserver_struct *)arg;  E4 }% B$ ~' i; C( L
  310.   if (es != NULL)
    9 ~" k7 ~! ^" M
  311.   {1 y5 k; L: n. P' z
  312.     if (es->p != NULL)' @: Q3 R  A' w
  313.     {
    % N2 C4 y7 P" g0 E' [1 a" Y- D
  314.       tcp_sent(tpcb, tcp_echoserver_sent);, ]1 ^* `% U. J* ^" v% S
  315.       /* there is a remaining pbuf (chain) , try to send data */
    & T) T- }# J+ B$ F1 o
  316.       tcp_echoserver_send(tpcb, es);/ x& h, M' N2 t5 Z
  317.     }  N: A! k! J$ B# ?' ?+ H
  318.     else
    ) B4 r/ T: f( Y) D
  319.     {+ J( l- Z: |+ D$ s; @
  320.       /* no remaining pbuf (chain)  */
    9 S* H2 t' b4 c& V  y# R* Q
  321.       if(es->state == ES_CLOSING): L; v6 f8 k1 f8 l
  322.       {
    ' a7 h5 a7 r- {- r
  323.         /*  close tcp connection */( B$ C$ p, l' L" R6 \
  324.         tcp_echoserver_connection_close(tpcb, es);8 c& m) u& }4 S  e7 {0 D
  325.       }
    ' b/ Q/ M( M7 ?/ J) `
  326.     }4 |  H$ F, V3 ]3 |: I& H
  327.     ret_err = ERR_OK;
    8 |  B) H/ S! B" i( h- I
  328.   }
    ( E5 ^9 r+ |7 f  W  C& J* {( N
  329.   else* p# Q/ N# k" N
  330.   {
    3 v8 C$ g0 r+ l9 B" s
  331.     /* nothing to be done *// X( r; ?# k2 |# G3 C0 ]
  332.     tcp_abort(tpcb);4 @5 T$ b4 C2 f, T) P
  333.     ret_err = ERR_ABRT;6 Q5 L# }0 l# B
  334.   }
    7 y+ \5 @7 z: B1 J& ]# y* v3 k/ [
  335.   return ret_err;
    / A) c( g8 u% T$ l
  336. }
    0 r7 R' X) T2 k+ M3 @8 F8 F

  337. " K1 e4 V# f- k7 \: F% m
  338. /**
    ' s4 r. j; D9 L( D6 a. k) D' E
  339.   * @brief  This function implements the tcp_sent LwIP callback (called when ACK
    ! E+ V5 b' ~, K1 ?; @
  340.   *         is received from remote host for sent data) 6 M& Z" H- ^) |% l5 v9 n
  341.   * @param  None: e# g' @+ r/ {% Z
  342.   * @retval None
    : j0 L# q, S, t
  343.   */
    ; O! m, `# y7 U. _6 i1 ~
  344. static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)* A4 P: S8 d$ D4 K, O5 P+ J
  345. {  T( L, j3 a4 S7 h. z
  346.   struct tcp_echoserver_struct *es;
    4 K/ C1 H* `3 D9 y5 h
  347.   y4 o3 [7 h# K( I" z$ a! k. B
  348.   LWIP_UNUSED_ARG(len);' `4 `+ ]/ o, H

  349. 1 G3 G- y3 B3 x: K. q* I
  350.   es = (struct tcp_echoserver_struct *)arg;
    ; ~, a: ~& R: ?( ^5 x" w
  351.   es->retries = 0;
      G; f! n5 _# h
  352.   ! [# R6 {5 e9 n3 E' i- K/ y
  353.   if(es->p != NULL)
    - o0 s7 e' W5 s
  354.   {8 Y+ v. {* V) s+ P. Z* q& \7 D- y
  355.     /* still got pbufs to send */6 \( M% z! v+ Z9 p8 G. H6 D7 B6 H. [
  356.     tcp_sent(tpcb, tcp_echoserver_sent);( h6 _( n  J8 d
  357.     tcp_echoserver_send(tpcb, es);
    - Y! R. a- y  v& M
  358.   }' h. \- f) s6 V8 F/ f: T) L
  359.   else6 @+ p, X1 s& S- L' [7 A8 @2 a
  360.   {
    : s+ Q9 s( O' ^! V% X# e# r; S
  361.     /* if no more data to send and client closed connection*/* U3 H# l8 F: o; c
  362.     if(es->state == ES_CLOSING)2 @0 F9 @' V+ I  C
  363.       tcp_echoserver_connection_close(tpcb, es);
    0 @$ T( R+ X. w( S
  364.   }
    ' m( s" m5 g7 O. m9 ]. F) |
  365.   return ERR_OK;
    & s! I; }9 n" K% t% |+ j
  366. }
    9 X$ E" R1 O! B( ]: n2 [1 M

  367. - N' Y0 e# i# q

  368. / F3 y- O7 M0 A7 G1 G2 p
  369. /**, \8 T; t7 q" t
  370.   * @brief  This function is used to send data for tcp connection
    - U3 x9 q! X: p! i3 ~8 U
  371.   * @param  tpcb: pointer on the tcp_pcb connection0 M, t5 P$ u/ r8 c( N5 Z
  372.   * @param  es: pointer on echo_state structure
    * t# {9 v* t  Q' I. a1 G, X" _
  373.   * @retval None6 {9 W/ G# ]" X% q" i' \
  374.   */
    8 c& B! t/ B0 \. Q# g' P
  375. static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
    # m! \. K7 G2 w4 y" W) y
  376. {. a- j* W$ {6 q& j5 R
  377.   struct pbuf *ptr;
    / b. N1 O0 Q+ T$ @" S! j% X0 B
  378.   err_t wr_err = ERR_OK;3 W9 m6 I" b" U' D/ G& B: C% ?
  379.   uint16_t Count ;8 s5 e% f  b" K" f) _5 _, _% N
  380.   printf("发送数据的总长度 : %ld \r\n",es->p->tot_len);
    + @2 A3 d' ?: f8 j9 ?5 [- F
  381.   printf("发送数据: \r\n");
    / @- B8 K* i; a+ i# i( X) K
  382.   while ((wr_err == ERR_OK) &&
    / |: a% T$ B! B6 O3 T
  383.          (es->p != NULL) &&
    ' G% q- M7 i" I- o! f% O7 t9 E
  384.          (es->p->len <= tcp_sndbuf(tpcb)))
    + ~/ t/ |  ~  e0 j
  385.   {! p) T) U: D$ T+ R. ]
  386.    
    ' ~$ W" G1 Q+ L# @: h
  387.     /* get pointer on pbuf from es structure */. u% x# F" v- I5 ?* U2 m
  388.     ptr = es->p; //得到当前需要发送的数据缓存 pbuf
    ! M) {3 y' A: A2 R# ?9 v" W; t" U
  389. " A$ e8 q6 v3 S: g
  390.     for( Count =0 ; Count < ptr->len; Count++ )0 P: W( e- n" i$ y
  391.     {
    $ a+ z* t9 n( S6 W; k
  392.       printf("0X%02X ",((uint8_t *)ptr->payload)[Count]);
      Z3 y& \4 t1 f" N8 l( g
  393.     }6 ~, B! u$ `9 Q0 c& n: X
  394.     /* enqueue data for transmission */
    9 g4 j3 N& c* t' b7 u
  395.     wr_err = tcp_write(tpcb, ptr->payload, ptr->len, TCP_WRITE_FLAG_COPY);  //发送. {& j4 p7 R3 l4 I( W1 }
  396.     7 C+ v. S  K% t0 {' G9 k
  397.     if (wr_err == ERR_OK)* f2 R) V" k. F5 I
  398.     {
    ( H. l! p' t$ T6 N
  399.       u16_t plen;' i0 L& P( K4 c' p' Y. b
  400.       u8_t freed;
    # o2 V3 ?: @! N

  401. + Q8 ]+ Y3 E0 {9 j
  402.       plen = ptr->len; //得到当前节点的数据长度/ m' d# a* Y1 L( D" j: b' f; X
  403.      ( M1 y" d! M) r: W; n- g' q
  404.       /* continue with next pbuf in chain (if any) */
    , H) {& y) r/ E" V6 l+ f) I
  405.       es->p = ptr->next; //得到链表的下一个节点
    : V2 L5 |2 r, D9 i8 Z" a3 A
  406.       
    0 M+ k* N) c4 n0 X1 K, [' Y
  407.       if(es->p != NULL)  //如果节点不为空
      |7 {; s$ S+ ]) {2 _& I1 c# R
  408.       {/ h( }) B9 {* x6 E7 B
  409.         /* increment reference count for es->p */7 A+ V( c/ @$ W, f
  410.         pbuf_ref(es->p); //成员 ref 的值加1  当收到数据时,pbuf成员ref的值为1 ,在pbuf_free()函数中是对成员ref减一 如果结果为0则释放此节点内存并进入下一个节点  否则只是把成员ref的值减一
    - b( b% r# }) A& k* T7 p- Z" Q/ `
  411.       }
    " w7 B( ^) D& W& n+ P9 d. f
  412.       5 x/ D3 v+ e% n
  413.      /* chop first pbuf from chain */
    6 P8 t1 g. w0 _+ o
  414.       do* m6 N4 i  b) ~4 Y' M6 E0 [4 K
  415.       {
    + t: h. v0 Q9 b. c: [
  416.         /* try hard to free pbuf */
    ) ]+ J# \* t' m3 }( f; O* j) B
  417.         freed = pbuf_free(ptr);  //每执行一个 pbuf中的成员ref的值减一  当它这个节点没有释放时,返回一直为0" f, M) \5 j4 G' ?% t) r
  418.       }1 b. s! }1 F5 y1 j4 j6 G
  419.       while(freed == 0);  //执行直到释放这个节点内存为止  pbuf_free()函数的返回值表示释放的内存的节点数8 E" B0 g- `7 P; `
  420.      /* we can read more data now */
    ( b8 u2 y9 q) J, V! Q
  421.      tcp_recved(tpcb, plen);   //增加可以接收数据的大小
    5 t" h  _$ q# U% x, R; p% b' R' P
  422.    }$ T5 m0 O7 G; `1 d' i" J
  423.    else if(wr_err == ERR_MEM)1 S( J- J1 @6 f0 X
  424.    {
    2 R+ ]! o( T8 J4 y) u. @- J7 ~
  425.       /* we are low on memory, try later / harder, defer to poll */
    6 M- o* w) z3 P5 ~
  426.      es->p = ptr; //重发
    * h5 J9 w& a. u
  427.    }
    $ r( ]7 X9 t8 J: Y' u+ N( R6 Z7 o
  428.    else
    , l, M0 G/ b; L9 Q' O, r2 m2 I
  429.    {: t0 d0 K. I/ i: V% Z& g
  430.      /* other problem ?? */
    8 W3 J( x! m) ~  e+ r
  431.    }
    4 c# `; V" }9 J: {
  432.   }# g" O* C2 H* i6 ]" s' ?2 J) L
  433.   printf("\r\n");
    * d9 N* f1 |9 ^+ W
  434. }
    , M( ]3 Q) h1 @+ o) x

  435. ) t+ I+ ~+ x1 m/ Q3 ~. G
  436. /**: n; j5 P2 v: P8 p' f  k: Z
  437.   * @brief  This functions closes the tcp connection
    ( H. F: X! Y3 y) E4 Y$ B: g
  438.   * @param  tcp_pcb: pointer on the tcp connection
    3 q! B6 o7 }7 C+ W  r/ ]6 I. Q0 L
  439.   * @param  es: pointer on echo_state structure
    & |1 i' O3 q, g# w
  440.   * @retval None
    - q3 |4 m2 v3 k3 V) k" @
  441.   */* U) M* O* w$ q" G# N$ w
  442. static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
    * y2 t1 D$ j$ k+ R. D: z
  443. {$ l! ^% P. m* K% n; u3 D
  444.   
    ' ^$ }: U7 X- [$ L: Z& {5 M
  445.   /* remove all callbacks */% e! x6 u4 A! U% e/ X  N
  446.   tcp_arg(tpcb, NULL);4 q- A5 o! i9 s4 e# A
  447.   tcp_sent(tpcb, NULL);5 p5 m7 J8 b* H5 ?) ^
  448.   tcp_recv(tpcb, NULL);
    / o: c2 e1 B! k4 V: x2 f
  449.   tcp_err(tpcb, NULL);
    0 {, g- _! a6 k" y; k* N
  450.   tcp_poll(tpcb, NULL, 0);" Z0 T2 ^) ~9 m2 r$ {, t
  451.   / h' Q3 f( r/ p' h3 ?( l
  452.   /* delete es structure */* m, _/ q, N* N5 `
  453.   if (es != NULL)7 \7 |. g% n6 Z0 ]
  454.   {7 ?6 B) O& [5 D. v# O
  455.     mem_free(es);
    7 |/ A( Y/ u1 a- V/ S" [4 i, @
  456.   }  
    3 w) F" r7 _( i; d0 @& V
  457.   
    7 c" Y' o* j$ i' ]8 ~4 m1 \- }6 }% j
  458.   /* close tcp connection */
    0 J/ h  L1 m6 V9 o3 U
  459.   tcp_close(tpcb);9 f; B$ K* o+ w: _
  460.   printf("已关闭连接 \r\n");
    4 n( O- {' [* E' T2 ?% a; N( t
  461. }( z0 ]0 g% G) T) p. K9 w7 U4 b2 i

  462. : g0 ^* Z  Z5 w. Y* U- l3 r
  463. #endif /* LWIP_TCP */# P8 E, ^- Q# P
复制代码
这个代码也是从官方的 TCP echoserver 例子COPY出来的,不过里面加入了串口输出信息,及一些注释,感觉写的很好的,就没怎么改了7 V8 X. ?' ^6 u$ q$ h4 p# k
看起来代码很多,起始真的不复杂,听了之前的以太网配置,这个理解起来还是很简单的。大致流程如下,先新建一个TCB_PCB、开始监听、挂载客户端连接请求回调函数、挂载接收数据回调函数、挂载发送完成回调函数、挂载出错回调函数、挂载心跳包回调函数、关闭连接等,最重要的是释放内存!每收到一个数据链,它已经被申请了内存,所有在处理完这个数据后一定要释放。
0 T" p& p2 c" z/ I' ?+ K* c在mian.c文件中加入如下代码:9 K/ k% n  P  m/ Y" ^, I
QQ截图20170724093213.png 2 O0 z2 u5 n8 b( j/ _
编译,下载到开发板中。
% m# z3 t% m* o& ^3 o串口输出如下数据,表示服务器已经开始监听2 ?/ k3 T% q3 x, H. d
Start
" W, b+ Z( |+ ^  q- G: Z  r9 Z+ c6 `开始监听
# Y8 V+ z+ f% `$ A. G挂载客户端连接回调函数
  Y5 d1 V* L: _/ V5 y* dStatic IP address: 192.168.0.120
2 J4 p# m3 Z, V* ^0 I
: q! J/ i* z  b% l, F* V  @点击电脑开始菜单,输入CMD点回车,出现命令行,在命令行中输入 :"ping 192.168.0.120"0 }2 q& D5 g6 r: |# \: G$ o; J* R
能ping通,显示如下:
9 K( w: {; x1 u QQ截图20170724085406.png , y$ ?; \+ e2 A9 Z9 Q( z
命令行使用技巧: 直接输入 “help” 可以返回支持的命令, 在命令后面输入  "/?"  可以返回命令的使用说明,比如输入 “ping /?”
0 T0 B4 f$ D) m& S0 n$ V2 S2、TCP服务器 测试
1 w- B0 @7 F- m& l% i# m$ o) m用网络调试助手连接TCP服务器(两者必须在一个网关里面,192.168.000.XXX),网络调试助手里选择TCP客户端,远程主机IP为 192.168.0.120 , 端口号为 7,5 W/ I# q: G3 E$ ]$ \* T+ {7 x
QQ截图20170723183809.png 点击连接,串口调试助手显示如下信息:1 V* ^  @* n7 S$ h2 ^# L
   Start # v6 ^$ }3 y2 L
   开始监听
' y. [. |+ L3 u8 I) _   挂载客户端连接回调函数
/ ^: v" m. |, B   Static IP address: 192.168.0.120  e6 [1 p. ^9 O# m
   收到客户端连接请求,设置刚连接的客户端为最低优先级 % W, G9 P8 A4 |# ~5 V0 G
   客户端 IP address: 192.168.0.1012 F5 a2 g* H  ]" ?! e$ H
   为新连接的客户端挂载需要的回调函数及 调用参数
" t9 j. G* o' `: n+ u  g则表示连接正常,这时网络调试助手输出什么数据就会收到什么数据,同时串口也会输出相关信息9 K* ~7 G* [9 u( U$ j  a! X
网络调试助手 输出: http://www.cmsoft.cn QQ:10865600
( I) g. L9 K5 \; \7 j网络调试助手 显示: 【Receive from 192.168.0.120 :7】:http://www.cmsoft.cn QQ:10865600
+ j) i  _* p# q% a6 x
3 ~$ A" F: ]" s8 }串口调试助手显示如下:' w& c; p. j+ q
Start 3 c% P( T4 ~5 O8 u$ u  Z
开始监听 3 E1 V; z6 i6 p
挂载客户端连接回调函数 - v8 d) Q9 z, l+ O3 p! A: @; ]- r
Static IP address: 192.168.0.120
! S0 t- ^. p  f收到客户端连接请求,设置刚连接的客户端为最低优先级 , z9 Q5 }* b+ c* `& H; [
客户端 IP address: 192.168.0.101& @& p, [* S; y* K" M& `! s
为新连接的客户端挂载需要的回调函数及 调用参数
! u8 A7 U5 H( U9 p# L收到客户端数据* L3 @" U" T6 t* B1 ^; W# M
设备刚连接,挂载刚才接收到的数据,设置发送完成回调 " m* k! m) F3 p. u! f+ X- f
发送数据的总长度 : 32 ; P( v* f9 J* {9 W" H3 o* g
发送数据: + I; X2 H+ |1 y3 `) y# F7 P
0X68 0X74 0X74 0X70 0X3A 0X2F 0X2F 0X77 0X77 0X77 0X2E 0X63 0X6D 0X73 0X6F 0X66 0X74 0X2E 0X63 0X6E 0X20 0X51 0X51 0X3A 0X31 0X30 0X38 0X36 0X35 0X36 0X30 0X30
" C" P9 o% [! Z; R4 \  y. q5 u8 q' [, ~0 z) W6 E; i

; z5 Z/ ]- v3 N! r/ r! L# i( H网络调试助手点击“断开”按键,串口调试助手输出如下信息:1 [$ {* w' d* v& Y% p7 z! k
Start % G  W, x( L1 F' @7 t6 h; O% u6 T
开始监听 * n) U% r8 F0 B4 @9 D* M
挂载客户端连接回调函数 * _. U) H4 {2 B, d% [7 V* ?" d
Static IP address: 192.168.0.120
5 q7 I) [9 a: Q收到客户端连接请求,设置刚连接的客户端为最低优先级 . T  p# N# k. k5 y8 R* l# O  [
客户端 IP address: 192.168.0.101
9 r. Z) T. j  a( a2 @3 N6 A6 N. A" J为新连接的客户端挂载需要的回调函数及 调用参数 1 Q0 N1 z% i3 O! c8 w
收到客户端数据5 X7 A7 i: a( A5 I
设备刚连接,挂载刚才接收到的数据,设置发送完成回调 4 h; V- Z6 Z4 ~* b5 k
发送数据的总长度 : 32
' u9 q2 r% o* o7 X; S发送数据:
. m% o! d( |7 X* E0X68 0X74 0X74 0X70 0X3A 0X2F 0X2F 0X77 0X77 0X77 0X2E 0X63 0X6D 0X73 0X6F 0X66 0X74 0X2E 0X63 0X6E 0X20 0X51 0X51 0X3A 0X31 0X30 0X38 0X36 0X35 0X36 0X30 0X30 3 T5 u- c0 C' X: l  P7 T# c* P
收到客户端数据
5 ]  _$ V2 R2 O7 b) V! _收到断开连接请求
+ w+ h0 O6 [: E. x7 [已关闭连接
" y' B0 A$ b& ]( i: \$ y( \7 Y% k2 p2 O' H8 s+ F/ B2 S. x" {

: V" L6 r; q% n  d* X) J
通过上面的测试,则表示我们的TCP服务器已经完成。您也可以通过手机连接这个路由器的WIFI,通过 网络调试助手APP 来连接这个开发板的服务器,电脑可以通过 TCP客户端与开发板的TCP服务器连接同时也可以用手机做TCP客户端连接开发板的TCP服务器。
4 r. T8 X0 G: y$ R) _( I
3 n" `/ Y% |# _# Y( ?: V7 Y# a, [# N# q& P# r6 \
3、TFTP Server 的实现
/ O3 a( ~4 a; A  |( h+ h还是在上面的代码上来实现此功能,即在TCP 服务器的代码上实现 TFTP 服务器。- x5 U" {5 E. R  b+ r
打开刚才STM32CubeMX工程,在原来的基础上来配置 LWIP,配置如下,时能TFTP
" u3 _" C' k5 r. l( _( ? QQ截图20170723184918.png + K. f& ~9 j5 f* d% i6 ^- T
点击OK,然后点黄色的齿轮,重新生成代码。因为我们之前写的代码要么是在 /* USER CODE BEGIN X */ 与 /* USER CODE END X */ 中间添加代码要么是在新建的文件中添加代码,所以不会覆盖之前的代码。1 |+ @- J( M2 J9 P# E0 o
# D/ S' ?( _9 m/ E
- s% g0 z0 S2 l, U
生成的代码视图如下:. L& u, J/ Z' k5 ~3 M
QQ截图20170723185330.png 3 I! p  d, _  ^
4 G7 e  ]; P7 t1 ~& X5 ]
注意到了吗?LWIP文件夹下多了一个新的.c,tftp_server.c,既然是在LWIP文件夹下,言外之意就是别人帮我们写好了,不要动这里面的文件内容,相当于库,但是我们还是要知道去怎么用。这里我们之间偷懒,直接打开tptp_server.h,因为如果要给别的文件提供功能函数,那么必须会在.h文件中存在声明。打开tptp_server.h文件,内容如下:& C( t# B" p6 R- r; o( L1 d0 b
  1. /** @ingroup tftp8 u; S- G) ^& R4 V/ i0 J
  2. * TFTP context containing callback functions for TFTP transfers
    1 B$ A$ t: F: U6 g' J$ \; y  v) W7 n
  3. */
    2 G% }/ {' S! i7 u
  4. struct tftp_context {* S9 |3 h" p) Y( m
  5.   /**+ f+ B& q: Q9 @1 J5 n
  6.    * Open file for read/write., \5 ~; n+ Q  M  H
  7.    * @param fname Filename/ o2 d2 u; _" S) @5 f
  8.    * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail)5 q+ b% O5 m0 v5 l* k
  9.    * @param write Flag indicating read (0) or write (!= 0) access
    0 L: m0 H& |3 r9 [5 S' S! {( Q& d
  10.    * @returns File handle supplied to other functions
    + s2 A2 o% b# R
  11.    */  z- M, ~$ T4 Q: C3 V
  12.   void* (*open)(const char* fname, const char* mode, u8_t write);
    7 R4 y6 q) V2 C: p+ X* Z. J" Q5 I
  13.   /**
    . E& m' B& ^) Q/ U% T
  14.    * Close file handle( m! ^! n- q% I- }$ B+ _2 M
  15.    * @param handle File handle returned by open(). M$ s' Q# {! c/ p" Q3 k" |
  16.    */
      g2 E. D; h5 l& t- L- u3 j1 C: ?
  17.   void (*close)(void* handle);( [9 W) ^+ u7 J- R- W& w
  18.   /**
      P2 p+ X0 Z3 s" s. n" Z( H# d
  19.    * Read from file
    8 ~* c) r9 S3 e6 P
  20.    * @param handle File handle returned by open()( Y( v. H3 ~$ M$ L
  21.    * @param buf Target buffer to copy read data to
    - N: J7 @6 U0 c" Q8 @; y
  22.    * @param bytes Number of bytes to copy to buf
    & R* K% x- R. a, ?% k# \
  23.    * @returns >= 0: Success; < 0: Error% X7 W) C$ O% g2 P0 g/ ~: {
  24.    */: b6 F: n! L9 [2 Y( k- e! O
  25.   int (*read)(void* handle, void* buf, int bytes);
    ( z- x3 x6 P8 _% q: ?0 H7 b/ Q) a
  26.   /**
    / A' B" H5 k7 B  F, h/ z
  27.    * Write to file
    1 K# M! i5 L! x
  28.    * @param handle File handle returned by open()
    + a$ y# d$ o9 R7 i! }. Z
  29.    * @param pbuf PBUF adjusted such that payload pointer points
    4 K  p( @% U3 d! H
  30.    *             to the beginning of write data. In other words,
    4 V: l9 a' {) j
  31.    *             TFTP headers are stripped off.
    / \+ i; S- ?0 ~! `# m# o
  32.    * @returns >= 0: Success; < 0: Error4 h  ?# p3 f, D& r' J4 b
  33.    */% k" `) r, T" J0 G
  34.   int (*write)(void* handle, struct pbuf* p);
    % m4 c6 _: H2 }# d9 M* ^
  35. };4 ~$ v) I: J& I" G3 ^
  36. # r1 T' D" b( u! a! o
  37. err_t tftp_init(const struct tftp_context* ctx);
复制代码
" r$ i' u5 C* f
- O3 f* M7 t  u" B/ x" D! Q0 C
里面就一个 err_t tftp_init(const struct tftp_context* ctx);  从函数名的意思就是初始化,形参是一个结构体指针,而这个结构体里面定义的全是函数指针,那么很明确了,这个文件就是要我们填写轮子。
( \8 L2 R  i6 f+ t6 v/ e我们新建一个文件 mytftpserverif.c ,这个文件里面的内容 就是用来写那些轮子的实现函数,内容如下:
- R3 |( o/ Q1 s3 ^3 ^6 J  y
  1. #include "mytftpserverif.h"5 o2 q6 P. q) E/ f, x# ~4 @8 `7 {  d
  2. #include <string.h>
    : }9 d1 Y. C* G5 M, |( T
  3. #include <stdio.h>7 @9 c; m3 X3 p2 b3 T
  4. #include "main.h"
    , H3 S, u- Z" @0 y0 q
  5. #include "flash_if.h"
    6 K6 m9 j2 R- C; f
  6. #include "stdlib.h"
    - t7 p6 f6 `0 k- T" Q( L
  7. #include "string.h"! I: I/ |& I2 ]6 {5 V& w

  8. 3 B7 Q  z4 @7 U) ^
  9. / H) E/ \, G# v- Q7 ~( Z
  10. __packed typedef struct
      u  {5 I# i1 ~0 Q
  11. {' M. {) ?8 E1 h2 x$ ~6 c
  12.   uint8_t   InitFlat;
    ' d" A- E6 N! N
  13.   uint8_t   W_R_Mode;   //读写模式   1写  0读; {$ U7 s: \- X8 o! e
  14.   uint32_t  Flash_Addr ;
    2 y& z" t% x. e0 e3 S5 n
  15. }Iap_FlashCont;
    % N$ j% |9 V% t- A! O

  16. 5 N) m. Z/ ?6 N8 w

  17. & F& ]7 R$ U  M  Q* S' O, W* |
  18. static  Iap_FlashCont   Iapflashcont ;
    , \3 \) u7 r* F$ b0 \5 I- T
  19. 8 ?, X$ c! A1 h3 q: Z

  20. 7 t+ y+ s9 y7 `

  21. $ Y& J$ Q' b( D7 U$ j5 x
  22. static void * OpenFile(const char* fname, const char* mode, u8_t write);6 C% B6 g5 R& D  |$ W+ }9 X: ^! d
  23. static void Close_File(void* handle);
    ' ?3 F4 `; F5 k: y3 d" f9 y2 E* Z
  24. static int Read_File(void* handle, void* buf, int bytes);
    : p0 ?$ l" r: Z7 D
  25. static int Write_File(void* handle, struct pbuf* p);& d1 _5 d" M9 l! z" i+ z( y
  26. 4 g9 P; u/ p3 Q5 j) Z
  27. const struct tftp_context  tftpContext={       //TFTP SERVER 对接接口6 B* H- r% n8 \0 D) Y
  28. OpenFile,& v! D& J' |9 L3 }5 S' {
  29. Close_File,
    8 h; |# x2 z3 X
  30. Read_File,   
    ; p. z' L( i8 L$ t1 T3 x
  31. Write_File,% ~, |+ ]" z$ _, t/ b
  32. };, H5 n0 v! `/ Z2 q. H

  33. & Z1 S4 D5 G/ Q9 f( C
  34. " D" ]5 @5 x8 h7 v3 y/ K" A: K, [' z
  35.   /**& I" d& U+ @: \/ ~( }; S; m
  36.    *  打开文件  返回文件句柄
    & x' m" L- M" b; a1 \+ D  q
  37.    * @param const char* fname   文件名
    : L- e; m% c4 S8 k4 |
  38.    * @param const char* mode% h# ?3 H$ W: G2 K8 }
  39.    * @param u8_t write   模式  1写  0读
    % y- _' C3 B3 i5 f1 z
  40.    * @returns 文件句柄
    % r7 U+ u0 k; T6 w. b, w
  41.   */
    + s$ {5 T* U. h! H9 q% L
  42. static void * OpenFile(const char* fname, const char* mode, u8_t write). s/ i* G( q. O+ Q( R2 \
  43. {   
    + |2 c- n$ j9 M3 E6 Y7 ?
  44.   printf("打开文件  %s \r\n",fname);   
    / q0 [  |( a" L" ~6 k( P8 v& ~
  45.   printf("打开模式  %s \r\n",mode);
    0 u9 E$ y) w4 x0 Q( O
  46.   Iapflashcont.W_R_Mode = write ;5 c/ _- C8 C* c! J
  47.   Iapflashcont.W_R_Mode ? printf("写文件\r\n") : printf("读文件\r\n");
    9 o! G3 {9 a# u+ v# Q* @( e9 S: G
  48. ' \% b& A1 Z/ ?' ~, g% S# f
  49.   if(Iapflashcont.W_R_Mode == 1)( ~8 z" N- V7 i: ]) C, T
  50.   {
    ( i" I6 j1 f" L) ~3 J
  51.     FLASH_If_Init();   //解锁3 f# U# l' ~! m* u
  52. Iapflashcont.Flash_Addr = USER_FLASH_FIRST_PAGE_ADDRESS ;  //FLASH起始地址
    7 \# s  C6 k; R
  53.     printf("开始擦除Flash  时间较长 \r\n");  
    . d& w) X5 {; P" @  l
  54.     if( FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS) == 0 )   //擦除用户区FLASH数据" I3 C% W) T- b) t0 C
  55.     {) s) ]( k& @* U, [7 V2 R8 S# G
  56.       Iapflashcont.InitFlat =1 ;   //标记初始化完成3 A# d% T& l' C
  57.       printf("Write File Init Succes \r\n");% h& G6 n, E. r& ?
  58.     }5 ^- n1 D+ X' l6 }& c% O
  59.   } //如果为读文件模式. }( C) l: l( L4 f5 j: [5 Z' Z
  60.   else if(memcmp(fname,"flash.bin",strlen("flash.bin"))==0)  //可以读内部FLASH0 Z! t3 H6 `3 G9 j- [6 `/ g7 ?8 i
  61.   {7 l+ J8 c1 g& d4 u) o& T2 r" _
  62.     Iapflashcont.InitFlat =1 ;   //标记初始化完成5 w+ `% n  L0 E! M$ N
  63.     printf("Read File Init Succes \r\n");* W! a6 A- d5 c* R
  64.     Iapflashcont.Flash_Addr = FLASH_BASE ;  //FLASH起始地址8 h6 s  ~; o. ^" ^+ V: x
  65.   }
    3 L" ?! |* H: L! r
  66.   return (Iapflashcont.InitFlat) ? (&Iapflashcont) : NULL ;  //如果初始化成功  返回有效句柄
    # M' D  V  @1 z9 c( K; e
  67. }
    - F, B) P9 t+ N. Q1 W  E% |/ i

  68. # ?9 {: O5 P  P: g( z( c9 J9 s& v( z
  69.   /**3 x' B6 Z( R- y, A; c7 \0 A
  70.    *  关闭文件句柄/ T, F3 S: i+ c8 b! k
  71.    * @param None
    3 \9 `3 H1 q: o# Z$ n
  72.    * @param None
    / G2 [( [- A- N( ?" k+ F5 r. `
  73.    * @param None
    + M) U4 [( u/ L$ H. |
  74.    * @returns None5 f) z7 {. A: X$ t+ ?
  75.    */* J. ?0 L6 f8 }1 Z) H
  76. static void Close_File(void* handle), h/ B$ c1 e  M: O
  77. {
    ) x0 k& y! d# p9 H6 ]5 i& U* T
  78.   Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;
    5 m' ?6 h# X: N3 ^* {2 |9 Z$ ~
  79.      
    ( i+ [* X$ T' _! d& E7 V! t9 f0 K4 m
  80.   FLASH_If_UnInit();  //FLASH上锁) `% ^- b& N% g  K
  81.   Filehandle->InitFlat = 0 ;
    8 \+ r0 D5 V0 _8 j7 o. @/ R# Z
  82.   printf("关闭文件\r\n");  ' |7 V2 i) I' e* z2 j, H
  83.   if(Filehandle->W_R_Mode)  //如果之前是写文件
    " F+ n" j2 c5 h! J
  84.   {
    . E5 l2 K+ J' D; ^1 W/ S' M& M# U
  85.     typedef  void (*pFunction)(void);
    5 j% Q: D6 }; l$ k
  86.     pFunction Jump_To_Application;
    ' Y- x9 C3 d  K: s* x- R/ d5 ]
  87.     uint32_t JumpAddress;3 j/ ~& Y, W8 @# z  G: S
  88.     /* Check if valid stack address (RAM address) then jump to user application */9 s# [; V% v. f* m+ ?) n
  89.    if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000)0 s3 R4 j2 y$ _8 F
  90.    {
    ) B7 ?8 f; ]6 E0 X4 }2 Q- Y
  91.      /* Jump to user application */4 z( U$ a, S" [! N& t, u8 G
  92.      JumpAddress = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);) j' x2 g, |) Y1 h* h% P  y/ h# e' D
  93.      Jump_To_Application =   (pFunction)JumpAddress;
    : s- M# c8 s( g, c- h3 U1 T( D. ~
  94.      /* Initialize user application's Stack Pointer */
    6 v: V9 G) E) v9 ]8 M9 f5 Z
  95.      __set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS);" `& x* ~3 p% h' K
  96.      Jump_To_Application();  t0 {$ r+ `1 k% `& `4 \
  97.      /* do nothing */! y7 s5 J4 B9 P! t
  98.      while(1);2 I2 |/ `, Y+ `- |) ?' k1 t4 ~
  99.    }
    6 L+ Z/ O7 C" l- n% f
  100.   }% I, V/ K& k! R; V! C
  101. }
    : X) g1 q' l' d) ^
  102. 8 ^) C3 ?* w+ @( h  X& u: \
  103.   /**0 B) ~0 o6 a4 `/ Q
  104.    *  读取文件数据
    8 q4 n/ Q5 ?, a4 I: j
  105.    * @param handle  文件句柄 ( y1 ?, L  I4 f  V, t8 C' f
  106.    * @param *buf    保存数据的缓存
    , W' W% [# m  O* F
  107.    * @param bytes   读取的数据长度
    4 u+ k  d! H! W) I# |: p, z
  108.    * @returns 返回读取数据长度   小于0则错误
    : n% U, `3 q; R0 u* L
  109.    */8 }8 j9 Q  p3 o$ _8 i5 l3 d' W6 a
  110. static int Read_File(void* handle, void* buf, int bytes)& F, s6 N1 O* |# p2 K
  111. {  
    , [4 R& i- T2 G0 r3 _- g
  112.   Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;
    4 p8 w8 ]- H0 H+ P
  113.   printf("读取文件数据 读取长度: %ld \r\n",bytes);    # f( e4 Y+ m, {1 E/ l7 x/ h. l
  114.   if(!Filehandle->InitFlat)  //未初始化
    & k' e9 O0 t& u/ G" d
  115.   {
    6 ^) @3 T. q4 [6 p7 G
  116.     return ERR_MEM ;, v# E% @" F: _6 N) n
  117.   } 3 U; H# t1 C3 o8 H- V6 {
  118.   uint16_t Count ;
    $ u5 R: @$ F; u# |3 k! ^' W; x
  119.   for(  Count = 0 ; (Count < bytes)&&(Filehandle->Flash_Addr<=FLASH_END)  ;  Count++ ,Filehandle->Flash_Addr++ ); z1 m) b2 W" z  B% k- v
  120.   {8 u9 f& k! J& A5 o# {
  121.    ((uint8_t *)buf)[Count] = *((__IO uint8_t *) Filehandle->Flash_Addr);  
    , A( P+ s% k# H4 s: W
  122.   }( T# p% |0 I' ~- e) ^, B
  123.   return Count;/ E/ A% X2 P  m  X6 ^& b# o; a
  124. }
    7 n3 X# l) O- J( V% u$ z* L
  125. 3 ~' t' L& n! Q" ]6 R) G, p
  126. $ h# K& e' m  v  J: @5 {8 b; l: {
  127.   /**( l3 {1 ~4 O& Q2 z0 P( k  H& |- A$ |0 E
  128.    *  写文件数据! v/ G+ j4 v5 c1 N  u% E, j
  129.    * @param handle           文件句柄% n9 l5 m( c: I
  130.    * @param struct pbuf* p   数据缓存结构体  里面的数据缓存全为实际需要写入的数据, q0 D: s2 `9 j& ~( ^$ g+ _
  131.    * @returns 小于0为错误  
    ! `( F8 l' A6 F6 Q& D
  132.    */4 U3 C, n% w# y* l
  133. static int Write_File(void* handle, struct pbuf* p)
    : Q. i& X. c7 M
  134. {3 q1 ^) R) P* }4 A; D5 s& z
  135.   uint16_t Count ;
    3 I  J. n- M6 D5 j" Y  i
  136.   Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;     
    $ D. M& }5 i' V/ f! W$ [5 D% b
  137.   printf("写文件数据  数据长度 %ld \r\n",p->len);
      u4 t# r3 @7 j/ S
  138.   if(!Filehandle->InitFlat)
    1 v# A# \' o' X$ c4 j( I3 n
  139.   {$ L0 S4 |0 E2 W0 `* f9 A
  140.     printf("写文件  没有初始化 \r\n");/ e& h* N: U6 b, N3 v
  141.     return ERR_MEM ;3 a: Z$ ~. c6 n  W; J, ~* J& Z
  142.   }7 S  r6 h5 I5 K
  143.   Count = p->len/4 +((p->len%4)>0);  //得到要写入的数据
    $ D) J# \7 q3 r2 g8 P% U
  144.   printf("开始写FLASH  地址 :0X%08X \r\n",Filehandle->Flash_Addr);
    ( `) \8 v3 x  T3 d7 O
  145.   if( FLASH_If_Write((__IO uint32_t*)&Filehandle->Flash_Addr,(uint32_t *)p->payload,Count) == 0 )
    * _: s( y2 Z7 K) Q/ p
  146.   {5 {% A$ W' _/ V6 L/ c) g
  147.     printf("写FLASH成功  下一次地址 :0X%08X \r\n",Filehandle->Flash_Addr);, N6 v# @* t8 _4 [% E- O
  148.     return ERR_OK;; n9 M1 G* N2 e5 P: a6 j& ~
  149.   }
    - b! u' \2 z9 K' y$ }- {1 R
  150.   else8 c$ t* I$ F' J4 Q* ]/ m9 o
  151.   {  w9 x  d, T, W
  152.     printf("写FLASH 失败 出错地址 : 0X%08X \r\n",Filehandle->Flash_Addr);9 R9 l! K+ B, s' ?
  153.   }) w9 W" M( m& M: v+ ]7 \6 ~
  154.   return ERR_MEM;
    % H0 O9 T6 X" ~1 W+ J% P
  155. }9 G; D# Q: D# ?/ z# x+ n6 u7 K
  156. ( U' r" z$ X7 p$ s
  157. 4 [3 m6 w  P; a0 t' Y/ Y

  158. 8 i! e  o) u7 j& B

  159. 9 k9 g) V. P$ v5 C* l0 L

  160. 7 w$ ^5 o2 w4 X3 h0 g- R

  161. , a) e* ^* Z1 l+ U; T3 O% b2 o
复制代码
这上面的代码我都写了注释,理解应该难度不大,就是根据tptp_server.h中的结构体实现对应的轮子函数,分别为:
2 c- F# d' x$ |# F+ c: }% Qstatic void * OpenFile(const char* fname, const char* mode, u8_t write);
( C5 k% h0 j! Nstatic void Close_File(void* handle);7 f, H0 i) q; S1 Q$ D+ G9 [
static int Read_File(void* handle, void* buf, int bytes);
* v9 B! d- N" S2 J, hstatic int Write_File(void* handle, struct pbuf* p);- N1 g$ M* e0 U# T* t& `1 c

, p4 R1 q8 G5 X+ H' F- x/ D6 ?) m9 K% t' Y2 \! s, e
mytftpserverif.h文件内容如下:8 E8 t9 v- b2 n/ Z3 L0 i, X
9 T( X( k* ?+ ^9 ]- l& B
#ifndef     _MYTFTPSERVERIF_H_
" _- Z; F& m6 C. s#define     _MYTFTPSERVERIF_H_
0 ^/ V3 \: z, x/ D, l6 G#include "lwip/mem.h"% N$ A$ X' D- @! P8 L
#include "lwip/udp.h"  ?* {6 G- y: Z1 A1 c- a* d% C, f
#include "lwip/apps/tftp_server.h"
7 w2 t  D3 s$ Z; {( ^6 Cextern const struct tftp_context  tftpContext;
0 I& }2 J/ J$ L3 i$ ^#endif
2 r  G& \% q+ O4 O) E! n
. G0 _( d2 h9 b' u' y3 f9 j
# E3 @9 a; J3 X) s8 c" Q
3 ]: }; W# T2 W9 m/ [* L因为我们是要通过TFTP 来实现 IAP 功能 ,所有一定有FLASH 的操作,这里我们之间COPY官方例子的里面的flash_if.c和flash_if.h,flash_if.c文件内容如下:- c3 a. [. t9 U& J1 p9 K$ B
  1. #include "flash_if.h"
      Q- R) R/ |3 i  M3 L, Q7 Y

  2. & P& o+ l. }: k
  3. /* Private typedef -----------------------------------------------------------*/4 F# r! |2 B2 W2 g8 h, J
  4. /* Private define ------------------------------------------------------------*/
    % n( R1 p& U# y4 ^5 ^
  5. /* Private macro -------------------------------------------------------------*/
    2 h; q* S# C( b# C4 y  \/ {# N, @
  6. /* Private variables ---------------------------------------------------------*/+ }' X* Y( S0 d* q% m
  7. /* Private function prototypes -----------------------------------------------*/. F% {" m# p0 c# [4 @
  8. , r& ^# L  C$ E
  9. /* Private functions ---------------------------------------------------------*/) g; [9 s" ~& e2 g

  10. , f" [' s. j5 `9 R( o6 j
  11. /**
    + S! @; o- ]; m% c
  12.   * @brief  Unlocks Flash for write access; h  K- p7 d6 F: B9 U
  13.   * @param  None, E% h; p' u/ L2 A, `/ @9 h, {- Q$ L
  14.   * @retval None* o/ b, T) w3 i8 O/ |9 W
  15.   */( V, z3 r1 l2 |  j7 O8 o, y4 g3 {, X
  16. void FLASH_If_Init(void)6 m6 o$ [5 _6 n/ R
  17. {
    3 Y& f' O5 o2 d* r, r
  18.    HAL_FLASH_Unlock();
    ' S; R7 w: {) q
  19. }6 g6 Z+ @. H' c) O) e0 ?7 x

  20.   q! G" Y) N6 |
  21. /**
    % I, [1 R4 _! y8 O8 w
  22.   * @brief  This function does an erase of all user flash area
    3 W  J2 ~2 {, p3 Z0 i# ?! R
  23.   * @param  StartSector: start of user flash area
    ) q& {8 V# Z3 D# m# b1 G
  24.   * @retval 0: user flash area successfully erased
    ! H# W% l3 G& i3 ~3 b# s
  25.   *         1: error occurred
    & }! e- p" Y5 F1 S& `( J
  26.   */3 a. J8 l" m$ [) K, {
  27. int8_t FLASH_If_Erase(uint32_t StartSector)) ]* P, I' @, m  o- F$ L
  28. {
    ; m" m5 R  y2 O2 i$ G+ K
  29.   uint32_t FlashAddress;+ F$ r% ~' k" k, d

  30. $ p  U- @+ o9 M  z. q" p
  31.   FlashAddress = StartSector;: |. I6 g' w7 R& ]

  32. 2 _( z+ y$ f! H% t$ @4 m! n# |) M: \4 l
  33.   /* Device voltage range supposed to be [2.7V to 3.6V], the operation will
    3 o% H2 P+ I+ J. p/ P+ F
  34.      be done by word */
    . B' V4 o- a# z' Y

  35. 6 E) G# G' ^: ?3 p, a* r
  36.   if (FlashAddress <= (uint32_t) USER_FLASH_LAST_PAGE_ADDRESS)
    3 F" Q0 p; @, u/ R; }8 [
  37.   {
    2 P+ I) E- s, P; v% z  H5 @
  38.     FLASH_EraseInitTypeDef FLASH_EraseInitStruct;
    5 a, X: Y; c& {! c1 Y
  39.     uint32_t sectornb = 0;8 y8 k0 t3 q% o/ p
  40.     6 Q: `3 S5 Y9 w8 R; H" D+ Y+ g( G
  41.     FLASH_EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;) L$ ~% P  A( q" g) k% I
  42.     FLASH_EraseInitStruct.Sector = FLASH_SECTOR_5;! ^6 T1 {, H: ]: T
  43.     FLASH_EraseInitStruct.NbSectors = 7;" ~5 A& w% y' j# a
  44.     FLASH_EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
    * W# L2 _8 Z( K! S+ B# j5 J& H
  45.    
    % L5 g% g# N' N
  46.     if (HAL_FLASHEx_Erase(&FLASH_EraseInitStruct, §ornb) != HAL_OK)5 ^6 K1 A5 m& S7 m& r+ L
  47.     {$ ~. I8 N/ X& o& P/ W- l
  48.       return (1);
    ' S! o5 J  N2 |$ K' v# U% N
  49.     }
    7 \- N' [# F( ?+ s3 N7 U
  50.   }
    % j& B9 o$ y1 T
  51.   else
      F4 r/ |( `4 _3 b; o- g% K5 M" Q
  52.   {' k; a  q3 U! N" o5 d0 D4 ?% w: \9 E
  53.     return (1);6 j4 x, [  q6 N% B
  54.   }
    8 b8 M9 M3 J" D$ ~! D7 f0 ^
  55. 2 x6 P1 A. y* W  s9 B: [
  56.   return (0);
    ; W8 Z. D, d+ m6 d# g% {2 B
  57. }
    ! P/ m5 F! L! D- F
  58. /**
    ) M5 B! x' P% X6 D
  59.   * @brief  This function writes a data buffer in flash (data are 32-bit aligned).+ b) N; N1 x6 B+ @1 Y$ p6 D" K
  60.   * @note   After writing data buffer, the flash content is checked.
    + |2 x3 O, q# c
  61.   * @param  FlashAddress: start address for writing data buffer- {, F. ~, r  q) ~5 r9 `# c
  62.   * @param  Data: pointer on data buffer
    ! `2 ^, q# g$ B, }+ V  t' d
  63.   * @param  DataLength: length of data buffer (unit is 32-bit word)   2 ]' j% j# i0 V" @* B" ?/ e; q7 f  Y
  64.   * @retval 0: Data successfully written to Flash memory3 {5 {" a" x0 U( K* P
  65.   *         1: Error occurred while writing data in Flash memory
    ) C$ `. [& ^' m9 ?4 Z( L
  66.   *         2: Written Data in flash memory is different from expected one) D% i2 ~  M$ v; @2 `
  67.   */
    6 T; r2 N& l) u0 X2 @
  68. uint32_t FLASH_If_Write(__IO uint32_t* FlashAddress, uint32_t* Data ,uint16_t DataLength)2 o9 n5 ?2 r7 o0 b
  69. {2 h, G7 z& Y/ @! a- V9 T
  70.   uint32_t i = 0;4 J7 G' @* Y$ k& n- \% ]  L9 Y

  71. ( ]5 Q; Z" z. m; }
  72.   for (i = 0; (i < DataLength) && (*FlashAddress <= (USER_FLASH_END_ADDRESS-4)); i++)3 K1 l* h/ f; Q8 s6 S+ v' ~
  73.   {) P7 I6 K, i0 b  P7 s
  74.     /* Device voltage range supposed to be [2.7V to 3.6V], the operation will
    - I0 R" R! ~; a1 _; \
  75.        be done by word */   J) ~9 C: M& x1 b4 C% ~4 |* C/ R& P7 \
  76.     if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, *FlashAddress,  *(uint32_t*)(Data+i)) == HAL_OK)) U; [: a4 k7 g
  77.     {" S; X* W8 ]% W# E" Q, _
  78.      /* Check the written value */8 W  X* \) f' q( h& `) F6 Z4 N: Z
  79.       if (*(uint32_t*)*FlashAddress != *(uint32_t*)(Data+i))
    4 d3 e" G6 }  g: X! t
  80.       {2 r9 f" |! n5 S- y& j! `
  81.         /* Flash content doesn't match SRAM content */, v& g" L6 {8 }! P3 q
  82.         return(2);! _/ [6 z8 Q/ m$ K) g3 O0 E
  83.       }
    & u3 [) ?1 J! ^4 D- W' r7 K% t
  84.       /* Increment FLASH destination address */0 v  v. l/ W. i$ N3 G: t
  85.       *FlashAddress += 4;
    , l! J+ q! g9 [
  86.     }- x5 t% Z) k9 ~7 G* \2 [5 q
  87.     else- z' W" ?" @! X  n1 O8 M
  88.     {
    7 Q' f, A7 G# e, \4 M
  89.       /* Error occurred while writing data in Flash memory */' `- G8 f1 P2 x. W3 J
  90.       return (1);. C( D! _2 _, u
  91.     }9 ?( t! Q7 m% H) I; Y
  92.   }' L% ~' R6 Y! h$ p. Q+ s# O
  93. 4 Q8 |* g3 j% ?4 s% V* C5 K
  94.   return (0);# B  N7 f7 b5 z8 G
  95. }* s3 F; M5 R9 w& d

  96. 9 S. Q' e8 Y: o0 [% ^0 y. q

  97. 4 O! b5 |( q" p' {% L
  98. /**
    8 `+ t+ W$ L! v. i
  99.   * @brief  locks Flash for write access2 {3 y6 b! [) k  l
  100.   * @param  None3 \5 H) S! b3 }% o* b
  101.   * @retval None
      O: G7 b( }! z2 y$ s
  102.   */
    & \! i7 N6 s# s3 G6 V8 q
  103. void FLASH_If_UnInit(void)7 Y7 @& m* I, @( o% Q/ l. r! i
  104. { . d+ w& R4 a8 |
  105.    HAL_FLASH_Lock(); ) u* P$ F# [5 M- \  C
  106. }
复制代码
这里面就是FLASH 解锁、FLASH 扇区擦除、写FLASH、FLASH上锁(自己添加的)( J3 \/ z7 g+ [: F$ T2 A, N5 ~' e
flash_if.h内容如下:
3 O/ z- F( K& k0 U! ]7 Z' D
  1. #ifndef __FLASH_IF_H
    + `+ v5 a% T7 u3 \# o
  2. #define __FLASH_IF_H: Q) P. v9 {( ?9 S) m% F8 @
  3. ! V) c9 T( [' A2 _
  4. /* Includes ------------------------------------------------------------------*/8 ~$ R$ n7 y; i1 ?
  5. #include "main.h"6 S/ X8 S; `1 S6 j8 t

  6. 8 w- D' C9 S  @+ G
  7. 7 Z* ]$ ^/ I% l
  8. //起始地址3 J3 p0 c1 Y- h7 p( x1 S5 ~# n
  9. #define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000 /* Only as example see comment */6 a: J; |$ v9 f7 }# O# S

  10. 6 d4 A, q0 q$ J1 b( W6 u7 [9 D& g
  11. #define USER_FLASH_LAST_PAGE_ADDRESS  0x080E0000; B9 r9 I7 D4 O

  12. 7 K% }4 v3 M2 U
  13. //结束地址2 G, C% |/ ~" {3 ~% \1 o5 ]* u
  14. #define USER_FLASH_END_ADDRESS        FLASH_END  % o* F4 ^4 F: Z! |$ p" W- c$ q9 f
  15. 6 k8 r7 L2 G9 E: H) e; W  T+ P
  16. /* Exported types ------------------------------------------------------------*/9 R5 {9 J  E; }- z3 q
  17. /* Exported constants --------------------------------------------------------*/
    : M$ K6 g" Y8 ]6 v
  18. #define USER_FLASH_SIZE   (USER_FLASH_END_ADDRESS - USER_FLASH_FIRST_PAGE_ADDRESS)
    0 R. i2 G& C; S

  19. 5 c  G. D0 }3 a* ^
  20. /* Exported macro ------------------------------------------------------------*/
    , R9 k; r- \5 d2 F. B# I
  21. /* Exported functions ------------------------------------------------------- */0 B- D5 E0 u3 P0 ~- ?' f
  22. uint32_t FLASH_If_Write(__IO uint32_t* Address, uint32_t* Data, uint16_t DataLength);
    9 p8 J" k% j; X# t7 E
  23. int8_t FLASH_If_Erase(uint32_t StartSector);: z6 W* d, i4 f5 k+ U" O
  24. void FLASH_If_Init(void);9 t# g/ Q* E2 O- N+ {; Y
  25. void FLASH_If_UnInit(void);
    " ^2 M) M, t* m  o0 v4 S# X# A
  26. * P) g5 {! k) ?, j& H9 D) ^
  27. #endif /* __FLASH_IF_H */
复制代码

& v- F) F( C2 g% h( d# N: c2 M3 c2 A5 n+ Q9 [& @( S# Z
在main.c文件中添加如下代码:) v" _. R' \. B: A. H: {
QQ截图20170723191418.png 3 h0 C% C- e9 d5 q# O* Q5 t, j6 ~* n/ R0 u

+ X7 C: L/ M8 _+ a5 |5 Q0 q
0 G6 Y  l  j' n9 o( X

" n( F& \, `8 N% O. |! k

# V- U9 J# a7 E至此,TFTP服务器搭建完成,下载到开发板' I: z6 A0 B5 N$ X9 ]
串口显示如下信息:5 o( U( D5 s/ [2 w5 e5 S; {! F
Start
" x  J  ?9 i/ f开始监听 & x2 z( a1 v& e0 ~5 `
挂载客户端连接回调函数
0 f+ T1 h4 a1 u' ETFTP初始化成功
; ^4 ]: C; W( \Static IP address: 192.168.0.120; O- u/ Y0 w1 i6 h1 l
! K( \. y  b* y( x

! v9 l: K  g( ~可以通过上面的 TCP服务器测试步骤来测试TCP服务器功能,我这边已经测试了,工作正常,ping 也能通
( O- a* C$ G* ?/ x: @7 b
- F/ n8 y& N; O5 A# L
7 J1 |0 G$ x  H; s+ j9 ^& c* h5 V
TFTP 服务器测试
' G1 ]1 N  {) y0 G先在网上下载 tftpd32.exe 软件,选择tftp Client,输入主机IP地址,选择需要下载的bin文件,BLOCK SIZE 选择 512。这里在FLASH_IF.H里面定义APP的起始地址
2 u4 a" D: G) q! }3 a$ M3 {1 y4 Y4 t#define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000
+ t, J, e# N+ e! ]所以APP的起始地址要设置成0x08020000,这里选择原子的RTC 的APP,在里面我把地址重新改了下
, Q. s, \6 ^  f0 p/ ]' `/ E% k3 ] tftpd32.exe 软件 设置如下:
- U: G5 i4 x7 V QQ截图20170723192305.png ' p: b" q7 z, D+ W6 G
点put即可发送文件数据给MCU,MCU会把这个文件的数据写入到内部FLASH里面,完成后会跳转到APP代码。/ w  D# t0 u& _! j& y1 z
0 I: _2 u  B* V4 Z% n# m) M" h! T( l
点击Put按钮后,串口输出如下:' h4 b7 A* p5 T
QQ截图20170723192749.png
1 v% T* O( P4 U5 r. F) x% V
4 e2 V8 e# ?+ M# z  s" y  e
3 S7 N8 u1 a7 I: g
执行完成后会运行APP代码. `% K: T9 M! P
IMG_0400.JPG 6 \; T2 ]5 m- @+ Z% |
! a. Q, i- u6 l4 O5 C; B* X) Q) k
重新复位后又会返回服务器的代码,在TFTP 服务器中,加入了读取内部FLASH数据的功能,但只能读取全部的数据,在tftpd32.exe软件中选择flash.bin,点击Get按钮,即会在你选择的目录下创建一个flash.bin文件,内容为MCU内部FLASH数据。注意,块大小一定要为512.
8 f/ t% }/ R4 x' I6 g  {6 P QQ截图20170723193230.png
% F4 F; q7 c7 @+ J$ ], [点击Get后,串口助手显示如下:/ g3 y7 |9 f$ c) [( [6 {6 K. k
QQ截图20170723193419.png , ]) q/ O' Y3 f& w7 k
开始读的第1块和第2块非常慢,而且读取完成后返回说有2个块好像有问题,因为我是从0X08000000开始读的,我想可能是有冲突或者不允许读,这个问题也很好解决,只要把读取的地址不设置成FLASH的基地址就可以了。) J/ B3 R* p1 R# h. r3 f8 n+ E3 P
2 J7 \8 n/ x$ D' b' ?# y" v) j8 [
工程代码:
  _) K' X' U+ M* T HAL_LWIP_TFTP_TEST.zip (848.12 KB, 下载次数: 539)
QQ截图20170723191149.png

评分

参与人数 1 ST金币 +18 收起 理由
wofei1314 + 18 赞一个!

查看全部评分

1 收藏 25 评论60 发布时间:2017-7-23 19:40

举报

60个回答
群星闪烁 回答时间:2017-7-30 18:28:21
wansaiyon289239 发表于 2017-7-27 23:580 f2 t5 q2 W: j% h/ P
楼主你用串口接收不定长怎么实现的能发我一下吗
( m. W- j7 L" b/ F9 A
串口接收不定长其实就是用到了DMA和串口的空闲中断,当串口产生空闲中断时就认为接收完成了,关闭DMA,DMA收到数据的数据长度就是刚才收到的数据长度。
$ Y, F% z; r" E* S: L主要是要注意配置DMA时的接收长度要略大于实际要接到数据,并且要开启DMA接收完成中断。- E1 F( w9 I7 N/ o+ @
具体的可以在开源论坛上看下我发布的代码,里面都有详细注释
tao2008 回答时间:2019-5-10 12:04:34
你这个下载好程序Flash APP区后,复位之后又进入IAP程序了。  
+ V1 h, g$ d/ r8 v8 _' W7 T你知道接收到以太网数据包后,进入以太网中断,接收数据包完成标志,如果完成,置标志,main函数轮询标志后,进入APP程序;9 W6 Q5 q/ j, l7 H0 z
! w; U: [' I  p( \1 a3 X1 p
就是不太会用以太网中断。。你这个好像用的是轮询,不是用以太网中断。
群星闪烁 回答时间:2017-9-7 12:29:14
在路上11111111 发表于 2017-8-30 11:07. \; n7 H1 m# I) W: D3 F2 q( D+ r
这个怎么解决啊,楼主

2 I1 Z8 q" G3 T( T& v: X9 _你这个设置的是服务器还是客户端啊
/ x2 E8 Z; C1 p# H) V- C需要把在这个配置成客户端   Tftp Client  
0 P$ X) e3 B5 A  Z) _/ a如果要Get文件的话   只能 选择 flash.bin+ |+ ~5 O& u/ }1 C5 t: T; s
Stm32McuLover 回答时间:2017-7-23 20:25:30
感谢分享
群星闪烁 回答时间:2017-7-23 20:54:17
之前上传的一些图不见了,重新补上了....
Paderboy 回答时间:2017-7-23 22:04:00
多谢分享。。。。
epochal 回答时间:2017-7-24 06:46:44
多谢分享!!!
zero99 回答时间:2017-7-24 08:41:04
谢谢分享
MrJiu 回答时间:2017-7-24 08:43:06
围观!
Inc_brza 回答时间:2017-7-24 09:28:12
谢谢分享
群星闪烁 回答时间:2017-7-24 10:25:49
zero99 发表于 2017-7-24 08:413 d4 }2 B# k- N  V2 {
谢谢分享

/ O/ c8 V4 R) B4 a: r# {% @4 n谢谢,之前是看了您的那个实战经验才学会如何去配置,呵呵
埃斯提爱慕 回答时间:2017-7-24 18:25:08
提示: 作者被禁止或删除 内容自动屏蔽
huaiqiao 回答时间:2017-7-24 21:50:20
感谢分享,写的很仔细
群星闪烁 回答时间:2017-7-25 13:04:41
huaiqiao 发表于 2017-7-24 21:50
- z% ~: M% V5 b5 q感谢分享,写的很仔细

" E9 L/ D' ], p1 }, Z谢谢
wansaiyon289239 回答时间:2017-7-27 23:58:30
楼主你用串口接收不定长怎么实现的能发我一下吗
同...步 回答时间:2017-7-30 10:30:54
这个帖子内容很充实,让我学到了很多,多谢分享,希望以后多出点这样的干货
12345下一页

所属标签

相似分享

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