打印
[研电赛技术支持]

GD32F450之以太网(Lwip2.1.2+FreeRTOS+UDP+TCP)

[复制链接]
1633|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本文简要介绍GD32单片机关于以太网的应用,参照官方应用手册,基于Lwip2.1.2版本对UDP及TCP通信进行测试。使用PHY芯片为LAN8720,实时操作系统FreeRTOS。实现自适应10M/100M以太网通信。

一、基础配置:
引进外设初始化,选择RMII模式,复用单片机引脚(相关引脚为固定脚,直接复用即可),首先使能总线时钟,选择接口模式(RMII),再复用引脚。
//IO总线时钟使能,此处将所有外设时钟使能

    rcu_periph_clock_enable(RCU_GPIOA);

    rcu_periph_clock_enable(RCU_GPIOB);

    rcu_periph_clock_enable(RCU_GPIOC);

    rcu_periph_clock_enable(RCU_GPIOD);

    rcu_periph_clock_enable(RCU_GPIOG);

    rcu_periph_clock_enable(RCU_GPIOH);

    rcu_periph_clock_enable(RCU_GPIOI);

    //使能系统时钟

    rcu_periph_clock_enable(RCU_SYSCFG);   

    //选择RMII模式

    syscfg_enet_phy_interface_config(SYSCFG_ENET_PHY_RMII);

    //PA1:REF_CLK 时钟引脚,外部PHY提供50M

    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);

    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);



    //PA2: ETH_MDIO

    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);

    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);



    //PA7: ETH_RMII_CRS_DV

    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);

    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_7);  

    //引脚复用

    gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);

    gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);

    gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);



    //PB11: ETH_RMII_TX_EN

    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);

    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_11);



    //PB12: ETH_RMII_TXD0

    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);

    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_12);



    //PB13: ETH_RMII_TXD1

    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);

    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13);  



    gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_11);

    gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_12);

    gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_13);



    //PC1: ETH_MDC

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);

    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);



    //PC4: ETH_RMII_RXD0

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);

    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_4);



    //PC5: ETH_RMII_RXD1

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);

    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_5);



    gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);

    gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);

    gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);

2、以太网初始化:使能以太网相关时钟,初始化MAC、DMA相关控制(使用官方默认库函数即可)

ErrStatus reval_state = ERROR;

rcu_periph_clock_enable(RCU_ENET);//使能以太网总线时钟

rcu_periph_clock_enable(RCU_ENETTX);//使能发送时钟

rcu_periph_clock_enable(RCU_ENETRX);//使能接收时钟

enet_deinit();//复位总线时钟AHB

reval_state = enet_software_reset();//等待时钟复位完成

if(ERROR == reval_state){//复位失败

   while(1){

   }

}

//以太网设备初始化

#ifdef CHECKSUM_BY_HARDWARE//不定义

enet_init_status = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);

#else  //PHY自动协商、禁用IP帧校验功能、地址过滤器通过所有接收到的广播帧ENET_100M_FULLDUPLEX/ENET_AUTO_NEGOTIATION

enet_init(ENET_AUTO_NEGOTIATION, ENET_NO_AUTOCHECKSUM, ENET_BROADCAST_FRAMES_PASS);

#endif /* CHECKSUM_BY_HARDWARE */

    /*注:当前使用的LAN8720PHY芯片只能使用自动协商模式,手动模式无法通信*/

3、中断配置,结合实时操作系统,在中断中接收数据:

//以太网正常中断启用

  enet_interrupt_enable(ENET_DMA_INT_NIE);

  //接收中断使能

  enet_interrupt_enable(ENET_DMA_INT_RIE);

//使能以太网中断请求

    nvic_irq_enable(ENET_IRQn, 5, 0);//配置中断优先级
//中断函数

    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    if(SET == enet_interrupt_flag_get(ENET_DMA_INT_FLAG_RS)){

        /* 数据中断后释放信号量(在接收任务中处理) */

        xSemaphoreGiveFromISR(g_rx_semaphore, &xHigherPriorityTaskWoken);

    }

    /* 清除相关标志位 */

    enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_RS_CLR);

    enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_NI_CLR);

    if(pdFALSE != xHigherPriorityTaskWoken){

        portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

    }

4、Lwip协议栈初始化,主要配置IP、MAC等信息,创建数据收发任务。

ip_addr_t fsl_netif0_ipaddr, fsl_netif0_netmask, fsl_netif0_gw;

tcpip_init(NULL, NULL);   

    inet_aton((char *)&phy_cfg.ip, &fsl_netif0_ipaddr);

    inet_aton((char *)&phy_cfg.netmask, &fsl_netif0_netmask);

    inet_aton((char *)&phy_cfg.gateway, &fsl_netif0_gw);   

    netif_add(&fsl_netif0, &fsl_netif0_ipaddr, &fsl_netif0_netmask, &fsl_netif0_gw, NULL, &ethernetif_init, &ethernet_input);

    netif_set_default(&fsl_netif0);

