• 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

  • 相关阅读:
    单链表的学习
    数据库连接
    Oracle学习指南
    Java登录第一版
    LeetCode(141): Linked List Cycle
    LeetCode(24): Swap Nodes in Pairs
    LeetCode(2):Add Two Numbers
    LeetCode(83):Remove Duplicates from Sorted List
    LeetCode(21):Merge Two Sorted Lists
    LeetCode(234):Palindrome Linked List
  • 原文地址:https://www.cnblogs.com/biaohc/p/6413547.html
Copyright © 2020-2023  润新知