Wiznet makers

gavinchang

Published January 02, 2023 ©

50 UCC

23 WCC

56 VAR

0 Contests

2 Followers

0 Following

Original Link

Socket programming of Rtthread is based on W5500

Socket programming of Rtthread is based on W5500

COMPONENTS Hardware components

WIZnet - W5500

x 1


PROJECT DESCRIPTION

Network programming based on RT-Thread SAL +W5500
【1 Introduction
WIZnet software package
SHALL
SAL TLS encrypted transmission function
【2】Hardware design
MCU: stm32f103rbt6
W5500
What is a full hardware TCP/IP protocol stack?
Pin statement
[3] Bottom transplant
(1) Add WIZNET software package! [Insert picture description here](https://img-blog.csdnimg.cn/cc57a9fd674f476087e82f93a5f893aa.png)
(2) Configure WIZNET software package
(3) Software adaptation
(4) Compile and download successfully
【4】Socket programming
Client code:
Server code:
【1 Introduction
WIZnet software package
WIZnet: WIZnet software package is RT-Thread porting implementation based on ioLibrary_Driver code library on WIZnet official website, currently only supports W5500 devices . gitee address
Based on the functions of the original code base, this software package is connected to the RT-Thread SAL socket abstraction layer to support the standard BSD Socket APIs. It is perfectly compatible with various software packages and network functions, and improves the compatibility of WIZnet devices. .

insert image description here
SHALL
Socket abstraction layer SAL, namely the socket abstraction layer, is located between the network hardware layer and the application layer. It is used to abstract and unify multiple network protocol stack interfaces and provide standard BSD Socket APIs to the application layer. SAL allows the RT-Thread system to seamlessly connect to various network chips or modules, improving system compatibility.
Main features of SAL components:

insert image description here
At present, there are three main network access methods supported by SAL: software TCP/IP protocol stack (lwIP), custom serial port network implementation (AT Socket), and the third access method brought below, full hardware TCP/IP protocol stack ( W5500 ).


SAL TLS encrypted transmission function
Currently supports MbedTLS type encryption mode, (to be studied later)

【2】Hardware design
MCU: stm32f103rbt6

insert image description here
W5500
W5500 is one of the high-performance Ethernet interface chip series launched by WIZnet. It implements the full hardware TCP/IP protocol stack + MAC+PHY inside. The chip has the following characteristics:

8 independent hardware Sockets, each communication does not affect each other
32K bytes On-chip buffer is used to process sending and receiving data
Integrated 802.3 Ethernet MAC
host interface is SPI High-speed serial peripheral interface (up to 80 Mhz)

What is a full hardware TCP/IP protocol stack?

insert image description here

insert image description here
Compared with the resource occupation of the lwIP software protocol stack (RAM 50K+, ROM 80K+), the resource occupation (RAM 1K, ROM 20K) of using the hardware protocol stack chip (eg W5500) on the MCU has been greatly reduced.

Pin statementinsert image description here

W5500 circuit diagram: 

insert image description here

 

insert image description hereW5500 hardware information


[3] Bottom transplant
https://www.proyy.com/7074821127830257700.html

insert image description here
(1) Add WIZNET software package

insert image description here
(2) Configure WIZNET software package

insert image description here

How to query pin number:

insert image description here
(3) Software adaptation

insert image description here

void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
{
       GPIO_InitTypeDef GPIO_InitStruct;
   #ifdef BSP_USING_SPI1
       if (spiHandle->Instance == SPI1)
       {
           /* SPI1 clock enable */
           __HAL_RCC_SPI1_CLK_ENABLE();
           __HAL_RCC_GPIOA_CLK_ENABLE();
           /**SPI1 GPIO Configuration
           PA5     ------> SPI1_SCK
           PA6     ------> SPI1_MISO
           PA7     ------> SPI1_MOSI
           */
           GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
           GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
           GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
           HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
           GPIO_InitStruct.Pin = GPIO_PIN_6;
           GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
           GPIO_InitStruct.Pull = GPIO_NOPULL;
           HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
       }
   #endif
}
static int rt_hw_spi_flash_init(void)
{
   __HAL_RCC_GPIOA_CLK_ENABLE();
   rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);
   return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);

insert image description here


(4) Compile and download successfully

insert image description here
【4】Socket programming
Client code:

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <stdio.h>      // 在这个示例中没用到
#include <string.h>     // 提供 strlen() 函数
#include <rtdbg.h>      // 提供 LOG_D() 等函数,没用到
#include <board.h>      // 提供一些设备的定义,没用到
#include <sys/socket.h> // 提供 socket 的标准函数,如:bind()、connect()等
#include <netdb.h>      // 提供了获取 host 的方法,没有到
#include <netdev.h>     // 提供了网卡相关的定义和函数

