STM32片上驱动 - ETH驱动(以太网)

一、ETH概述

STM32F4xx 系列控制器内部集成了一个以太网外设,实际是一个通过DMA控制器进行介质访问控制MAC,功能就是实现 MAC 层任务,可以通过 ETH 外设按照 IEEE 802.3-2002 标准发送和接收MAC数据包;

ETH 内部自带专用的 DMA 控制器用于 MAC,ETH 支持两个工业标准接口介质独立接口 (MII) 和简化介质独立接口 (RMII) 用于与外部 PHY 芯片连接;

MII 和 RMII 接口用于 MAC 数据包传输,ETH 还集成了站管理接口 (SMI) 接口专门用于与外部 PHY 通信,用于访问 PHY 芯片寄存器;

物理层定义了以太网使用的传输介质、传输速度、数据编码方式和冲突检测机制,PHY 芯片是物理层功能实现的实体,生活中常用水晶头网线+水晶头插座+PHY组合构成了物理层。

1、RMII接口

RMII 全称是Reduced Media Independent Interface,翻译过来就是精简的介质独立接口,也就是MII 接口的精简版本RMII 接口只需要7 根数据线,相比MII 直接减少了9 根,极大的方便了板子布线,RMII 接口连接PHY 芯片的示意图如图所示:

  1. TX_EN:发送使能信号
  2. TXD[1:0]:发送数据信号线,一共2
  3. RXD[1:0]:接收数据信号线,一共2
  4. CRS_DV:相当于MII 接口中的RX_DV CRS 这两个信号的混合
  5. REF_CLK:参考时钟,由外部时钟源提供,频率为50MHz这里与MII不同,MII的接收和发送时钟是独立分开的,而且都是由PHY芯片提供的。
2、MDIO接口

MDIO全称是Management Data Input/Output,直译过来就是管理数据输入输出接口,是一个简单的两线串行接口,一根MDIO数据线,一根MDC时钟线

驱动程序可以通过MDIOMDC这两根线访问PHY芯片的任意一个寄存器

MDIO接口支持多达32PHY同一时刻内只能对一个PHY进行操作,和IIC一样,使用器件地址即可同一MDIO接口下的所有PHY芯片,其器件地址不能冲突,必须保证唯一,具体器件地址值要查阅相应的PHY数据手册因此,MAC和外部PHY芯片进行连接的时候主要是MII/RMIIMDIO接口,另外可能还需要复位、中断等其他引脚。

3、RJ45接口

网络设备是通过网线连接起来的,插入网线的叫做RJ45

  

RJ45座要与PHY芯片连接在一起,但是中间需要一个网络变压器现在很多RJ45座子内部已经集成网络变压器,网络变压器用于隔离以及滤波等,网络变压器也是一个芯片,外形如图所示所设计的硬件是需要内置网络变压器的RJ45座,肯定不能随便焊接一个不内置变压器的RJ45座,否则网络工作不正常!

  

RJ45座子上一般有两个灯,一个黄色(橙色),一个绿色,绿色亮的话表示网络连接正常,黄色闪烁的话说明当前正在进行网络通信这两个灯由PHY 芯片控制,PHY 芯片会有两个引脚来连接RJ45 座上的这两个灯。

二、CubeMX驱动生成

(注意引脚分配)

ETH_MDIO:PA2

ETH_MDC:PC1

RMII_TXD0:PG13

RMII_TXD1:PG14

RMII_TXEN:PG11

RMII_RXD0:PC4

RMII_RXD1:PC5

RMII_CRS_DV:PA7

RMII_REF_CLK:PA1
ETH_RESET:PD3

三、使用以太网物理层设备驱动/LWIP

确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):

#define HAL_ETH_MODULE_ENABLED

添加eth.c硬件驱动到board.c:

void HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle)

