驱动网卡芯片DM9000调试过程及其具体的单片机程序.docx
- 文档编号:25350538
- 上传时间:2023-06-07
- 格式:DOCX
- 页数:25
- 大小:283.91KB
驱动网卡芯片DM9000调试过程及其具体的单片机程序.docx
《驱动网卡芯片DM9000调试过程及其具体的单片机程序.docx》由会员分享,可在线阅读,更多相关《驱动网卡芯片DM9000调试过程及其具体的单片机程序.docx(25页珍藏版)》请在冰豆网上搜索。
驱动网卡芯片DM9000调试过程及其具体的单片机程序
驱动网卡芯片DM9000A的过程及具体驱动程序
一、电路连接
DM9000E网卡芯片支持8位、16位、32位模式的处理器,通过芯片引脚EEDO(65脚)和WAKEUP(79脚)的复位值设置支持的处理器类型,如16位处理器只需将这两个引脚接低电平即可,其中WAKEUP内部有60K下拉电阻,因此可悬空该引脚,或作为网卡芯片唤醒输出用。
其它型号请参考相应的数据手册。
图1DM9000引脚
如图所示,对处理器驱动网卡芯片来说,我们比较关心的有以下几个引脚:
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等直接连接即可。
对于没有总线接口的来说,如AVRmega32等可以直接用I/O引脚模拟总线时序进行连接。
连接时要参考读写时序,如上图所示。
具体连接电路,有时间我再画出来,暂时先略了。
二、编写驱动程序
在这,我使用C语言编写驱动程序,这需要非常注意一点,即处理器所用的C编译器使用“大端格式”还是“小端格式”,这可以在相应处理器的C编译器说明上找到。
一般比较常见的是小端格式。
而对于8位处理器来说,在编写驱动程序时,可以不考虑,但是在编写网络协议的时候,一定好考虑,因为网络协议的格式是大端格式,而大部分编译器或者我们习惯的是小端格式,这一点需要注意。
在DM9000中,只有两个可以直接被处理器访问的寄存器,这里命名为CMD端口和DATA端口。
事实上,DM9000中有许多控制和状态寄存器(这些寄存器在上一篇文章中有详细的使用说明),但它们都不能直接被处理器访问,访问这些控制、状态寄存器的方法是:
(1)、将寄存器的地址写到CMD端口;
(2)、从DATA端口读写寄存器中的数据;
1、读、写寄存器
其实,INDEX端口和DATA端口的就是由芯片上的CMD引脚来区分的。
低电平为INDEX端口,高电平为DATA端口。
所以,要想实现读写寄存器,就必须先控制好CMD引脚。
若使用总线接口连接DM9000的话,假设总线连接后芯片的基地址为0x800300(24根地址总线),只需如下方法:
#defineDM_ADD(*((volatileunsignedint*)0x8000300))
#defineDM_CMD(*((volatileunsignedint*)0x8000304))
//向DM9000寄存器写数据
voiddm9000_reg_write(unsignedcharreg,unsignedchardata)
{
udelay(20);//之前定义的微妙级延时函数,这里延时20us
DM_ADD=reg;//将寄存器地址写到INDEX端口
udelay(20);
DM_CMD=data;//将数据写到DATA端口,即写进寄存器
}
//从DM9000寄存器读数据
unsignedintdm9000_reg_read(unsignedcharreg)
{
udelay(20);
DM_ADD=reg;
udelay(20);
returnDM_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端口组成)控制数据端口。
这样宏命名更直观些。
写寄存器的函数如下:
voiddm9000_reg_write(unsignedcharreg,unsignedchardata)
{
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(MediaIndependentInterface)寄存器。
对这些寄存器的操作会影响网卡芯片的初始化和网络连接,这里不对其进行操作,所以对这些寄存器的访问方法这里也略了(在上篇文章中有介绍)。
操作不当反而使网卡不能连接到网络。
至此,我们已经写好了两个最基本的函数:
dm9000_reg_write()和dm9000_reg_read(),以及前面的宏定义DM_ADD和DM_CMD。
下面将一直用到。
2、初始化DM9000网卡芯片。
初始化DM9000网卡芯片的过程,实质上就是填写、设置DM9000的控制寄存器的过程,这里以程序为例进行说明。
其中寄存器的名称宏定义在DM9000.H中已定义好。
注:
一下函数中unsignedchar为一个字节unsignedint为两个字节
//DM9000初始化
voidDM9000_init(void)
{
unsignedinti;
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);//GPRbit[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);
/*以上完成了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为要发送的数据长度(两个字节)。
voidsendpacket(unsignedchar*datas,unsignedintlength)
{
unsignedintlen,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 unsignedintreceivepacket(unsignedchar*datas) { unsignedinti,tem; unsignedintstatus,len; unsignedcharready; 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);//打开网卡中断 } retrun0; } } /*ready=dm9000_reg_read(MRCMDX);//readabytewithoutpointerincrement if(! (ready&0x01)) { return0; }*/ /***********************************************************************************/ /*以上表示若接收到的第一个字节不是“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 { return0; } /*以上接收数据包,注意的地方与发送数据包的地方相同*/ if(len>1000)return0; if((HON(ETHBUF->type)! =ETHTYPE_ARP)&& (HON(ETHBUF->type)! =ETHTYPE_IP)) { return0; } packet_len=len; /*以上对接收到的数据包作一些必要的限制,去除大数据包,去除非ARP或IP的数据包*/ returnHON(ETHBUF->type);//返回数据包的类型,这里只选择是ARP或IP两种类型 } 注意: 上面的函数用到了一些宏定义,已经在头文件中定义过,这里说明一下: 其中uint16定义为两个字节的变量,根据C编译器进行定义。 unsignedcharBuffer[1000];//定义了一个1000字节的接收发送缓冲区 uint16packet_len;//接收、发送数据包的长度,以字节为单位。 structeth_hdr//以太网头部结构,为了以后使用方便 { unsignedchard_mac[6]; //目的地址 unsignedchars_mac[6]; //源地址 uint16type; //协议类型 }; structarp_hdr//以太网头部+ARP首部结构 { structeth_hdrethhdr; //以太网首部 uint16hwtype; //硬件类型(1表示传输的是以太网MAC地址) uint16protocol; //协议类型(0x0800表示传输的是IP地址) unsignedcharhwlen; //硬件地址长度(6) unsignedcharprotolen; //协议地址长度(4) uint16opcode; //操作(1表示ARP请求,2表示ARP应答) unsignedcharsmac[6]; //发送端MAC地址 unsignedcharsipaddr[4]; //发送端IP地址 unsignedchardmac[6]; //目的端MAC地址 unsignedchardipaddr[4]; //目的端IP地址 }; structip_hdr//以太网头部+IP首部结构 { structeth_hdrethhdr; //以太网首部 unsignedcharvhl, //4位版本号4位首部长度(0x45) tos; //服务类型(0) uint16len, //整个IP数据报总字节长度 ipid, //IP标识 ipoffset; //3位标识13位偏移 unsignedcharttl, //生存时间(32或64) proto; //协议(1表示ICMP,2表示IGMP,6表示TCP,17表示UDP) uint16ipchksum; //首部校验和 unsignedcharsrcipaddr[4], //源IP destipaddr[4]; //目的IP }; 以上定义的三种首部结构,是根据TCP/IP协议的相关规范定义的,后面会对ARP协议进行详细讲解。 【上半部分完】 4、验证初始化中的各个函数。 下面我们来看一下,上面所写的初始化函数是否可用。 以上我们写好了三个函数,分别为 DM9000_init(),sendpacket()和receivepacket(),保存并命名为dm9000.c。 既然我们要进行调试,当 然要有结果输出,根据自己的处理器的情况写一个串口程序,这些函数是学某个单片机的基础,这里不 做详细介绍,用到是时候会在函数里注释一下。 接下来我们来写个主函数,新建C文件,命名为mian.c,填写如下函数: voidmain(void) { unsignedinti; unsignedcharc; uart0_init();//初始化串口,调试时用到 DM9000_init();//初始化网卡 print_regs();/*通过串口,将DM9000中的寄存器打印出来,显示在超级终端上。 此函数根据自己 的处理器进行修改,功能仅仅是读DM9000寄存器dm9000_reg_read(),再通过串口打印出来而已*/ } 函数写好,保存文件,连接硬件,连接网线到电脑上或局域网上,运行结果如下图所示: 图4显示寄存器值 这里首先检查,各个控制寄存器是否是自己写进去的值,在检查状态寄存器是否正确,其中主要要 看NSR寄存器的bit[5]是否为“1”,该位表示是否连接成功。 本例中NSR的值为40H,括号里
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 驱动 网卡 芯片 DM9000 调试 过程 及其 具体 单片机 程序