Mini2440裸机开发之DM9000
网络对于嵌入式系统来说必不可少。可是S3C2440没有集成以太网接口,所以要想使S3C2440具备以太网的功能,就必须扩展网卡接口。我们使用的Mini2440就是外接DM9000EP,使其可以与以太网相连接。DM9000和DM9000EP主要是封装不一样,其他基本都是一样的。
一、DM9000介绍
1.1 概述
DM9000是一款完全集成的、性价比高、引脚数少、带有通用处理器接口的单芯片快速以太网MAC控制器。一个10/100M自适应的PHY和4K DWORD值的SRAM 。它是出于低功耗和高性能目的的设计,其IO端口支持3.3V与5V容限值。
DM9000提供了MII接口,来连接所有提供支持MII接口功能的HPNA网络设备或其他收发器。
此外DM9000支持8位, 16位和32位接口访问内部存储器,以支持不同的处理器。
DM9000物理协议层接口完全支持使用10MBps下3类、4类、5类非屏蔽双绞线和100MBps下5类非屏蔽双绞线。这是完全符合IEEE 802.3u规格。它的自动协调功能将自动完成配置以最大限度地适合其线路带宽。还支持IEEE 802.3x全双工流量控制。
DM9000具有以下特性:
- 100引脚LQFP封装;
- 支持处理器接口:I/O口的字节或字命令对内部存储器进行读写操作(比如外接S3C2440);
- 集成自适应10/100M收发器(PHYceiver)
- 支持MII接口(支持外接支持MII接口的收发器)
- 支持背压模式半双工流量控制模式
- IEEE802.3x全双工模式的流量控制
- 支持4个通用输入输出口
- 集成4K双字SRAM
- 支持自动加载EEPROM里面生产商ID和产品ID
1.2 模块图
工作实质就是MAC通过MII控制PHY的过程。
这张图的左侧是一个收发器,有TX、RX引脚,类似于我们的串口,不难想象,这里最终是和网线连接的,用来接收和发送数据。
而右侧是处理器接口,当然是可以用来连接S3C2440处理器的,处理器可以直接对SRAM读取和写入,从而控制数据的接收和发送。
1.3 网卡和网络模型的映射关系
MAC对应的是数据链路层,PHY对应的是物理层。
1.4 MAC的工作原理
当网络协议栈的IP包送到网卡的时候,先要到达MAC,MAC就根据数据链路层的协议对接收到的数据进行封装,将IP包封装成以太网包,完成数据帧的构建。当然它还具备数据纠错以及传送控制等功能。
1.5 关于PHY
PHY是物理接口收发器,主要和实际的传输硬件打交道。它接收到来自MAC的以太网包,先加上校检码。然后按照物理层的规则进行数据编码,然后传输到物理介质,接受过程则与之相反。
1.6 Mini2440硬件接线
DM9000与S3C2440接线关系:
- SD0~SD15:16位地址、数据线,由CMD引脚决定访问类型,连接S3C2440的LDDATA0~LDATA15;
- CMD:命令线,当CMD为高表示传输的是数据,CMD为低表示传输的是地址,连接S3C2440的LADDR2(PC16);
- INT:中断引脚,连接S3C2440的EINT7(GPF7);
- IOR#:读引脚,连接S3C2440的nOE(GPC5);
- IOW#:写引脚,连接S3C2440的nWE(GPE6);
- AEN:片选,连接S3C2440的bank4的片选引脚LnGCS4(GPA15);
我们可以把网卡设备看做16位ROM存储器,S3C2440内存访问引脚与DM9000引脚相连:
1.6.1 DM9000和HR911103A之间只有6根接线
DM9000 | HR911103A |
TXO+ | TX+ |
TXO- | TX- |
RXI+ | RX+ |
RXI- | RX- |
SPEED#、WACKUP、LINK_O(只焊接了SPEED) | NETLINK |
LINK_ACT# | NETACT |
一般来说网卡都有两个LED指示灯、一个亮绿灯、一个亮橙灯;
- 绿灯:代表连接指示灯LINK ,也就是电路原理图中NETLINK所连接的YLEDK;连接指示灯亮,表示网卡与网络连接状况良好;
- 橙灯:代表信号传输指示灯ACT,也就是电路原理图中NETACT所连接的GLEDK;信号传输指示灯闪烁表示网卡正在进行数据传输,同时灯的闪烁频率随数据传输的大小而变化 , 通信量大的闪烁频率快 ;
灯不亮原因:
- 线路问题 :网线没插好,网线老化,水晶头接触不良,信号干扰等线路问题都可能引起网络连接信号中断或不能定 , 导致网卡灯不亮 ;
- 网卡驱动程序:没有正常安装驱动网卡导致没法工作 ,当然网卡灯就不亮了,这也是常见的网卡故障;
HR911103A是网络插座变压器,使用RJ45接口,外接RJ4水晶头,也就是我们的网线。
1.6.2 DM9000 10/100M 物理层与光纤接口
24 |
SD |
I |
光纤信号检测 PECL电平信号,显示光纤接收是否有效 |
25 |
DGGND | P |
带隙地信号线 |
26 |
BGRES | I/O |
带隙引脚 |
27 |
AVDD |
P |
带隙与电源保护环 |
28 | AVDD | P | 接收端口电源 |
29 |
RXI+ | I |
物理层接收端的正极 |
30 |
RXI- | I |
物理层接收端的负极 |
31 |
AGND |
P |
接收端口地 |
32 |
AGND |
P |
发送端口地 |
33 |
TXO+ |
O |
物理层发送端口正极 |
34 |
TXO- |
O |
物理层发送端口负极 |
35 |
AVDD |
P |
发送端口电源 |
1.6.3 DM9000 LED引脚
60 |
SPEED# | O |
低电平指示100M带宽指示,高电平指示10M带宽 |
61 |
DUP# | O | 全双工指示LED LED模式0时,低电平显示工作在10M带宽,或在100M带宽浮动 |
62 |
LINK_ACT# | O |
连接LED,在模式0时,只作物理层的载波监听检测连接状态 |
1.7 DM9000引脚介绍(使用的引脚标红)
I=输入 O=输出 I/O=输入/输出 O/D=漏极开路 P=电源 LI=复位锁存输入 #=普遍低电位。
1.7.1 MII接口相关引脚(未使用)
引脚号 |
引脚名 |
I/O |
功能描述 |
37 |
LINK_I |
I |
外部MII接口器件连接状态 |
38、39、40、41 |
RXD [3:0] |
I |
外部MII接口接收数据 4位 半字节输入(同步于接收时钟) |
43 |
CRS |
I/O |
外部MII接口的载波检测 |
44 |
COL |
I/O |
外部MII接口的冲突检测,输出到外部设备 |
45 |
RX_DV |
I |
外部MII接口数据有效信号 |
46 |
RX_ER |
I |
外部MII接口接收错误 |
47 |
RX_CLK |
I |
外部MII接口接收时钟 |
49 |
TX_CLK |
I/O |
外部MII接口发送时钟 |
50~53 |
TXD[3:0] |
O |
外部MII接口发送数据低4位输出 TXD[2:0]决定内部存储空间基址:TXD [2:0]) * 10H + 300H |
54 | TX_EN | I/O | 外部MII传输使能 |
56 |
MDIO |
I/O |
外部MII接口串行数据通信 |
57 |
MDC |
O |
外部MII串行数据通信口时钟,且与中断引脚有关 该引脚高电平时候,中断引脚低电平有效;否则高有效 |
注意:以上介质无关端口都内部自带60K 欧姆的下拉电阻。
1.7.2 处理器接口相关引脚(使用)
1 |
IOR# |
I |
处理器读命令 低电平有效,极性能够被EEPROM修改,详细请参考对EEPROM内容的描述 |
2 |
IOW# |
I |
处理器写命令 低电平有效,同样能修改极性 |
3 |
AEN# |
I |
芯片选择,低电平有效 |
4 |
IOWAIT |
O |
处理器命令就绪 当上一指令没有结束,该引脚电平拉低表示当前指令需要等待 |
14 |
RST |
I |
硬件复位信号,高电平有效复位 |
6~13 82~89 |
SD0~15 |
I/O |
0~15位的数据地址复用总线,由CMD引脚决定当期访问类型 |
93~98 |
SA4~9 |
I |
地址线4~9;仅作芯片选择信号 SA9、SA8高电平、SA7和AEN低电平、SA6~SA4搭配TXD2~0,则DM9000被选中 |
92 |
CMD |
I |
访问类型 高电平是访问数据端口;低电平是访问地址端口 |
91 |
IO16 |
O |
字命令标志,默认低电平有效 当访问外部数据存储器是字或双字宽度时,被置位 |
100 |
INT |
O |
中断请求信号 高电平有效,极性能修改 |
37~53 56 |
SD31~16 |
I/O |
双字模式,高16位数据引脚 |
57 |
IO32 |
O |
双字命令标志,默认低电平有效 |
注意:以上引脚除去SD8,SD9和IO16,都内部自带60K 欧姆的下拉电阻
1.7.3 电源引脚(使用)
5,20,36,55, 72,90,73 |
DVDD |
P |
数字电源 |
15,23,42,58 63,81,99,76 |
DGND | P |
数字地 |
1.7.4 时钟引脚(使用)
21 |
X2_25M |
O |
25M晶振输出 |
22 |
X1_25M |
I |
25M晶振输入 |
59 |
CLK20MO |
O |
20M晶振再生输出给外部MII设备,自带60K欧姆下拉电阻 |
1.7.5 各种其他功能引脚
16~19 |
TEST1~4 |
I |
工作模式 Test1~4(1,1,0,0)正常工作状态 |
48 |
TEST5 |
I |
必须接地 |
68~69 |
GPIO0~3 |
I/O |
通用I/O端口 通用端口控制寄存器和通用端口寄存器能编程该系列引脚 GPIO0默认输出为高来关闭物理层和其他外部介质无关器件 GPIO1~3默认为输入引脚 |
78 |
LINK_O |
O |
电缆连接状态显示输出,高电平有效 |
79 |
WAKEUP |
O |
流出一个唤醒信号当唤醒事件发生 内置60K欧姆的下拉电阻 |
80 |
PW_RST# |
I |
上电复位 低电平激活DM9000的重新初始化,5us后初始化当该引脚测试到电平变化 |
74,75,77 |
NC |
无用 |
二、DM9000 寄存器
DM9000包含一系列可被访问的控制状态寄存器,这些寄存器是字节对齐的,他们在硬件或软件复位时被设置成初始值。以下为DM9000的寄存器功能详解。
2.1 NCR (00H):网络控制寄存器(Network Control Register )
位 | 名称 | 描述 | 默认值 |
7 | EXT_PHY | 选择外部PHY,0选择内部PHY,不受软件复位影响 | 0 RW |
6 | WAKEEN | 事件唤醒使能,1使能,0禁止并清除事件唤醒状态,不受软件复位影响 | 0 RW |
5 | 保留 | 保留 | 0 RO |
4 | FCOL | 强制冲突模式,用于用户测试 | 0 RW |
3 | FDX | 全双工模式。内部PHY模式下只读,外部PHY下可读写 | 0 RW |
2-1 | LBK |
回环模式(Loopback) 00通常 01MAC内部回环 10内部PHY 100M模式数字回环 11保留 |
00 RW |
0 | RST | 软件复位,10us后自动清零 | 0 RW |
2.2 NSR (01H):网络状态寄存器(Network Status Register )
位 | 名称 | 默认值 | 描述 |
7 | SPEED | 0 RO | 媒介速度,在内部PHY模式下,0为100Mbps,1为10Mbps。当LINKST=0时,此位不用. |
6 | LINKST | 0 RO | 连接状态,在内部PHY模式下,0为连接失败,1为已连接 |
5 | WAKEST | 0 RW/C1 | 唤醒事件状态。读取或写1将清零该位。不受软件复位影响 |
4 | 保留 | 0 RO | 保留 |
3 | TX2END | 0 RW/C1 | TX(发送)数据包2完成标志,读取或写1将清零该位。数据包指针2传输完成 |
2 | TX1END | 0 RW/C1 | TX(发送)数据包1完成标志,读取或写1将清零该位。数据包指针1传输完成 |
1 | RXOV | 0 RO | RX(接收)FIFO(先进先出缓存)溢出标志 |
0 | 保留 | 0 RO | 保留 |
位3:2写1清除数据包发送完标志位。
2.3 TCR(02H):发送控制寄存器(TX Control Register)
位 | 名称 | 默认值 | 描述 |
7 | 保留 | 0 RO | 保留 |
6 | TJDIS | 0 RW | Jabber传输使能。1禁止Jabber传输定时器(2048字节),0使能 |
5 | EXCECM | 0 RW | 额外冲突模式控制。0当额外的冲突计数多于15则终止本次数据包,1始终尝试发发送本次数据包 |
4 | PAD_DIS2 | 0 RW | 禁止为数据包指针2添加PAD |
3 | CRC_DIS2 | 0 RW | 禁止为数据包指针2添加CRC校验 |
2 | PAD_DIS2 | 0 RW | 禁止为数据包指针1添加PAD |
1 | CRC_DIS2 | 0 Rw | 禁止为数据包指针1添加CRC校验 |
0 | TXREQ | 0 Rw | TX(发送)请求。发送完成后自动清零该位 |
2.4 TSR_I(03H):数据包指针1的发送状态寄存器1(TX Status Register I)
位 | 名称 | 默认值 | 描述 |
7 | TJTO | 0 RO | Jabber传输超时。该位置位表示由于多于2048字节数据被传输而导致数据帧被截掉 |
6 | LC | 0 RO | 载波信号丢失。该位置位表示在帧传输时发生红载波信号丢失。在内部回环模式下该位无效 |
5 | NC | 0 RO | 无载波信号。该位置位表示在帧传输时无载波信号。在内部回环模式下该位无效 |
4 | LC | 0 RO | 冲突延迟。该位置位表示在64字节的冲突窗口后又发生冲突 |
3 | COL | 0 RO | 数据包冲突。该位置位表示传输过程中发生冲突 |
2 | EC | 0 RO | 额外冲突。该位置位表示由于发生了第16次冲突(即额外冲突)后,传送被终止 |
1-0 | 保留 | 0 RO | 保留 |
2.5 TSR_II(04H):数据包指针2的发送状态寄存器2(TX Status Register II)
位 | 名称 | 默认值 | 描述 |
7 | TJTO | 0 RO | Jabber传输超时。该位置位表示由于多于2048字节数据被传输而导致数据帧被截掉 |
6 | LC | 0 RO | 载波信号丢失。该位置位表示在帧传输时发生红载波信号丢失。在内部回环模式下该位无效 |
5 | NC | 0 RO | 无载波信号。该位置位表示在帧传输时无载波信号。在内部回环模式下该位无效 |
4 | LC | 0 RO | 冲突延迟。该位置位表示在64字节的冲突窗口后又发生冲突 |
3 | COL | 0 RO | 数据包冲突。该位置位表示传输过程中发生冲突 |
2 | EC | 0 RO | 额外冲突。该位置位表示由于发生了第16次冲突(即额外冲突)后,传送被终止 |
1-0 | 保留 | 0 RO | 保留 |
2.6 RCR(05H):接收控制寄存器(RX Control Register )
位 | 名称 | 默认值 | 描述 |
7 | 保留 | 0 RO | 保留 |
6 | WTDIS | 0 RW | 看门狗定时器禁止。1禁止,0使能 |
5 | DIS_LONG | 0 RW | 丢弃长数据包。1为丢弃数据包长度超过1522字节的数据包 |
4 | DIS_CRC | 0 RW | 丢弃CRC校验错误的数据包 |
3 | ALL | 0 RW | 忽略所有多点传送 |
2 | RUNT | 0 RW | 忽略不完整的数据包 |
1 | PRMSC | 0 Rw | 混杂模式(Promiscuous Mode) |
0 | RXEN | 0 Rw | 接收使能 |
2.7 RSR(06H):接收状态寄存器(RX Status Register )
位 | 名称 | 默认值 | 描述 |
7 | RF | 0 RO | 不完整数据帧。该位置位表示接收到小于64字节的帧 |
6 | MF | 0 RO | 多点传送帧。该位置位表示接收到帧包含多点传送地址 |
5 | LCS | 0 RO | 冲突延迟。该位置位表示在帧接收过程中发生冲突延迟 |
4 | RWTO | 0 RO | 接收看门狗定时溢出。该位置位表示接收到大于2048字节数据帧 |
3 | PLE | 0 RO | 物理层错误。该位置位表示在帧接收过程中发生物理层错误 |
2 | AE | 0 RO | 对齐错误(Alignment)。该位置位表示接收到的帧结尾处不是字节对齐,即不是以字节为边界对齐 |
1 | CE | 0 RO | CRC校验错误。该位置位表示接收到的帧CRC校验错误 |
0 | FOE | 0 RO | 接收FIFO缓存溢出。该位置位表示在帧接收时发生FIFO溢出 |
2.8 ROCR(07H):接收溢出计数寄存器(Receive Overflow Counter Register)
位 | 名称 | 默认值 | 描述 |
7 | RXFU | 0 R/C | 接收溢出计数器溢出。该位置位表示ROC(接收溢出计数器)发生溢出 |
6:0 | ROC | 0 R/C | 接收溢出计数器。该计数器为静态计数器,指示FIFO溢出后,当前接收溢出包的个数 |
2.9 BPTR(08H):背压门限寄存器(Back Pressure Threshold Register)
位 | 名称 | 默认值 | 描述 |
7:4 | BPHW | 3H RW |
背压门限最高值。 当接收SRAM空闲空间低于该门限值,则MAC将产生一个拥挤状态。 默认值为3H,即3K字节空闲空间。不要超过SRAM大小 |
3:0 | JPT | 7H RW |
拥挤状态时间。默认为200us。 0000 为5us 0001为10us 0010为15us 0011为25us 0100为50us 0101为100us 0110为150us 0111为 200us 1000为250us 1001为300us 1010为350us 1011为400us 1100为450us 1101为500us 1110为550us,1111为600us |
2.10 FCTR(09H):溢出控制门限寄存器(Flow Control Threshold Register)
位 | 名称 | 默认值 | 描述 |
7:4 | HWOT | 3H RW |
接收FIFO缓存溢出门限最高值。 当接收SRAM空闲空间小于该门限值,则发送一个暂停时间(pause_time)为FFFFH的暂停包。 若该值为0,则无接收空闲空间。 默认值为3H,即3K字节空闲空间。不要超过SRAM大小 |
3:0 | LWOT | 8H RW |
接收FIFO缓存溢出门限最低值。 当接收SRAM空闲空间大于该门限值,则 发送一个暂停时间(pause_time)为0000H的暂停包。 当溢出门限最高值的暂停包发送之后,溢出门限最低值的暂停包才有效。 默认值为8K字节。 不要超过SRAM大小 |
2.11 RTFCR(0AH):接收/发送溢出控制寄存器(RX/TX Flow Control Register)
位 | 名称 | 默认值 | 描述 |
7 | TXP0 | 0 RW | 发送暂停包。发送完成后自动清零,并设置TX暂停包时间为0000H |
6 | TXPF | 0 RW | 发送暂停包。发送完成后自动清零,并设置TX暂停包时间为FFFFH |
5 | TXPEN | 0 RW | 强制发送暂停包使能。按溢出门限最高值使能发送暂停包 |
4 | BKPA | 0 RW | 背压模式。该模式仅在半双工模式下有效。当接收SRAM超过BPHW并且接收新数据包时,产生一个拥挤状态 |
3 | BKPM | 0 RW | 背压模式。该模式仅在半双工模式下有效。当接收SRAM超过BPHW并数据包DA匹配时,产生一个拥挤状态 |
2 | RXPS | 0 R/C | 接收暂停包状态。只读清零允许 |
1 | RXPCS | 0 RO | 接收暂停包当前状态 |
0 | FLCE | 0 RW | 溢出控制使能。1设置使能溢出控制模式 |
2.12 EPCR/PHY_CR(0BH):EEPROM和PHY控制寄存器(EEPROM & PHY Control Register)
位 | 名称 | 默认值 | 描述 |
7:6 | 保留 | 0 RO | 保留 |
5 | REEP | 0 RW | 重新加载EEPROM。驱动程序需要在该操作完成后清零该位 |
4 | WEP | 0 RW | EEPROM写使能 |
3 | EPOS | 0 RW | EEPROM或PHY操作选择位。0选择EEPROM,1选择PHY |
2 | ERPRR | 0 RW | EEPROM读,或PHY寄存器读命令。驱动程序需要在该操作完成后清零该位 |
1 | ERPRW | 0 RW | EEPROM写,或PHY寄存器写命令。驱动程序需要在该操作完成后清零该位 |
0 | ERRE | 0 RO | EEPROM或PHY的访问状态。1表示EEPROM或PHY正在被访问 |
2.13 EPAR/PHY_AR(0CH):EEPROM或PHY地址寄存器(EEPROM & PHY Address Register)
位 | 名称 | 默认值 | 描述 |
7:6 | PHY_ADR | 01 RW |
PHY地址的低两位(bit1,bit0),而PHY地址的bit[4:2]强制为000。 如果要选择内部PHY,那么此2位强制为01,实际应用中要强制为01 |
5:0 | EROA | 0 RW |
EEPROM字地址或PHY寄存器地址 |
2.14 EPDRL/PHY_DRL(0DH):EEPROM或PHY数据寄存器低半字节(EEPROM & PHY Low Byte Data Register)
位 | 名称 | 默认值 | 描述 |
7:0 | EE_PHY_L | X RW |
EEPROM或PHY低字节数据 |
2.15 EPDRL/PHY_DRH(0EH):EEPROM或PHY数据寄存器高半字节(EEPROM & PHY High Byte Data Register)
位 | 名称 | 默认值 | 描述 |
7:0 | EE_PHY_H | X RW |
EEPROM或PHY高字节数据 |
2.16 WUCR(0FH):唤醒控制寄存器(Wake Up Control Register)
位 | 名称 | 默认值 | 描述 |
7:6 | 保留 | 0 RO | 保留 |
5 | LINKEN | 0 RW | 使能“连接状态改变”唤醒事件。该位不受软件复位影响 |
4 | SAMPLEEN | 0 RW | 使能“Sample帧”唤醒事件。该位不受软件复位影响 |
3 | MAGICEN | 0 RW | 使能“Magic Packet”唤醒事件。该位不受软件复位影响 |
2 | LINKST | 0 RO | 表示发生了连接改变事件和连接状态改变事件。该位不受软件复位影响 |
1 | SAMPLEST | 0 RO | 表示接收到“Sample帧”和发生了“Sample帧”事件。该位不受软件复位影响 |
0 | MAGICST | 0 RO | 表示接收到“Magic Packet”和发生了“Magic Packet”事件。该位不受软件复位影响 |
2.17 PAR(10H -- 15H):物理地址(MAC)寄存器(Physical Address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | PAB5 | X RW |
物理地址字节5(15H) |
7:0 | PAB4 | X RW |
物理地址字节4(14H) |
7:0 | PAB3 | X RW | 物理地址字节3(13H) |
7:0 | PAB2 | X RW |
物理地址字节2(12H) |
7:0 | PAB1 | X RW | 物理地址字节1(11H) |
7:0 | PAB0 | X RW | 物理地址字节0(10H) |
用来保存6个字节的MAC地址。
2.18 MAR(16H -- 1DH):多点发送地址寄存器(Multicast Address Register )
位 | 名称 | 默认值 | 描述 |
7:0 | MAR7 | X RW |
多点发送地址字节7(1DH) |
7:0 | MAR6 | X RW |
多点发送地址字节6(1CH) |
7:0 | MAR5 | X RW | 多点发送地址字节5(1BH) |
7:0 | MAR4 | X RW |
多点发送地址字节4(1AH) |
7:0 | MAR3 | X RW | 多点发送地址字节3(19H) |
7:0 | MAR2 | X RW | 多点发送地址字节2(18H) |
7:0 | MAR1 | X RW | 多点发送地址字节1(17H) |
7:0 | MAR0 | X RW | 多点发送地址字节0(16H) |
2.19 GPCR(1EH):GPIO控制寄存器(General Purpose Control Register)
位 | 名称 | 默认值 | 描述 |
7:4 | 保留 | 0 RO |
保留 |
3:0 | GEP_CNTL | 0001 RW |
GPIO控制。 定义GPIO的输入输出方向。1为输出,0为输入。 GPIO0默认为输出做POWER_DOWN功能。其它默认为输入。因此默认值为0001 |
2.20 GPR(1FH):GPIO寄存器(General Purpose Register)
位 | 名称 | 默认值 | 描述 |
7:4 | 保留 | 0 RO |
保留 |
3:1 | GEPIO3-1 | 0 RW |
GPIO为输出时,相关位控制对应GPIO端口状态; GPIO为输入时,相关位反映对应GPIO端口状态; |
0 | GEPIO0 | 1 RW |
功能同上。 该位默认为输出1到POWER_DOWN内部PHY。 若希望启用PHY,则驱动程序需要通过写“0”将PWER_DOWN信号清零。 该位默认值可通过EEPROM编程得到。参考EEPROM相关描述 |
2.21 TRPAL(22H):发送SRAM读指针地址寄存器(TX SRAM Read Pointer Address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | TRPAH | 00H RO |
发送SRAM读指针地址低字节 |
2.22 TRPAH(23H):发送SRAM读指针地址寄存器(TX SRAM Read Pointer Address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | TRPAL | 00H RO |
发送SRAM读指针地址高字节 |
2.23 RWPAL(24H):接收SRAM写指针地址寄存器(RX SRAM Write Pointer Address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | RWPAL | X04HRO |
接收SRAM写指针地址低字节 |
2.24 RWPAH(25H):接收SRAM写指针地址寄存器(RX SRAM Write Pointer Address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | RWPAH | OCH RO |
接收SRAM写指针地址高字节 |
2.25 VID(28H -- 29H):生产厂家序列号寄存器(Vendor ID Register)
位 |
名称 |
默认值 |
描述 |
7:0 |
VIDH |
0AH RO |
生产厂家序列号高字节(29H) |
7:0 |
VIDL |
46H RO |
生产厂家序列号低字节(28H) |
2.26 PID(2AH --2BH):产品序列号(Product ID Register)
位 |
名称 |
默认值 |
描述 |
7:0 |
PIDH |
90H RO |
产品序列号高字节(2BH) |
7:0 |
PIDL |
00H RO |
产品序列号低字节(2AH) |
2.27 CHIPR(2CH):芯片修订版本(CHIP Revision Register )
位 | 名称 | 默认值 | 描述 |
7:0 | CHIPR | 00H R0 |
芯片修订版本 |
2.28 SMCR(2FH):特殊模式控制寄存器(Special Mode Control Register)
位 |
名称 |
默认值 |
描述 |
7 |
SM_EN |
0 RW |
特殊模式使能 |
6:3 |
保留 |
0 WO |
保留 |
2 |
FLC |
0 RW |
强制冲突延迟 |
1 | FB1 | 0 RW | 强制最长“Back-off”时间 |
0 | FB0 | 0 RW | 强制最短“Back-off”时间 |
2.29 MRCMDX(F0H):存储器地址不变的读数据命令寄存器(Memory Data Read Command Without Address Increment Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MRCMDX | X R0 |
从接收SRAM中读数据,读取之后,指向内部SRAM的读指针不变 |
2.30 MRCMD(F2H):存储器读地址自动增加的读数据命令寄存器(Memory Data Read Command With Address Increment Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MRCMD | X R0 |
从接收SRAM中读数据,读取之后,根据操作模式(8位、16位、32位)读指针分别增加1,2,或4 |
2.31 MRRL(F4H~F5H):存储器读地址寄存器(Memory Data Read_ address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MDRAH | 00H R/W |
存储器读地址高位。 若IMR的bit7=1,则该寄存器设置为0CH(F5H) |
7:0 | MDRAL | 00H R/W |
存储器读地址低位(F4H) |
2.32 MWCMDX(F6H):存储器读地址不变的读数据命令寄存器(Memory Data Write Command Without Address Increment Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MWCMDX | X WO |
写数据到发送SRAM中,之后指向内部SRAM的写地址指针不变 |
2.33 MWCMD(F8H):存储器读地址自动增加的读数据命令寄存器(Memory Data Write Command With Address Increment Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MWCMD | X WO |
写数据到发送SRAM中,之后指向内部SRAM的读指针自动增加1、2或4,根据处理器的操作模式而定(8位、16位或32位) |
2.34 MWRL(FAH~FBH):存储器写地址寄存器(Memory Data Write_ address Register)
位 | 名称 | 默认值 | 描述 |
7:0 | MDRAM | 00H R/W |
存储器写地址高位(FBH) |
7:0 | MDRAL | 00H R/W |
存储器写地址低位(FAH) |
2.35 TXPLL(FCH~FDH):发送数据包长度寄存器(TX Packet Length Register)
位 | 名称 | 默认值 | 描述 |
7:0 | TXPLH | X R/W |
发送数据包长度高字节(FDH) |
7:0 | TXPLL | X R/W |
发送数据包长度低字节(FCH) |
2.36 ISR(FEH):中断状态寄存器(Interrupt Status Register)
位 |
名称 |
默认值 |
描述 |
7:6 |
IOMODE |
0 RO |
处理器模式。 00为16位模式 01为32位模式 10为8位模式 11保留 |
5:4 |
保留 |
0 RO |
保留 |
3 | ROOS | 0 RW/C1 | 接收溢出计数器溢出 |
2 | ROS | 0 RW/C1 | 接收溢出 |
1 | PTS | 0 RW/C1 | 数据包传输 |
0 | PRS | 0 RW/C1 | 数据包接收 |
ISR寄存器各状态写1清除中断标志。
2.37 IMR(FFH):中断屏蔽寄存器(Interrupt Mask Register)
位 |
名称 |
默认值 |
描述 |
7 |
PAR |
0 RW |
使能指针自动跳回。当SRAM的读、写指针超过SRAM的大小时,指针自动跳回起始位置。 需要驱动程序设置该位,若设置则REG_F5(MDRAH)将自动位0CH |
6:4 |
保留 |
0 RO |
保留 |
3 | ROOM | 0 RW | 使能接收溢出计数器溢出中断 0屏蔽 1使能 |
2 | ROM | 0 RW | 使能接收溢出中断 0屏蔽 1使能 |
1 | PTM | 0 RW | 使能数据包传输中断 0屏蔽 1使能 |
0 | PRM | 0 RW | 使能数据包接收中断 0屏蔽 1使能 |
访问以上寄存器的方法是通过总线驱动的方式,即通过对IOR、IOW、AEN、CMD以及SD0--SD15等相关引脚的操作来实现。
在DM9000中,还有一些介质无关接口MII寄存器,需要我们去访问。这些寄存器是字对齐的,即16位宽。下这里就不具体介绍了。
三、DM9000初始化
3.1 DM9000基地址
在Mini2440的原理图:
- DM9000引脚CMD连接在S3C2440的ADDR2口上;
- DM9000数据线SD0~SD15连接在S3C2440的LDATA0~LDATA15;
- DM9000片选线是nLAN_CS(AEN),低电平有效。片选线连接在S3C2440的nGCS4上;
我们查看S3C2440的内存空间分布:
可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。也就说当CPU读写该区间范围内的物理地址时,CPU会自动将ngCS4引脚拉低,片选信号nGCS4有效,网卡设备被选中。
地址线为什么只有一根,这是DM9000决定的,看手册可以知道:
- CMD管脚为0时,数据线送的是DM9000的寄存器地址;
- CMD管脚为1时,数据线送的是16位的寄存器数据。
所以对DM9000的操作至少需要两步:先写地址,再写(读)数据。它不像其它类内存总线那样直接把数据写到地址传输一次就可以了,而它要传输两次,因为它的总线地址就退化成了类似于NAND的ALE信号,就是地址和数据的区分信号了,而不是DM9000里面操作的寄存器地址。
DM9000对于CPU的地址线只认识一根线,只要这根线对应的CPU传送的地址的对应位匹配就可以了,上面接到LADDR2,也就是说:
- 读写寄存器地址的时候操作的总线地址满足:以基地址起头(0x20000000),偏移基地址LADDR2为0(低四位:0000),也就是写的时候总线地址为:0x20000000;
- 读写寄存器数据的时候操作的总线地址满足:以基地址起头(0x20000000),偏移基地址LADDR2为1 (低四位:0100),也就是写的时候总线地址为:0x20000004;
所以首先满足总线地址在BANK4里面(这样CS4才能自动选中),其次满足最后四位为0(传送寄存器地址)或4(传送数据)就可以了,满足这两个条件地址随便设。
/* dm9000的地址寄存器地址是0x20000000,数据寄存器的地址是0x20000004 */ #define DM_ADD (*((volatile u16 *)0x20000000)) #define DM_DAT (*((volatile u16 *)0x20000004))
DM9000 IRQ_LAN(INT)接的是S3C2440的ENT7(GPF7),用的外部中断7,这个中断用于接收数据时触发的。当DM9000收到外部的数据后,会暂存到内部地址中,然后产生一个上升沿中断,等待S3C2440读取数据;当DM9000将S3C2440的数据转发出去后,也会产生一个上升沿中断给2440。
3.2 片选信息设置
我们需要初始化DM9000,那么我们必然需要设置BSWCON上有关bank4的位,以及寄存器BANKCON4。具体参考Mini2440裸机开发之存储器控制器。
#define B4_Tacs 0x0 /* 0clk */ #define B4_Tcos 0x0 /* 3clk */ #define B4_Tacc 0x7 /* 14clk */ #define B4_Tcoh 0x1 /* 1clk */ #define B4_Tah 0x0 /* 0clk */ #define B4_Tacp 0x0 /* 6clk */ #define B4_PMC 0x0 /* normal */
/************************************************************************** * * Function : 初始化读写时序 1、数据宽度(BWSCON) 2、时序信号填写(BANKCON4) * *************************************************************************/ void cs_init() { // 数据宽度设置 BWSCON &= ~(3<<16); BWSCON |= (1<<16); //时序信号设置 BANKCON4 =((B4_Tacs<<13)|(B4_Tcos<<11)|(B4_Tacc<<8)|(B4_Tcoh<<6)|(B4_Tah<<4)|(B4_Tacp<<2)|(B4_PMC<<0)); }
3.3 中断初始化
(1). 配置相应的中断引脚;
(2). 设置EINT7的触发方式,高电平;
(3). 清除中断标志(SRCPND,INTPND,EINTPND);
(4). 使能中断,设置中断屏蔽寄存器(INTMSK,EINTMSK);
/************************************************************************** * * Function : dm9000中断初始化 外部中断ENT4~7 引脚GPF7,高电平触发 * dm9000在接受到数据时会向cpu发送一个中断,从mini2440的原理图可以得知它会在GPF7产生中断 * *************************************************************************/ void dm9000_int_init() { // 设置引脚复用为为中断 GPFCON &= ~(0x3<<14); GPFCON |= 0x2<<14; // 设置中断触发方式 EXTINT0 &= ~(0x7<<28); EXTINT0 |= 0x1<<28; /* 设置EINT7的信号触发方式,高电平 */ // 中断清除 EINTPEND |= 1<<7; /* 向相应位置写1清除次级源挂起寄存器 */ SRCPND |= BIT_EINT4_7; /* 向相应位置写1清除源挂起寄存器 */ INTPND |= BIT_EINT4_7; /* 向相应位置写1清除挂起寄存器 */ // 使能中断 EINTMASK &= ~(1<<7); /* 关闭外部中断屏蔽 */ INTMSK &= ~BIT_EINT4_7; /* 关闭EINT4~7中断屏蔽,总中断 */ }
3.5 复位设备
(1).实现往DM9000读写数据的函数
/************************************************************************** * * Function : dm9000 IO写数据 * *************************************************************************/ void dm9000_reg_write(u16 reg,u16 data){ DM_ADD = reg; DM_DAT = data; } /************************************************************************** * * Function : dm9000 IO读数据 * *************************************************************************/ u8 dm9000_reg_read(u16 reg){ DM_ADD =reg; return DM_DAT; }
(2).设置I/O为输出模式
(3).通过对GPIO0写0为内部的PHY提供电源
(4).软件复位(自动清0),MAC内部回环模式
(5).对(4)中的寄存器全部写入0
(6).重复(4)(5)
/************************************************************************** * * Function : dm9000 芯片复位 * *************************************************************************/ u8 dm9000_reset(){ // 设置GPIO控制寄存器 GPIO0设置为输出 dm9000_reg_write(DM9000_GPCR, GPCR_GPIO0_OUT); // 通过对GPIO0写入0为内部的PHY提供电源 dm9000_reg_write(DM9000_GPR, 0); // 软件复位(自动清0),MAC内部回环模式 dm9000_reg_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); // 对上一步的寄存器写入全0 dm9000_reg_write(DM9000_NCR, 0); // 重复上面,用两次实现真正复位 dm9000_reg_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); dm9000_reg_write(DM9000_NCR, 0); }
3.6 捕获网卡
(1). 读取生产厂家ID(0A46H)
(2).读取产品ID(9000H)
(3).将两个ID组合与之前预定义的值进行对比
/************************************************************************** * * Function : dm9000 芯片的捕获,实际上就是对dm9000上的生产厂家ID、产品ID信息进行对比 * *************************************************************************/ int dm9000_probe(){ u32 id_val; // 读取生产厂家ID低字节 id_val = dm9000_reg_read(DM9000_VIDL); // 读取生产厂家ID高字节 id_val |= (dm9000_reg_read(DM9000_VIDH) << 8); // 读取产品ID低字节 id_val |= (dm9000_reg_read(DM9000_PIDL) << 16); // 读取产品ID高字节 id_val |= (dm9000_reg_read(DM9000_PIDH) << 24); if(id_val == DM9000_ID) { printf("dm9000 is found!\r\n"); return 0; }else{ printf("dm9000 is not found!\r\n"); return -1; } }
3.7 MAC初始化
/************************************************************************** * * Function : dm9000 mac初始化 * *************************************************************************/ void dm9000_mac_init() { /* Program operating register, only internal phy supported */ dm9000_reg_write(DM9000_NCR, 0x0); /* TX Polling clear */ dm9000_reg_write(DM9000_TCR, 0); /* Less 3Kb, 200us */ dm9000_reg_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US); /* Flow Control : High/Low Water */ dm9000_reg_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); /* SH FIXME: This looks strange! Flow Control */ dm9000_reg_write(DM9000_FCR, 0x0); /* 特殊位 */ dm9000_reg_write(DM9000_SMCR, 0); /* 清除发送状态 */ dm9000_reg_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); /* 清除中断状态 */ dm9000_reg_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS); }
3.8 填充MAC地址
/************************************************************************** * * Function : dm9000 填充mac地址 * *************************************************************************/ void dm9000_fill_macadd() { u16 oft = 0,i = 0; /* fill device MAC address registers */ for (i = 0; i < 6; i++) dm9000_reg_write(DM9000_PAR + i, mac_addr[i]); /*maybe this is some problem*/ for (i = 0, oft = 0x16; i < 8; i++, oft++) dm9000_reg_write(oft, 0xff); /* read back mac, just to be sure */ for (i = 0, oft = 0x10; i < 6; i++, oft++) printf("%02x:", dm9000_reg_read(oft)); printf("\r\n"); }
3.9 dm9000初始化
/************************************************************************** * * Function : dm9000 初始化,其主要操作就是填入MAC地址,然后启动dm9000 * *************************************************************************/ int dm9000_init() { /* 片选初始化 */ cs_init(); /* 中断初始化 */ dm9000_int_init(); /* 芯片重置 */ dm9000_reset(); /* 芯片捕获 */ if(dm9000_probe()<0) return -1; /* MAC初始化 */ dm9000_mac_init(); /*设置MAC*/ dm9000_fill_macadd(); /* 启动DM9000,这里如果加入RCR_ALL意为接受广播数据,会造成接收数据异常 */ dm9000_reg_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); /* Enable TX/RX interrupt mask */ dm9000_reg_write(DM9000_IMR, IMR_PAR); }
3.10 发送数据包
(1). 禁止所有中断
(2). 写入发送数据长度
(3). 写入待发送数据
- 将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中;
- 利用循环,将数据写入数据端口;
(4) 启动发送
(5).等待发送完成,当发送结束的时候,TCR的0位会自动清0,所以去等待他变0即可
(6).清除发送状态
(7)开启接收中断
/************************************************************************** * * Function : dm9000 网卡的发送 * *************************************************************************/ void dm9000_tx(u8 *data,u32 length) { u32 i; /* 禁止中断*/ dm9000_reg_write(DM9000_IMR,0x80); /*写入发送数据的长度*/ dm9000_reg_write(DM9000_TXPLL, length & 0xff); dm9000_reg_write(DM9000_TXPLH, (length >> 8) & 0xff); /*写入待发送的数据*/ DM_ADD = DM9000_MWCMD; for(i=0;i<length;i+=2) { DM_DAT = data[i] | (data[i+1]<<8); } /*启动发送*/ dm9000_reg_write(DM9000_TCR, TCR_TXREQ); /*等待发送结束*/ while(1) { u8 status; status = dm9000_reg_read(DM9000_TCR); if((status&0x01)==0x00) break; } /*清除发送状态*/ dm9000_reg_write(DM9000_NSR,0x2c); /*开启接收中断*/ dm9000_reg_write(DM9000_IMR,0x81); }
3.11 接收数据包
接收是中断处理的,接收到一个包就会产生中断。在中断处理的时候调用接收函数。
(1). 判断是否产生中断,是就继续,否则退出接收函数
- 读取ISR寄存器第0位即可。
(2). 清除中断标志
- ISR寄存器第0位写1即可。
(3).空读
- 读取MRCMDX寄存器
(4).读取包的状态和长度
- 读取MRCMD寄存器得到状态,此时地址端口的数据就是对应MRCMD的偏移量,所以可以直接读取此时数据寄存器的值,不用再重新指定偏移量,就可以得到长度;
- 在长度后面会自动送入有效的数据,所以后面可以页直接读数据寄存器得到有效数据。
(5).读取包的数据
- 在读取数据之前应该对读到的长度进行检查,看是否小于以太网包的最大长度。然后利用for循环读取数据,注意数据的组合方式。
/************************************************************************** * * Function : dm9000 网卡的接收 * *************************************************************************/ u16 dm9000_rx(u8 * data) { u16 i,len,status,tmp,ready; /*检查是否有接收中断并清除*/ if(!dm9000_reg_read(DM9000_ISR&0x01)) return 0; /*清除接收中断标志*/ dm9000_reg_write(DM9000_ISR,0x01); /*空读*/ ready = dm9000_reg_read(DM9000_MRCMDX); if(ready&0x01 != 0x01) { ready = dm9000_reg_read(DM9000_MRCMDX); if(ready&0x01 != 0x01) return 0; } /*读取包的状态和长度*/ status = dm9000_reg_read(DM9000_MRCMD); len = DM_DAT; /*读取包的数据*/ if(len<DM9000_PKT_MAX) { for(i=0;i<len;i+=2) { tmp = DM_DAT; data[i] = tmp&0xff; data[i+1] = (tmp>>8)&0xff; } } return len; }
3.12 中断处理函数
当DM9000发生接收数据中断时,INT引脚触发高电平,进入S3C2440中断处理程序。中断处理程序主要包含:
- 调用接收函数存放接收到的数据;
- 清除中断标志(SRCPND,INTPND,EINTPND);
/************************************************************************* * * Function : 中断源4 外部中断4至7 DM9000接收中断 * *************************************************************************/ void EINT4_7_IRQHandler() { packet_len = dm9000_rx(dm9000_buffer); printf('rec data %s, length%d',dm9000_buffer,packet_len); //清中断 EINTPEND |= (1 << 7) ; /* 清中断标志位 */ SRCPND |= BIT_EINT4_7; /* 清中断标志位 */ INTPND |= BIT_EINT4_7; /* 清中断标志位 */ }
四 ARP协议实现
4.1 ARP定义
这里只对ARP协议进行简单的介绍,更多相关信息需要去学习计算机网络相关知识。
ARP全称Address Resolution Protocol,中文名为地址解析协议,它工作在数据链路层,在本层和硬件接口联系,同时对上层提供服务。IP数据包常通过以太网发送,以太网设备并不识别32位IP地址,它们是以48位以太网地址传输以太网数据包。因此,必须把IP目的地址转换成以太网目的地址。
在以太网中,一台主机要和另一台主机进行直接通信,必须要知道目标主机的MAC地址。但是这个目标MAC地址是如何获得的呢?它就是通过地址解析协议获得的。ARP协议用于将网络中的IP地址解析成硬件地址(MAC地址),以保证通信的顺利进行。
4.2 ARP工作原理
(1) 同一网段
在局域网中,每台主机都会在自己的ARP缓冲区建立一个ARP列表,以表示IP地址和MAC地址的对应关系。
- 当源主机需要将一个数据包发送到目标主机时,会首先检查自己的ARP列表中是否存在该IP对应的MAC地址,如果有,就直接将数据包发送到这个MAC地址;
- 如果没有,就向本段网络发起一个ARP请求的广播包,查询此目的主机对应的MAC地址。此ARP请求数据包里包括源主机的IP、硬件地址、以及目标主机的IP地址;
- 网络中所有的主机收到这个ARP请求后,会检查数据包中的目的IP是否和自己的IP地址一致,如果不相同则忽略此数据包;
- 如果相同,该主机首先将发送端的MAC地址和IP添加到自己的ARP列表中,如果ARP列表中已经存在该IP的地址,则将其覆盖,然后给源主机发送一个ARP响应数据包,告诉对方自己是它需要查找的MAC地址,源主机收到这个ARP响应数据包后,将得到的目标主机的IP地址和MAC地址添加到自己的ARP列表中,并利用此信息开始数据的传输;
- 如果源主机一直没有收到ARP响应数据包,则表示ARP查询失败;
例如:
A的地址为:IP——192.168.10.1 ;MAC——AA-AA-AA-AA-AA-AA
B的地址为:IP——192.168.10.2 ;MAC——BB-BB-BB-BB-BB-BB
根据上面所讲的原理,简单说明这个过程:A要和B通信,A就需要知道B的以太网地址,于是A发送了一个ARP请求广播(谁是192.168.10.2,请告诉192.168.10.1),当B收到该广播时,就检查自己,结果发现和自己的IP一致,然后向A发送一个ARP单播应答(192.168.10.2在BB-BB-BB-BB-BB-BB).
(2) 不同网段
当主机A和主机B不在同一网段时,主机A就会先向网关发出ARP请求,ARP请求报文中的目标IP地址为网关的IP地址。当主机A从收到的响应报文中获得网关的MAC地址后,将报文封装并发给网关。如果网关没有主机B的ARP表项,网关会广播ARP请求,目标IP地址为主机B的IP地址,当网关从收到的响应报文中获得主机B的MAC地址后,就可以将报文发给主机B;如果网关已经有主机B的ARP表项,网关直接把报文发给主机B。
ARP一般可以用在主机扫描中,判断一个主机是否存在,此外通过ARP协议也可以实现ARP欺骗,感兴趣的自行了解。
4.3 ARP数据包
ARP报文结构:
- 帧类型:它的值为0x0800表示IP数据包,0x0806表示ARP包,0x8035表示RARP包;
- 硬件类型:表示硬件地址的类型,值为1表示以太网地址;
- 协议类型:表示要映射的协议地址类型,它的值为0x0800表示IP地址类型;
- 硬件地址长度和协议地址长度以字节为单位,对于以太网上的IP地址的ARP请求或应答来说,他们的值分别为6和4;
- 操作类型(op):1表示ARP请求,2表示ARP应答;
- 发送端MAC地址:发送方设备的硬件地址;
- 发送端IP地址:发送方设备的IP地址;
- 目标MAC地址:接收方设备的硬件地址;
- 目标IP地址:接收方设备的IP地址;
假设主机IP:192.168.0.155 MAC:08-00-3e-26-0a-5b,目标192.168.0.104,构建ARP请求数据包:
ff-ff-ff-ff-ff-ff 广播MAC |
08-00-3e-26-0a-5b | 0x0806 | 0x0001 | 0x0800 | 06 | 04 | 0x0001 | 08-00-3e-26-0a-5b | 192.168.0.155 |
00-00-00-00-00-00 目标MAC未知 |
192.168.0.104 |
我们S3C2440运行在小端模式下,即低位字节数据在低地址,高位字节数据在高地址。比如数据0x12345678,在内存中存储数据为:
0x78 | 0x56 | 0x34 | 0x12 |
低地址 | 高地址 |
然而网络字节序采用的大端模式,网络上传输的数据都是字节流,UDP/TCP/IP协议规定:把接收到的第一个字节当做高位字节看待,这就要求发送的第一个字节是高位字节。
4.4 ARP实现代码
(1) 构建ARP请求包
(2) DM9000发送ARP数据包
arp.h文件:
/************************************************************************** * * FileName : arp.c * Function : ARP协议实现 * Author : zy * *************************************************************************/ #ifndef __ARP_H__ #define __ARP_H__ #include "type.h" #include "dm9000.h" /******************************************************************************************************************/ #define SWAP(n) ((((u16)n & 0xff) << 8) | ((((u16)n >> 8) & 0xff))) /* 以太网头部结构体 */ typedef struct eth_header{ u8 d_mac[6]; u8 s_mac[6]; u16 frame_type; }ETH_HDR; /* ARP头部结构体 */ typedef struct arp_header{ ETH_HDR ethhdr; u16 hw_type; u16 protocol; u8 hwadd_len; u8 protoc_len; u16 opcode; u8 smac[6]; u8 sipaddr[4]; u8 dmac[6]; u8 dipaddr[4]; }ARP_HDR; /* IP头部结构体 */ typedef struct ip_hdr { ETH_HDR ethhdr; u8 vhl; u8 tos; u16 len; u16 ipid; u16 ipoffset; u8 ttl; u8 proto; u16 ipchksum; u8 srcipaddr[4]; u8 destipaddr[4]; }IP_HDR; /* UDP头部结构体 */ typedef struct udp_hdr { IP_HDR iphdr; u16 sport; u16 dport; u16 len; u16 udpchksum; }UDP_HDR; /* TFTP数据包结构体 */ typedef struct tftp_package { u16 opcode; u16 blocknum; u8 data[0]; }TFTP_PAK; /*网络协议类型*/ #define PROTO_ARP 0x0806 #define PROTO_IP 0x0800 #define PROTO_UDP 0x11 extern u8 host_mac_addr[6]; extern u8 mac_addr[6]; extern u8 ip_addr[4]; extern u8 host_ip_addr[4]; /* 函数声明 */ extern void arp_request(); /* ARP请求数据包 */ extern u8 arp_respond(u8* buf,u32 len); /* ARP响应数据包 */ #endif
arp.c文件:
/************************************************************************** * * FileName : arp.c * Function : ARP协议实现 * Author : zy * *************************************************************************/ #include "arp.h" #include "stdio.h" #include "string.h" /************************************************************************** * * Function : ARP请求数据包 * *************************************************************************/ void arp_request() { /* ARP数据包 */ ARP_HDR arpbuf; u8 unkown[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; /* 构成ARP请求包 */ memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6); //以太网目的地址 memcpy(arpbuf.ethhdr.s_mac,mac_addr,6); //以太网源地址 arpbuf.ethhdr.frame_type = SWAP(PROTO_ARP); //帧类型 arpbuf.hw_type = SWAP(1); //硬件类型 arpbuf.protocol = SWAP(0x0800); //协议类型 arpbuf.hwadd_len = 6; //硬件地址长度 arpbuf.protoc_len = 4; //协议地址长度 arpbuf.opcode = SWAP(1); //操作码 memcpy(arpbuf.smac,mac_addr,6); //发送端以太网地址 memcpy(arpbuf.sipaddr,ip_addr,4); //发送端IP地址 memcpy(arpbuf.dmac,unkown,6); //目的MAC地址 memcpy(arpbuf.dipaddr,host_ip_addr,4); //目的IP地址 /* 调用dm9000发送函数,发送请求包 */ dm9000_tx((u8*)&arpbuf,14+28); } /************************************************************************** * * Function : ARP响应数据包 * *************************************************************************/ u8 arp_respond(u8* buf,u32 len) { /* ARP数据包 */ ARP_HDR arpbuf; ARP_HDR* p = (ARP_HDR*)buf; u32 i = 0; if(len < 28) return 0; switch(SWAP(p->opcode)){ /* 对PC发到开发板的应答包解析 */ case 2: memcpy(host_ip_addr,p->sipaddr,4); printf("host ip is : "); for(i=0;i<4;i++) printf("%03d ",host_ip_addr[i]); printf("\n\r"); memcpy(host_mac_addr,p->smac,6); printf("host mac is : "); for(i=0;i<6;i++) printf("%02X ",host_mac_addr[i]); printf("\n\r"); break; /* 响应PC发到开发板的请求包 */ case 1: memcpy(arpbuf.ethhdr.d_mac,p->ethhdr.s_mac,6); //以太网目的地址 memcpy(arpbuf.ethhdr.s_mac,mac_addr,6); //以太网源地址 arpbuf.ethhdr.frame_type = SWAP(PROTO_ARP); //帧类型 arpbuf.hw_type = SWAP(1); //硬件类型 arpbuf.protocol = SWAP(0x0800); //协议类型 arpbuf.hwadd_len = 6; //硬件地址长度 arpbuf.protoc_len = 4; //协议地址长度 arpbuf.opcode = SWAP(2); //操作码 memcpy(arpbuf.smac,mac_addr,6); // 发送端以太网地址,即以太网原地址 memcpy(arpbuf.sipaddr,ip_addr,4); //发送端IP地址 memcpy(arpbuf.dmac,p->smac,6); memcpy(arpbuf.dipaddr,p->sipaddr,4); //目的IP地址 /* 调用dm9000发送函数,发送请求包 */ dm9000_tx((u8*)&arpbuf,14+28); break; default: break; } return 1; }
需要注意的是目标主机和开发板的IP地址设置,需要在同一个网段:
u8 host_mac_addr[6]={0xff,0xff,0xff,0xff,0xff,0xff}; /* 以太网目的地址 */ u8 host_ip_addr[4] = {192,168,0,104}; /* 目标ip*/ u8 mac_addr[6] ={0x08,0x00,0x3E,0x26,0x0A,0x5B}; /* 发送者mac地址 */ u8 ip_addr[4] = {192,168,0,155}; /* 发送者ip */ u16 packet_len; /* 接收到的数据长度 */ u8 dm9000_buffer[DM9000_PKT_MAX] = {0}; /* 数据缓冲区 */
五、代码下载
Young / s3c2440_project[11.dm9000】
参考文章
[2]DM9000中文文档