不得不说LWIP真是一个好用的库,ETH_PHY驱动也是一个好用的硬件以太网接口,以往F103系列是没有硬件以太网的,要升级到F107才有,但是F107资料少,型号冷门,芯片单价也贵,就算现在价格降下来了也比不上F407一根毛,毕竟要论资料多,F107可完全没法跟F407比,再说了F407性能也强啊!那么,既要用F103,又要用以太网接口的话怎么办呢?就只能通过SPI转传输层或者串口转传输层了,比如W5500就是SPI转传输层的,自封装传输层以下接口,通过SPI接口与主控芯片通信,性能尚可,但是肯定没法跟F407自带的硬件PHY比的了。
要移植正点原子 STM32探索者的LWIP实验例程,有两个方式,第一是可以下载已经停产的探索者V2的例程,上面的LWIP使用1.41版本的库,或者是直接下载探索者V3更新过的LWIP例程,两个例程的区别,第一是PHY芯片不同,探索者V2使用的PHY芯片是LAN8720,而探索者V3用的是国产的YT8512,PHY寄存器地址和值都不同,V2例程需要改过来才能用,第二就是探索者V3更新过的LWIP例程是用最新版本的LWIP库,很多结构体变量的成员结构修改了,我一时间适应不过来,毕竟从前几年开始就一直用老版本的LWIP库,用习惯了!要移植LWIP例程给自己的工程用,代码中需要有多处修改,我尽量把记得的地方记下来写到帖子里面。
首先是必须定义PHY硬件地址和硬件寄存器,要根据YT8512C的资料改过来,PHY地址默认是0,YT8512C的SR寄存器地址为0x11,连通状态位是0x2000:
然后是初始化F407的ETH_PHY硬件接口:
- uint8_t ETH_PHY_Init(void)
- {
- uint8_t macaddress[6];
- GPIO_InitTypeDef GPIO_Initure;
- __HAL_RCC_ETH_CLK_ENABLE();
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __HAL_RCC_GPIOB_CLK_ENABLE();
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_GPIOD_CLK_ENABLE();
- __HAL_RCC_GPIOG_CLK_ENABLE();
- /*
- ETH_MDIO -------------------------> PA2
- ETH_MDC --------------------------> PC1
- ETH_RMII_REF_CLK------------------> PA1
- ETH_RMII_CRS_DV ------------------> PA7
- ETH_RMII_RXD0 --------------------> PC4
- ETH_RMII_RXD1 --------------------> PC5
- ETH_RMII_TX_EN -------------------> PG11
- ETH_RMII_TXD0 --------------------> PG13
- ETH_RMII_TXD1 --------------------> PG14
- ETH_RESET-------------------------> PD3*/
- //PA1,2,7
- GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
- GPIO_Initure.Mode=GPIO_MODE_AF_PP;
- GPIO_Initure.Pull=GPIO_NOPULL;
- GPIO_Initure.Speed=GPIO_SPEED_HIGH;
- GPIO_Initure.Alternate=GPIO_AF11_ETH;
- HAL_GPIO_Init(GPIOA,&GPIO_Initure);
- //PC1,4,5
- GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5
- HAL_GPIO_Init(GPIOC,&GPIO_Initure);
- //PG11,13,14
- GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14; //PG11,PG13,14
- HAL_GPIO_Init(GPIOG,&GPIO_Initure);
- GPIO_Initure.Pin=GPIO_PIN_3; //PD3
- GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;
- GPIO_Initure.Pull=GPIO_NOPULL;
- GPIO_Initure.Speed=GPIO_SPEED_HIGH;
- HAL_GPIO_Init(GPIOD,&GPIO_Initure);
- HAL_NVIC_SetPriority(ETH_IRQn,1,0);
- HAL_NVIC_EnableIRQ(ETH_IRQn);
-
- ETH_PHY_RST_L;
- Delay_ms(50);
- ETH_PHY_RST_H;
- macaddress[0]=lwipdev.mac[0];
- macaddress[1]=lwipdev.mac[1];
- macaddress[2]=lwipdev.mac[2];
- macaddress[3]=lwipdev.mac[3];
- macaddress[4]=lwipdev.mac[4];
- macaddress[5]=lwipdev.mac[5];
- ETH_Handler.Instance = ETH;
- ETH_Handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
- ETH_Handler.Init.Speed=ETH_SPEED_100M;
- ETH_Handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
- ETH_Handler.Init.PhyAddress = PHY_ADDRESS;
- ETH_Handler.Init.MACAddr = macaddress;
- ETH_Handler.Init.RxMode = ETH_RXINTERRUPT_MODE;
- ETH_Handler.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
- ETH_Handler.Init.MediaInterface=ETH_MEDIA_INTERFACE_RMII;
- HAL_ETH_Init(Ð_Handler);
- }
复制代码
这个初始化硬件接口在不同厂家不同型号的 单片机上都要做,在比如像NXP的LPC546XX系列等带PHY的型号,也有自己的一套初始化机制。
然后是lwip_comm.c函数,这里是lwip库与STM32用户层代码交互的位置,STM32代码需要直接调用lwip_comm.c中的函数以实现初始化,不需要DHCP功能可以注释DHCP相关宏:
- uint8_t lwip_comm_mem_malloc(void)
- {
- uint32_t mempsize;
- uint32_t ramheapsize;
- mempsize=memp_get_memorysize();
- memp_memory=mymalloc(SRAMIN,mempsize);
- ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;
- ram_heap=mymalloc(SRAMIN,ramheapsize);
- if(!(uint32_t)&memp_memory||!(uint32_t)&ram_heap)
- {
- lwip_comm_mem_free();
- return 1;
- }
- return 0;
- }
- void lwip_comm_mem_free(void)
- {
- myfree(SRAMIN,memp_memory);
- myfree(SRAMIN,ram_heap);
- }
- void lwip_comm_default_ip_set(__lwip_dev *lwipx)
- {
- uint32_t sn0;
- sn0=*(volatile uint32_t*)(0x1FFF7A10);
- lwipx->remoteip[0]=192;
- lwipx->remoteip[1]=168;
- lwipx->remoteip[2]=1;
- lwipx->remoteip[3]=11;
- lwipx->mac[0]=2;
- lwipx->mac[1]=0;
- lwipx->mac[2]=0;
- lwipx->mac[3]=(sn0>>16)&0XFF;
- lwipx->mac[4]=(sn0>>8)&0XFFF;
- lwipx->mac[5]=sn0&0XFF;
- lwipx->ip[0]=192;
- lwipx->ip[1]=168;
- lwipx->ip[2]=2;
- lwipx->ip[3]=30;
- lwipx->netmask[0]=255;
- lwipx->netmask[1]=255;
- lwipx->netmask[2]=255;
- lwipx->netmask[3]=0;
- lwipx->gateway[0]=192;
- lwipx->gateway[1]=168;
- lwipx->gateway[2]=1;
- lwipx->gateway[3]=1;
- lwipx->dhcpstatus=0;
- }
- uint8_t lwip_comm_init(void)
- {
- uint8_t retry=0;
- struct netif *Netif_Init_Flag;
- struct ip_addr ipaddr;
- struct ip_addr netmask;
- struct ip_addr gw;
- if(ETH_Mem_Malloc())return 1;
- if(lwip_comm_mem_malloc())return 2;
- lwip_comm_default_ip_set(&lwipdev);
- printf("before ETH_PHY_Init\n");
- while(ETH_PHY_Init())
- {
- retry++;
- if(retry>5) {retry=0;return 3;}
- }
- lwip_init();
- //#if LWIP_DHCP
- // ipaddr.addr = 0;
- // netmask.addr = 0;
- // gw.addr = 0;
- //#else
- // 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]);
- //#endif
- Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input);
-
- //#if LWIP_DHCP
- // lwipdev.dhcpstatus=0;
- // dhcp_start(&lwip_netif);
- //#endif
-
- if(Netif_Init_Flag == NULL)
- {
- return 4;
- }
- else
- {
- netif_set_default(&lwip_netif);
- netif_set_up(&lwip_netif);
- }
- return 0;
- }
复制代码
再然后是ethernetif.c文件,此处的函数是ETH_PHY库直接与LWIP库交互的地方,也就是LWIP库用于发送的底层ETH IO接口函数会直接调用此处的接口函数,此处函数是需要厂家或者用户自己实现的,像原子的例程就用到了ETH自带的DMA通道和自编写的内存分配函数(malloc.c文件),此处的函数接口编写十分重要,直接决定PHY通信是否正常,因此能否正确编写此处的接口函数,是评判一个第三方厂家技术实力的重要依据,原子当然是毋庸置疑的,嵌入式工程师编写此处函数时,需要网络协议栈工程师指导下进行:
最后是SRAM外部内存颗粒的初始化函数和自编写malloc函数,因为LWIP例程用到了外部内存做存储池,使用方式是自编写的malloc函数接口:
当所有初始化工作完毕之后,就可以使用UDP初始化的相关结构体和接口函数了,这个属于应用层代码,必须自己写:
- struct udp_PCB * UDP_Init_Connect()
- {
- uint8_t err;
- struct udp_pcb *udppcb;
- struct ip_addr rmtipaddr;
- udppcb = udp_new();
- if(udppcb)
- {
- rmtipaddr.addr = 0x0202A8C0;
- err = udp_connect(udppcb , &rmtipaddr , UDP_DEMO_PORT);
- printf("udp_connect = %d\n" , err);
- if(err == ERR_OK)
- {
- err = udp_bind(udppcb , IP_ADDR_ANY , UDP_DEMO_PORT);
- printf("udp_bind = %d\n" , err);
- if(err == ERR_OK)
- {
- udp_recv(udppcb , udp_demo_recv , NULL);
- }
- }
- }
- return udppcb;
- }
- struct udp_pcb *udppcb;
- extern struct netif lwip_netif;
- void udp_demo_senddata(struct udp_pcb *upcb , uint8_t buf[] , uint32_t len)
- {
- struct pbuf *ptr;
- ptr = pbuf_alloc(PBUF_TRANSPORT , len , PBUF_POOL);
- if(ptr)
- {
- ptr->payload=(void*)buf;
- udp_send(upcb,ptr);
- pbuf_free(ptr);
- }
- }
复制代码
程序运行之后,先ping一下IP看看能否通:
大功告成:
|