GD32F470之网络lwip+UDP配置+lan8720芯片

先申明,本栏目用的都是GD32F470芯片240M,软件用的是keil,编写用的是C++(其实和C没有区别).

和STM32的lwip配置大致一样,主要不一样的地方在于
PHY的配置顺序问题,下面会讲到.

头文件部分:

我用的是lan8720,所以头文件要修改一下,在gd32f4xx_enet.h中。
把PHY_TYPE改为LAN8700,
PHY_ADDRESS改为0,
这是lan8720需要的。
然后就不需要修改了。里面的宏不会有问题,可以和原子的lan8720头文件对比一下。

#ifndef _PHY_H_
#define DP83848                          0
#define LAN8700                          1
#define PHY_TYPE                         LAN8700
//#define PHY_TYPE                         DP83848
//#define PHY_ADDRESS                      ((uint16_t)1U)                         /*!< phy address determined by the hardware */
#define PHY_ADDRESS                      ((uint16_t)0U)                         /*!< phy address determined by the hardware */

lwip部分:

整体上和原子的不会有很多区别,直接把原子的全部搬过来也是可以的。你也可以搬GD32例程的lwip过来也行。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其中Lwip/Core,Lwip/Arch,Lwip/Api都和STM32的是一样一模.

Lwip/Netif的ethernetif.c文件,和GD32例程一样,这和STM32的例程就有点不一样的。

#include "netif/ethernetif.h" 

#include "lwip_comm.h" 
#include "netif/etharp.h"  
#include "string.h"  
#include "sys.h"
#include "network.h"  

uint32_t current_pbuf_idx =0;

/* ENET RxDMA/TxDMA descriptor */
extern enet_descriptors_struct  rxdesc_tab[ENET_RXBUF_NUM], txdesc_tab[ENET_TXBUF_NUM];


/*global transmit and receive descriptors pointers */
extern enet_descriptors_struct  *dma_current_txdesc;
extern enet_descriptors_struct  *dma_current_rxdesc;

//extern uint8_t 	LocalMacAddr[6];				//本地Mac地址
//uint8_t 	LocalMacAddr[6] = {2,10,15,14,13,6};	//本地Mac地址

//由ethernetif_init()调用用于初始化硬件
//netif:网卡结构体指针 
//返回值:ERR_OK,正常
//       其他,失败
static void low_level_init(struct netif *netif)
{ 
    uint32_t i = 0;
  
    netif->hwaddr_len=ETHARP_HWADDR_LEN;  //设置MAC地址长度,为6个字节
		//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
		netif->hwaddr[0]=lwipdev.mac[0]; 
		netif->hwaddr[1]=lwipdev.mac[1]; 
		netif->hwaddr[2]=lwipdev.mac[2];
		netif->hwaddr[3]=lwipdev.mac[3];   
		netif->hwaddr[4]=lwipdev.mac[4];
		netif->hwaddr[5]=lwipdev.mac[5];
	
		/* initialize MAC address in ethernet MAC */ 
    enet_mac_address_set(ENET_MAC_ADDRESS0, netif->hwaddr);
	
		netif->mtu=1500;//最大允许传输单元,允许该网卡广播和ARP功能
	
    netif->flags|=NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
	
    /* initialize descriptors list: chain/ring mode */
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
    enet_ptp_enhanced_descriptors_chain_init(ENET_DMA_TX);
    enet_ptp_enhanced_descriptors_chain_init(ENET_DMA_RX);
#else

    enet_descriptors_chain_init(ENET_DMA_TX);
    enet_descriptors_chain_init(ENET_DMA_RX);
    
//    enet_descriptors_ring_init(ENET_DMA_TX);
//    enet_descriptors_ring_init(ENET_DMA_RX);

#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */

    /* enable ethernet Rx interrrupt */
    {   int i;
        for(i=0; i<ENET_RXBUF_NUM; i++){ 
           enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
        }
    }
		
#ifdef CHECKSUM_BY_HARDWARE
    /* enable the TCP, UDP and ICMP checksum insertion for the Tx frames */
    for(i=0; i < ENET_TXBUF_NUM; i++){
        enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
    }
#endif /* CHECKSUM_BY_HARDWARE */
		
		/* note: TCP, UDP, ICMP checksum checking for received frame are enabled in DMA config */

    /* enable MAC and DMA transmission and reception */
    enet_enable();
		
		
}

