本文简要介绍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, ðernetif_init, ðernet_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
|
|