{

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    if(ethHandle->Instance==ETH) {

        /* USER CODE BEGIN ETH_MspInit 0 */

        

        /* USER CODE END ETH_MspInit 0 */

        /* ETH clock enable */

        __HAL_RCC_ETH_CLK_ENABLE();

        

        __HAL_RCC_GPIOC_CLK_ENABLE();

        __HAL_RCC_GPIOA_CLK_ENABLE();

        __HAL_RCC_GPIOG_CLK_ENABLE();

        /**ETH GPIO Configuration

        PC1     ------> ETH_MDC

        PA1     ------> ETH_REF_CLK

        PA2     ------> ETH_MDIO

        PA7     ------> ETH_CRS_DV

        PC4     ------> ETH_RXD0

        PC5     ------> ETH_RXD1

        PG11     ------> ETH_TX_EN

        PG13     ------> ETH_TXD0

        PG14     ------> ETH_TXD1

        */

        GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;

        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

        GPIO_InitStruct.Alternate = GPIO_AF11_ETH;

        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

        

        GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;

        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

        GPIO_InitStruct.Alternate = GPIO_AF11_ETH;

        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        

        GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;

        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

        GPIO_InitStruct.Alternate = GPIO_AF11_ETH;

        HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

        

        /* USER CODE BEGIN ETH_MspInit 1 */

        

        /* USER CODE END ETH_MspInit 1 */

    }

}

修改board.h:

#define BSP_USING_ETH

#ifdef BSP_USING_ETH

#define PHY_USING_LAN8720A

/*#define PHY_USING_DM9161CEP*/

/*#define PHY_USING_DP83848C*/

#endif

配置PHY寄存器(cubemx/Inc/stm32f4xx_hal_conf.h):

#define PHY_SR                          ((uint16_t)0x1F)     /*!< PHY special control/ status register Offset     */

#define PHY_SPEED_STATUS                ((uint16_t)0x0004)   /*!< PHY Speed mask                                  */

#define PHY_DUPLEX_STATUS               ((uint16_t)0x0010)   /*!< PHY Duplex mask */

屏蔽错误(drivers/drv_eth.c):

enum {

    PHY_LINK        = (1 << 0),

    PHY_100M        = (1 << 1),

//    PHY_FULL_DUPLEX = (1 << 2),

};

实现phy_reset函数(main.c):

#include <rtthread.h>

#include <rtdevice.h>

#include <drv_common.h>

#define RESET_IO GET_PIN(D, 3)

void phy_reset(void)

{

    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);

    rt_pin_write(RESET_IO, PIN_HIGH);

    rt_thread_mdelay(50);

    rt_pin_write(RESET_IO, PIN_LOW);

    rt_thread_mdelay(50);

    rt_pin_write(RESET_IO, PIN_HIGH);

}

eth设备:

ifconfig:

ping局域网内ip:

四、netdev网络接口设备

netdev(network interface device),即网络接口设备,又称网卡;

每一个用于网络连接的设备都可以注册成网卡,为了适配更多的种类的网卡,避免系统中对单一网卡的依赖,RT-Thread 系统提供了 netdev 组件用于网卡管理和控制;

netdev 组件主要作用是解决设备多网卡连接时网络连接问题,用于统一管理各个网卡信息与网络连接状态,并且提供统一的网卡调试命令接口。 其主要功能特点如下所示:

  1. 抽象网卡概念,每个网络连接设备可注册唯一网卡;
  2. 提供多种网络连接信息查询,方便用户实时获取当前网卡网络状态;
  3. 建立网卡列表和默认网卡,可用于网络连接的切换;
  4. 提供多种网卡操作接口(设置 IP、DNS 服务器地址,设置网卡状态等);
  5. 统一管理网卡调试命令(ping、ifconfig、netstat、dns 等命令)。

头文件:

#include <arpa/inet.h>         /* 包含 ip_addr_t 等地址相关的头文件 */

#include <netdev.h>            /* 包含全部的 netdev 相关操作接口函数 */

参阅文档:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev?id=%e9%85%8d%e7%bd%ae%e9%80%89%e9%a1%b9

五、示例代码

1、获取/设置网卡信息
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <arpa/inet.h>
#include <netdev.h>

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

#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

#define NETDEV_NAME "e0"

int enable_netdev() {
    struct netdev *netdev;
    netdev = netdev_get_by_name(NETDEV_NAME);
    if (netdev == RT_NULL) {
        rt_kprintf("network interface device %s not found!\n", NETDEV_NAME);
        return -1;
    }
    netdev_set_up(netdev);

    return 0;
}

int disable_netdev() {
    struct netdev *netdev;
    netdev = netdev_get_by_name(NETDEV_NAME);
    if (netdev == RT_NULL) {
        rt_kprintf("network interface device %s not found!\n", NETDEV_NAME);
        return -1;
    }
    netdev_set_down(netdev);

    return 0;
}