//用于发送数据包的最底层函数(lwip通过netif->linkoutput指向该函数)
//netif:网卡结构体指针
//p:pbuf数据结构体指针
//返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{

    struct pbuf *q;
    int framelength = 0;
    uint8_t *buffer;

    while((uint32_t)RESET != (dma_current_txdesc->status & ENET_TDES0_DAV)){
    }  
    buffer = (uint8_t *)(enet_desc_information_get(dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
    
    /* copy frame from pbufs to driver buffers */
    for(q = p; q != NULL; q = q->next){ 
        memcpy((uint8_t *)&buffer[framelength], q->payload, q->len);
        framelength = framelength + q->len;
    }
    
    /* note: padding and CRC for transmitted frame 
       are automatically inserted by DMA */

    /* transmit descriptors to give to DMA */ 
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
    ENET_NOCOPY_PTPFRAME_TRANSMIT_ENHANCED_MODE(framelength, NULL);
#else
    ENET_NOCOPY_FRAME_TRANSMIT(framelength);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */

    return ERR_OK;
}

//用于接收数据包的最底层函数
//neitif:网卡结构体指针
//返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{
    struct pbuf *p, *q;
    u16_t len;
    int l =0;
    uint8_t *buffer;
     
    p = NULL;
    
    /* obtain the size of the packet and put it into the "len" variable. */
    len = enet_desc_information_get(dma_current_rxdesc, RXDESC_FRAME_LENGTH);
    buffer = (uint8_t *)(enet_desc_information_get(dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));
    
    /* we allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
    
    /* copy received frame to pbuf chain */
    if (p != NULL){
        for (q = p; q != NULL; q = q->next){ 
            memcpy((uint8_t *)q->payload, (u8_t*)&buffer[l], q->len);
            l = l + q->len;
        }    
    }
  
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
    ENET_NOCOPY_PTPFRAME_RECEIVE_ENHANCED_MODE(NULL);
  
#else
    
    ENET_NOCOPY_FRAME_RECEIVE();
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */

    return p;
}

//网卡接收数据(lwip直接调用) 
//netif:网卡结构体指针
//返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
err_t ethernetif_input(struct netif *netif)
{
    err_t err;
    struct pbuf *p;
  
    p=low_level_input(netif);
    if(p==NULL) return ERR_MEM;
    err=netif->input(p, netif);
    if (err!=ERR_OK)
    {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
        pbuf_free(p);
        p=NULL;
    }
   
    return err;
}

//此函数使用low_level_init()函数来初始化网络
//netif:网卡结构体指针
//返回值:ERR_OK,正常
//       其他,失败
err_t ethernetif_init(struct netif *netif)
{
    LWIP_ASSERT("netif != NULL", (netif != NULL));
  
#if LWIP_NETIF_HOSTNAME
    netif->hostname="lwip"; //主机名字
#endif

    netif->name[0]=IFNAME0;
    netif->name[1]=IFNAME1;
    netif->output=etharp_output;
    netif->linkoutput=low_level_output;
    low_level_init(netif);
    return ERR_OK;
}




















重要配置代码部分

然后说到Lwip/App中的lwip_comme文件里面的lwip_comm_init()的函数,
注释有详细的配置流程。

重点是先不需要对内部enet初始化,和例程不一样,因为我们要对LAN8720初始化,
而enet_init库函数会对外部phy(LAN8720)读取,根据LAN8720来配置ENET_MAC_CFG 寄存器,这时候需要一致,不然出错。

//LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
//       1,内存错误
//       2,LAN8720初始化失败
//       3,网卡添加失败.


//第4点很重要,先配置LAN8720,再配置内部enet

//1.使能以太网时钟:
//	配置RCU模块来使能HCLK时钟和以太网发送/接收时钟。 
//2.配置通讯接口:
//	配置SYSCFG模块,选择接口模式(MII或RMII);
//	配置GPIO模块,将相应的功能脚映射到复用功能11(AF11)上。 
//3.等待复位完成:
//	轮询 ENET_DMA_BCTL 寄存器直到 SWR 位复位(SWR 位在上电复位后或系统复位后
//	默认置位)。 
//4.获取并配置PHY寄存器参数:
//	根据 HCLK 频率,配置 SMI 时钟频率,并访问 PHY 寄存器获取 PHY 的信息(例如是否
//	支持半/全双工,是否支持 10M/100Mbit 速度等等)。根据外部 PHY 支持的模式,配置
//	ENET_MAC_CFG 寄存器使与 PHY 寄存器信息一致。 

//5.初始化以太网DMA模块用于数据传输:
//	配 置 ENET_DMA_BCTL , ENET_DMA_RDTADDR , ENET_DMA_TDTADDR 和
//	ENET_DMA_CTL 寄存器,完成 DMA 模块初始化(详细信息请参考 DMA 控制器描述章
//	节)。
//6.初始化用于存放描述符列表以及数据缓存的物理内存空间:
//	根据 ENET_DMA_RDTADDR 和 ENET_DMA_TDTADDR 寄存器中的地址,初始化发送
//	和接收描述符(DAV=1),以及数据缓存。
//7.使能MAC和DMA模块,开始发送和接收:
//	置位 ENET_MAC_CFG 寄存器中的 TEN 和 REN 位,开启 MAC 发送器和接收器。置位
//	ENET_DMA_CTL 寄存器中的 STE 位和 SRE 位,使能 DMA 的发送和接收。
u8  Lwip_comm_Class::lwip_comm_init(void)
{
  u8 retry=0,enet_init_flag=0;
	struct netif *Netif_Init_Flag;			//调用netif_add()函数时的返回值,用于判断网络初始化是否成功
	struct ip_addr ipaddr;  						//ip地址
	struct ip_addr netmask; 						//子网掩码
	struct ip_addr gw;      						//默认网关 

	
	//若修改IP地址导致无法通信,无法修改回正确的IP地址,则使用以下默认的IP地址,重新下载程序
	
	lwip_comm_default_ip_set(&lwipdev);	//设置默认IP等信	

	LAN_8720_my_Private_Obj->IO_ETH_RST_M4();
	LAN_8720_my_Private_Obj->Lan8720_Io_Config_Init();

	//里面配了时钟 ,复位 MAC 所有内核寄存器
	while(LAN_8720_my_Private_Obj->LaN8720_Config_Init())
	{
			
			retry++;
			if(retry>5) {retry=0;return 3;} //LAN8720初始化失败
	
	}
	
	//初始化内部enet的内部寄存器和外部phy一致   DMA初始化
	if(LAN_8720_my_Private_Obj->enet_Global_Init()!=SUCCESS)
		return 4;
	
	lwip_init();						//初始化LWIP内核

	IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
	IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
	IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
	
	//这里配置MAC地址 ethernetif_init不netif_set_link_up。和原子的在这个函数有netif_set_link_up
	Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&ethernet_input);//向网卡列表中添加一个网口
	
	if(Netif_Init_Flag==NULL)
	{
		return 5;//网卡添加失败 
	}
	else//网口添加成功后,设置netif为默认值,并且打开netif网口
	{
		netif_set_default(&lwip_netif); //设置netif为默认网口
		netif_set_up(&lwip_netif);		//打开netif网口
		 // netif_set_link_up(&lwip_netif);                   //开启网卡连接
	}
	
	//配置udp连接
	if(udp_config_init(&lwipdev,8089)!=ERR_OK)
			return 6;//配置udp错误
	
	return 0;//操作OK.
}   

