正点原子学习小组
直播中

donatello1996

7年用户 687经验值
擅长:处理器/DSP 控制/MCU RF/无线
私信 关注
[经验]

【正点原子STM32探索者V3开发板体验】【这帖更是重量级】移植厂家LWIP例程&UDP发送

      不得不说LWIP真是一个好用的库,ETH_PHY驱动也是一个好用的硬件以太网接口,以往F103系列是没有硬件以太网的,要升级到F107才有,但是F107资料少,型号冷门,芯片单价也贵,就算现在价格降下来了也比不上F407一根毛,毕竟要论资料多,F107可完全没法跟F407比,再说了F407性能也强啊!那么,既要用F103,又要用以太网接口的话怎么办呢?就只能通过SPI转传输层或者串口转传输层了,比如W5500就是SPI转传输层的,自封装传输层以下接口,通过SPI接口与主控芯片通信,性能尚可,但是肯定没法跟F407自带的硬件PHY比的了。

21.jpg 22.jpg

要移植正点原子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:
23.jpg

然后是初始化F407的ETH_PHY硬件接口:
  1. uint8_t ETH_PHY_Init(void)
  2. {      
  3.     uint8_t macaddress[6];
  4.     GPIO_InitTypeDef GPIO_Initure;
  5.     __HAL_RCC_ETH_CLK_ENABLE();
  6.     __HAL_RCC_GPIOA_CLK_ENABLE();
  7.     __HAL_RCC_GPIOB_CLK_ENABLE();
  8.     __HAL_RCC_GPIOC_CLK_ENABLE();
  9.     __HAL_RCC_GPIOD_CLK_ENABLE();
  10.     __HAL_RCC_GPIOG_CLK_ENABLE();
  11.     /*
  12.     ETH_MDIO -------------------------> PA2
  13.     ETH_MDC --------------------------> PC1
  14.     ETH_RMII_REF_CLK------------------> PA1
  15.     ETH_RMII_CRS_DV ------------------> PA7
  16.     ETH_RMII_RXD0 --------------------> PC4
  17.     ETH_RMII_RXD1 --------------------> PC5
  18.     ETH_RMII_TX_EN -------------------> PG11
  19.     ETH_RMII_TXD0 --------------------> PG13
  20.     ETH_RMII_TXD1 --------------------> PG14
  21.     ETH_RESET-------------------------> PD3*/
  22.     //PA1,2,7
  23.     GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
  24.     GPIO_Initure.Mode=GPIO_MODE_AF_PP;
  25.     GPIO_Initure.Pull=GPIO_NOPULL;
  26.     GPIO_Initure.Speed=GPIO_SPEED_HIGH;
  27.     GPIO_Initure.Alternate=GPIO_AF11_ETH;
  28.     HAL_GPIO_Init(GPIOA,&GPIO_Initure);
  29.     //PC1,4,5
  30.     GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5
  31.     HAL_GPIO_Init(GPIOC,&GPIO_Initure);
  32.     //PG11,13,14
  33.     GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;   //PG11,PG13,14
  34.     HAL_GPIO_Init(GPIOG,&GPIO_Initure);
  35.     GPIO_Initure.Pin=GPIO_PIN_3;                 //PD3
  36.     GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;
  37.     GPIO_Initure.Pull=GPIO_NOPULL;
  38.     GPIO_Initure.Speed=GPIO_SPEED_HIGH;
  39.     HAL_GPIO_Init(GPIOD,&GPIO_Initure);
  40.     HAL_NVIC_SetPriority(ETH_IRQn,1,0);
  41.     HAL_NVIC_EnableIRQ(ETH_IRQn);
  42.    
  43.     ETH_PHY_RST_L;
  44.     Delay_ms(50);   
  45.     ETH_PHY_RST_H;
  46.     macaddress[0]=lwipdev.mac[0];
  47.     macaddress[1]=lwipdev.mac[1];
  48.     macaddress[2]=lwipdev.mac[2];
  49.     macaddress[3]=lwipdev.mac[3];   
  50.     macaddress[4]=lwipdev.mac[4];
  51.     macaddress[5]=lwipdev.mac[5];
  52.     ETH_Handler.Instance = ETH;
  53.     ETH_Handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
  54.     ETH_Handler.Init.Speed=ETH_SPEED_100M;
  55.     ETH_Handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
  56.     ETH_Handler.Init.PhyAddress = PHY_ADDRESS;
  57.     ETH_Handler.Init.MACAddr = macaddress;
  58.     ETH_Handler.Init.RxMode = ETH_RXINTERRUPT_MODE;
  59.     ETH_Handler.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
  60.     ETH_Handler.Init.MediaInterface=ETH_MEDIA_INTERFACE_RMII;
  61.     HAL_ETH_Init(Ð_Handler);
  62. }


这个初始化硬件接口在不同厂家不同型号的单片机上都要做,在比如像NXP的LPC546XX系列等带PHY的型号,也有自己的一套初始化机制。

