和其它网卡芯片不同,DM9000系列网卡芯片在嵌入式开发板上很常见,尤其是有关ARM-Linux的开发板上的网络衔接部分简直都是选用该芯片完结的。当然,其它网卡芯片,如RTL8019的运用也很常见,在许多开发板上得到运用可是RTL8019的介绍在网上能够找到十分具体的介绍,尤其是用单片机对其做底层驱动的介绍十分丰富。下面的网站就介绍了用AVR驱动RTL8019网卡芯片的十分具体的进程,有爱好的朋友能够参阅一下。
http://members.home.nl/bzijlstra/software/examples/RTL8019as.htmAVR驱动RTL8019网卡芯片的具体介绍。
言归正传。在网上也能找到许多关于DM9000网卡芯片的介绍,可是这些介绍大多是关于Linux或WinCE下的驱动程序或移植,很少有介绍单片机驱动DM9000的比如。因而我在这儿把我调试DM9000E的进程具体阐明一下,仅供参阅。
本文首要介绍单片机驱动DM9000E网卡芯片的具体进程。从网卡电路的衔接,到网卡初始化相关程序调试,再到ARP协议的完结,一步一步具体介绍调试进程。假如有时刻也会把UDP和TCP通讯试验进程写出来。当然,会用单片机编写DM9000的驱动,再想编写ARM下的Linux的驱动就简略的多了。在调试之前,应该先参阅两份技能文档,能够从下面网站中下载。
DM9000E.pdf(芯片数据材料)和 DM9000 Application Notes Ver 1_22 061104.pdf(运用手册)
http://www.davicom.com.tw
一、电路衔接
DM9000E网卡芯片支撑8位、16位、32位形式的处理器,经过芯片引脚EEDO(65脚)和WAKEUP(79脚)的复位值设置支撑的处理器类型,如16位处理器只需将这两个引脚接低电平即可,其间WAKEUP内部有60K下拉电阻,因而可悬空该引脚,或作为网卡芯片唤醒输出用。其它类型请参阅相应的数据手册。

