我的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;ietherframe.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;ii reg10=txdnet->bytes.bytebuf[ii];
for(i=0;i<6;i++){ //最多重发6次
for(ii=0;ii>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;ibytes.bytebuf[i]=reg10;
i=rxdnet->bytes.bytebuf[3]; //将长度字段的高低字节掉转
rxdnet->bytes.bytebuf[3]=rxdnet->bytes.bytebuf[2];
rxdnet->bytes.bytebuf[2]=i;
rxdnet->etherframe.length=rxdnet->etherframe.length-4; //去掉4个字节的crc
//表示读入的数据包有效
if(((rxdnet->bytes.bytebuf[0]&0x01)==0)||(rxdnet->bytes.bytebuf[1]>0x7f)||(rxdnet->bytes.bytebuf[1]bytes.bytebuf[2]>0x06)){
//接收状态错误,或者next_page_start错误或者长度错误,将丢弃所有数据包
page(1);
curr=reg07; //page1
page(0); //切换回page0
bnry=curr-1;
if(bnryetherframe.protocal==0x0800)||(rxdnet->etherframe.protocal==0x0806)){
//协议为ip或arp才接收
reg09=bnry; //read page address high
reg08=4; //read page address low
reg0b=rxdnet->etherframe.length>>8; //read count high
reg0a=rxdnet->etherframe.length&0xff; //read count low;
reg00=0x0a; //read dma
for(ii=4;iietherframe.length+4;ii++)
rxdnet->bytes.bytebuf[ii]=reg10;
}
bnry=rxdnet->bytes.bytebuf[1]-1;//next page start-1
if(bnry<0x4c) bnry=0x7f;
reg03=bnry; //write to bnry
return 1; //have new packet
}
}
return 0;
}
用于增强汽车设计的DC/DC降压转换器
为什么时钟抖动技术可以降低EMI呢?
《星际迷航》的粉丝们现在可以通过VR进入星际飞船,进行互动虚拟之旅
关于城市智能路灯,这几个方案有发言权
新型机械可调谐耿氏二极管波导振荡器介绍
51单片机RTL8019AS网卡驱动程序
三分钟了解惯性导航系统、加速度计、陀螺仪的工作原理
充电速度快4倍!60W双向Type-C/PD移动电源方案来袭
骤降95%!仅用两周,高空抛物乱象如何整治?
如何保持宽电压输入而供电电流能够保持稳定?
介绍一种四开关BUCK-BOOST的无缝切换空载方法
华为Mate9、荣耀V9对比评测:华为自家“双9之争”谁更厉害?
改期通知 | 关于2022自动驾驶地图与定位大会、自动驾驶与人机共架论坛改期的通知
基于Bang-Bang控制的温湿度调节系统
18650锂电池是几号电池_18650电池和五号电池有什么区别
关于使用高边开关驱动车灯的性能分析和应用
TCL联手京东 用科技打破生活边界
想要治好水,龙王也要拜托这朵云
MTP-16连接器推动者传输速率400G高速带宽升级
降压型线性恒流USB灯 5V供电LED台灯驱动方案分享