#define SERVER_HOST "192.168.20.85"         // 服务器 IP 地址
#define SERVER_PORT  5000                   // 服务器使用的端口


int main(void)
{

    struct sockaddr_in client_addr;         // 客户端套接字地址
    struct sockaddr_in server_addr;         // 服务端套接字地址
    struct netdev *net_dev = RT_NULL;        // netdev 网卡设备
    int socketfd = -1;                      // 套接字文件描述符


    /* 通过网卡名称获取 netdev 网卡对象 */
    net_dev = netdev_get_by_name("W5500");
    if (net_dev == RT_NULL){
        rt_kprintf("get network interface device(%s) failed!\n", "W5500");
        return -RT_ERROR;
    }rt_kprintf("get network interface device(%s) success!\n", "W5500");

    /* 等待 DHCP 获取 IP 地址成功,超时时间 20s。如果没有 IP 地址,Socket 无法创建。 */
    int count = 100;
    while(count-- && !(net_dev->ip_addr.addr)){
        rt_thread_mdelay(1000);
    }
    if(!count){
        rt_kprintf("get ip failed!\n");
        return -RT_ERROR;
    }else {
        rt_kprintf("get ip:%s success!\n",inet_ntoa(net_dev->ip_addr.addr));
    }

    /* 创建一个套接字 */
    if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        rt_kprintf("create socket failed!\n");
        return -RT_ERROR;
    }rt_kprintf("create socket success!\n");

    /* 初始化需要绑定的客户端地址 */
    client_addr.sin_family = AF_INET;                                       // IPV4
    client_addr.sin_port = htons(8080);                                     // 客户端使用的端口
    client_addr.sin_addr.s_addr = net_dev->ip_addr.addr;                    // 获取网卡对象中的 IP 地址,这个地址是由 DHCP 获取的,没有地址时为 0
    rt_memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));    // 这个字段只起填充作用,使得 sockaddr_in 与 sockaddr 长度一样

    /* 客户端绑定套接字 */
    if (bind(socketfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) < 0){
        rt_kprintf("socket bind failed!\n");
        closesocket(socketfd);
        return -RT_ERROR;
    }rt_kprintf("socket bind network interface device(%s) success!\n", net_dev->name);

    /* 初始化预连接的服务端地址(配置服务端地址) */
    server_addr.sin_family = AF_INET;                                      // IPV4
    server_addr.sin_port = htons(SERVER_PORT);                             // 服务端使用的端口
    server_addr.sin_addr.s_addr = inet_addr(SERVER_HOST);                  // 服务器的 IP 地址
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));   // 无实际作用,填充长度

    /* 连接到服务端 */
    if (connect(socketfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0){
        rt_kprintf("socket connect failed!\n");
        closesocket(socketfd);
        return -RT_ERROR;
    }rt_kprintf("socket connect success!\n");


    /* 向服务端发送数据  */
    int send_size = 0;                                         // 发送的数据长度
    static const char *msg = "Hello, i am stm32f103.\n";     // 准备向服务端发送的数据
    if ((send_size = send(socketfd, msg, strlen(msg), 0)) <= 0) {
        rt_kprintf("send msg failed!\n", send_size);
    }rt_kprintf("send msg success, send_size:%d.\n", send_size);

    /* 接收服务器发送的数据,阻塞等待服务端数据,如果想要持续通信,使用 while 循环即可 */
    int recv_size = -1;                     // 接收到的数据长度
    char *recv_buffer = rt_calloc(1, 1024);  // 接收数据的缓冲

    while(1)
    {

         if((recv_size = recv(socketfd, recv_buffer, 1024, 0)) < 0){
             rt_kprintf("receive data failed!\n");
         }else {
             rt_kprintf("receive data success, recv_size:%d.\n", recv_size);
             rt_kprintf("the data is:%s\n", recv_buffer);
        }
    }
    return RT_EOK;
}

Server code:

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <stdio.h>      // 在这个示例中没用到
#include <string.h>     // 提供 strlen() 函数
#include <rtdbg.h>      // 提供 LOG_D() 等函数,没用到
#include <board.h>      // 提供一些设备的定义,没用到
#include <sys/socket.h> // 提供 socket 的标准函数,如:bind()、connect()等
#include <netdb.h>      // 提供了获取 host 的方法,没有到
#include <netdev.h>     // 提供了网卡相关的定义和函数

#define SERVER_HOST "192.168.20.85"         // 服务器 IP 地址
#define SERVER_PORT  5000                   // 服务器使用的端口

#define BUFSZ       (1024)
static const char send_data[] = "This is TCP Server from RT-Thread."; /* 发送用到的数据 */


