程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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 IRegister)

名称

默认值

描述

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
View Code

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】

参考文章

[1]S3C2440裸机程序【3】DM9000A

[2]DM9000中文文档

[3]DM9000裸机驱动及ARP实现

[4]dm9000aep芯片介绍

posted @ 2021-12-04 22:45  大奥特曼打小怪兽  阅读(880)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步