先申明,本栏目用的都是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,ðernetif_init,ðernet_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();文章来源:https://www.toymoban.com/news/detail-407464.html
有数据的话标志位会置1,然后判断标志位就可以读取数据了。文章来源地址https://www.toymoban.com/news/detail-407464.html
//网络的,还要判断是否有数据来。
if(enet_rxframe_size_get()>0)
{
Global_Lwip_comm_Obj->lwip_periodic_handle();
Global_Lwip_comm_Obj->lwip_pkt_handle();
/*****/
//处理数据
/************/
}
到了这里,关于GD32F470之网络lwip+UDP配置+lan8720芯片的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!