配色: 字号:
《ARM9嵌入式系统设计基础教程》第7章嵌入式系统网络接口
2023-05-24 | 阅:  转:  |  分享 
  
第7章 嵌入式系统网络接口 7.1 以太网接口 嵌入式系统通常使用的以太网协议是IEEE802.3标准。从硬件的角度看,802.3模型层
间结构如图7.1.1所示,以太网接口电路主要由媒质接入控制MAC控制器和物理层接口(Physical Layer,PHY)两大部分
构成。图7.1.1 802.3模型层间结构1.传输编码在802.3版本的标准中,没有采用直接的二进制编码(即用0V表示“0”,用
5V表示“1”),而是采用曼彻斯特编码(Manchester Encoding)或者差分曼彻斯特编码(Differential M
anchester Encoding),不同编码形式如图7.1.2所示。图7.1.2 不同编码形式其中:曼彻斯特编码的规律是:每
位中间有一个电平跳变,从高到低的跳变表示为“0”,从低到高的跳变表示为“1”。差分曼彻斯特编码的规律是:每位的中间也有一个电平跳变
,但不用这个跳变来表示数据,而是利用每个码元开始时有无跳变来表示“0”或“1”,有跳变表示“0”,无跳变表示“1”。曼彻斯特编码和
差分曼彻斯特编码相比,前者编码简单,后者能提供更好的噪声抑制性能。在802.3系统中,采用曼彻斯特编码,其高电平为+0.85V,低
电平信号为-0.85V,这样指令信号电压仍然是0V。2.802.3Mac层的帧802.3 Mac层的以太网的物理传输帧如表7.1.
1所示。表7.1.1 802.3帧的格式● PR:同步位,用于收发双方的时钟同步,同时也指明了传输的速率,是56位的的二进制数
101010101010…,最后2位是10。 ● SD:分隔位,表示下面跟着的是真正的数据而不是同步时钟,为8位的1010101
1。 ● DA:目的地址,以太网的地址为48位(6个字节)二进制地址,表明该帧传输给哪个网卡。如果为FFFFFFFFFFFF,则
是广播地址。广播地址的数据可以被任何网卡接收到。 ● SA:源地址,48位,表明该帧的数据是哪个网卡发的,即发送端的网卡地址,同
样是6个字节。● TYPE:类型字段,表明该帧的数据是什么类型的数据,不同协议的类型字段不同。如:0800H表示数据为IP包,0
806H表示数据为ARP包,814CH是SNMP包,8137H为IPX/SPX包。小于0600H的值是用于IEEE802的,表示数
据包的长度。● DATA:数据段,该段数据不能超过1500B。因为以太网规定整个传输包的最大长度不能超过1514E(14B为DA
,SA,TYPE)。● PAD:填充位。由于以太网帧传输的数据包最小不能小于60B,除去(DA、SA、TYPE的14B),还必须
传输46B的数据,当数据段的数据不足46B时,后面通常是补0(也可以补其他值)。● FCS:32位数据校验位。32位的CRC校验
,该校验由网卡自动计算,自动生成,自动校验,自动在数据段后面填入。不需要软件管理。● 通常,PR、SD、PAD、FCS这几个数据
段都是网卡(包括物理层和Mac层的处理)自动产生的,剩下的DA、SA、TYPE、DATA这4个段的内容是由上层的软件控制的。3.以
太网数据传输的特点● 所有数据位的传输由低位开始,传输的位流是用曼彻斯特编码。● 以太网是基于冲突检测的总线复用方法,冲突退避
算法是由硬件自动执行的。● 以太网传输的数据段的长度,DA+SA+TYPE+DATA+PAD最小为60B,最大为1514B。●
通常的以太网卡可以接收3种地址的数据,一个是广播地址,一个是多播地址(或者叫组播地址,在嵌入式系统中很少用到),一个是它自己的地
址。但有时,用于网络分析和监控,网卡也可以设置为接收任何数据包。● 任何两个网卡的物理地址都是不一样的,是世界上唯一的,网卡地址
由专门机构分配。不同厂家使用不同地址段,同一厂家的任何两个网卡的地址也是唯一的。根据网卡的地址段(网卡地址的前3个字节)可以知道网
卡的生产厂家。7.1.2 嵌入式以太网接口的实现方法在嵌入式系统中增加以太网接口,通常有如下两种方法实现:(1)嵌入式处理器+网
卡芯片这种方法只要把以太网芯片连接到嵌入式处理器的总线上即可。此方法通用性强,对嵌入式处理器没有特殊要求,不受处理器的限制,但是,
嵌入式处理器和网络数据交换通过外部总线(通常是并行总线)交换数据,速度慢,可靠性不高,电路板走线复杂。目前常见的以太网接口芯片,如
CS8900、RTL8019/8029/8039、DM9008及DWL650无线网卡等。(2)带有以太网接口的嵌入式处理器带有以太
网接口的嵌入式处理器通常是面向网络应用而设计的,要求嵌入式处理器有通用的网络接口(比如:MII接口),处理器和网络数据交换通过内部
总线,速度快。7.1.3 在嵌入式系统中主要处理的以太网协议TCP/IP是一个分层的协议,包含有用于层、传输层、网络层、数据链路
层、物理层等。每一层实现一个明确的功能,对应一个或者几个传输协议。每层相对于它的下层都作为一个独立的数据包来实现。典型的分层和每层
上的协议如表7.1.2所示。表7.1.2 TCP/IP协议的典型分层和协议1.ARP(Address Resolation Pr
otocol,地址解析协议)网络层用32位的地址来标识不同的主机(即IP地址),而链路层使用48位的物理(MAC)地址来标识不同的
以太网或令牌环网接口。只知道目的主机的IP地址并不能发送数据帧给它,必须知道目的主机网络接口的物理地址才能发送数据帧。ARP的功能
就是实现从IP地址到对应物理地址的转换。源主机发送一份包含目的主机IP地址的ARP请求数据帧给网上的每个主机,称作ARP广播,目的
主机的ARP收到这份广播报文后,识别出这是发送端在询问它的IP地址,于是发送一个包含目的主机IP地址及对应的物理地址的ARP回答给
源主机。为了加快ARP协议解析的数据,每台主机上都有一个ARP cache存放最近的IP地址到硬件地址之间的映射记录。其中每一项的
生存时间(一般为20分钟),这样当在ARP的生存时间之内连续进行ARP解析的时候,不需要反复发送ARP请求了。2.ICMP(Int
ernet Control Messages Protocol,网络控制报文协议)ICMP是IP层的附属协议,IP层用它来与其他主
机或路由器交换错误报文和其他重要控制信息。ICMP报文是在IP数据包内部被传输的。在Linux或者Windows中,两个常用的网络
诊断工具ping和traceroute(Windows下是Tracert),其实就是ICMP协议。3.IP (Internet P
rotocol,网际协议)IP工作在网络层,是TCP/IP协议族中最为核心的协议。所有的TCP、UDP、ICMP及IGMP数据都以
IP数据包格式传输(IP封装在IP数据包中)。IP数据包最长可达65535字节,其中报头占32位。还包含各32位的源IP地址和32
位的目的IP地址。TTL(time-to-live,生存时间字段)指定了IP数据包的生存时间(数据包可以经过的最多路由器数)。TT
L的初始值由源主机设置,一旦经过一个处理它的路由器,它的值就减去1。当该字段的值为0时,数据包就被丢弃,并发送ICMP报文通知源主
机重发。IP提供不可靠、无连接的数据包传送服务,高效、灵活。不可靠(unreliable)的意思是它不能保证IP数据包能成功地到达
目的地。如果发生某种错误,IP有一个简单的错误处理算法:丢弃该数据包,然后发送ICMP消息报给信源端。任何要求的可靠性必须由上层来
提供(如TCP)。无连接(connectionless )的意思是IP并不维护任何关于后续数据包的状态信息。每个数据包的处理是相互
独立的。IP数据包可以不按发送顺序接收。如果一信源向相同的信宿发送两个连续的数据包(先是A,然后是B),每个数据包都是独立地进行路
由选择,可能选择不同的路线,因此B可能在A到达之前先到达。IP的路由选择:源主机 IP接收本地TCP、UDP、ICMP、GMP的数
据,生成IP数据包,如果目的主机与源主机在同一个共享网络上,那么IP数据包就直接送到目的主机上。否则就把数据包发往一默认的路由器上
,由路由器来转发该数据包。最终经过数次转发到达目的主机。IP路由选择是逐跳(hop-by-hop)进行的。所有的IP路由选择只为数
据包传输提供下一站路由器的IP地址。4.TCP(Transfer Control Protocol,传输控制协议)TCP协议是一个
面向连接的可靠的传输层协议。TCP为两台主机提供高可靠性的端到端数据通信。它所做的工作包括:① 发送方把应用程序交给它的数据分成合
适的小块,并添加附加信息(TCP头),包括顺序号,源、目的端口,控制、纠错信息等字段,称为TCP数据包。并将TCP数据包交给下面的
网络层处理。② 接受方确认接收到的TCP数据包,重组并将数据送往高层。5.UDP(User Datagram Protocol,用
户数据包协议)UDP协议是一种无连接不可靠的传输层协议。它只是把应用程序传来的数据加上UDP头(包括端口号,段长等字段),作为UD
P数据包发送出去,但是并不保证它们能到达目的地。可靠性由应用层来提供。因为协议开销少,和TCP协议相比,UDP更适用于应用在低端的
嵌入式领域中。很多场合如网络管理SNMP,域名解析DNS,简单文件传输协议TFTP,大都使用UDP协议。6. 端口 TCP和UDP
采用16位的端口号来识别上层的TCP用户,即上层应用协议,如FTP和TELNET等。常见的TCP/IP服务都用众所周知的1~255
之间的端口号。例如FTP服务的TCP端口号都是21,Telnet服务的TCP端口号都是23。TFTP(简单文件传输协议)服务的UD
P端口号都是69。256~1023之间的端口号通常都是提供一些特定的UNIX服务。TCP/IP临时端口分配1024~5 000之间
的端口号。7.1.4 网络编程接口BSD套接字(BSD Sockets)使用的最广泛的网络程序编程方法,主要用于应用程序的编写,用
于网络上主机与主机之间的相互通信。很多操作系统都支持BSD套接字编程。例如,UNIX、Linux、VxWorks、Windows的
Winsock基本上是来自BSD Sockets。套接字(Sockets)分为Stream Sockets和Data Socket
s。Stream Sockets是可靠性的双向数据传输,对应使用TCP协议传输数据;Data Sockets是不可靠连接,对应使用
UDP协议传输数。下面给出一个使用套接字接口的UDP通信的流程。UDP服务器端和一个UDP客户端通信的程序过程:(1)创建一个So
cket:sFd =socket(AF_INET,SOCK_DGRAM,0)(2)把Socket和本机的IP,UDP口绑定:bin
d (sFd,(struct sockaddr)& serverAddr,sockAddrSize)(3)循环等待,接收(rec
vfrom)或者发送(sendfrom)信息。(4)关闭Socket,通信终止:close(sFd)7.1.5 以太网的物理层接口
及编程大多数ARM都内嵌一个以太网控制器,支持媒体独立接口(Media Independent Interface MII)和带缓
冲DMA接口(Buffered DMA Interface,BDI),可在半双工或全双工模式下提供10M/100Mbps的以太网接
入。在半双工模式下,控制器支持CSMA/CD协议;在全双工模式下,支持IEEE802.3MAC控制层协议。ARM内部虽然包含了以太
网MAC控制,但并未提供物理层接口,因此,需外接一片物理层芯片以提供以太网的接入通道。常用的单口10M/100Mbps高速以太网物
理层接口器件均提供MII接口和传统7线制网络接口,可方便地与ARM接口。以太网物理层接口器件主要功能一般包括:物理编码子层、物理媒
体附件、双绞线物理媒体子层、10BASE-TX编码/解码器和双绞线媒体访问单元等。如CS8900、RTL8019/8029/803
9等。CS8900A是Cirrus Logic公司生产的16位以太网控制器,芯片内嵌片内RAM10BASE-T收发滤波器,直接IS
A总线接口。该芯片的物理层接口、数据传输模式和工作模式等都能根据需要而动态调整,通过内部寄存器的设置来适应不同的应用环境。CS89
00A采用3V供电电压,最大工作电流55mA,具有全双工通信方式,可编程发送功能,数据碰撞自动重发,自动打包及生成CRC校验码,可
编程接收功能,自动切换于DMA和片内RAM,提前产生中断便于数据帧预处理,数据流可降低CPU消耗,自动阻断错误包,可跳线控制EEP
ROM功能,启动编程支持无盘系统,边沿扫描和回环测试,待机和睡眠模式,支持广泛的软件驱动,工业级温度范围,LED指示连接状态和网络
活动情况等特点。采用TQFP-100封装。CS8900A内部结构方框图如图7.1.3所示。图7.1.3 CS8900A内部结构方
框图1.CS8900A工作原理CS8900A有两种工作模式:和I/O模式。当配置成MEMORY MODE模式操作时,CS8900A
的内部寄存器和帧缓冲区映射到主机内存中连续的4KB的块中,主机可以通过这个块直接访问CS8900A的内部寄存器和帧缓冲区。MEMO
RY 模式需要硬件上多根地址线和网卡相连。而在I/O MODE模式,对任何寄存器操作均要通过I/O端口0写入或读出。I/O MOD
E模式在硬件上实现比较方便,而且这也是芯片的默认模式。在I/O模式下,PacketPage存储器被映射到CPU的8个16位的I/O
端口上。在芯片被加电后,I/O基地址的默认值被置为300H。使用CS8900A作为以太网的物理层接口,在收到由主机发来的数据报后(
从目的地址域到数据域),侦听网络线路。如果线路忙,它就等到线路空闲为止,否则,立即发送该数据帧。在发送过程中,首先它添加以太网帧头
(包括前导字段和帧开始标志),然后生成CRC校验码,最后将此数据帧发送到以太网上。在接收过程中,它将从以太网收到的数据帧在经过解码
、去帧头和地址检验等步骤后缓存在片内。在CRC校验通过后,它会根据初始化配置情况,通知主机CS8900A收到了数据帧,最后,用某种
传输模式(FO模式、Memory模式、DMA模式)传到主机的存储区中。2.CS 8900A引脚端和功能CS 8900A的ISA总线
接口引脚端和功能如表7.1.3所示,EEPROM和引导编程接口引脚端和功能如表7.1.4所示,IOBASE-T接口引脚端和功能如表
7.1.5所示,附加单元接口AUD引脚端和功能如表7.1.6所示,通用引脚端和功能如表7.1.7所示。表7.1.3 ISA总线接
口引脚端和功能表7.1.4 EEPROM和引导编程接口引脚端和功能表7.1.5 IOBASE-T接口引脚端表7.1.6 附加
单元接口引脚端和功能表7.1.7 通用引脚端和功能3.电路连接 采用CS 8900A与S3C2410A连接构成的以太网接口电路如
图7.1.4所示。4.CS8900A的以太网接口驱动程序[于明](1)初始化函数初始化函数完成设备的初始化功能,由数据结构devi
ce中的init函数指针来调用。加载网络驱动模块后,就会调用初始化过程。首先通过检测物理设备的硬件特征来检测网络物理设备是否存在,
之后配置设备所需要的资源。比如,中断。这些配置完成之后就要构造设备的数据结构device,用检测到的数据初始化device中的相关
变量,最后向Linux内核中注册该设备并申请内存空间。函数定义为:static int __init init_cs8900a_s
3c2410(void) { struct net_local lp; int ret = 0; dev_cs
89x0.irq = irq; dev_cs89x0.base_addr = io; dev_cs89x0.init
= cs89x0_probe; dev_cs89x0.priv = kmalloc(sizeof(struct net_lo
cal), GFP_KERNEL);if (dev_cs89x0.priv = = 0) { printk(KERN_E
RR "cs89x0.c: Out of memory.\n"); return -ENOMEM; } me
mset(dev_cs89x0.priv, 0, sizeof(struct net_local)); lp = (stru
ct net_local )dev_cs89x0.priv; request_region(dev_cs89x0.base
_addr, NETCARD_IO_EXTENT, "cs8900a");spin_lock_init(&lp->lock); /
boy, they''d better get these right / if (!strcmp(media, "rj
45")) lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
else if (!strcmp(media, "aui")) lp->adapter_cnf = A_CNF_MEDI
A_AUI | A_CNF_AUI; else if (!strcmp(media, "bnc")) lp->
adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2; else lp->a
dapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;if (duplex= = ?1)
lp->auto_neg_cnf = AUTO_NEG_ENABLE; if (io = = 0) { pr
intk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n"); ret = -EPE
RM; goto out; } if (register_netdev(&dev_cs89x0) != 0)
{ printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
ret = -ENXIO; goto out; }out: if (ret) kfree(dev
_cs89x0.priv); return ret;}在这个网络设备驱动程序中,设备的数据结构device就是dev_cs8
9x0。探测网络物理设备是否存在,利用cs89x0_probe函数实现,通过调用register_netdrv(struct ne
t_devicedev)函数进行注册。与init函数相对应的cleanup函数在模块卸载时运行,主要完成资源的释放工作,如取消设
备注册、释放内存、释放端口等。函数定义为:static void __exit cleanup_cs8900a_s3c2410(v
oid) { if (dev_cs89x0.priv != NULL) { / Free up the priv
ate structure, or leak memory :-) / unregister_netdev(&dev
_cs89x0); outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);
kfree(dev_cs89x0.priv); dev_cs89x0.priv = NULL; / gets
re-allocated by cs89x0_probe1 / / If we don''t do this, we
can''t re-insmod it later. / release_region(dev_cs89x0.base
_addr, NETCARD_IO_EXTENT); }}(2)打开函数打开函数在网络设备驱动程序中是在网络设备被激活时调用
,即设备状态由down至up。函数定义为:static int net_open(struct net_device dev){
struct net_local lp = (struct net_local )dev->priv;int ret;
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IR
Q); ret = request_irq(dev->irq, &net_interrupt, SA_SHIRQ, "cs8
9x0", dev); if (ret) { printk("%s: request_irq(%d) failed\n",
dev->name, dev->irq); goto bad_out;}if (lp->chip_type = = CS
8900)writereg(dev, PP_CS8900_ISAINT, 0);else writereg(dev, PP_CS8
920_ISAINT, 0);writereg(dev, PP_BusCTL, MEMORY_ON);lp->linectl =
0; writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SE
RIAL_RX_ON | SERIAL_TX_ON);lp->rx_mode = 0; writereg(dev, PP_R
xCTL, DEF_RX_ACCEPT); lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ER
ROR_ENBL;if (lp->isa_config & STREAM_TRANSFER) lp->curr_rx_cfg |=
RX_STREAM_ENBL;writereg(dev, PP_RxCFG, lp->curr_rx_cfg); writ
ereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX
_OK_ENBL | TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_EN
BL | TX_16_COL_ENBL); writereg(dev, PP_BufCFG, READY_FOR_T
X_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | TX_COL_COUNT_OVRFLOW_EN
BL | TX_UNDERRUN_ENBL);writereg(dev, PP_BusCTL, readreg(dev, PP_B
usCTL) | ENABLE_IRQ); enable_irq(dev->irq); netif_start_que
ue(dev); DPRINTK(1, "cs89x0: net_open() succeeded\n"); retu
rn 0;bad_out: return ret;} 打开函数中对寄存器操作使用了两个函数:readreg和writereg
。readreg函数用来读取寄存器内容,writereg函数用来写寄存器。函数定义为:inline int readreg(str
uct net_device dev, int portno){outw(portno, dev->base_addr + AD
D_PORT);return inw(dev->base_addr + DATA_PORT);}inline void write
reg(struct net_device dev, int portno, int value) {outw(portno,
dev->base_addr + ADD_PORT);outw(value, dev->base_addr + DATA_PORT
);}(3)关闭函数关闭函数释放资源减少系统负担,设备状态有up转为down时被调用。函数定义为:static int net_c
lose(struct net_device dev){netif_stop_queue(dev);writereg(dev,
PP_RxCFG, 0);writereg(dev, PP_TxCFG, 0);writereg(dev, PP_BufCFG,
0);writereg(dev, PP_BusCTL, 0);free_irq(dev->irq, dev);/ Update
the statistics here. /return 0;}(4)发送函数首先,在网络设备驱动加载时,通过device域中的
init函数指针调用网络设备的初始化函数对设备进行初始化,如果操作成功,就可以通过device域中的open函数指针调用网络设备的
打开函数打开设备,再通过device域中的包头函数指针hard_header来建立硬件包头信息。最后,通过协议接口层函数dev_q
ueue_xmit调用device域中的hard_start_xmit函数指针来完成数据包的发送。如果发送成功,hard_star
t_xmit释放sk_buff,返回0。如果设备暂时无法处理,比如,硬件忙,则返回l。此时如果dev->tbusy置为非0,则系统
认为硬件忙,要等到dev->tbusy置0以后才会再次发送。tbusy的置0任务一般由中断完成。硬件在发送结束会产生中断,这时可以
把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。在CS8900A驱动程序中,网络设备的传输函数dev->har
d_start__xmit定义为net_send_ packet:static int net_send_packet(struc
t sk_buff skb, struct net_device dev){ struct net_local lp
= (struct net_local )dev->priv; writereg(dev, PP_BusCTL, 0x0)
; writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IR
Q); DPRINTK(3, "%s: sent %d byte packet of type %x\n", dev-
>name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | (skb
->data[ETH_ALEN+ETH_ALEN+1])); spin_lock_irq(&lp->lock); ne
tif_stop_queue(dev); / initiate a transmit sequence / writew
ord(dev, TX_CMD_PORT, lp->send_cmd); writeword(dev, TX_LEN_POR
T, skb->len); / Test to see if the chip has allocated memory
for the packet /if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW)
== 0){ spin_unlock_irq(&lp->lock); DPRINTK(1, "cs89x0:
Tx buffer not free!\n"); return 1; } / Write the cont
ents of the packet / writeblock(dev, skb->data, skb->len);
spin_unlock_irq(&lp->lock); dev->trans_start = jiffies; de
v_kfree_skb (skb); return 0;} (5)中断处理和接收函数网络设备接收数据通过中断实现,
当数据收到后,产生中断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来,填
充sk_buff中的一些信息。处理完后,如果是获得数据包,则执行数据接收子程序,该函数被中断服务程序调用。函数定义:static
void net_rx(struct net_device dev) { struct net_local lp = (
struct net_local )dev->priv; struct sk_buff skb; int stat
us, length; int ioaddr = dev->base_addr; status = inw(ioadd
r + RX_FRAME_PORT);if ((status & RX_OK) == 0){ count_rx_erro
rs(status, lp); return; } length = inw(ioaddr + RX_FRA
ME_PORT); / Malloc up new buffer. / skb = dev_alloc_skb(l
ength + 2);if (skb == NULL) { lp->stats.rx_dropped++; r
eturn; } skb_reserve(skb, 2),/ longword align L3 header /
skb->len = length; skb->dev = dev; readblock(dev, skb->dat
a, skb->len); DPRINTK(3, "%s: received %d byte packet of type
%x\n", dev->name, length, (skb->data[ETH_ALEN+ETH_ALEN]<<
8|skb->data[ETH_ALEN+ETH_ALEN+1]); skb->protocol=eth_type_tran
s(skb,dev); netif_rx(skb); dev->last_rx = jiffies; lp->s
tats.rx_packets++; lp->stats.rx_bytes += length;} 在net_rx()函数中
调用netif_rx()把数据传送到协议层。netif_rx()函数把数据放入处理队列,然后返回,真正的处理是在中断返回以后,这样
可以减少中断时间。调用netif_rx()后,驱动程序不能再存取数据缓冲区skb。netif_rx()函数在net/core/de
v.c中定义为:int netif_rx(struct sk_buff skb){int this_cpu = smp_proc
essor_id();struct softnet_data queue;unsigned long flags;if (skb
->stamp.tv_sec = = 0) do_gettimeofday(&skb->stamp);queue = &soft
net_data[this_cpu];local_irq_save(flags);netdev_rx_stat[this_cpu]
.total++;if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
if (queue->input_pkt_queue.qlen) {if (queue->throttle) goto drop;
enqueue: dev_hold(skb->dev);__skb_queue_tail(&queue->input_pkt_qu
eue,skb);cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);local_irq_re
store(flags);#ifndef OFFLINE_SAMPLEget_sample_stats(this_cpu);#en
difreturn softnet_data[this_cpu].cng_level; }if (queue->throttle)
{queue->throttle = 0;#ifdef CONFIG_NET_HW_FLOWCONTROLif (atomic_d
ec_and_test(&netdev_dropping)) netdev_wakeup();#endif}goto enqueu
e;}if (queue->throttle == 0){queue->throttle = 1;netdev_rx_stat[t
his_cpu].throttled++;#ifdef CONFIG_NET_HW_FLOWCONTROLatomic_inc(&
netdev_dropping);#endif}drop: netdev_rx_stat[this_cpu].dropped++;
local_irq_restore(flags);kfree_skb(skb);return NET_RX_DROP; }中断函数
net_interrupt在打开函数中申请,中断发生后,首先驱动中断管脚为高电平,然后主机读取CS8900A中的中断申请序列ISQ
值,以确定事件类型,根据事件类型做出响应。函数定义为:static void net_interrupt(int irq, voi
d dev_id, struct pt_regs regs){ struct net_device dev = de
v_id; struct net_local lp; int ioaddr, status; ioaddr =
dev->base_addr; lp = (struct net_local )dev->priv; while
((status = readword(dev, ISQ_PORT))){ DPRINTK(4, "%s: event=
%04x\n", dev->name, status); switch(status & ISQ_EVENT_MASK)
{ case ISQ_RECEIVER_EVENT:/ Got a packet(s). /net_rx(dev)
;break; case ISQ_TRANSMITTER_EVENT:lp->stats.tx_packets++;ne
tif_wake_queue(dev); / Inform upper layers. /if ((status & ( TX
_OK |TX_LOST_CRS | TX_SQE_ERROR |TX_LATE_COL | TX_16_COL)) != TX_
OK) {if ((status & TX_OK) == 0) lp->stats.tx_errors++;if (status
& TX_LOST_CRS) lp->stats.tx_carrier_errors++;if (status & TX_SQE_
ERROR) lp->stats.tx_heartbeat_errors++;if (status & TX_LATE_COL)
lp->stats.tx_window_errors++;if (status & TX_16_COL) lp->stats.tx
_aborted_errors++;}break; case ISQ_BUFFER_EVENT:if (status & READ
Y_FOR_TX){ netif_wake_queue(dev); / Inform upper layers. /}i
f (status & TX_UNDERRUN){DPRINTK(1, "%s: transmit underrun\n", de
v->name);lp->send_underrun++;if (lp->send_underrun == 3) lp->sen
d_cmd = TX_AFTER_381;else if (lp->send_underrun == 6) lp->send_cm
d = TX_AFTER_ALL;netif_wake_queue(dev); / Inform upper layers.
/}break;case ISQ_RX_MISS_EVENT:lp->stats.rx_missed_errors += (sta
tus >>6);break;case ISQ_TX_COL_EVENT:lp->stats.collisions += (sta
tus >>6);break;}}} 7.2 CAN总线接口7.2.1 CAN总线概述CAN(Controller Area
Network,控制器局域网)是德国Bosch公司于1983年为汽车应用而开发的,它是一种现场总线(FieldBus),能有效支
持分布式控制和实时控制的串行通信网络。1993年11月,ISO正式颁布了控制器局域网CAN国际标准(IS011898)。一个理想的
由CAN总线构成的单一网络中可以挂接任意多个节点,实际应用中节点数目受网络硬件的电气特性所限制。例如:当使用Philips P82
C250作为CAN收发器时,同一网络中允许挂接110个节点。CAN可提供1 Mb/s的数据传输速率。CAN总线是一种多主方式的串行
通信总线。基本设计规范要求有高的位速率,高抗电磁干扰性,并可以检测出产生的任何错误。当信号传输距离达到10Km时CAN总线仍可提供
高达50Kb/s的数据传输速率。CAN总线具有很高的实时性能,已经在汽车工业、航空工业、工业控制、安全防护等领域中得到了广泛应用。
CAN总线的通信介质可采用双绞线、同轴电缆和光导纤维,最常用的是双绞线。通信距离与波特率有关,最大通信距离可达10km,最大通信波
特率可达1Mbps。CAN总线仲裁采用11位标识和非破坏性位仲裁总线结构机制,可以确定数据块的优先级,保证在网络节点冲突时最高优先
级节点不需要冲突等待。CAN总线采用了多主竞争式总线结构,具有多主站运行和分散仲裁的串行总线以及广播通信的特点。CAN总线上任意节
点可在任意时刻主动向网络上其他节点发送信息而不分主次,因此可在各节点之间实现自由通信。CAN总线信号使用差分电压传送,两条信号线被
称为CAN_H和CAN_L,静态时均是2.5V左右,此时状态表示为逻辑1,也可以叫做“隐性”。采用CAN_H比CAN_L高表示逻辑
0,称为“显性”,通常电压值为CAN_H=3.5V和CAN_L=1.5V。当“显性”位和“隐性”位同时发送的时候,最后总线数值将为
“显性”。CAN总线的一个位时间可以分成四个部分:同步段,传播时间段,相位缓冲段1和相位缓冲段2。每段的时间份额的数目都是可以通过
CAN总线控制器编程控制,而时间份额的大小tq由系统时钟tsys和波特率预分频值BRP决定:tq=BRP/tsys。图7.2.1说
明了CAN总线的一个位时间的各个组成部分。图7.2.1 CAN总线的一个位时间● 同步段:用于同步总线上的各个节点,在此段内期
望有一个跳变沿出现(其长度固定)。如果跳变沿出现在同步段之外,那么沿与同步段之间的长度叫做沿相位误差。采样点位于相位缓冲段1的末尾
和相位缓冲段2开始处。● 传播时间段:用于补偿总线上信号传播时间和电子控制设备内部的延迟时间。因此,要实现与位流发送节点的同步,
接收节点必须移相。CAN总线非破坏性仲裁规定,发送位流的总线节点必须能够收到同步于位流的CAN总线节点发送的显性位。● 相位缓冲
段1:重同步时可以暂时延长。● 相位缓冲段2:重同步时可以暂时缩短。● 同步跳转宽度:长度小于相位缓冲段。同步段,传播时间段,
相位缓冲段1和相位缓冲段2的设定和CAN总线的同步、仲裁等信息有关。其主要思想是要求各个节点在一定误差范围内保持同步。必须考虑各个
节点时钟(振荡器)的误差和总线的长度带来的延迟(通常每米延迟为5.5ns)。正确设置CAN总线各个时间段,是保证CAN总线良好工作
的关键。7.2.2 在嵌入式处理器上扩展CAN总线接口一些面向工业控制的嵌入式处理器本身就集成了一个或者多个CAN总线控制器。例如
:韩国现代公司的hms30c7202(ARM720T内核)带有两个CAN总线控制器; Phillips公司的LPC2194和LPC
2294(ARM7TDMI内核)带有4个CAN总线控制器。CAN总线控制器主要是完成时序逻辑转换等工作,要在电气特性上满足CAN总
线标准,还需要一个CAN总线的物理层芯片,用它来实现TTL电平到CAN总线电平特性的转换,即CAN收发器。实际上,多数嵌入式处理器
都不带CAN总线控制器。通常的解决方案是在嵌入式处理器的外部总线上扩展CAN总线接口芯片,例如:Phillips公司的SJA100
0CAN总线接口芯片,Microchip公司的MCP251x系列(MCP2510和MCP2515)CAN总线接口芯片,这两种芯片都
支持CAN2.0B标准。SJA 1000的总线采用的是地址线和数据线复用的方式,多数嵌入式处理器采用SJA 1000扩展CAN总线
较为复杂。MCP2510是由Microchip公司生产的CAN协议控制器,完全支持CAN总线V2. OA/B技术规范。0~8字节的
有效数据长度,支持远程帧;最大1Mb/s的可编程波特率;两个支持过滤器Filter,Mask)的接收缓冲区,三个发送缓冲区;支持回
环(Loop Back)模式,便于测试;SPI高速串行总线,最大5MHz;3V到5.5V供电。MCP2510主要由CAN协议引擎,
用来为器件及其运行进行配置的控制逻辑,SRAM寄存器和SPI协议模块3部分组成。MCP2510支持CANT2、CAN2.0A、主动
和被动CAN2.0B等版本的协议,能够发送和接收标准和扩展报文,还同时具备验收过滤以及报文管理功能。MCP2510包含三个发送缓冲
器和两个接收缓冲器,减少了处理器(CPU)的管理负担。CPU的通信是通过行业标准串行外设接口(SPI)来实现的,其数据传输速率高达
5Mbps。CPU通过SPI接口与器件进行通信。通过使用标准SPI读/写命令对寄存器进行所有读/写操作。器件上有一个多用途中断引脚
以及各接收缓冲器专用的中断引脚,可用于指示有效报文是否被接收和载入各接收缓冲器。是否使用专用中断引脚由用户决定,若不使用,也可用通
用中断引脚和状态寄存器(通过SPI接口访问)确定有效报文是否已被接收。1.CAN协议引擎CAN协议引擎的功能是处理所有总线上的报文
发送和接收。报文发送时,首先将报文装载到正确的报文缓冲器和控制寄存器中。利用控制寄存器位、通过SPI接口或使用发送使能引脚均可启动
发送操作。通过读取相应的寄存器可以检查通信状态和错误。任何在CAN总线上侦测到的报文都会进行错误检测,然后与用户定义的滤波器进行匹
配,以确定是否将其转移到两个接收缓冲器之一中。CAN协议引擎的核心是有限状态机(FSM)。该状态机逐位检查报文,当各个报文帧发生数
据字段的发送和接收时,状态机改变状态。FSM确保了报文接收、总线仲裁、报文发送以及错误信号发生等操作过程依据CAN总线协议进行。总
线上报文的自动重发送也由FSM处理。2.CAN报文帧MCP2510支持CAN2.0B技术规范中所定义的标准数据帧、扩展数据帧以及远
程帧(标准和扩展),详细的描述请登录www.microchip.com,查阅MCP2510数据手册。3.寄存器映射表MCP2510
寄存器映射表如表7.2.1所示。通过使用行(低4位)列(高4位)值可对映射表中的寄存器地址进行确定。寄存器的地址排列优化了寄存器数
据的顺序读/写。一些特定控制和状态寄存器允许使用SPI位修改命令进行单独位的设定。可以使用位修改命令对表7.2.1中的阴影部分的寄
存器进行位修改操作。4.SPI接口MCP2510可以与许多微控制器的串行外设接口(SPI)直接相连,支持0,0和1,1运行模式。外
部数据和命令通过SI引脚传送到器件中,而数据在SCK时钟信号的上升沿传送进去。MCP2510在SCK下降沿通过SO引脚发送。MCP
2510 SPI指令如表7.2.3所示。有关0,0和1,1运行模式详细的输入输出时序请登录www.microchip.com,查阅
MCP2510数据手册。CAN SPI接口函数为:unsigned char CAN_SPI_CMD(unsigned char
cmd,unsigned long addr,unsigned char argl,unsignedchar arg2)其中cmd
表示指令名称,addr为寄存器地址,argl和arg2为可选的参数。● cmd为SPI_CMD_READ时,将读取addr地址的
寄存器值;argl和arg2没有使用。● cmd为SPI_CMD_WRITE时,将往addr地址的寄存器写argl值,arg2没
有使用。● cmd为SPI_CMD_RTS时,将发送RTS请求,argl和arg2没有使用。● cmd为SPI_CMD_REA
DSTA时,将读取MCP2510的状态,并返回该状态。● cmd为SPI_CMD_BITMOD时,将对addr地址的寄存器进行位
修改。位修改命令提供了一种对特定控制和状态寄存器中单独的位进行设定和清除的方法。argl为屏蔽字节,arg2为数据字节。屏蔽字节决
定寄存器中的哪一位将被修改。屏蔽字节中的“1”表示允许对寄存器相应的位进行修改,0则禁止修改。数据字节确定寄存器位修改后的最终结果
。如图7.2.3所示,如果屏蔽字节相应位设置为1,数据字节中的1表示将对寄存器对应位置1,而0则将对该位清零。● cmd为SPI
_CMD_RESET时,将发送复位指令,复位指令为单字节指令,可以重新初始化MCP2510的内部寄存器,并设置配置模式。它一般在期
间上电初始化过程中进行。图7.2.3 MCP2510位修改指令 5.报文发送CAN报文发送函数为:void MCP2510_TX
(int TxBuf,int IdType,unsigned intid,int DataLen,Chardata)MCP251
0采用三个发送缓冲器。通过TxBuf参数指定发送到哪个缓冲器(TXBUF0、TXBUF1或TXBUF2。IdType决定发送报文帧
的类型,STAN-DID表示标准数据帧,EXTID表示扩展数据帧。Id为帧ID,DataLen为待发送数据长度,必须小于等于8,d
ata为待发送数据内容。6.报文接收CAN报文接收函数为:void MCP2510_RX(int RxBuf,int IdType,unsigned int id,int DataLen,char data)MCP2510具有两个全文接收缓冲器。通过RxBuf参数指定从哪个缓冲器接收< RX-BUFO或RXBUFl。接收后的报文帧的类型、帧ID,数据长度以及数据内容分别保存在Id-Type、id、DataLen和data中。7.2.3 S3C2410A与MCP2510的CAN通信接口电路1.MCP2510 CAN通信接口电路大多数嵌入式处理器都有SPI总线控制器,MCP2510可以3V到5.5V供电,能够直接和3.3V I/O口的嵌入式处理器连接,电路结构形式如图7.2.4所示。图7.2.4 MCP251x组成的嵌入式CAN节点S3C2410A包含两个SPI接口,例如可以使用S3C2410A中的SPI0与MCP2510接口,连接电路如图7.2.5所示。在这个电路中,MCP2510使用3.3V电压供电,它可以直接和S3C2410A通过SPI总线连接。相关的资源如下:● 使用一个扩展的I/O口(EXI02)作为片选信号,低电平有效。● 用S3C2410A的外部中断6(EXINT6 )作为中断管脚,低电平有效。● 16MHz晶体作为输入时钟,MCP2510内部有振荡电路,用晶体可以直接起振。● 使用TJA 1050作为CAN总线收发器。CAN总线收发器TJA1050必须使用5V供电。但MCP2510和TJA1050连接的两个信号都是单向的信号。对于MCP2510,TXCAN是输出信号,RXCAN是输入信号。 ● TJA1050为5V供电时,输入高电平Vih的范围是2~5.3V。而3.3V供电的MCP2510输出TXCAN信号高电平Voh最小值为2.6V,可以满足要求。图7.2.5 MCP2510组成的CAN总线接口2.S3C2410A SPI接口编程SPI接口函数有:① SPI初始化函数。BOOL SPI_Init(VOID)②发送数据。BOOL SPI_SendByte(BYTE bData,BYTEpData)③读取数据。BOOL SPI_ReadByte(BYTEpData)更多的内容请参考SPI接口编程。表7.2.4对SPI接口相关的部分寄存器进行了简要说明。表7.2.4 SPI控制寄存器第7章 思考题与习题分析曼彻斯特编码和差分曼彻斯特编码的同异。简述802.3 Mac层的以太网的物理传输帧。在嵌入式系统中增加以太网接口通常采用哪些方法实现?TCP/IP协议包含有哪些层?各自的功能? 。简述BSD套接字网络程序编程方法。登录www.cirrus.com/cn,查阅CS8900的有关资料,分析其内部结构、引脚端功能、应用电路和编程方法。登录www.realtek.com.tw,查阅RTL8019/8029/8039的有关资料,分析其内部结构、引脚端功能、应用电路和编程方法。简述CAN总线的结构与特点。怎样在嵌入式处理器上扩展CAN总线接口?登录www.semiconductors.philips.com,查阅SJA1000CAN的有关资料,分析其内部结构、引脚端功能、应用电路和编程方法。登录www.microchip.com,查阅MCP251x的有关资料,分析其内部结构、引脚端功能、应用电路和编程方法。
献花(0)
+1
(本文系大高老师首藏)