然后是lwip_comm.c函数,这里是lwip库与STM32用户层代码交互的位置,STM32代码需要直接调用lwip_comm.c中的函数以实现初始化,不需要DHCP功能可以注释DHCP相关宏:
  1. uint8_t lwip_comm_mem_malloc(void)
  2. {
  3.     uint32_t mempsize;
  4.     uint32_t ramheapsize;
  5.     mempsize=memp_get_memorysize();   
  6.     memp_memory=mymalloc(SRAMIN,mempsize);
  7.     ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;
  8.     ram_heap=mymalloc(SRAMIN,ramheapsize);
  9.     if(!(uint32_t)&memp_memory||!(uint32_t)&ram_heap)
  10.     {
  11.         lwip_comm_mem_free();
  12.         return 1;
  13.     }
  14.     return 0;   
  15. }
  16. void lwip_comm_mem_free(void)
  17. {     
  18.     myfree(SRAMIN,memp_memory);
  19.     myfree(SRAMIN,ram_heap);
  20. }
  21. void lwip_comm_default_ip_set(__lwip_dev *lwipx)
  22. {
  23.     uint32_t sn0;
  24.     sn0=*(volatile uint32_t*)(0x1FFF7A10);
  25.     lwipx->remoteip[0]=192;   
  26.     lwipx->remoteip[1]=168;
  27.     lwipx->remoteip[2]=1;
  28.     lwipx->remoteip[3]=11;
  29.     lwipx->mac[0]=2;
  30.     lwipx->mac[1]=0;
  31.     lwipx->mac[2]=0;
  32.     lwipx->mac[3]=(sn0>>16)&0XFF;
  33.     lwipx->mac[4]=(sn0>>8)&0XFFF;
  34.     lwipx->mac[5]=sn0&0XFF;
  35.     lwipx->ip[0]=192;   
  36.     lwipx->ip[1]=168;
  37.     lwipx->ip[2]=2;
  38.     lwipx->ip[3]=30;
  39.     lwipx->netmask[0]=255;   
  40.     lwipx->netmask[1]=255;
  41.     lwipx->netmask[2]=255;
  42.     lwipx->netmask[3]=0;
  43.     lwipx->gateway[0]=192;   
  44.     lwipx->gateway[1]=168;
  45.     lwipx->gateway[2]=1;
  46.     lwipx->gateway[3]=1;   
  47.     lwipx->dhcpstatus=0;
  48. }
  49. uint8_t lwip_comm_init(void)
  50. {
  51.   uint8_t retry=0;
  52.     struct netif *Netif_Init_Flag;
  53.     struct ip_addr ipaddr;
  54.     struct ip_addr netmask;
  55.     struct ip_addr gw;
  56.     if(ETH_Mem_Malloc())return 1;   
  57.     if(lwip_comm_mem_malloc())return 2;
  58.     lwip_comm_default_ip_set(&lwipdev);
  59.     printf("before ETH_PHY_Init\n");
  60.     while(ETH_PHY_Init())
  61.     {
  62.             retry++;
  63.             if(retry>5) {retry=0;return 3;}
  64.     }
  65.     lwip_init();
  66. //#if LWIP_DHCP
  67. //    ipaddr.addr = 0;
  68. //    netmask.addr = 0;
  69. //    gw.addr = 0;
  70. //#else
  71. //    IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
  72. //    IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
  73. //    IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
  74. //#endif
  75.     Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input);
  76.    
  77. //#if LWIP_DHCP
  78. //    lwipdev.dhcpstatus=0;
  79. //    dhcp_start(&lwip_netif);   
  80. //#endif
  81.    
  82.     if(Netif_Init_Flag == NULL)
  83.     {
  84.         return 4;
  85.     }
  86.     else
  87.     {
  88.         netif_set_default(&lwip_netif);
  89.         netif_set_up(&lwip_netif);
  90.     }
  91.     return 0;
  92. }   
24.jpg


再然后是ethernetif.c文件,此处的函数是ETH_PHY库直接与LWIP库交互的地方,也就是LWIP库用于发送的底层ETH IO接口函数会直接调用此处的接口函数,此处函数是需要厂家或者用户自己实现的,像原子的例程就用到了ETH自带的DMA通道和自编写的内存分配函数(malloc.c文件),此处的函数接口编写十分重要,直接决定PHY通信是否正常,因此能否正确编写此处的接口函数,是评判一个第三方厂家技术实力的重要依据,原子当然是毋庸置疑的,嵌入式工程师编写此处函数时,需要网络协议栈工程师指导下进行:
25.jpg 26.jpg
27.jpg


最后是SRAM外部内存颗粒的初始化函数和自编写malloc函数,因为LWIP例程用到了外部内存做存储池,使用方式是自编写的malloc函数接口:
28.jpg 29.jpg


当所有初始化工作完毕之后,就可以使用UDP初始化的相关结构体和接口函数了,这个属于应用层代码,必须自己写:
  1. struct udp_PCB * UDP_Init_Connect()
  2. {        
  3.     uint8_t err;
  4.     struct udp_pcb *udppcb;
  5.     struct ip_addr rmtipaddr;
  6.       udppcb = udp_new();
  7.     if(udppcb)
  8.     {
  9.         rmtipaddr.addr = 0x0202A8C0;
  10.         err = udp_connect(udppcb , &rmtipaddr , UDP_DEMO_PORT);
  11.         printf("udp_connect = %d\n" , err);
  12.         if(err == ERR_OK)
  13.         {
  14.             err = udp_bind(udppcb , IP_ADDR_ANY , UDP_DEMO_PORT);
  15.             printf("udp_bind = %d\n" , err);
  16.             if(err == ERR_OK)
  17.             {
  18.                 udp_recv(udppcb , udp_demo_recv , NULL);
  19.             }
  20.         }
  21.     }
  22.     return udppcb;
  23. }
  24. struct udp_pcb *udppcb;
  25. extern struct netif lwip_netif;
  26. void udp_demo_senddata(struct udp_pcb *upcb , uint8_t buf[] , uint32_t len)
  27. {
  28.     struct pbuf *ptr;
  29.     ptr = pbuf_alloc(PBUF_TRANSPORT , len , PBUF_POOL);
  30.     if(ptr)
  31.     {
  32.         ptr->payload=(void*)buf;
  33.         udp_send(upcb,ptr);
  34.         pbuf_free(ptr);
  35.     }
  36. }
程序运行之后,先ping一下IP看看能否通:
30.jpg

大功告成:
31.jpg


更多回帖

发帖
×
20
完善资料,
赚取积分