int main(void)
{
        struct netdev *net_dev = RT_NULL;        // netdev 网卡设备
        char *recv_data; /* 用于接收的指针,后面会做一次动态分配以请求可用内存 */
        socklen_t sin_size;
        int sock, connected, bytes_received;
        struct sockaddr_in server_addr, client_addr;
        rt_bool_t stop = RT_FALSE; /* 停止标志 */
        int ret;
        recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的数据缓冲 */
        if (recv_data == RT_NULL)
        {
            rt_kprintf("No memory\n");
            return;
        }

        /* 通过网卡名称获取 netdev 网卡对象 */
        net_dev = netdev_get_by_name("W5500");
        if (net_dev == RT_NULL){
                rt_kprintf("get network interface device(%s) failed!\n", "W5500");
                return -RT_ERROR;
        }rt_kprintf("get network interface device(%s) success!\n", "W5500");

        /* 等待 DHCP 获取 IP 地址成功,超时时间 20s。如果没有 IP 地址,Socket 无法创建。 */
        int count = 100;
        while(count-- && !(net_dev->ip_addr.addr)){
            rt_thread_mdelay(1000);
        }
        if(!count){
            rt_kprintf("get ip failed!\n");
            return -RT_ERROR;
        }else {
            rt_kprintf("get ip:%s success!\n",inet_ntoa(net_dev->ip_addr.addr));
        }

        /* 一个socket在使用前,需要预先创建出来,指定SOCK_STREAM为TCP的socket */
        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            /* 创建失败的错误处理 */
            rt_kprintf("Socket error\n");
            /* 释放已分配的接收缓冲 */
            rt_free(recv_data);
            return;
        }
        /* 初始化服务端地址 */
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(5000); /* 服务端工作的端口 */
        server_addr.sin_addr.s_addr = net_dev->ip_addr.addr;//INADDR_ANY;
        rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
        /* 绑定socket到服务端地址 */
        if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
        {
            /* 绑定失败 */
            rt_kprintf("Unable to bind\n");
            /* 释放已分配的接收缓冲 */
            rt_free(recv_data);
            return;
        }
        /* 在socket上进行监听 */
        if (listen(sock, 5) == -1)
        {
            rt_kprintf("Listen error\n");
            /* release recv buffer */
            rt_free(recv_data);
            return;
        }
        rt_kprintf("\nTCPServer Waiting for client on %s port 5000...\n",inet_ntoa(server_addr.sin_addr.s_addr));
        while (stop != RT_TRUE)
        {
            sin_size = sizeof(struct sockaddr_in);
            /* 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */
            connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
            /* 返回的是连接成功的socket */
            if (connected < 0)
            {
                rt_kprintf("accept connection failed! errno = %d\n", errno);
                continue;
            }else {
                rt_kprintf("accept success!");
            }
            /* 接受返回的client_addr指向了客户端的地址信息 */
            rt_kprintf("I got a connection from (%s , %d)\n",
                       inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
            /* 客户端连接的处理 */
            while (1)
            {
                /* 发送数据到connected socket */
                ret = send(connected, send_data, strlen(send_data), 0);
                if (ret < 0)
                {
                    /* 发送失败,关闭这个连接 */
                    closesocket(connected);
                    rt_kprintf("\nsend error,close the socket.\r\n");
                    break;
                }
                else if (ret == 0)
                {
                    /* 打印send函数返回值为0的警告信息 */
                    rt_kprintf("\n Send warning,send function return 0.\r\n");
                }
                /* 从connected socket中接收数据,接收buffer是1024大小,但并不一定能够收到1024大小的数据 */
                bytes_received = recv(connected, recv_data, BUFSZ, 0);
                if (bytes_received < 0)
                {
                    /* 接收失败,关闭这个connected socket */
                    closesocket(connected);
                    break;
                }
                else if (bytes_received == 0)
                {
                    /* 打印recv函数返回值为0的警告信息 */
                    rt_kprintf("\nReceived warning,recv function return 0.\r\n");
                    closesocket(connected);
                    break;
                }
                /* 有接收到数据,把末端清零 */
                recv_data[bytes_received] = '\0';
                if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0)
                {
                    /* 如果是首字母是q或Q,关闭这个连接 */
                    closesocket(connected);
                    break;
                }
                else if (strcmp(recv_data, "exit") == 0)
                {
                    /* 如果接收的是exit,则关闭整个服务端 */
                    closesocket(connected);
                    stop = RT_TRUE;
                    break;
                }
                else
                {
                    /* 在控制终端显示收到的数据 */
                    rt_kprintf("RECEIVED DATA = %s \n", recv_data);
                }
            }
        }
        /* 退出服务 */
        closesocket(sock);
        /* 释放接收缓冲 */
        rt_free(recv_data);
        return ;
}


原文链接:https://blog.csdn.net/a15236617777/article/details/127203254

Documents
Comments Write