koorio.com
海量文库 文档专家
当前位置:首页 >> 信息与通信 >>

单片机驱动DM9000网卡芯片

单片机驱动 DM9000 网卡芯片(详细调试过程)【上】 2009-03-04 11:13 和其它网卡芯片不同,DM9000 系列网卡芯片在嵌入式开发板上很常见,尤其是有关 ARM-Linux 的开发 板上的网络连接部分几乎都是采用该芯片完成的。当然,其它网卡芯片,如 RTL8019 的应用也很常见, 在很多开发板上得到应用然而 RTL8019 的介绍在网上可以找到非常详细的介绍,尤其是用单片机对其做 底层驱动的介绍非常丰富。下面的网站就介绍了用 AVR 驱动 RTL8019 网卡芯片的非常详细的过程,有兴 趣的朋友可以参考一下。 http://members.home.nl/bzijlstra/software/examples/RTL8019as.htm AVR 驱动 RTL8019 网 卡芯片的详细介绍。 言归正传。在网上也能找到许多关于 DM9000 网卡芯片的介绍,然而这些介绍大多是关于 Linux 或 Win CE 下的驱动程序或移植,很少有介绍单片机驱动 DM9000 的例子。 因此我在这里把我调试 DM9000E 的 过程详细说明一下,仅供参考。 本文主要介绍单片机驱动 DM9000E 网卡芯片的详细过程。从网卡电路的连接,到网卡初始化相关程序调 试,再到 ARP 协议的实现,一步一步详细介绍调试过程。如果有时间也会把 UDP 和 TCP 通讯实验过程写 出来。当然,会用单片机编写 DM9000 的驱动,再想编写 ARM 下的 Linux 的驱动就容易的多了。在调试 之前,应该先参考两份技术文档,可以从下面网站中下载。 DM9000E.pdf 芯片数据资料)和 DM9000 Application Notes Ver 1_22 061104.pdf 应用手册) ( ( http://www.davicom.com.tw 或者 DM9000 Datasheet VF03: http://www.davicom.com.tw/userfile/24247/DM9000-DS-F03-041906_1.pdf DM9000A Datasheet: http://www.davicom.com.tw/userfile/24247/DM9000A-DS-F01-101906.pdf DM9000 Application Notes V1.22 http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9000_Application_Notes_V

一、电路连接 DM9000E 网卡芯片支持 8 位、16 位、32 位模式的处理器,通过芯片引脚 EEDO(65 脚)和 WAK EUP(79 脚)的复位值设置支持的处理器类型,如 16 位处理器只需将这两个引脚接低电平即可,其中 W AKEUP 内部有 60K 下拉电阻,因此可悬空该引脚,或作为网卡芯片唤醒输出用。其它型号请参考相应的 数据手册。

图 1 DM9000 引脚 如图所示,对处理器驱动网卡芯片来说,我们比较关心的有以下几个引脚:IOR、IOW、AEN、CMD(S A2)、INT、RST,以及数据引脚 SD0-SD15-SD31 和地址引脚 SA4-SA9。其中,地址引脚配合 AEN 引脚来选通该网卡芯片,对于大多数的应用来说没有意义,因为在我们的应用中一般只用一个网卡芯片, 而这些地址引脚主要用于在多网卡芯片环境下选择其中之一。DM9000 工作的默认基地址为 0x300,这 里我们按照默认地址选择,将 SA9、SA8 接高电平,SA7-DA4 接低电平。多网卡环境可以根据 TXD0-T XD3 配置 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 端口,即写进寄存器 }

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);

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 初始化