int is_up() {
    struct netdev *netdev;
    netdev = netdev_get_by_name(NETDEV_NAME);
    if (netdev == RT_NULL) {
        rt_kprintf("network interface device %s not found!\n", NETDEV_NAME);
        return -1;
    }
    rt_kprintf("netdev_is_up: %d\n", netdev_is_up(netdev));

    return 0;
}

int is_link_up() {
    struct netdev *netdev;
    netdev = netdev_get_by_name(NETDEV_NAME);
    if (netdev == RT_NULL) {
        rt_kprintf("network interface device %s not found!\n", NETDEV_NAME);
        return -1;
    }
    rt_kprintf("netdev_is_link_up: %d\n", netdev_is_link_up(netdev));

    return 0;
}

MSH_CMD_EXPORT(enable_netdev, enable_netdev);
MSH_CMD_EXPORT(disable_netdev, disable_netdev);
MSH_CMD_EXPORT(is_up, is_up);
MSH_CMD_EXPORT(is_link_up, is_link_up);

int main(void)
{
    while (1) {
        //LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}
2、TCP服务端
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <sys/socket.h>
#include <netdb.h>

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

#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

#define BUFSZ       (1024)

static const char send_data[] = "This is TCP Server from RT-Thread.";
static void tcpserv(int argc, char **argv)
{
    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;
    }

    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 = INADDR_ANY;
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
        // 绑定失败
        rt_kprintf("Unable to bind\n");
        // 释放已分配的接收缓冲
        rt_free(recv_data);
        return;
    }
    if (listen(sock, 5) == -1) {
        rt_kprintf("Listen error\n");
        // 释放已分配的接收缓冲
        rt_free(recv_data);
        return;
    }

    rt_kprintf("\nTCPServer Waiting for client on port 5000...\n");
    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;
        }

        // 接受返回的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");
            }

            // 接收数据,接收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) {
                // 客户端断开连接
                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 ;
}
MSH_CMD_EXPORT(tcpserv, a tcp server sample);

int main(void)
{

    while (1) {
        //LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}
3、TCP客户端
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <sys/socket.h>
#include <netdb.h>

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

#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

#define BUFSZ   1024

static const char send_data[] = "This is TCP Client from RT-Thread.";
void tcpclient(int argc, char **argv)
{
    int ret;
    char *recv_data;
    struct hostent *host;
    int sock, bytes_received;
    struct sockaddr_in server_addr;
    const char *url;
    int port;

    if (argc < 3) {
        rt_kprintf("Usage: tcpclient URL PORT\n");
        rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
        return ;
    }

    url = argv[1];
    port = strtoul(argv[2], 0, 10);

    // 通过函数入口参数url获得host地址(如果是域名,会做域名解析)
    host = gethostbyname(url);

    // 分配用于存放接收数据的缓冲
    recv_data = rt_malloc(BUFSZ);
    if (recv_data == RT_NULL) {
        rt_kprintf("No memory\n");
        return;
    }

    // 创建一个socket,类型是SOCKET_STREAM,TCP类型
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        /* 创建socket失败 */
        rt_kprintf("Socket error\n");

        /* 释放接收缓冲 */
        rt_free(recv_data);
        return;
    }

    // 初始化预连接的服务端地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

    // 连接到服务端
    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
        /* 连接失败 */
        rt_kprintf("Connect fail!\n");
        closesocket(sock);

        /*释放接收缓冲 */
        rt_free(recv_data);
        return;
    }

    while (1) {
        // 从sock连接中接收最大BUFSZ - 1字节数据
        bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
        if (bytes_received < 0) {
            // 接收失败,关闭这个连接
            closesocket(sock);
            rt_kprintf("\nreceived error,close the socket.\r\n");

            // 释放接收缓冲
            rt_free(recv_data);
            break;
        } else if (bytes_received == 0) {
            // 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接
            closesocket(sock);
            rt_kprintf("\nreceived error,close the socket.\r\n");

            // 释放接收缓冲
            rt_free(recv_data);
            break;
        }

        // 有接收到数据,把末端清零
        recv_data[bytes_received] = '\0';

        if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0) {
            // 如果是首字母是q或Q,关闭这个连接
            closesocket(sock);
            rt_kprintf("\n got a 'q' or 'Q',close the socket.\r\n");

            // 释放接收缓冲
            rt_free(recv_data);
            break;
        } else {
            // 在控制终端显示收到的数据
            rt_kprintf("\nReceived data = %s ", recv_data);
        }

        // 发送数据到sock连接
        ret = send(sock, send_data, strlen(send_data), 0);
        if (ret < 0) {
            // 接收失败,关闭这个连接
            closesocket(sock);
            rt_kprintf("\nsend error,close the socket.\r\n");

            rt_free(recv_data);
            break;
        } else if (ret == 0) {
            // 打印send函数返回值为0的警告信息
            rt_kprintf("\n Send warning,send function return 0.\r\n");
        }
    }
    return;
}