图1 DM9000引脚
如图所示,对处理器驱动网卡芯片来说,咱们比较关心的有以下几个引脚:IOR、IOW、AEN、CMD(SA2)、INT、RST,以及数据引脚SD0-SD15-SD31和地址引脚SA4-SA9。其间,地址引脚合作AEN引脚来选通该网卡芯片,关于大多数的运用来说没有意义,由于在咱们的运用中一般只用一个网卡芯片,而这些地址引脚首要用于在多网卡芯片环境下挑选其间之一。DM9000作业的默许基地址为0x300,这儿咱们依照默许地址挑选,将SA9、SA8接高电平,SA7-DA4接低电平。多网卡环境能够依据TXD0-TXD3装备SA4-SA7来挑选不同的网卡,这儿不做介绍,有爱好的朋友请参阅运用手册和数据手册。数据引脚SD0-SD31则依据前面所讲的装备处理器形式与处理器的数据总线进行挑选衔接即可,没用到的引脚悬空。那么,除了地址、数据引脚外,剩余的与处理器有关引脚对咱们来说及其重要了,而与处理器无关的引脚,只需依照运用手册衔接即可。
IOR和IOW是DM9000的读写挑选引脚,低电平有用,即低电平时进行读(IOR)写(IOW)操作;AEN是芯片选通引脚,低电平有用,该引脚为低时才干进行读写操作;CMD的指令/数据切换引脚,低电平时读写指令操作,高电平时读写数据操作。
图2 读时序
图3 写时序
这些引脚接口和其它单片机外围器材的引脚接口根本相同,其运用也相同。关于有总线接口的单片机来说,如51系列,ARM等直接衔接即可。关于没有总线接口的来说,如AVR mega32等能够直接用I/O引脚模仿总线时序进行衔接。衔接时要参阅读写时序,如上图所示。具体衔接电路,有时刻我再画出来,暂时先略了。
二、编写驱动程序
在这,我运用C言语编写驱动程序,这需求十分留意一点,即处理器所用的C编译器运用“大端格局”仍是“小端格局”,这能够在相应处理器的C编译器阐明上找到。一般比较常见的是小端格局。而关于8位处理器来说,在编写驱动程序时,能够不考虑,可是在编写网络协议的时分,一定好考虑,由于网络协议的格局是大端格局,而大部分编译器或许咱们习气的是小端格局,这一点需求留意。
在DM9000中,只要两个能够直接被处理器拜访的寄存器,这儿命名为CMD端口和DATA端口。事实上,DM9000中有许多操控和状况寄存器(这些寄存器在上一篇文章中有具体的运用阐明),但它们都不能直接被处理器拜访,拜访这些操控、状况寄存器的办法是:
(1)、将寄存器的地址写到CMD端口;
(2)、从DATA端口读写寄存器中的数据;
1、读、写寄存器
其实,INDEX端口和DATA端口的便是由芯片上的CMD引脚来区别的。低电平为INDEX端口,高电平为DATA端口。所以,要想完结读写寄存器,就必须先操控好CMD引脚。
若运用总线接口衔接DM9000的话,假定总线衔接后芯片的基地址为0x800300(24根地址总线),只需如下办法:
#define DM_ADD (*((volatile unsigned int *) 0x8000300))
#define DM_CMD (*((volatile unsigned int *) 0x8000304))
//向DM9000寄存器写数据
void dm9000_reg_write(unsigned char reg, unsigned char data)
{
udelay(20);//之前界说的奇妙级延时函数,这儿延时20us
DM_ADD = reg;//将寄存器地址写到INDEX端口
udelay(20);
DM_CMD = data;//将数据写到DATA端口,即写进寄存器
}
//从DM9000寄存器读数据
unsigned int dm9000_reg_read(unsigned char reg)
{
udelay(20);
DM_ADD = reg;
udelay(20);
return DM_CMD;//将数据从寄存器中读出
}
只得留意的是前面的两个宏界说DM_ADD和DM_CMD,界说的内容表明指向无符号整形变量的指针,在这儿0x800300是DM9000指令端口的地址,对它的赋值操作就相当于把数据写到该地址中,即把数据写到DM9000的指令端口中。读的道理也相同。这是一种很常见的宏界说,一般在处理器中界说通用寄存器也是这样界说的。
若没有总线接口的话,能够运用IO口模仿总线时序的办法完结寄存器的读写。这儿只阐明完结进程。首要将处理器的I/O端口与DM9000的IOR等引脚直接相连(电平匹配的状况下),又假定现已有宏界说“IOR”I/O端口操控DM9000的IOR引脚,其它端口操控DM9000引脚的命名相同,“PIO1”(依据处理器状况,能够是8位、16位或32位的I/O端口组成)操控数据端口。这样宏命名更直观些。写寄存器的函数如下:
void dm9000_reg_write(unsigned char reg, unsigned char data)
{
PIO1 = reg;
AEN = 0;
CMD = 0;
IOR = 1;
IOW = 0;
udelay(1);
AEN = 1;
IOW = 1;
udelay(20);
PIO1 = data;
AEN = 0;
CMD = 0;
IOR = 1;
IOW = 0;
udelay(1);
AEN = 1;
IOW = 1;
}
读寄存器的写法相似,这儿就略一下了。这一进程看上去有些杂乱,呵呵,其实履行起来也蛮有用率的,履行时刻差不多。这种模仿总线时序的办法实践并不杂乱,仅仅把总线办法下主动履行的进程手动的履行了一遍罢了。
在DM9000中,还有一些PHY寄存器,也称之为介质无关接口MII(Media Independent Interface)寄存器。对这些寄存器的操作会影响网卡芯片的初始化和网络衔接,这儿不对其进行操作,所以对这些寄存器的拜访办法这儿也略了(在上篇文章中有介绍)。操作不妥反而使网卡不能衔接到网络。
至此,咱们现已写好了两个最根本的函数:dm9000_reg_write()和dm9000_reg_read(),以及前面的宏界说DM_ADD和DM_CMD。下面将一向用到。
2、初始化DM9000网卡芯片。
初始化DM9000网卡芯片的进程,实质上便是填写、设置DM9000的操控寄存器的进程,这儿以程序为例进行阐明。其间寄存器的称号宏界说在DM9000.H中已界说好。
注:一下函数中unsigned char为一个字节unsigned int为两个字节
//DM9000初始化
void DM9000_init(void)
{
unsigned int i;
IO0DIR |= 1 << 8;
IO1CLR |= 1 << 8;
udelay(500000);
IO2SET |= 1 << 8;
udelay(500000);
IO1CLR |= 1 << 8;
udelay(500000);
/*以上部分是使用一个IO口操控DM9000的RST引脚,使其复位。这一步能够省掉,能够用下面的软件复位替代*/
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);//第2次软件复位,为了确保软件复位彻底成功。此进程是必要的。
udelay(30);
dm9000_reg_write(NCR, 0x00);
/*以上完结了DM9000的复位操作*/
dm9000_reg_write(NSR, 0x2c);//铲除各种状况标志位
dm9000_reg_write(ISR, 0x3f);//铲除一切中止标志位
/*以上铲除标志位*/
dm9000_reg_write(RCR, 0x39);//接纳操控
dm9000_reg_write(TCR, 0x00);//发送操控
dm9000_reg_write(BPTR, 0x3f);
dm9000_reg_write(FCTR, 0x3a);
dm9000_reg_write(RTFCR, 0xff);
dm9000_reg_write(SMCR, 0x00);
/*以上是功用操控,具体功用参阅上一篇文章中的阐明,或参阅数据手册的介绍*/
for(i=0; i<6; i++)
dm9000_reg_write(PAR + i, mac_addr[i]);//mac_addr[]自己界说一下吧,6个字节的MAC地址
/*以上存储MAC地址(网卡物理地址)到芯片中去,这儿没有用EEPROM,所以需求自己写进去*/
/*关于MAC地址的阐明,要参阅网络相关书本或材料*/
dm9000_reg_write(NSR, 0x2c);
dm9000_reg_write(ISR, 0x3f);
/*为了稳妥,上面有铲除了一次标志位*/
dm9000_reg_write(IMR, 0x81);
/*中止使能(或许说中止屏蔽),即敞开咱们想要的中止,封闭不想要的,这儿只敞开的一个接纳中止*/
/*以上一切寄存器的具体意义参阅上一篇文章,或参阅数据手册*/
}
这样就对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 { 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 试验时证明:每个数据包都是经过第2次的读取MRCMDX寄存器操作而获悉为有用数据包的,对初始化的寄存器做了屡次修正依然是此成果,可是用如下办法来完结,绝不会漏掉数据包。*/ ready = dm9000_reg_read(MRCMDX); // 第一次读取,一般读取到的是 00H if((ready & 0x0ff) != 0x01) { ready = dm9000_reg_read(MRCMDX); // 第2次读取,总能获取到数据 if((ready & 0x01) != 0x01) { if((ready & 0x01) != 0x00) //若第2次读取到的不是 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 { udelay(20); tem = DM_CMD; datas[i] = tem & 0x0ff; datas[i + 1] = (tem >> 8) & 0x0ff; } } else { } /*以上接纳数据包,留意的当地与发送数据包的当地相同*/ 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表明%&&&&&%MP,2表明IGMP,6表明TCP,17表明UDP) uint16 ipchksum; //首部校验和 unsigned char srcipaddr[4], //源IP destipaddr[4]; //意图IP }; 以上界说的三种首部结构,是依据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[5]是否为“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数据包类型,0表明ARP恳求,1表明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 HON(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8)) /*此宏界说是将小端格局存储的字(两个字节)转换成大端格局存储*/ void arp_request(void) //发送ARP恳求数据包 { //以太网首部 memcpy(ARPBUF->ethhdr.d_mac, host_mac_addr, 6); /*字符串复制函数,文件要包括 贝字符数*/ 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( 0 ); 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 0 : //处理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地址 。 { 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 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,是否是给自己的应对 { 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协议》第一卷,将会有很大协助。期望这些 调试进程能为读者或多火烧的供给些有用的信息,也欢迎咱们和我一同评论。
return 0;
ARPBUF->opcode = HON( 2 );//设置为ARP应对