{ 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 输出为低以激活内部 P HY。 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); /*以上完成了 DM9000 的复位操作*/

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[]自己定义一下吧, 个字节的 MA 6 C 地址 /*以上存储 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<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 protocol;

// 协议类型(0x0800 表示传输的是 IP 地址) // 硬件地址长度(6) //协议地址长度(4)

unsigned char hwlen; unsigned char protolen; uint16 opcode;

// 操作(1 表示 ARP 请求,2 表示 ARP 应答) //发送端 MAC 地址 //发送端 IP 地址 // 目的端 MAC 地址 // 目的端 IP 地址

unsigned char smac[6]; unsigned char sipaddr[4]; unsigned char dmac[6]; unsigned char dipaddr[4]; };

struct ip_hdr // 以太网头部+IP 首部结构 { struct eth_hdr ethhdr; unsigned char vhl, tos; uint16 len, ipid, ipoffset; unsigned char ttl, proto; uint16 ipchksum; // 以太网首部 //4 位版本号 4 位首部长度(0x45)

// 服务类型(0) // 整个 IP 数据报总字节长度 //IP 标识 //3 位标识 13 位偏移 // 生存时间(32 或 64) // 协议(1 表示 ICMP,2 表示 IGMP,6 表示 TCP,17 表示 UDP) // 首部校验和 // 源 IP

unsigned char srcipaddr[4], destipaddr[4]; };

// 目的 IP

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

单片机驱动 DM9000 网卡芯片(详细调试过程)【下】 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 三、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 请求报文的头部格式。

注意,以太网的传输存储是“大端格式”,即先发送高字节后发送低字节。例如,两个字节的数据 ,先发送高 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); /*字符串拷贝函数,文件要包含<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( 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 地 址 。 { ARPBUF->opcode = HON( 2 );//设置为 ARP 应答 memcpy(ARPBUF->dmac, ARPBUF->smac, 6); memcpy(ARPBUF->ethhdr.d_mac, ARPBUF->smac, 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 协议》第一卷,将会有很大帮助。希望这些 调试过程能为读者或多或少的提供些有用的信息,也欢迎大家和我一起讨论。 我的 Email:mengqx25@163.com


推荐相关:

单片机驱动DM9000网卡芯片.doc

单片机驱动DM9000网卡芯片 - 单片机驱动 DM9000 网卡芯片 和其它网


单片机驱动DM9000网卡芯片(详细调试过程)【下】.doc

单片机驱动DM9000网卡芯片(详细调试过程)【下】_工学_高等教育_教育专区。如题 单片机驱动 DM9000 网卡芯片(详细调试过程)【下】 2008 年 05 月 20 日 星期二 ...


单片机驱动DM9000网卡芯片(详细调试过程).doc

单片机驱动DM9000网卡芯片(详细调试过程) - 单片机驱动 DM9000 网卡芯片(详细调试过程)【上和下】 和其它网卡芯片不同,DM9000 系列网卡芯片在嵌入式开发板上很常见,...


DM9000单片机驱动代码.doc

DM9000单片机驱动代码 - 标题:DM9000 单片机驱动代码 2010-09-12 10:16:07 /***dm9000.c writer ...


单片机控制DM9000A.doc

摘要:为了实现嵌入式以太网通信,使用以太网控制芯片 DM9000A 和单片机 MSP430F5529,组成了嵌入式以太网接口,实现了网络通信,其中单片机完成自身以及以太 网控制芯片...


DM9000网卡调试教程_图文.doc

DIY_DE2 之 DM9000A 网卡调试系列例程(一)准备工作 一、摘要 根据最近一...DM9000网卡芯片详细调试... 22页 3下载券 单片机驱动DM9000网卡芯... 15...


基于ARM和DM9000的网卡接口设计与实现..doc

基于ARM和DM9000网卡接口设计与实现. - 基于 ARM 和 DM9000网卡接口设计与 实现 摘要:针对 ARMCPUS3C2410 的特点,设计开发了外围网卡接口平台,通过驱动 ...


经典DM9000驱动详解.doc

dm9000.C 中,还需要做两方面的工作:设置芯片 MAC 地址,使能 DM9000 的中断。...单片机驱动DM9000网卡芯... 22页 5下载券 DM9000中文手册 16页 5下载券 DM9...


dm9000驱动解析.pdf

DM9000芯片*/ dm9000_reset(db); /*初始化DM9000芯片*/ dm9000_init_dm9000(...单片机驱动DM9000网卡芯... 15页 1下载券 喜欢此文档的还喜欢 移植调试dm9000...


基于MSP430和DM9000的以太网接口设计_刘亚萍.pdf

总线与网络文章编号 :1001-9944(2010)07-0017-04 基于 MSP430 和 DM9000 的...通过单片机完成网卡芯片的初始化 、 数据的封装 、接收和发送控制等 ,而网卡...


嵌入式系统中常见的网卡驱动比较.doc

嵌入式系统中常见的网卡驱动比较 - 嵌入式系统中常见的网卡驱动比较(CS8900A,RTL8019,DM9000)


DM9000驱动移植详解及问题点.doc

DM9000驱动移植详解及问题点_计算机软件及应用_IT/计算机_专业资料。完全搞定...单片机驱动DM9000网卡芯... 23页 2下载券 U-BOOT DM9000驱动完全注... 16...


网卡驱动 (CS8900A,RTL8019,DM9000).doc

网卡驱动 (CS8900A,RTL8019,DM9000) - 1. CS8900A CS8900 芯片是 Cirrus Logic 公司生产的一种局域网处理芯片, 在嵌入 式领域中使用非常常见...


DM9000A网卡技术.doc

? ? ? ? DM9000A 驱动包含 5 个文件,如下所示: DM9000A.c:驱动程序文件,...dm9000a嵌入式网卡芯片故... 2页 1下载券 基于8051与DM9000A的应用... ...


DM9000A接口电路和驱动程序设计.pdf

DM9000A接口电路和驱动程序设计 - DM9000A 接口电路和驱动程序设计 介绍网络接口芯片 DM9000A 与 ARM 处理器 AT91RM9200 之间的硬件接口设计, 实现了在嵌 ...


基于S3C2440的DM9000网卡驱动的移植.txt

基于S3C2440的DM9000网卡驱动的移植 摘 要: 主要研究了基于Linux内核的网卡驱动的移植。Linux网络设备驱动程序的体系结构可以分为4层,首先分析了各层的具体功能实现,...


基于DM9000A的网络接口设计.pdf

基于DM9000A的网络接口设计 - 为了实现嵌入式以太网通信,使用以太网控制芯片DM9000A和单片机MSP430F5529,组成了嵌入式以太网接口,实现了网络通信,其中单片机完成自身...


基于DM9000A的网络接口设计.doc

单片机 MSP430F5529,组成了嵌入式以太网接口,实现了网络通信,其中单片机完成自身以及以太 网控制芯片的初始化、 数据的封包和收发控制, 而 DM9000A 芯片负责网络...


DM9000驱动经典详解.doc

dm9000.C 中,还需要做两方面的工作:设置芯片 MAC 地址,使能 DM9000 的中断。...DM9000网卡驱动分析 39页 2下载券 单片机驱动DM9000网卡芯... 22页 5下载券...


arm 驱动网卡 ethernet-v1.0教程.pdf

(iobase + 4) << 24; // 读取网卡芯片中的固定地址 //0x28,0x29,0x2A...单片机驱动DM9000网卡芯... 22页 5下载券 ARM7 中文手册 94页 5下载券 ...

网站首页 | 网站地图
All rights reserved Powered by 酷我资料网 koorio.com
copyright ©right 2014-2019。
文档资料库内容来自网络,如有侵犯请联系客服。zhit325@126.com