为LWIP移植DM9000驱动

   以前设计了一个stm32F407+DM9000的板子,跑的是UIP网络协议栈,但在使用中遇到了各种问题,经过很多次补丁才算稳定,但性能还是不尽如人意。现在转来研究下LWIP,正好开发板有个freeRTOS+LWIP+DP83848的例程,今天把这个程序移植到stm32F407+DM9000网卡的这个板子上,以后可以做很多事情。

   其实对于任何一个网卡驱动,对于ARM来讲,都需要做三个事情:硬件初始化,接收一个数据包传递给协议栈,发送一个数据包到网卡。基于这个思路进行移植,就变得简单了。

   先看下LWIP的工作过程。LWIP需要OS的支持,这里freeRTOS可以为收发创建相应的线程。 LWIP首先需要调用LwIP_Init对外设以及协议栈进行初始化。在LwIP_Inittcpip_init会初始化网络协议栈,然后调用netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input)设置网络参数。其中包括网络描述符,ip地址,子网掩码,网关,以及硬件初始化函数以及数据包接收函数的函数指针。其中ethernetif_init就是对硬件的初始化函数。在工程里,建立与DM9000相关的文件夹,添加dm9k.c以及dm9k.h,这里面包含了dm9000aep的初始化以及收发包函数。

         (1) ethernetif_init其实调用了low_level_init,这个函数在ethernetif.c中,这个文件是协议栈层与底层交互的唯一一个文件,所有的移植都围绕这个源文件进行。在low_level_init中需要初始化MAC地址,屏蔽掉对DP83848的初始化过程,调用dm9k_init(netif->hwaddr)完成对STM32FSMC总线的初始化以及DM9000的初始化。因为DM9000的初始化函数在这个板子上原来的工程中已经调试过,所有的IO以及寄存器的配置都不需要修改。可以调用dm9k_debug_test来测试程序对DM9000寄存器的读写是否正确。

       (2)dm9k_init调用完成后,low_level_init创建了一个数据包接收线程:

       xTaskCreate(ethernetif_input, (signed char*) "Eth_if", netifINTERFACE_TASK_STACK_SIZE, NULL,netifINTERFACE_TASK_PRIORITY,NULL);

  可以看到在DP83848的接收程序里,数据包的接收是通过中断完成的,当中断发生时,中断服务程序会发一个信号量给唤醒线程,这个线程去DMA中将数据直接读出。在这里,由于硬件设计的原因,采用轮询DM9000网卡的方式,在low_level_input中添加

      len = dm9k_receive_packet(buffer)来接收一个数据包,如果不为空的话就返回数据包的长度,在low_level_input这个函数中可以看出对于接收到的数据,LWIP使用了一个struct pbuf *p链表来存储各个数据包,并使用pbuf_alloc(PBUF_RAW, len, PBUF_POOL);来从内存池中获取一块内存链,在默认情况下,PBUF_POOL_BUFSIZE设置的大小为512自己,如果len 小于512的话就分配一个内存块,如果大于的话就分配多个内存块,然后buffer中的数据依次拷贝到分配到的pbuf 内存块中,然后调用s_pxNetIf->input将数据包传入LWIP协议栈。

      (3)ethernetif.c中可以找到low_level_output,就是底层输出函数,这个函数由协议栈自动调用。可以看出,这个函数中创建了一个互斥型信号量来保护数据的发送过程,把dm9k_send_packet()放到这个函数的最后将数据包发出。

  经过这简单的几个步骤,就可以测试使用PC机对板子Ping了。在这个过程中,发现ping总是时断时续,原因其实就出在接收线程中,因为这个程序是轮询接收,一定要把轮询的时间间隔设置的很小才不容易丢包,也是这个程序的缺点。把轮询周期设置为1ms,连续ping就没问题了。这里只是测试,以后一定使用中断来接收。

你可能感兴趣的:(TCP/IP)