logo资料库

51单片机RTL8019AS网卡驱动程序.doc

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
51 单片机 RTL8019AS 网卡驱动程序 原作者::未知 添加时间:2008-01-07 原文发表:2008-01-07 人气:355 来源:网络 我的 SNMP 网管板使用了 RTL8019AS 10M ISA 网卡芯片接入以太网。选它的好处是: NE2000 兼容,软件移植性好;接口简单不用转换芯片如 PCI-ISA 桥;价格便宜 2.1$/片(我 的购入价为 22 元 RMB/片);带宽充裕(针对 51);较长一段时间内不会停产。8019 有 3 种配 置模式:跳线方式、即插即用 P&P 方式、串行 Flash 配置方式。为了节省成本,我去掉了 9346 而使用 X5045 作为闪盘存储 MAC 地址和其他可配置信息。P&P 模式用在 PC 机中, 这里用不上。只剩下跳线配置模式可用,它的电路设计参考 REALTEK 提供的 DEMO 板图 纸。一天时间就可以完成,相对来说硬件设计比较简单。 与这部分硬件相对应的软件是网卡驱动。所谓驱动程序是指一组子程序,它们屏蔽了底层硬 件处理细节,同时向上层软件提供硬件无关接口。驱动程序可以写成子程序嵌入到应用程序 里(如 DOS 下的 I/O 端口操作和 ISR),也可以放在动态链接库里,用到的时候再动态调入以 便节省内存。在 WIN98 中,为了使 V86、WIN16、WIN32 三种模式的应用程序共存,提出 了虚拟机的概念,在 CPU 的配合下,系统工作在保护模式,OS 接管了 I/O、中断、内存访 问,应用程序不能直接访问硬件。这样提高了系统可靠性和兼容性,也带来了软件编程复杂 的问题。任何网卡驱动都要按 VXD 或 WDM 模式编写,对于硬件一侧要处理虚拟机操作、 总线协议(如 ISA、PCI)、即插即用、电源管理;上层软件一侧要实现 NDIS 规范。因此在 WIN98 下实现网卡驱动是一件相当复杂的事情。 我这里说的驱动程序特指实模式下的一组硬件芯片驱动子程序。从程序员的角度看,8019 工作流程非常简单,驱动程序将要发送的数据包按指定格式写入芯片并启动发送命令,8019 会自动把数据包转换成物理帧格式在物理信道上传输。反之,8019 收到物理信号后将其还 原成数据,按指定格式存放在芯片 RAM 中以便主机程序取用。简言之就是 8019 完成数据 包和电信号之间的相互转换:数据包<===>电信号。以太网协议由芯片硬件自动完成,对程 序员透明。驱动程序有 3 种功能:芯片初始化、收包、发包。 以太网协议不止一种,我用的是 802.3。它的帧结构如图 1 所示。物理信道上的收发操作均 使用这个帧格式。其中,前导序列、帧起始位、CRC 校验由硬件自动添加/删除,与上层软 件无关。值得注意的是,收到的数据包格式并不是 802.3 帧的真子集,而是如图 2 所示。明 显地,8019 自动添加了“接收状态、下一页指针、以太网帧长度(以字节为单位)”三个数据成 员(共 4 字节)。这些数据成员的引入方便了驱动程序的设计,体现了软硬件互相配合协同工 作的设计思路。当然,发送数据包的格式是 802.3 帧的真子集,如图 3 所示。
有了收发包的格式,如何发送和接收数据包呢?如图 4 所示,先将待发送的数据包存入芯片 RAM,给出发送缓冲区首地址和数据包长度(写入 TPSR、TBCR0,1),启动发送命令(CR=0x3E) 即可实现 8019 发送功能。8019 会自动按以太网协议完成发送并将结果写入状态寄存器。如 图 5 所示,接收缓冲区构成一个循环 FIFO 队列,PSTART、PSTOP 两个寄存器限定了循环 队列的开始和结束页,CURR 为写入指针,受芯片控制,BNRY 为读出指针,由主机程序控 制。根据 CURR==BNRY 1?可以判断是否收到新的数据包,新收到的数据包按图 2 格式存于 以 CURR 指出的地址为首址的 RAM 中。当 CURR==BNRY 时芯片停止接收数据包。如果 做过 FPGA 设计,用过 VHDL,可以想象到硬件芯片的工作原理。此处,设计 2 个 8bit 寄 存器和一个 2 输入比较器,当收到数据包时,接收状态机根据当前状态和比较器结果决定下 一个状态,如果 CURR=BNRY,进入停收状态;反之,CURR 按模增 1。8019 数据手册没 有给出硬件状态机实现方法,说明也很简略,往往要通过作实验的方法推理出工作过程。比 如,ISR 寄存器不只和中断有关,当接收缓冲溢出时,如果不清 ISR(写入 FFH),芯片将一 直停止接收。在流量较大时溢出经常发生,此时不清 ISR,就会导致网卡芯片死机。
明白了发送和接收数据包的原理,那么数据包又是怎样被主机写入芯片 RAM 和从 芯片 RAM 读出的呢?如图 6 所示,主机设置好远端 DMA 开始地址(RSAR0,1)和远 端 DMA 数据字节数(RBCR0,1),并在 CR 中设置读/写,就可以从远端 DMA 口寄存 器里读出芯片 RAM 里的数据/把数据写入芯片 RAM。 何谓本地/远端 DMA 呢?如图 7 所示,“远端”指 CPU 接口侧;“本地”指 8019 的硬件收发电路侧。没有更深的意思,与远近无关,仅仅为了区分主机和芯片硬 件两个接口端。这里的 DMA 与平时所说的 DMA 有点不同。RTL8019AS 的 local DMA 操作是由控制器本身完成的,而其 remote DMA 并不是在无主处理器的参与下, 数据能自动移到主处理器的内存中。remote DMA 指主机 CPU 给出起址和长度就 可以读写芯片 RAM,每操作一次 RAM 地址自动加 1。而普通 RAM 操作每次要先发 地址再处理数据,速度较慢。 在一些高档通信控制器上自带有 MAC 控制器,工作原理与 8019 的差不多,比如: Motorola 68360/MPC860T 内部的 CPM 带有以太网处理器,通过设置 BD 表,使软 件和硬件协同工作,它的缓冲区更大且可灵活配置。这些通信控制器的设计,体 现了软硬件互相融合协同工作的趋势:软件硬化(VHDL),硬件软化(DSP),希望 大家关注!
如图 7 所示,8019 以太网控制器以存储器(16K 双口 RAM)为核心,本地和远端控 制器并发操作。这种体系结构满足了数据带宽的需要。8019 拥有控制、状态、 数据寄存器,通过它们,51 单片机可以与 8019 通信。由于 51 资源紧张,在实 现 TCPIP 协议栈时不要进行内存块拷贝,建议(1)使用全局结构体变量,在内存 中只保存一个数据包拷贝,其他没有来得及处理的包保存在 8019 的 16K RAM 里; (2)使用查询方式而不用中断;(3)客户服务器模型中服务器工作于串行方式,并 发模式不适合 51 单片机。 芯片内部地址空间的分配如图 8 所示,其中 0x00-0x0B(工作于 8 位 DMA 模式)用 于存放本节点 MAC 地址,奇偶地址内容是重复放置的。如:MAC 地址 0000 1234 5678 存放在 0x00-0x0B 中为 000000001212343456567878,单地址和双地址的内容是 重复的.一般使用偶数地址的内容,这主要是为了同时适应 8 位和 16 位的 dma。 Prom 内容是网卡在上电复位的时候从 93C46 里读出来的。如果你没有使用 93C46, 就不要使用 Prom,那么使用了 93C46 后如何获得网卡的地址呢?有两种方法,一 是直接读 93C46,二是读 Prom。网卡 MAC 地址既不由 93C46 也不由 Prom 决定,而 是由 PAR0-PAR5 寄存器决定。Prom 只保存上电时从 9346 中读出的 MAC 地址(如 果有 93C46 的话),仅此而矣。 网卡 MAC 地址不是随便定义的,它的组成结构如图 9 所示。以太网的地址为 48 位,由 ieee 统一分配给网卡制造商,每个网卡的地址都必须是全球唯一的。共 6 个字节的长度。FF:FF:FF:FF:FF:FF 为广播地址,只能用在目的地址段,不能 作为源地址段。目的地址为广播地址的数据包,可以被一个局域网内的所有网卡 接收到。合法的以太网地址第 32 位组播标志必须为 0。例如: X0:XX:XX:XX:XX:XX X2:XX:XX:XX:XX:XX X4:XX:XX:XX:XX:XX X6:XX:XX:XX:XX:XX X8:XX:XX:XX:XX:XX
XA:XX:XX:XX:XX:XX XC:XX:XX:XX:XX:XX XE:XX:XX:XX:XX:XX 为合法以太网地址。上面的 X 代表 0-F 中的任一个。 地址 X1:XX:XX:XX:XX:XX X3:XX:XX:XX:XX:XX X5:XX:XX:XX:XX:XX X7:XX:XX:XX:XX:XX X9:XX:XX:XX:XX:XX XB:XX:XX:XX:XX:XX XD:XX:XX:XX:XX:XX XF:XX:XX:XX:XX:XX 为组播地址,只能作为目的地址,不能作为源地址。组播地址可以被支持该组播 地址的一组网卡接收到。组播地址主要用在视频广播,远程唤醒(通过发一个特 殊的数据包使网卡产生一个中断信号,启动电脑),游戏(多个人在局域网里联 机打游戏)里等。 以下是一些具体的组播地址: 地址范围 01:00:5E:00:00:00---01:00:5E:7F:FF:FF 用于 ip 地址的组播,其他组播地址 跟 tcp/ip 无关,不做介绍。 网卡可以接收以下 3 种地址的数据包: 第一种 目的地址跟自己的网卡地址是一样的数据包; 第二种 目的地址为 FF:FF:FF:FF:FF:FF 广播地址的数据包; 第三种 目的地址为跟自己的组播地址范围相同的数据包。 在以太网的应用当中,如果你希望你的数据包只发给一个网卡,目的地址用对方 的网卡地址; 如果你想把数据包发给所有的网卡,目的地址用广播地址; 如果你想把数据包发给一组网卡,目的地址用组播地址。 其他用到的寄存器: CR---命令寄存器 TSR---发送状态寄存器 ISR---中断状态寄存器 RSR---接收状态寄存器 RCR---接收配置寄存器 TCR---发送配置寄存器 DCR---数据配置寄存器 IMR---中断屏蔽寄存器 NCR---包发送期间碰撞次数 FIFO---环回检测后,查看 FIFO 内容 CNTR0---帧同步错总计数器 CNTR1---CRC 错总计数器 CNTR2---丢包总计数器 PAR0-5---本节点 MAC 地址 MAR0-7---多播地址匹配 建议:将图形中寄存器名称标注上页号和地址偏移(如:BNRY 0 页 0x03),打印 出此图,看图编程,直观且不容易出错。
备注:收缓冲区、发缓冲区、数据存储区在 16K 双口 RAM 里的安排由用户自行决 定,只要不引起冲突即可,以下源程序代码实现的只是其中的一种分配方案。 部分源程序清单: struct ethernet{ unsigned char status; //接收状态 unsigned char nextpage; //下一个页 unsigned int length; //以太网长度,以字节为单位 unsigned int destnodeid[3]; //目的网卡地址 unsigned int sourcenodeid[3]; //源网卡地址 unsigned int protocal; //下一层协议 unsigned char packet[1500]; //包的内容 }; void ne2000init()//ne2000 网卡初始化 { rtl8019as_rst(); reg00=0x21; //选择页 0 的寄存器,网卡停止运行,因为还没有初始化。 delay_ms(10); //延时 10 毫秒,确保芯片进入停止模式 //使芯片处于 mon 和 loopback 模式,跟外部网络断开 page(0); reg0a=0x00; reg0b=0x00; reg0c=0xE0; //monitor mode (no packet receive) reg0d=0xE2; //loop back mode //使用 0x40-0x4B 为网卡的发送缓冲区,共 12 页,刚好可以存储 2 个最大的以 太网包。 //使用 0x4c-0x7f 为网卡的接收缓冲区,共 52 页。 reg01=0x4C; //Pstart 接收缓冲区范围 reg02=0x80; //Pstop reg03=0x4C; //BNRY reg04=0x40; //TPSR 发送缓冲区范围 reg07=0xFF;/*清除所有中断标志位*/ reg0f=0x00;//IMR disable all interrupt reg0e=0xC8; //DCR byte dma 8 位 dma 方式 page(1); //选择页 1 的寄存器 reg07=0x4D; //CURR reg08=0x00; //MAR0 reg09=0x41; //MAR1 reg0a=0x00; //MAR2
reg0b=0x80; //MAR3 reg0c=0x00; //MAR4 reg0d=0x00; //MAR5 reg0e=0x00; //MAR6 reg0f=0x00; //MAR7 initNIC(); //初始化 MAC 地址和网络相关参数 //将网卡设置成正常的模式,跟外部网络连接 page(0); reg0c=0xCC; //RCR reg0d=0xE0; //TCR reg00=0x22; //这时让芯片开始工作? reg07=0xFF; //清除所有中断标志位 } void send_packet(union netcard *txdnet,unsigned int length)//ne2000 发 包子程序 {//发送一个数据包的命令,长度最小为 60 字节,最大 1514 字节需要发送的数据 包要先存放在 txdnet 缓冲区 unsigned char i; unsigned int ii; page(0); if(length<60) length=60; for(i=0;i<3;i ) txdnet->etherframe.sourcenodeid[i]=my_ethernet_address.words[i]; txd_buffer_select=!txd_buffer_select; if(txd_buffer_select) reg09=0x40 ; //txdwrite highaddress else reg09=0x46 ; //txdwrite highaddress reg08=0x00; //read page address low reg0b=length>>8; //read count high reg0a=length&0xFF; //read count low; reg00=0x12; //write dma, page0 for(ii=4;iibytes.bytebuf[ii]; for(i=0;i<6;i ){ //最多重发 6 次 for(ii=0;ii<1000;ii ) //检查 txp 为是否为低 if((reg00&0x04)==0) break;
if((reg04&0x01)!=0) break; //表示发送成功 reg00=0x3E; } if(txd_buffer_select) reg04=0x40; //txd packet start; else reg04=0x46; //txd packet start; reg06=length>>8; //high byte counter reg05=length&0xFF; //low byte counter reg00=0x3E; //to sendpacket; } bit recv_packet(union netcard *rxdnet)//ne2000 收包子程序 { unsigned char i; unsigned int ii; unsigned char bnry,curr; page(0); reg07=0xFF; bnry=reg03; //bnry page have read 读页指针 page(1); curr=reg07; //curr writepoint 8019 写页指针 page(0); if(curr==0) return 0; //读的过程出错 bnry=bnry ; if(bnry>0x7F) bnry=0x4C; if(bnry!=curr){ //此时表示有新的数据包在缓冲区里 //读取一包的前 18 个字节:4 字节的 8019 头部,6 字节目的地址,6 字节原地址,2 字节协议 //在任何操作都最好返回 page0 page(0); reg09=bnry; //read page address high reg08=0x00; //read page address low reg0b=0x00; //read count high reg0a=18; //read count low; reg00=0x0A; //read dma for(i=0;i<18;i ) rxdnet->bytes.bytebuf[i]=reg10; i=rxdnet->bytes.bytebuf[3]; //将长度字段的高低字节掉转 rxdnet->bytes.bytebuf[3]=rxdnet->bytes.bytebuf[2]; rxdnet->bytes.bytebuf[2]=i;
分享到:
收藏