• 三、W5100S/W5500+RP2040树莓派Pico<TCP Client数据回环测试>


    1. 前言

      在当今的计算机网络环境中,TCP(传输控制协议)是一种非常重要的通信协议。TCP提供了一种可靠、有序和错误控制的数据传输方式,被广泛应用于各种网络应用中。在TCP通信中,TCP Client是客户端的角色,它负责建立连接、发送数据和接收数据。

      本文以TCP Client为核心,让设备在TCP Client模式下进行数据回环测试。

      W5100S/W5500是一款集成全硬件 TCP/IP 协议栈的嵌入式以太网控制器,同时也是一颗工业级以太网控制芯片。在以太网应用中使用 W5100S/W5500 让用户可以更加方便地在设备之间实现远程连接和通信。

    2. 协议简介

    2.1 简述

      TCP (Transmission Control Protocol) 是一种面向连接的、可靠的、基于字节流的传输协议,用于在计算机网络上传输数据。TCP Client是指TCP网络服务的客户端连接,主动向服务器发起连接请求并建立连接,用于实现串口数据和服务器数据的交互,保证数据的可靠交换。TCP Clent通常用于设备与服务器之间的数据交互,是最常用的联网通信方式。
      TCP Client的主要作用是建立和管理与TCP服务器之间的连接,实现数据的可靠传输。通过TCP Client,设备可以向服务器发送数据并从服务器接收数据,从而实现设备与服务器之间的数据交互。
      在TCP Client中,客户端程序需要指定服务器的IP地址和端口号,并使用TCP协议与服务器建立连接。一旦连接建立成功,客户端程序就可以通过数据流对象 (NetworkStream) 与服务器进行数据交互。
      因此,TCP Client可以帮助设备实现与服务器之间的可靠数据交换,是设备联网通信的重要方式之一。在工业自动化、物联网、智能家居等应用中,TCP Client被广泛使用。

    2.2 优点

    • 可靠性:TCP Client在数据传输过程中,通过TCP协议的确认机制、重传机制、拥塞控制机制等来确保数据的准确性和完整性,避免数据丢失或乱序。
    • 稳定性:TCP Client在建立连接时,需要经过三次握手过程,确保连接的稳定性和可靠性。在数据传输完成后,还会断开连接,以节约系统资源。
    • 效率:TCP的全双工传输模式使得TCP Client具有很高的数据传输效率。
    • 有序:TCP的字节流传输方式可以保证数据的顺序性和完整性。
    • 应用友好性:TcpClient类为应用层协议如FTP、HTTP等提供了更简单、更友好的接口,降低了应用层协议开发维护的成本。

    2.3 应用

    • 网络数据库连接:许多数据库系统,如MySQL和PostgreSQL等,都支持使用TCP连接来接收客户端的连接请求并处理数据请求。
    • 远程桌面连接:使用TCP连接来远程控制另一台计算机的桌面或通过另一台计算机的操作进行网络浏览等。例如,RDP、VNC和SSH等协议都是使用TCP连接进行远程控制和操作的例子。
    • 文件传输:许多FTP和TFTP等文件传输协议使用TCP连接来传输文件。
    • 邮件服务器:SMTP、POP和IMAP等电子邮件协议都使用TCP连接来发送和接收电子邮件。
    • 网络游戏:许多网络游戏使用TCP连接来传输游戏数据和控制指令,以实现实时交互和同步。
    • 即时通讯:许多即时通讯应用程序使用TCP连接来传输音频、视频和文本消息。
    • 电子商务:TCP也可以用于电子商务应用中,例如处理用户登录、支付和购物车等。

    3. WIZnet以太网芯片

    WIZnet 主流硬件协议栈以太网芯片参数对比

    ModelEmbedded CoreHost I/FTX/RX BufferHW SocketNetwork Performance
    W5100STCP/IPv4, MAC & PHY8bit BUS, SPI16KB4Max.25Mbps
    W6100TCP/IPv4/IPv6, MAC & PHY8bit BUS, Fast SPI32KB8Max.25Mbps
    W5500TCP/IPv4, MAC & PHYFast SPI32KB8Max 15Mbps
    1. W5100S/W6100 支持 8bit数据总线接口,网络传输速度会优于W5500。
    2. W6100 支持IPV6,与W5100S 硬件兼容,若已使用W5100S的用户需要支持IPv6,可以Pin to Pin兼容。
    3. W5500 拥有比 W5100S更多的 Socket数量以及发送与接收缓存。

    4. TCP Client数据回环测试

    4.1 程序流程图

    在这里插入图片描述

    4.2 测试准备

    软件:

    • Visual Studio Code
    • WIZnet UartTool
    • SocketTester

    硬件:

    • W5100SIO模块 + RP2040 树莓派Pico开发板 或者 WIZnet W5100S-EVB-Pico开发板
    • Micro USB 接口的数据线
    • TTL 转 USB
    • 网线

    4.3 连接方式

    • 通过数据线连接PC的USB口(主要用于烧录程序,也可以虚拟出串口使用)
    • 通过TTL串口转USB,连接UART0 的默认引脚:
      • RP2040 GPIO 0(UART0 TX) <----> USB_TTL_RX
      • RP2040 GPIO 1(UART0 RX) <----> USB_TTL_TX
    • 使用模块连接RP2040进行连线时
      • RP2040 GPIO 16 <----> W5100S MISO
      • RP2040 GPIO 17 <----> W5100S CS
      • RP2040 GPIO 18 <----> W5100S SCK
      • RP2040 GPIO 19 <----> W5100S MOSI
      • RP2040 GPIO 20 <----> W5100S RST
    • 通过网线直接连接PC网口(或:PC和设备都通过网线连接交换机或路由器LAN口)

    4.4 相关代码

      我们直接打开tcp_client.c文件(路径:examples/tcp_client/tcp_client.c)看下具体实现:

      可以看到这里是以dhcp模式配置网络信息的,因此在主控和W5100S初始化完成后,会进行DHCP初始化,然后增加一个定时器初始化,用来做dhcp过程中的计时以进行超时处理;接着进入dhcp配置网络信息,成功则直接进入循环调用回环测试函数,失败则用我们初始化的静态网络信息进行配置,然后再进入循环调用回环测试函数,如下所示:

    /* Network information to be configured. */
    wiz_NetInfo net_info = {
        .mac = {0x00, 0x08, 0xdc, 0x1e, 0xed, 0x2e}, // Configured MAC address
        .ip = {192, 168, 1, 10},                     // Configured IP address
        .sn = {255, 255, 255, 0},                    // Configured subnet mask
        .gw = {192, 168, 1, 1},                      // Configured gateway
        .dns = {8, 8, 8, 8},                         // Configured domain address
        .dhcp = NETINFO_DHCP};                       // Configured dhcp model,NETINFO_DHCP:use dhcp; NETINFO_STATIC: use static ip.
    
    wiz_NetInfo get_info;
    static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
        0,
    };                                           // Send and receive cache
    static uint8_t des_ip[4] = {192, 168, 1, 2}; // Server IP address
    static uint16_t des_port = 8080;             // Server port
    static uint16_t local_port = 8000;           // Local port
    static uint8_t dhcp_get_ip_flag = 0;         // Define the DHCP acquisition flag
    
    int main()
    {
        struct repeating_timer timer; // Define the timer structure
    
        /* MCU init */
        stdio_init_all();     // Initialize the main control peripheral
        wizchip_initialize(); // Initialize the chip interface
    
        /*dhcp init*/
        DHCP_init(SOCKET_ID, ethernet_buf);                                   // DHCP initialization
        add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer); // Add DHCP 1s Tick Timer handler
    
        printf("wiznet chip tcp client example.\r\n");
        network_init(&net_info); // Configuring Network Information
        print_network_information(&get_info);   // Read back the configuration information and print it
        
        while (true)
        {
            loopback_tcpc(SOCKET_ID, ethernet_buf, des_ip, des_port, local_port); // tcp client data loop test
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

      跳进回环测试里面看下其具体实现: 该函数有这几个参数,socket端口号、数据收发缓存、目标IP地址、目标端口;可根据需要自行填入参数。其整体通过一个Swatch状态机轮询socket状态,根据不同进行相应的处理,依次完成了初始化、打开端口、连接服务器、收到数据后回传的操作 ;其中本地端口直接在函数内初始化了。如下所示:

    /**
     * @brief   tcp client loopback test
     * @param   sn:         socket number
     * @param   buf:        Data sending and receiving cache
     * @param   destip:     Destination IP address
     * @param   destport:   Destination port
     * @return  value for SOCK_ERRORs,return 1:no error 
    */
    int32_t loopback_tcpc(uint8_t sn, uint8_t* buf, uint8_t* destip, uint16_t destport)
    {
       int32_t ret; // return value for SOCK_ERRORs
       uint16_t size = 0, sentsize=0;
    
       // Destination (TCP Server) IP info (will be connected)
       // >> loopback_tcpc() function parameter
       // >> Ex)
       //	uint8_t destip[4] = 	{192, 168, 0, 214};
       //	uint16_t destport = 	5000;
    
       // Port number for TCP client (will be increased)
       uint16_t any_port = 	50000;
    
       // Socket Status Transitions
       // Check the W5500 Socket n status register (Sn_SR, The 'Sn_SR' controlled by Sn_CR command or Packet send/recv status)
       switch(getSn_SR(sn))
       {
          case SOCK_ESTABLISHED :
             if(getSn_IR(sn) & Sn_IR_CON)	// Socket n interrupt register mask; TCP CON interrupt = connection with peer is successful
             {
    #ifdef _LOOPBACK_DEBUG_
    			printf("%d:Connected to - %d.%d.%d.%d : %d\r\n",sn, destip[0], destip[1], destip[2], destip[3], destport);
    #endif
    			setSn_IR(sn, Sn_IR_CON);  // this interrupt should be write the bit cleared to '1'
             }
    
             //
             // Data Transaction Parts; Handle the [data receive and send] process
             //
    		 if((size = getSn_RX_RSR(sn)) > 0) // Sn_RX_RSR: Socket n Received Size Register, Receiving data length
             {
    			if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE; // DATA_BUF_SIZE means user defined buffer size (array)
    			ret = recv(sn, buf, size); // Data Receive process (H/W Rx socket buffer -> User's buffer)
             buf[ret]=0x00;            // Add a string terminator
             printf("recv: %s\n",buf); // print the receive data
    			if(ret <= 0) return ret; // If the received data length <= 0, receive failed and process end
    			size = (uint16_t) ret;
    			sentsize = 0;
    
    			// Data sentsize control
    			while(size != sentsize)
    			{
    				ret = send(sn, buf+sentsize, size-sentsize); // Data send process (User's buffer -> Destination through H/W Tx socket buffer)
    				if(ret < 0) // Send Error occurred (sent data length < 0)
    				{
    					close(sn); // socket close
    					return ret;
    				}
    				sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
    			}
             }
    		 //
             break;
    
          case SOCK_CLOSE_WAIT :
    #ifdef _LOOPBACK_DEBUG_
             //printf("%d:CloseWait\r\n",sn);
    #endif
             if((ret=disconnect(sn)) != SOCK_OK) return ret;
    #ifdef _LOOPBACK_DEBUG_
             printf("%d:Socket Closed\r\n", sn);
    #endif
             break;
    
          case SOCK_INIT :
    #ifdef _LOOPBACK_DEBUG_
        	 printf("%d:Try to connect to the %d.%d.%d.%d : %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
    #endif
        	 if( (ret = connect(sn, destip, destport)) != SOCK_OK) return ret;	//	Try to TCP connect to the TCP server (destination)
             break;
    
          case SOCK_CLOSED:
        	  close(sn);
        	  if((ret=socket(sn, Sn_MR_TCP, any_port++, 0x00)) != sn){
             if(any_port == 0xffff) any_port = 50000;
             return ret; // TCP socket open with 'any_port' port number
            } 
    #ifdef _LOOPBACK_DEBUG_
        	 //printf("%d:TCP client loopback start\r\n",sn);
             //printf("%d:Socket opened\r\n",sn);
    #endif
             break;
          default:
             break;
       }
       return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    4.5 测试现象

      硬件连接无误后,编译烧录程序(具体可参考第一章节),打开WIZ UartTool,选择对应的COM口,填入参数:波特率115200,8位数据位,1位停止位,无校验位,无流控,填完参数后点击open打开,观察串口打印的信息以获取设备运行状态;打开SocketTester,在左列填入相对应的参数,选择TCP server模式,本地IP和监听端口根据串口打印的信息填入,完成后点击Listen监听,连接成功后发送数据观察现象;可以看到数据成功发送并成功回传,如下图所示:

    在这里插入图片描述

    5. 注意事项

    • 不要混淆服务器IP和客户端IP。
    • 连接后交互过程不要突然断开网线,造成假链接。
    • 如果想用WIZnet的W5500来实现本章的示例,我们只需修改两个地方即可:
    1. 在library/ioLibrary_Driver/Ethernet/下找到wizchip_conf.h这个头文件,将_WIZCHIP_ 宏定义修改为W5500。
    2. 在library下找到CMakeLists.txt文件,将COMPILE_SEL设置为ON即可,OFF为W5100S,ON为W5500。

    6. 相关链接

    WIZnet官网

    WIZnet官方库链接

    本章例程链接

    想了解更多,评论留言哦!

  • 相关阅读:
    设计模式一: Observer(观察者模式)
    Air Strike Starter Kit 5.38 空中飞机大战可以几元单独买吗?和2.1版什么区别,多了啥?
    快速入门到精通到实战SpringBootWeb详解!
    VUE + JS 生成指定区间随机数
    非零基础自学Java (老师:韩顺平) 第10章 面向对象编程(高级部分) 10.3 代码块
    简单diff算法
    以太网的概念
    【#HDC2022】HarmonyOS体验官活动正式开启,赶快投稿赢限量奖品吧!
    重磅开源:基于.NET 6.0 自研轻量级ORM框架
    进程相关知识总结
  • 原文地址:https://blog.csdn.net/WIZnet2012/article/details/134043833