zoukankan      html  css  js  c++  java
  • Mini2440裸机开发之DM9000

    网络对于嵌入式系统来说必不可少。可是S3C2440没有集成以太网接口,所以要想使s3c2440具备以太网的功能,就必须扩展网卡接口。我们使用的Mini2440就是外接DM9000EP,使其可以与以太网相连接。Mini2440开发板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和HR911103A之间只有6根接线:

    DM900 HR911103A
    TXO+ TX+
    TXO- TX-
    RXI+ RX+
    RXI- RX-
    SPEED#、WACKUP、LINK_O NETLINK
    LINK_ACT# NETACT

    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
      
    物理层发送端口负极
      

    DM9000 LED引脚:

    60
     
    SPEED# O
      
    低电平指示100M带宽指示,高电平指示10M带宽
     
    61
      
    DUP# O 全双工指示LED
    LED模式0时,低电平显示工作在10M带宽,或在100M带宽浮动
    62
      
    LINK_ACT# O
     
    连接LED,在模式0时,只作物理层的载波监听检测连接状态

    HR911103A是网络插座变压器,使用RJ45接口,外接RJ4水晶头,也就是我们的网线。

    1.7 DM9000引脚介绍(使用的引脚标红)

    I=输入 O=输出 I/O=输入/输出 O/D=漏极开路 P=电源 LI=复位锁存输入 #=普遍低电位。

    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
      
    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 欧姆的下拉电阻

    电源引脚(使用):

    5,20,36,55,
    72,90,73
     
    DVDD
     
    P
      
    数字电源
    15,23,42,58
    63,81,99,76
    DGND P
     
    数字地

    时钟引脚(使用):

    21
      
    X2_25M
      
    O
      
    25M晶振输出
      
    22
      
    X1_25M
      
    I
      
    25M晶振输入
      
    59
      
    CLK20MO
     
    O
     
    20M晶振再生输出给外部MII设备,自带60K欧姆下拉电阻

    各种其他功能引脚:

    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位模式 00保留

    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上;

    地址线为什么只有一根,这是DM9000决定的,看手册可以知道CPU总线只访问它的两个地址,CMD管脚为0时,数据线送的是DM9000的寄存器地址,CMD管脚为1时,数据线上送的是16位的寄存器数据。

    所以对DM9000的操作至少需要两步:先写地址,再写(读)数据。它不像其他类内存总线那样直接把数据写到地址传输一次就可以了,而它要传输两次,因为它的总线地址就退化成了类似于NAND的ALE信号,就是地址和数据的区分信号了,而不是DM9000里面操作的寄存器地址。

    我们查看S3C2440的内存空间分布:

    可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。

    DM9000对于CPU的地址线只认识一根线,只要这根线对应的CPU传送的地址的对应位匹配就可以了,上面接到LADDR2,也就是说:

    传寄存器地址的时候操作的总线地址满足:

    • 以基地址起头(0x20000000),偏移基地址LADDR2为0(低四位:0000),也就是写的时候总线地址为: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,这个中断用于接收数据时触发的,高电平有效。

    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). 写入发送数据长度

    (4). 写入待发送数据

    • 将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中;
    • 利用循环,将数据写入数据端口;

    (5) 启动发送

    (6).等待发送完成

    (7).清除发送状态

    (8).清除中断状态(发送标志位)

    (9)开启接收中断

    /**************************************************************************
     *
     *  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;
        else {
            /*清除接收中断标志*/
            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芯片介绍

  • 相关阅读:
    构建之法阅读笔记03
    构建之法阅读笔记01
    构建之法阅读笔记02
    周总结06
    《大道至简》第一章伪代码
    《大道至简》观后感
    【leetcode】Valid Number
    【leetcode】Maximal Rectangle
    【Unity3D】Invoke,InvokeRepeating ,Coroutine 延迟调用,周期性调用
    【leetcode】Scramble String
  • 原文地址:https://www.cnblogs.com/zyly/p/15642093.html
Copyright © 2011-2022 走看看