其中的enet_clk_and_Mac_Reg_Config函数,里面重要的有注释

uint32_t LAN_8720_Class::enet_clk_and_Mac_Reg_Config()
{

    ErrStatus reval_state = ERROR;
    
    /* enable ethernet clock  */
    rcu_periph_clock_enable(RCU_ENET);
    rcu_periph_clock_enable(RCU_ENETTX);
    rcu_periph_clock_enable(RCU_ENETRX);
    
    /* reset ethernet on AHB bus */
    enet_deinit();

    reval_state = enet_software_reset();//先复位 MAC 所有内核寄存器复位。然后等待复位完成,最后MAC 内部寄存器正常工作
    if(ERROR == reval_state){
        return reval_state;
    }
		
		reval_state=SUCCESS;
		
  return reval_state;
}

enet_Global_Init函数:注释是关键。

//先不需要对内部enet初始化,和例程不一样,因为我们要对LAN8720初始化,
//而enet_init会对外部phy(LAN8720)读取,根据LAN8720来配置ENET_MAC_CFG 寄存器,需要一致。
uint32_t LAN_8720_Class::enet_Global_Init()
{
		u8 enet_init_flag=0;
	//再初始化一次.
	 enet_init_flag = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
	
//		printf("enet_init_flag2:%d\r\n",enet_init_flag);

//		printf("ENET_MAC_CFG2:%x\r\n",ENET_MAC_CFG);//寄存器是cc00 。全双工+100M
	return enet_init_flag;

}

到这里。基本配置基本都差不多了。
那些IO配置,LAN8720的配置我就不贴上,记得使能时钟就行。
syscfg_enet_phy_interface_config(SYSCFG_ENET_PHY_RMII);

UDP部分:

对UDP的初始化udp_config_init在最上面的函数中已经初始过了,和原子是一样的。

//UDP
u8 udp_config_init(lwip_dev *lwipx, u16 udpport)
{
	 	err_t err;
		struct ip_addr rmtipaddr;  	//远端ip地址
	
		udp_connect_Status = 0;
	
		udppcb=udp_new();

		if(udppcb)//创建成功
		{
     //  printf("udp_create_success\r\n");	
																	
			IP4_ADDR(&rmtipaddr,lwipx->remoteip[0],lwipx->remoteip[1],lwipx->remoteip[2],lwipx->remoteip[3]);

			//err=udp_connect(udppcb,IP_ADDR_ANY,UDP_PORT);//UDP客户端连接到指定IP地址和端口号的服务器
			err=udp_connect(udppcb,&rmtipaddr,udpport);//UDP客户端连接到指定IP地址和端口号的服务器

			if(err==ERR_OK)
			{
				err=udp_bind(udppcb,IP_ADDR_ANY,udpport);//绑定本地IP地址与端口号
				if(err==ERR_OK)	//绑定完成
				{
					udp_recv(udppcb,udp_demo_recv,NULL);//注册接收回调函数 
					udp_connect_Status = 1;
				}
				else
				{
					udp_connect_Status=0;
				}
			}
			else 
			{
				udp_connect_Status=0;	
			}				
		}
		else 
		{
			udp_connect_Status=0;
		}
		
		return err;
}

然后调用这两个函数,
Global_Lwip_comm_Obj->lwip_periodic_handle();
Global_Lwip_comm_Obj->lwip_pkt_handle();

有数据的话标志位会置1,然后判断标志位就可以读取数据了。

		//网络的,还要判断是否有数据来。
	 if(enet_rxframe_size_get()>0)
	 {
			Global_Lwip_comm_Obj->lwip_periodic_handle();
			Global_Lwip_comm_Obj->lwip_pkt_handle();
		
			/*****/
				//处理数据
		/************/
	 }
  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值