MSH_CMD_EXPORT(tcpclient, a tcp client sample);

int main(void)
{

    while (1) {
        //LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}
4、UDP服务端
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <sys/socket.h>
#include <netdb.h>

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

#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

#define BUFSZ   1024

static void udpserv(int argc, char **argv)
{
    int sock;
    int bytes_read;
    char *recv_data;
    socklen_t addr_len;
    struct sockaddr_in server_addr, client_addr;

    // 分配接收用的数据缓冲
    recv_data = rt_malloc(BUFSZ);
    if (recv_data == RT_NULL) {
        // 分配内存失败,返回
        rt_kprintf("No memory\n");
        return;
    }

    // 创建一个socket,类型是SOCK_DGRAM,UDP类型
    if ((sock = socket(AF_INET, SOCK_DGRAM, 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 = 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("Bind error\n");

        // 释放接收用的数据缓冲
        rt_free(recv_data);
        return;
    }

    addr_len = sizeof(struct sockaddr);
    rt_kprintf("UDPServer Waiting for client on port 5000...\n");

    while (1) {
        // 从sock中收取最大BUFSZ - 1字节数据
        bytes_read = recvfrom(sock, recv_data, BUFSZ - 1, 0,
                              (struct sockaddr *)&client_addr, &addr_len);
        // UDP不同于TCP,基本不会出现收取的数据失败的情况,除非设置了超时等待

        recv_data[bytes_read] = '\0';

        // 输出接收的数据
        rt_kprintf("\n(%s , %d) said : ", inet_ntoa(client_addr.sin_addr),
                   ntohs(client_addr.sin_port));
        rt_kprintf("%s", recv_data);

        // 如果接收数据是exit,退出
        if (strcmp(recv_data, "exit") == 0) {
            closesocket(sock);

            // 释放接收用的数据缓冲
            rt_free(recv_data);
            break;
        }
    }

    return;
}

MSH_CMD_EXPORT(udpserv, a udp server sample);

int main(void)
{

    while (1) {
        //LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}
5、UDP客户端
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_common.h>
#include <sys/socket.h>
#include <netdb.h>

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

#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

const char send_data[] = "This is UDP Client from RT-Thread.\n";
void udpclient(int argc, char **argv)
{
    int sock, port, count;
    struct hostent *host;
    struct sockaddr_in server_addr;
    const char *url;

    if (argc < 3) {
        rt_kprintf("Usage: udpclient URL PORT [COUNT = 10]\n");
        rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
        return ;
    }

    url = argv[1];
    port = strtoul(argv[2], 0, 10);

    if (argc > 3)
        count = strtoul(argv[3], 0, 10);
    else
        count = 10;

    // 通过函数入口参数url获得host地址(如果是域名,会做域名解析)
    host = (struct hostent *) gethostbyname(url);

    // 创建一个socket,类型是SOCK_DGRAM,UDP类型
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        rt_kprintf("Socket error\n");
        return;
    }

    // 初始化预连接的服务端地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

    // 总计发送count次数据
    while (count) {
        // 发送数据到服务远端
        sendto(sock, send_data, strlen(send_data), 0,
               (struct sockaddr *)&server_addr, sizeof(struct sockaddr));

        // 线程休眠一段时间
        rt_thread_delay(50);

        // 计数值减一
        count --;
    }

    // 关闭这个socket
    closesocket(sock);
}

MSH_CMD_EXPORT(udpclient, a udp client sample);

int main(void)
{

    while (1) {
        //LOG_D("Hello RT-Thread!");
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值