netif_set_up(&fsl_netif0);
其中ethernetif_init函数需在ethernetif.c文件中修改。此外,需要注意,在官方库中,默认使用的PHY为DP83848,需在“gd32f450xx_enet.h”中修改PHY寄存器地址,当前使用LAN8720。



上述内容大致为以太网的基本配置,此时应该能正常ping设备IP。

二、UDP连接:
UDP连接创建的大致流程为:创建套接字、绑定套接字、数据通信、套接字关闭。

1、创建套接字:socket(),指定协议类型等

int sockfd= socket(AF_INET, SOCK_DGRAM, 0);//UDP,IPV4协议
2、绑定套接字:bind(),将套接字与设备IP端口信息绑定

struct sockaddr_in local_addr;//本地IP、端口



local_addr.sin_family = AF_INET;//IPV4协议

local_addr.sin_port = htons(port);//设备端口

local_addr.sin_addr.s_addr = htons(INADDR_ANY);

bind(sockfd, (struct sockaddr *)& local_addr, sizeof(local_addr));
3、数据通信:

数据接收:

char buff[]={0};

struct sockaddr_in from_addr;//远端IP、端口

socklen_t fromlen;//长度

recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&from_addr, &fromlen); //此函数为阻塞函数,将接收的数据存放于buff中。
数据发送:数据发送需要知道发送的目标信息,可以是接收数据时的IP、端口,也可以自定义。

struct sockaddr_in service_addr;//服务器IP、端口

socklen_t service_len;

void init_service_socket()

{

  ip_addr_t sip;//服务器IP

  u_int16_t sport;//服务器端口

  //初始化服务端的IP和端口

  service_addr.sin_family = PF_INET;

  service_addr.sin_port = htons(sport);   

  service_addr.sin_addr.s_addr = sip.addr;         

  service_len = sizeof(mqtt_service_addr);

}

//通过创建的套接字将数据发送至服务端

sendto(sockfd, send_buff,send_len,0,(struct sockaddr *)&service_addr,service_len);

三、TCP客户端
TCP连接创建的大致流程为:创建套接字、绑定套接字、连接服务器、数据通信、套接字关闭。

1、创建套接字:socket(),指定协议类型等

int sockfd= socket(AF_INET, SOCK_STREAM, 0);//TCP,IPV4协议
2、绑定套接字:bind(),将套接字与设备IP端口信息绑定

struct sockaddr_in local_addr;//本地IP、端口
local_addr.sin_family = AF_INET;//IPV4协议

local_addr.sin_port = htons(port);//设备端口

local_addr.sin_addr.s_addr = htons(INADDR_ANY);

bind(sockfd, (struct sockaddr *)& local_addr, sizeof(local_addr));
3、连接服务器:TCP通信是面向连接的,连接前先确定服务器的IP、端口信息。

struct sockaddr_in service_addr;//服务器IP、端口

socklen_t service_len;

void init_service_socket()

{

  ip_addr_t sip;//服务器IP

  u_int16_t sport;//服务器端口

  //初始化服务端的IP和端口

  service_addr.sin_family = PF_INET;

  service_addr.sin_port = htons(sport);   

  service_addr.sin_addr.s_addr = sip.addr;         

  service_len = sizeof(mqtt_service_addr);

}

connect(sockfd, (struct sockaddr *)& service_addr, sizeof(service_addr));//与服务器之间建立TCP连接

4、数据通信:

数据接收:套接字只能接收来自建立连接的服务端的数据。

char buff[]={0};

recv(sockfd, buff, sizeof(buff), 0); //此函数为阻塞函数,将接收的数据存放于buff中。

数据发送:通过建立连接的套接字将数据发送至服务端。

//通过创建的套接字将数据发送至服务端

send(sockfd, send_buff,send_len,0);
四、TCP服务器
TCP服务器创建的大致流程为:创建新的TCP协议控制块、绑定IP端口、监听绑定的协议块、初始化接收回调函数。

1、创建TCP协议控制块:

struct tcp_pcb *Tcp_server_pcb=NULL;

Tcp_server_pcb = tcp_new();//创建新的TCP块

if(Tcp_server_pcb == NULL)

  {

    printf("tcp new failed");

  }
2、绑定IP、端口:固定端口(80),允许任意IP连接(同网段)。

if(tcp_bind(Tcp_server_pcb,(const ip_addr_t*)INADDR_ANY,80))//绑定IP与端口

  {

    term_printf("tcp bind failed");

  }
3、监听:

Tcp_server_pcb = tcp_listen(Tcp_server_pcb);//监听

if(Tcp_server_pcb == NULL)

{

  term_printf("tcp listen failed");

}

tcp_arg(Tcp_server_pcb,Tcp_server_pcb);//确认监听连接
4、初始化接收回调函数:初始化完成后可通过网页端进行测试,也可以telnet连接。

数据解析与响应:

static err_t tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {

  if (p != NULL) {

    char *req = (char *)p->payload;//接收的数据

    char client_ip[INET_ADDRSTRLEN];

    inet_ntop(AF_INET, (char *)&pcb->remote_ip, client_ip, INET_ADDRSTRLEN);//将IP放入数组中

printf("Client connected from %s:%d\n", client_ip, ntohs(pcb->remote_port));//打印客户端IP、端口

//通过网页端测试

    if (strstr(req, "GET / ")) {//来自网页端的请求

      const char *html = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"

        "<html><body>Hello, GD32!</body></html>";

      if(tcp_write(pcb, html, strlen(html), TCP_WRITE_FLAG_COPY))

      {

        printf("数据写入缓冲区失败!");

      }

      if(tcp_output(pcb))

      {

        printf("TCP send data failed");

      }

    }else {

      const char *error = "HTTP/1.1 404 Not Found\r\n\r\n";

      tcp_write(pcb, error, strlen(error), TCP_WRITE_FLAG_MORE);

      if(tcp_output(pcb))

      {

        printf("TCP send data failed");

      }

    }

  }

  pbuf_free(p);

  return ERR_OK;

}

绑定接收回调函数:

static err_t tcp_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)

{

        /* 配置接收回调函数 */

        tcp_recv(pcb, tcp_recv);

        return ERR_OK;

}
初始化回调函数:

tcp_accept(Tcp_server_pcb,tcp_server_accept);//初始化接收回调函数
浏览器输入IP显示如下:



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_60881302/article/details/145896003

使用特权

评论回复
沙发
weifeng90| | 2025-3-7 08:15 | 只看该作者
能分享一下源代码吗?谢谢。

使用特权

评论回复
板凳
pentruman| | 2025-3-19 11:14 | 只看该作者
实现UDP和TCP通信,是一个复杂的任务

使用特权

评论回复
地板
saservice| | 2025-3-19 12:13 | 只看该作者
将 LwIP 和 FreeRTOS 的源码添加到项目中。
配置 LwIP 的网络接口,绑定到以太网接口。
创建任务来处理 LwIP 的任务调度。

使用特权

评论回复
5
uiint| | 2025-3-19 14:20 | 只看该作者
应用程序通过LwIP的套接字API发送数据。
LwIP将数据封装成TCP或UDP数据包,并通过以太网MAC控制器发送给PHY芯片。

使用特权

评论回复
6
benjaminka| | 2025-3-19 17:27 | 只看该作者
调用LwIP的初始化函数,配置网络接口。

使用特权

评论回复
7
515192147| | 2025-3-20 09:05 | 只看该作者
本帖最后由 515192147 于 2025-3-20 09:10 编辑

我们用 GD32F427 实现过 TCP UDP 编程,并能实现Web浏览
GD32F427实物图

Web浏览图

详细咨询见:
https://bbs.21ic.com/icview-3335470-1-1.html

使用特权

评论回复
8
louliana| | 2025-3-20 10:02 | 只看该作者
GD32F450是GigaDevice推出的一款基于ARM Cortex-M4内核的高性能微控制器,它具备丰富的外设接口

使用特权

评论回复
9
maudlu| | 2025-3-20 12:39 | 只看该作者
合理配置中断优先级,确保网络中断能够及时处理。

使用特权

评论回复
10
geraldbetty| | 2025-3-20 15:30 | 只看该作者
如何使用LwIP实现UDP广播?

使用特权

评论回复
11
plsbackup| | 2025-3-20 17:33 | 只看该作者
测试UDP和TCP的基本功能              

使用特权

评论回复
12
FranklinUNK| | 2025-3-20 21:39 | 只看该作者
网络这个容易学吗?

使用特权

评论回复
13
lihuami| | 2025-3-21 15:21 | 只看该作者
FreeRTOS提供了任务调度、同步、通信等机制,有助于实现复杂的网络通信功能。

使用特权

评论回复
14
claretttt| | 2025-3-21 17:05 | 只看该作者
从简单UDP透传开始验证基础功能,逐步扩展TCP复杂场景

使用特权

评论回复
15
jimmhu| | 2025-3-21 20:54 | 只看该作者
LwIP 和 FreeRTOS 都需要使用一定的内存资源,要合理分配内存,避免内存溢出。

使用特权

评论回复
16
gygp| | 2025-3-22 17:46 | 只看该作者
开发基于UDP的应用程序,实现数据包的发送和接收。

使用特权

评论回复
17
10299823| | 2025-3-22 18:28 | 只看该作者
下载并配置 GD32F4xx 的固件库。

使用特权

评论回复
18
wwppd| | 2025-3-22 18:51 | 只看该作者
根据GD32F450的内存资源,配置LwIP的内存管理参数,如缓冲区大小、TCP/IP栈大小等。

使用特权

评论回复
19
hilahope| | 2025-3-22 19:07 | 只看该作者
整合GD32F450的硬件特性、FreeRTOS的实时调度能力及LwIP的轻量级协议栈,可构建高效稳定的网络应用。关键在于正确配置DMA通道、优化内存分配及任务优先级。

使用特权

评论回复
20
yeates333| | 2025-3-22 19:40 | 只看该作者
要根据实际的网络环境正确配置 IP 地址、子网掩码、网关等参数

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

79

帖子

0

粉丝