zoukankan      html  css  js  c++  java
  • s5pv210 uboot——移植DM9000网卡驱动

    1:DM9000原理分析

    s5pv210接DM9000 底板图:

    重要的引脚有:IOR、IOW、AEN、CMD、INT、RST 以及数据引脚 SD0-SD15

    看数据手册这些引脚的作用:

    IOR:读选择引脚,低电平有效,即低电平是读;

    IOW:写选择引脚,低电平有效,即低电平写;

    CS (chip select)片选信号,s5pv210中有6个外加srom接口 bank0-bank5 CS引脚可以接 CS0-CS5代表与这6个bank中其中一个相连

    cmd寄存器是命令/数据切换引脚,低电平时读写命令操作,高电平时读写数据操作其中。地址引脚配合AEN引脚来选通该网卡芯片,对于大多数的应用来说没有意义,

    因为在我们的应用中一般只用一个网卡芯片,而这些地址引脚主要用于在多网卡芯片环境下选择其中之一。DM9000工作的默认基地址为0x300,这里我们按照默认地址选择,

    将SA9、SA8接高电平,SA7-DA4接低电平。

     上面这段话很关键:dm9000芯片数据总线16根,地址总线只连接了1根,连接的ADDR2,dm9000中的寄存器寻址是通过数据总线来寻址的,并不是通过地址总线寻址;

    连接这个地址总线的作用是配合cs总线进行片选,来选择网卡芯片;比如说有两个网卡芯片的可以在用一个cs线和地址线连接另外一个网卡芯片;

    当addr2为低电平时,data总线发送的是地址,前面提到了9、8地址线为1,4-7地址线为0,所以基地址是0x8800_0300 ,当addr2为0

    即0x8800_0300地址中的值为地址,这个地址中的值对应的是dm9000芯片中的寄存器,而addr2为1即0x8800_0304这个地址对应的位数据。

    总结一下:0x8800_0300 加上一定的偏移量 用来选择dm9000中的寄存器,而0x8800_0304这个寄存器用来给dm9000中的寄存器读写值;

    在配合IOW、IOR两个命令总线,我们就可以读取和写入dm9000寄存器,从而进行对寄存器的操控。

    这里内容参考http://blog.csdn.net/googlemi/article/details/8887871这篇博客;

      在DM9000中,只有两个可以直接被处理器访问的寄存器,这里命名为CMD端口和DATA端口。事实上,DM9000中有许多控制和状态寄存器(这些寄存器在上一篇文章中有详细的使用说明),

    但它们都不能直接被处理器访问,访问这些控制、状态寄存器的方法是:

    (1)、将寄存器的地址写到CMD端口;

    (2)、从DATA端口读写寄存器中的数据;

        1、读、写寄存器

        其实,INDEX端口和DATA端口的就是由芯片上的CMD引脚来区分的。低电平为INDEX端口,高电平为DATA端口。所以,要想实现读写寄存器,就必须先控制好CMD引脚。

        若使用总线接口连接DM9000的话,假设总线连接后芯片的基地址为0x800300(24根地址总线),只需如下方法:

    #define _REG_DM_ADDR    (*(volatile unsigned short *)0x800300)

    #define _REG_DM_DATA    (*(volatile unsigned short *)0x800304)

    DM9000寄存器写函数:

     void dm9k_reg_write(u16 reg, u16 data)

    {

      udelay(20);    //延时20um

      _REG_DM_ADDR = reg;

      udelay(20);    //延时20um

      _REG_DM_DATA = data;

    }

    这个函数即可把data中的值写入dm9000相应的寄存器中;

    unsigned int dm9k_reg_read(u16 reg)

    {

      udelay(20);    //延时20um 

      _REG_DM_ADDR = reg;

      udelay(20);

      return _REG_DM_DATA ;

    }

    下图为dm9000中的寄存器对应基地址的偏移量,可以看出dm9000中的寄存器都是8bit的;

     

    以上两个图是s5pv210 datamunu的网卡读写时序

    下面我们来初始化dm9000网卡:

    首先,初始化srom控制器:

    SROM_BW寄存器用来选择bank以及设置数据位宽

     

    void cs_init()

         /*设置bank1为16bit 其他位为0*/

      SROM_BW &= (~(0xf<<4));

      SROM_BW |=  (0x1<<4);

      /*设置时序*/

      SROM_BC1 =(0<<0)|(0x2<<4)|(0x2<<8)|(0x2<<12)|(0x2<<16)|(0x2<<24)|(0x2<<28);

    }

    /*dm9000的复位代码*/

     void dm9k_reset(void)

    { 

     dm9000_reg_write(GPCR, 0x01);//设置 GPCR(1EH) bit[0]=1,使DM9000的GPIO3为输出。

        dm9000_reg_write(GPR, 0x00);//GPR bit[0]=0 使DM9000的GPIO3输出为低以激活内部PHY。

        udelay(5000);//延时2ms以上等待PHY上电。

        dm9000_reg_write(NCR, 0x03);//软件复位

        udelay(30);//延时20us以上等待软件复位完成

        dm9000_reg_write(NCR, 0x00);//复位完成,设置正常工作模式。

        dm9000_reg_write(NCR, 0x03);//第二次软件复位,为了确保软件复位完全成功。此步骤是必要的。

        udelay(30);

        dm9000_reg_write(NCR, 0x00);

    }

    void dm9k_clear(void)

    {  

      dm9000_reg_write(NSR, 0x2c);//清除各种状态标志位

      dm9000_reg_write(ISR, 0x3f);//清除所有中断标志位

    }

    void dm9k_conreg_init(void)

    {

       dm9k_reg_write(RCR, 0x39);//接收控制

        dm9k_reg_write(TCR, 0x00);//发送控制

        dm9k_reg_write(BPTR, 0x3f);

        dm9k_reg_write(FCTR, 0x3a);

        dm9k_reg_write(RTFCR, 0xff);

        dm9k_reg_write(SMCR, 0x00);

    /*以上是功能控制,具体功能参考上一篇文章中的说明,或参考数据手册的介绍*/

    }

    void dm9k_mac_init(void)

    {

      int i = 0;

      unsigned char mac_addr[] = {0x44, 0x45, 0x53, 0x54, 0x00, 0x00};

      for (i=0; i<6; i++)

       dm9000_reg_write(PAR + i, mac_addr[i]);//6个字节的MAC地址

    }

    void dm9k_enalbe(void)

    {

      

      dm9000_reg_write(IMR, 0x81);

    /*中断使能(或者说中断屏蔽),即开启我们想要的中断,关闭不想要的,这里只开启的一个接收中断*/

    }

     void dm9k_init(void)

    {

      cs_init();    //片选、时序

      dm9k_reset();  //dm9000重启  

      dm9k_clear();  //清中断、清标志位

      dm9k_conreg_init();  //控制寄存器初始化

      dm9k_mac_init();    //mac地址初始化

      dm9k_enalbe();     //dm9000使能

    }

         3、发送、接收数据包

        同样,以程序为例,通过注释说明。

    //发送数据包

    //参数:datas为要发送的数据缓冲区(以字节为单位),length为要发送的数据长度(两个字节)。

    void sendpacket(unsigned char *datas, unsigned int length)

    {
        unsigned int len, i;
        
        dm9000_reg_write(IMR, 0x80);//先禁止网卡中断,防止在发送数据时被中断干扰
        
        len = length;

        dm9000_reg_write(TXPLH, (len>>8) & 0x0ff);

        dm9000_reg_write(TXPLL, len & 0x0ff);

    /*这两句是将要发送数据的长度告诉DM9000的寄存器*/

        DM_ADD = MWCMD;//这里的写法是针对有总线接口的处理器,没有总线接口的处理器要注意加上时序。

        for(i=0; i<len; i+=2)//16 bit mode

        {

            udelay(20);

            DM_CMD = datas[i] | (datas[i+1]<<8);

        }

    /*上面是将要发送的数据写到DM9000的内部SRAM中的写FIFO中,注意没有总线接口的处理器要加上适当的时序*/

    /*只需要向这个寄存器中写数据即可,MWCMD是DM9000内部SRAM的DMA指针,根据处理器模式,写后自动增加*/

        dm9000_reg_write(TCR, 0x01);//发送数据到以太网上

        while((dm9000_reg_read(NSR) & 0x0c) == 0);//等待数据发送完成

        udelay(20);

        dm9000_reg_write(NSR, 0x2c);//清除状态寄存器,由于发送数据没有设置中断,因此不必处理中断标志位

        dm9000_reg_write(IMR, 0x81);//DM9000网卡的接收中断使能

    }

        以上是发送数据包,过程很简单。而接收数据包确需要些说明了。DM9000从网络中接到一个数据包后,会在数据包前面加上4个字节,分别为“01H”、“status”(同RSR寄存器的值)、“LENL”(数据包长度低8位)、“LENH”(数据包长度高8位)。所以首先要读取这4个字节来确定数据包的状态,第一个字节“01H”表示接下来的是有效数据包,若为“00H”则表示没有数据包,若为其它值则表示网卡没有正确初始化,需要从新初始化。

        如果接收到的数据包长度小于60字节,则DM9000会自动为不足的字节补上0,使其达到60字节。同时,在接收到的数据包后DM9000还会自动添加4个CRC校验字节。可以不予处理。于是,接收到的数据包的最小长度也会是64字节。当然,可以根据TCP/IP协议从首部字节中出有效字节数,这部分在后面讲解。下面为接收数据包的函数。

    //接收数据包

    //参数:datas为接收到是数据存储位置(以字节为单位)

    //返回值:接收成功返回数据包类型,不成功返回0

    unsigned int receivepacket(unsigned char *datas)

    {

        unsigned int i, tem;

        unsigned int status, len;

        unsigned char ready;

        ready = 0;//希望读取到“01H”

        status = 0;//数据包状态

         len = 0; //数据包长度

    /*以上为有效数据包前的4个状态字节*/

        if(dm9000_reg_read(ISR) & 0x01)

        {

            dm9000_reg_write(ISR, 0x01);

        }

    /*清除接收中断标志位*/

    /***********************************************************************************/

    /*这个地方遇到了问题,下面的黑色字体语句应该替换成成红色字体,也就是说MRCMDX寄存器如果第一次读不到数据,还要读一次才能确定完全没有数据。

    在做 PING 实验时证明:每个数据包都是通过第二次的读取MRCMDX寄存器操作而获知为有效数据包的,对初始化的寄存器做了多次修改依然是此结果,但是用如下方法来实现,绝不会漏掉数据包。*/

        ready = dm9000_reg_read(MRCMDX); // 第一次读取,一般读取到的是 00H

        if((ready & 0x0ff) != 0x01)

        {

            ready = dm9000_reg_read(MRCMDX); // 第二次读取,总能获取到数据

            if((ready & 0x01) != 0x01)

             {

                if((ready & 0x01) != 0x00) //若第二次读取到的不是 01H 或 00H ,则表示没有初始化成功

                {

                     dm9000_reg_write(IMR, 0x80);//屏幕网卡中断

                     DM9000_init();//重新初始化

                     dm9000_reg_write(IMR, 0x81);//打开网卡中断

                }

                retrun 0;

             }

        }

    /* ready = dm9000_reg_read(MRCMDX); // read a byte without pointer increment

        if(!(ready & 0x01))

        {

             return 0;

        }*/

    /***********************************************************************************/

    /*以上表示若接收到的第一个字节不是“01H”,则表示没有数据包,返回0*/

        status = dm9000_reg_read(MRCMD);

        udelay(20);

        len = DM_CMD;

        if(!(status & 0xbf00) && (len < 1522))

        {

            for(i=0; i<len; i+=2)// 16 bit mode

            {

                udelay(20);

                tem = DM_CMD;

                datas[i] = tem & 0x0ff;

                datas[i + 1] = (tem >> 8) & 0x0ff;

            }

        }

        else

        {
            return 0;

        }

    /*以上接收数据包,注意的地方与发送数据包的地方相同*/

        if(len > 1000) return 0;

        if( (HON( ETHBUF->type ) != ETHTYPE_ARP) &&

            (HON( ETHBUF->type ) != ETHTYPE_IP) )

        {

            return 0;

        }

        packet_len = len;

    /*以上对接收到的数据包作一些必要的限制,去除大数据包,去除非ARP或IP的数据包*/
        
          
        return HON( ETHBUF->type ); //返回数据包的类型,这里只选择是ARP或IP两种类型

    }

        注意:上面的函数用到了一些宏定义,已经在头文件中定义过(补充在本文结尾处),这里说明一下:其中uint16定义为两个字节的变量,根据C编译器进行定义。

    unsigned char Buffer[1000];//定义了一个1000字节的接收发送缓冲区

    uint16 packet_len;//接收、发送数据包的长度,以字节为单位。

    struct eth_hdr //以太网头部结构,为了以后使用方便

    {

    unsigned char d_mac[6];   //目的地址

    unsigned char s_mac[6];   //源地址

    uint16 type;     //协议类型

    };

    struct arp_hdr //以太网头部+ARP首部结构

    {

    struct eth_hdr ethhdr;    //以太网首部

    uint16 hwtype;     //硬件类型(1表示传输的是以太网MAC地址)

    uint16 protocol;    //协议类型(0x0800表示传输的是IP地址)

    unsigned char hwlen;     //硬件地址长度(6)

    unsigned char protolen;    //协议地址长度(4)

    uint16 opcode;     //操作(1表示ARP请求,2表示ARP应答)

    unsigned char smac[6];    //发送端MAC地址

    unsigned char sipaddr[4];    //发送端IP地址

    unsigned char dmac[6];    //目的端MAC地址

    unsigned char dipaddr[4];    //目的端IP地址

    };

    struct ip_hdr //以太网头部+IP首部结构

    {

    struct eth_hdr ethhdr;    //以太网首部

    unsigned char vhl,      //4位版本号4位首部长度(0x45)

               tos;     //服务类型(0)

       uint16 len,      //整个IP数据报总字节长度

             ipid,           //IP标识

             ipoffset;     //3位标识13位偏移

    unsigned char ttl,             //生存时间(32或64)

              proto;         //协议(1表示ICMP,2表示IGMP,6表示TCP,17表示UDP)

    uint16 ipchksum;    //首部校验和

    unsigned char srcipaddr[4],    //源IP

                 destipaddr[4];   //目的IP

    };

    #define ETHBUF ((struct eth_hdr*)(&Buffer[0]))

    #define ARPBUF ((struct arp_hdr*)(&Buffer[0]))

    #define IPBUF ((struct ip_hdr *)(&Buffer[0]))

        以上定义的三种首部结构,是根据TCP/IP协议的相关规范定义的,后面会对ARP协议进行详细讲解。

    【上半部分完】

    。。。。。。。。。。。。。。。。。。。。。。。。。

    。。。。。。。。。。。。。。。。。。。。。。。。。

     4、验证初始化中的各个函数。

        下面我们来看一下,上面所写的初始化函数是否可用。以上我们写好了三个函数,分别为

    DM9000_init(),sendpacket()和receivepacket(),保存并命名为dm9000.c。既然我们要进行调试,当

    然要有结果输出,根据自己的处理器的情况写一个串口程序,这些函数是学某个单片机的基础,这里不

    做详细介绍,用到是时候会在函数里注释一下。

        接下来我们来写个主函数,新建C文件,命名为mian.c,填写如下函数:

    void main(void)

    {

        unsigned int i;

        unsigned char c;

        uart0_init();//初始化串口,调试时用到

        DM9000_init();//初始化网卡

        print_regs();/*通过串口,将DM9000中的寄存器打印出来,显示在超级终端上。此函数根据自己

    的处理器进行修改,功能仅仅是读DM9000寄存器dm9000_reg_read(),再通过串口打印出来而已*/

    }

        函数写好,保存文件,连接硬件,连接网线到电脑上或局域网上,运行结果如下图所示: 

    图4 显示寄存器值

        这里首先检查,各个控制寄存器是否是自己写进去的值,在检查状态寄存器是否正确,其中主要要

    看NSR寄存器的bit[6]是否为“1”,该位表示是否连接成功。本例中NSR的值为40H,括号里的数为对应

    的十进制数。

        下面我们将主函数改进一下,增加个中断接收函数,查看是否能接收到数据。

    void main(void)

    {

        unsigned int i;

        unsigned char c;

        uart0_init();//初始化串口,调试时用到

        DM9000_init();//初始化网卡

    /********************************************************************************/

    /*这一部分要根据自己的处理器情况,将DM9000的INT引脚连接到处理器的外部中断上,打开中断*/

    /********************************************************************************/

        sendpacket(60);/*我事先已经在Buffer[]中存储了ARP请求数据包,这里就直接发送了,以便接收

    ARP应答包。大家可以先参考后面讲的ARP协议,根据自己机器的情况,将数据事先存到Buffer[]中*/

        while(1);//等待中断

    }

    void int_issue(void) //中断处理函数,需要根据自己的处理器进行设置

    {

        unsigned int i;

        i = receivepacket(Buffer);//将数据读取到Buffer中。

    int_again :

        if(i == 0)

        {

            return;

        }

         else

         {

            print_buffer();//将接收到的所有数据打印出来

             while(1);//停止在这里等待观察,注意:实际应用中是不允许停止在中断中的。

         }

    /************************************************************************************/

    /*这里加上这一段,目的是判断中断期间是否接收到其它数据包。有则加以处理。不加也完全可以*/

    /* 根据自己的处理器,判断处理器是否还处在中断状态,若是则进行如下操作,不是则跳过该段。*/

        i = receivepacket(Buffer);

        if(i != 0)

        {

            goto int_again;

        }

    /************************************************************************************/

    }

        编译调试,运行结果如下: 

    图5 接收数据包中的数据

        这是一个ARP应答包,包含了我电脑上的MAC地址和局域网内的IP地址。反正我也不是啥重要人物,

    这里就不保密了,呵呵。

        如果一些顺利,到这里对DM9000网卡芯片的初始化工作就完成了。如果出现问题,首先要

    检查寄存器的值是否正确。可以将DM9000中的寄存器打印出来,查看到底是哪里的问题。如果打印出的

    值很混乱,在确保串口程序无误的前提下,查看硬件连接,以及寄存器读写时序是否正确,重复调试几

    次查找原因。

        三、ARP协议的实现

        1、ARP协议原理简述

        ARP协议(Address Resolution Protocol 地址解析协议),在局域网中,网络中实际传输的是“

    帧”,帧里面有目标主机的MAC地址。在以太网中,一个注意要和另一个主机进行直接通信,必须要知

    道目标主机的MAC地址。这个MAC地址就是标识我们的网卡芯片唯一性的地址。但这个目标MAC地址是如

    何获得的呢?这就用到了我们这里讲到的地址解析协议。所有“地址解析”,就是主机在发送帧前将目

    标IP地址转换成MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC

    地址,以保证通信的顺利进行。所以在第一次通信前,我们知道目标机的IP地址,想要获知目标机的

    MAC地址,就要发送ARP报文(即ARP数据包)。它的传输过程简单的说就是:我知道目标机的IP地址,

    那么我就向网络中所有的机器发送一个ARP请求,请求中有目标机的IP地址,请求的意思是目标机要是

    收到了此请求,就把你的MAC地址告诉我。如果目标机不存在,那么此请求自然不会有人回应。若目标

    机接收到了此请求,它就会发送一个ARP应答,这个应答是明确发给请求者的,应答中有MAC地址。我接

    到了这个应答,我就知道了目标机的MAC地址,就可以进行以后的通信了。因为每次通信都要用到MAC地

    址。

        ARP报文被封装在以太网帧头部中传输,如图为ARP请求报文的头部格式。 

    图6 用于以太网的ARP请求或应答分组格式

        注意,以太网的传输存储是“大端格式”,即先发送高字节后发送低字节。例如,两个字节的数据

    ,先发送高8位后发送低8位。所以接收数据的时候要注意存储顺序。

        整个报文分成两部分,以太网首部和ARP请求/应答。下面挑重点讲述。

    “以太网目的地址”字段:若是发送ARP请求,应填写广播类型的MAC地址FF-FF-FF-FF-FF-FF,意思是

    让网络上的所有机器接收到;

    “帧类型”字段:填写08-06表示次报文是ARP协议;

    “硬件类型”字段:填写00-01表示以太网地址,即MAC地址;

    “协议类型”字段:填写08-00表示IP,即通过IP地址查询MAC地址;

    “硬件地址长度”字段:MAC地址长度为6(以字节为单位);

    “协议地址长度”字段:IP地址长度为4(以字节为单位);

    “操作类型”字段:ARP数据包类型,1表示ARP请求,2表示ARP应答;

    “目的以太网地址”字段:若是发送ARP请求,这里是需要目标机填充的。


        2、ARP的处理程序

        ARP协议原理很简单,下面我们来编写ARP协议的处理函数。新建文件命名为arp.c,填写如下函数

    unsigned char mac_addr[6] = {*,*,*,*,*,*};

    unsigned char ip_addr[4] = { 192, 168, *, * };

    unsigned char host_ip_addr[4] = { 192, 168, *, * };

    unsigned char host_mac_addr[6]={ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

    unsigned char Buffer[1000];

    uint16 packet_len;

    /*这些全局变量,在前面将的文件中有些已经有过定义,这里要注意在前面加上“extern”关键字。“

    *”应该根据自己的机器修改*/

    #define ARPBUF ((struct arp_hdr*)(&Buffer[0]))

    #define HON(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))

    /*此宏定义是将小端格式存储的字(两个字节)转换成大端格式存储*/

    void arp_request(void) //发送ARP请求数据包

    {

    //以太网首部

    memcpy(ARPBUF->ethhdr.d_mac, host_mac_addr, 6);

    /*字符串拷贝函数,文件要包含<string.h>头文件。参数依次是,拷贝目标指针,拷贝数据源指针,拷

    贝字符数*/

    memcpy(ARPBUF->ethhdr.s_mac, mac_addr, 6);

    ARPBUF->ethhdr.type = HON( 0x0806 );

    /*小端格式的编译器,可以用HON()宏来转换成大端格式,如果你的编译器是大端格式,直接填写

    0x0806即可*/

    /*就是简单的按照协议格式填充,以下同*/

    //ARP首部

    ARPBUF->hwtype = HON( 1 );

    ARPBUF->protocol = HON( 0x0800 );

    ARPBUF->hwlen = 6;

    ARPBUF->protolen = 4;

    ARPBUF->opcode = HON( 1 );

    memcpy(ARPBUF->smac, mac_addr, 6);

    memcpy(ARPBUF->sipaddr, ip_addr, 4);

    memcpy(ARPBUF->dipaddr, host_ip_addr, 4);

    packet_len = 42;//14+28=42

    sendpacket( Buffer, packet_len );

    }

    注释:ARPBUF的宏定义和ARP首部结构,在前面已经讲过。同时注意执行该函数时中断的处理。这里没

    作处理。

        看上去很easy吧,下面函数实现接收ARP请求或接收ARP应答的处理。

    unsigned char arp_process(void)//ARP接收函数,成功返回1,否则返回0

    {

    //简单判断ARP数据包有无损坏,有损坏则丢弃,不予处理

    if( packet_len < 28 )//ARP数据长度为28字节为无效数据

    {

    return 0;

    }

    switch ( HON( ARPBUF->opcode ) )

    {

       case 1    : //处理ARP请求

             if( ARPBUF->dipaddr[0] == ip_addr[0] &&

                 ARPBUF->dipaddr[1] == ip_addr[1] &&

                 ARPBUF->dipaddr[2] == ip_addr[2] &&

                 ARPBUF->dipaddr[3] == ip_addr[3] )//判断是否是自己的IP,是否向自己询问MAC地址

             { 
                 ARPBUF->opcode = HON( 2 );//设置为ARP应答

                 memcpy(ARPBUF->dmac, ARPBUF->smac, 6);

                 memcpy(ARPBUF->ethhdr.d_mac, ARPBUF->smac, 6);

                 memcpy(ARPBUF->smac, mac_addr, 6);

                 memcpy(ARPBUF->ethhdr.s_mac, mac_addr, 6);

                 memcpy(ARPBUF->dipaddr, ARPBUF->sipaddr, 4);

                 memcpy(ARPBUF->sipaddr, ip_addr, 4);

                 ARPBUF->ethhdr.type = HON( 0x0806 );

                 packet_len = 42;

                 sendpacket( Buffer, packet_len );//发送ARP数据包

                 return 1;

             }

             else

             {

                 return 0;

             }

             break;

       case 2    : //处理ARP应答

             if( ARPBUF->dipaddr[0] == ip_addr[0] &&

                 ARPBUF->dipaddr[1] == ip_addr[1] &&

                 ARPBUF->dipaddr[2] == ip_addr[2] &&

                 ARPBUF->dipaddr[3] == ip_addr[3] )//再次判断IP,是否是给自己的应答

             {

              memcpy(host_mac_addr, ARPBUF->smac, 6);//保存服务器MAC地址

              return 1;

             }

             else

             {

                 return 0;

             }

             break;

    default     ://不是ARP协议

             return 0;

    }

    }

        根据ARP协议格式看这两个函数并不困难。于是我们又得到两个函数:arp_request()和

    arp_process()。

        3、ARP程序调试

        下面我们修改主函数和中断处理函数。

        将mian()函数中的“sendpacket(60);”语句换成“arp_request();”语句。

    void int_issue(void) //中断处理函数,需要根据自己的处理器进行设置

    {

        unsigned int i;

        i = receivepacket(Buffer);//将数据读取到Buffer中。

        if(i == 0)

        {

            return;

        }

         else

         {

             i = arp_process();

             if(i == 1)//判断是否是ARP协议

                 print_hostmacaddr();//打印目标机的MAC地址,就是用串口打印host_mac_addr[]中的6

    个字节

         }

    }

        保存运行调试。 

    图7 主机MAC地址

        至此,关于DM9000的调试过程就完成了。之后我还调试了UDP通讯、TCP通讯等,主要是关于协议的

    处理了,这里就不介绍了。有兴趣的朋友可以参看《TCP/IP协议》第一卷,将会有很大帮助。希望这些

    调试过程能为读者或多或少的提供些有用的信息,也欢迎大家和我一起讨论。

    我的Email:

    补充:增加了udp实现 http://hi.baidu.com/firstm25/blog/item/d22f3443e373781f73f05d9b.html

  • 相关阅读:
    IntelliJ IDEA 14.03 java 中文文本处理中的编码格式设置
    应聘感悟
    STL string分析
    CUDA SDK VolumeRender 分析 (1)
    BSP
    CUDA SDK VolumeRender 分析 (3)
    CUDA SDK VolumeRender 分析 (2)
    Windows软件发布时遇到的一些问题
    Ten Commandments of Egoless Programming (转载)
    复习下光照知识
  • 原文地址:https://www.cnblogs.com/biaohc/p/6413547.html
Copyright © 2011-2022 走看看