1 总体介绍
DM9000是以太网MAC控制器。它有一个一般处理接口、一个10/100M自适应的PHY和4K DWORD值的SRAM。它的目的是在低功耗和高性能进程的3.3V与5V的支持宽带。
DM9000还提供了介质无关接口(MII)。该DM9000支持8位、16位和32位接口访问内部存储器,以支持不同的处理器。DM9000的PHY接口完全支持10MBps下3类、4类、5类非屏蔽双绞线和100MBps下5类非屏蔽双绞线。它的自动协调功能将自动完成配置以最大限度地适合其线路带宽。它还支持IEEE 802.3x全双工流量控制。
2 准备知识
物理层的任务接受一个原始的位流,并试图将它传递到目标机器。
PHY是物理层接口收发器,它实现物理层。包括MII(与介质无关接口)子层、PCS(物理编码子层)等其它子层。
PHY在发送数据的时候,接受MAC发过来的数据(对PHY来说,没有帧的概念,对它来说一切都是原始的位流)。然后每4bit增加1bit的检错码,然后把并行数据转化为串行流数据,再按照物理层的编码规则把数据编码,再变为模拟信号把数据送出去。
隔离变压器把PHY送出来的差分信号用差模耦合的线圈耦合滤波以增强信号,并通过电磁场的转换耦合到连接网线的另一端。
MII(与介质无关接口)。与介质无关表明在不对MAC硬件重新设计或交换的情况下,任何类型的PHY设备都可以正常工作。
MAC(介质访问控制协议)主要负责控制与连接物理层的物理介质,以实现无差错传输。在发送数据的时候,MAC协议可以事先判断是否可以发送数据,如果可以发送将给数据加上一些控制信息,最终将数据以及控制信息以规定的格式发送到物理层;在接收数据的时候,MAC协议首先判断输入的信息是否有传输错误,如果没有错误,则去掉控制信息发送至LLC层。
3 DM9000原理
DM9000是Davicom公司的一款以太网控制芯片,在网络中它可自动获得同设定MAC地址一致的IP包,完成IP包得收发,再用一个单片机来结合完成上层协议,就构成了一个完整的网络终端。在单片机中嵌入了一个精简TCP/IP协议栈。
3.1 DM9000结构框图
3.2 DM9000的PIN
DM9000的管脚分为8大类。
第一类:MII Interface
外部扩张接口,TQ2440没用。
第二类:Processor Interface
处理器(ARM)与DM9000的接口,包括:片选、地址、数据、命令、状态标志。
IO16:字命令标志,当内部存储器的总线是16位或32位时。
INT:中断请求。
第三类:EEPROM Interface
控制EEPROM的接口,TQ2440没有用。
第四类:Clock Interface
25M时钟接口。
第五类:LED Interface
第六类:10/100M PHY/Fiber
与网线相连的。
第七类:Miscellaneous
对TQ2440来说没什么用。
第八类:Power Pins
用在TQ2440上的管脚
管脚号 | 输入输出类型 | 管脚名称 | 管脚功能 |
1 | I | IOR# | 读命令 |
2 | I | IOW# | 写命令 |
3 | I | AEN | 地址使能 |
4 | O | IOWAIT | L表示命令等待,正常情况下时H。使用上拉电阻进行上拉,增强信号。 |
6~13, 89~82 | I/O | SD0~15 | 数据总线 |
92 | I | CMD | 命令类型: H:数据;L:地址 |
100 | O | INT | 中断请求 |
以下为不重要的 | |||
14(硬件) | I | RST | 复位(H)。被接地,故不能硬件复位。 |
93~98(硬件) | I | SA4~9 | 地址总线,选择DM9000。 在TQ2440中, SA9、SA8:H; SA7、SA6、SA5、SA4:L; |
80(硬件) | I | PW_RST# | 低电平有效,用于上电复位。 |
67(硬件) | I/O | EECS | 用于LED模式选择引脚。 H:模式1(TQ2440) L:模式0 |
62(LED灯) | O | LINK&ACT# | 与LED相连 (1)在LED mode1表示连接和内部PHY的载波侦测信号的运行情况 (2)在LED mode2 表示内部PHY的载波侦测信号的运行情况 |
60(LED灯) | O | SPEED100# | L表示内部PHY运行在100Mbps; H表示内部PHY运行在10Mbps |
29 | I | RXI+ | TP RX Input |
30 | I | RXI- | TP Rx Input |
33 | O | TXO+ | TP TX Output |
34 | O | TXO- | TP TX Output |
3.3 DM9000的寄存器
DM9000有许多的控制和状态寄存器,我们可以通过主机控制它。这些控制和状态寄存器(CSRs)是字节对齐的。所有的CSRs被硬件被设置为它们默认的值,也可以被软件设置为制定的值。
具体请参考DM9000的datasheet。
3.4 DM9000的初始化
(1)配置相关管脚
(2)进行ID测试
(3)配置寄存器
a) 激活内部PHY
b) 软件复位
c) 使能中断
d) 清除原网络状态和中断状态
e) 对发射和接收进行新的控制
f) 设置MAC地址
g) 清除原网络状态和中断状态
h) 使能中断
注意将MMU的BANK4的权限设为NCNB。
MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_NCNB);
DM9000初始化函数如下
- //================================================================
- //DM9000_Init()
- //1.将相关的管脚配置好
- //2.将相关的寄存器配置好
- //初始化寄存器主要做的事情:
- //1.复位
- //2.清楚原先的状态
- //3.对发射、接收进行控制
- //4.使能中断
- //在设置时,有些地方需要重复设置。
- //================================================================
- void DM9000_Init()
- {
- U8 i;
- //1.配置管脚
- EINT_INIT( EINT7, Rising_edge);
- //2.进行ID测试
- Test_DM9000_ID();
- //3.配置寄存器
- //3.1.激活内部PHY
- DM9000_Write(GPCR,0x01);//设置GPCR bit[0]=1,使DM9000为GPIO0为输出
- DM9000_Write(GPR,0x00);//GPR bit[0]=0,使GPIO0输出低电平以激活内部PHY
- uDelay(5000);
- //3.2 软件复位
- DM9000_Write(NCR,0x03); //软件复位,MAC内部循环反馈(?)
- uDelay(3000); //延时10us以上,等待软件复位完成
- DM9000_Write(NCR,0x00); //复位完成,设置正常工作模式
- DM9000_Write(NCR,0x03); //第二次软件复位。确保软件复位完全成功
- uDelay(3000);
- DM9000_Write(NCR,0x00);
- //3.3 使能中断
- DM9000_Write(IMR,0x80);//使能指向SRAM的指针的自动返回功能
- //3.4 清除原网络和中断状态
- DM9000_Write(NSR,0x2c); //清除各种状态标志位
- DM9000_Write(ISR,0xbf); //清除所有中断标志位,8-bit
- //3.5 对发射和接收进行新的控制
- // 对中断进行新的控制
- DM9000_Write(RXCR,0x39);//接收控制
- DM9000_Write(TXCR,0x00);//发送控制
- DM9000_Write(BPTR,0x3f);//设置RX的最低阀值,小于将产生拥塞
- DM9000_Write(FCTR,0x00);//接收FIFO门限3K,8K
- DM9000_Write(FCR,0xff);//启动一些控制功能
- DM9000_Write(SMCR,0x00);//未启动特殊模式
- //3.6 设置MAC地址
- for(i=0; i<6; i++)
- DM9000_Write(PAR+i, mac_addr[i]);
- //3.7 清除原网络状态和中断标志位
- DM9000_Write(NSR,0x2c); //清除各种状态标志位
- DM9000_Write(ISR,0x3f); //清除所有中断标志位,8-bit
- //3.8 使能中断
- DM9000_Write(IMR,0x81);//使能指向SRAM的指针满后自动返回功能。
- //使能数据包接收中断
- Uart_Printf("DM9000初始化结束");
- /MAC地址///
- Uart_Printf("\r\nMAC = %x",DM9000_Read(PAR)&0x00ff);
- Uart_Printf(" %x",DM9000_Read(PAR+1)&0x00ff);
- Uart_Printf(" %x",DM9000_Read(PAR+2)&0x00ff);
- Uart_Printf(" %x",DM9000_Read(PAR+3)&0x00ff);
- Uart_Printf(" %x",DM9000_Read(PAR+4)&0x00ff);
- Uart_Printf(" %x\r\n",DM9000_Read(PAR+5)&0x00ff); }
3.5 DM9000的数据包发送函数
3.5.1 思路
抓住主干;step by step。
3.5.2 发送数据包的过程
DM9000发送数据总共包括两个过程:(1)发送过程;(2)状态职置位过程。
首先,我们看发送过程:
a) 检测内存数据位宽
b) 将数据写入TX SRAM
c) 将传输的长度写入MDRAL & MDRAH
d) 将TXCR的TXREQ置为1,当数据发送完毕后将TXREQ置0(这个标志位可以用来检测数据是否发送完毕)。
然后,我们看状态置位过程:
当数据包被发送完毕后,
a) 系统会将ISR(reg_FEH)的bit[1]即PTS置1(之前必须设置IMR的PTM位为1,即使能数据包发送)。这个状态位可以用在中断服务函数中。
b) 系统会使NSR(reg_01)的bit[2]即TX1END置1。这个中断位可以用在轮询中。
注意:内存数据位宽是可以通过ISR的bit[7:6]设置的,可以设置为8bit, 16bit, 32bit。
3.5.3 数据包发送函数
- //================================================================
- //DM9000_send_Packet
- //DM9000 packet发送函数
- //================================================================
- void DM9000_sendPacket(U8 *data_str, U32 length)
- {
- U32 len; //数据的长度
- U32 i;
- U8 tmp;
- //==========数据发送开始=============//
- Uart_Printf("\r\nDM9000\r\n发送数据...\r\n");
- //1.关闭网卡中断
- DM9000_Write(IMR,0x80); //先禁止网卡中断,防止在发送数据时被中断干扰。
- //2.将传输长度写到TXPLH和TXPLL中
- len = length; //将发送长度写入
- DM9000_Write(TXPLH,(len>>8)&0x0ff);
- DM9000_Write(TXPLL,len & 0x0ff);
- //3.将数据写入TX SRAM中,通过MWCMD。
- rDM9000_ADDR = MWCMD; //将Memory Write CMD发送到ADD上
- for(i=0; i<len; i+=2)
- {
- uDelay(2);
- DM9000_WriteC_DATA(data_str[i] | (data_str[i+1]<<8));
- }
- //4.将TXCR的TXREQ置位
- DM9000_Write(TXCR, 0x01);
- //5.等待数据发送完成
- while(1)
- {
- U8 status;
- status = DM9000_Read(TXCR);
- if((status & 0x01) == 0x00)
- break;
- }
- //Uart_Printf("\r\n数据发送完毕,进入检测阶段\r\n");
- //==========数据发送结束=============//
- //==========状态置位开始=============//
- tmp = DM9000_Read(NSR);
- //Uart_Printf("\r\nNSR = %x\r\n",tmp);
- if(tmp && 0x04)
- {
- if((TSRI&0xfc) == 0x00)
- ;//Uart_Printf("TSRI成功\r\n");
- else
- ;//Uart_Printf("TSRI失败\r\n");
- }
- DM9000_Write(NSR,0xfc); //清除状态寄存器,以防止对下一次的干扰。
- //由于发送数据没有设置中断,因此不必处理中断标志位。
- DM9000_Write(IMR,0x81); //DM9000网卡的接收中断使能。
- Uart_Printf("\r\n数据发送完毕\r\n");
- }
3.6 数据包接收函数
3.6.1 DM9000接收数据包结构
第一个字节表明其后是否还有数据包;
第二个字节和接收状态寄存器(RXSR)的值相同;
第个和第四个为接收数据包的长度。
其它字节为接收数据包。
3.6.2 DM9000接收数据包过程
DM9000接收数据包共包括两个过程:(1)接收过程;(2)状态置位过程。
接收过程如下:
(1)通过MRCMDX读数据包的第一个字节,并辨别其后是否有数据包;
(2)驱动IO的宽度;
(3)通过MRCMD读数据包的第二和第三字节,得到RXSR的值和接收数据包的长度;
(4)接收数据包
状态置位过程:
数据包接收完成后,会置位ISR的第一位置为1;
3.6.3 DM9000的数据包接收函数
- //================================================================
- //DM9000_received_Packet
- //DM9000 packet接收函数
- //================================================================
- U32 DM9000_receivePacket(U8 *data_str)
- {
- U8 RX_First_byte=0;//RX SRAM的第一个字节的值
- U8 RX_status=0;//寄存器RXSR的值
- U32 RX_length=0;//接收数据包的长度
- U16 data_temp=0;
- U32 i;//计数用
- //读取RX SRAM的第一个字节
- RX_First_byte = DM9000_Read(MRCMDX);//读取RX SRAM的第一个字节的值
- Uart_Printf("RX_First_byte = 0x%x",RX_First_byte); //第一次读取经常得到00
- RX_First_byte = DM9000_Read(MRCMDX);//第二次读取一般能得到数据
- Uart_Printf("RX_First_byte = 0x%x",RX_First_byte);
- //读取RX SRAM的第二、三、四个字节
- if(RX_First_byte == 0x01)
- {
- U8 io_mode = DM9000_Read(ISR)>>6;
- rDM9000_ADDR = MRCMD; //将Memory Write CMD发送到ADD上
- if(io_mode == 0)//IO word mode
- {
- RX_First_byte = DM9000_ReadC_DATA();//读取接收状态寄存器的值
- Uart_Printf("\r\nRX_First_byte = %d\r\nRX_status = %d\r\n",RX_First_byte&0x00ff,(RX_status>>8)&0x00ff);
- RX_length = DM9000_ReadC_DATA();//读取接收状态寄存器的值
- Uart_Printf("\r\nRX_length = %d\r\n",RX_length);;
- }
- else if(io_mode == 1)//IO dword mode
- {
- }
- else if(io_mode == 2)//IO byte mode
- {
- }
- //读取接收的数据包
- for(i=0;i<RX_length;i=i+2)
- {
- //uDelay(20);
- data_temp = DM9000_ReadC_DATA();//读取到的16bit的数据
- data_str[i] = data_temp&0x00ff;
- //Uart_Printf("data_str[%d] = %x",i,data_str[i]);
- Uart_Printf(" %x",data_str[i]);
- data_str[i+1] = (data_temp >> 8)&0x00ff;
- //Uart_Printf("data_str[%d] = %x",i+1,data_str[i+1]);
- Uart_Printf(" %x",data_str[i+1]);
- }
- //Uart_Printf("\r\nRX Data :%s.\r\n",data_str);//接收的数据
- //Uart_Printf("\r\nRX_length is %ld\r\n",RX_length);//接收的数据的长度
- return RX_length;
- }
- else
- {
- Uart_Printf("无数据包");
- return -1;
- }
- }
1 ARP协议简介
ARP协议是Address Resolution Protocol(地址解析协议)的缩写。所谓的“地址解析”就是主机在发送帧前将目标IP地址转换为目标MAC地址的过程。
ARP协议的基本功能就是通过目标设备的IP地址(32位),查询目标设备的MAC地址(48位),以保证通信的顺利进行。
2 ARP协议的数据结构
- typedef struct _arp_hdr //以太网头部 + ARP首部结构
- {
- ETH_HDR ethhdr;//以太网首部
- U16 hwtype;//硬件类型(1表示传输的是以太网MAC地址)
- U16 protocol;//协议类型(0x800表示传输的IP地址)
- U8 hwlen;//硬件地址长度(6)
- U8 protolen;//协议地址长度(4)
- U16 opcode;//操作(1表示ARP请求,2表示ARP应答)
- U8 s_mac[6];//发送者MAC地址
- U8 s_ipaddr[4];//发送者IP地址
- U8 d_mac[6];//目的端MAC地址
- U8 d_ipaddr[4];//目的端IP地址
- }ARP_HDR;
3 发送ARP请求数据包
3.1 发送ARP数据包的过程
(1)为ARP协议的数据包的相关值进行赋值。
(2)设置发送的数据包的长度
(3)发送数据包
3.2 发送ARP数据包的函数
- void arp_request(void) //发送ARP请求数据包
- {
- ///经验发现:每次发射前,需要进行一定的初始化
- //3.2 软件复位
- DM9000_Write(NCR,0x03); //软件复位,MAC内部循环反馈(?)
- uDelay(3000); //延时10us以上,等待软件复位完成
- DM9000_Write(NCR,0x00); //复位完成,设置正常工作模式
- DM9000_Write(NCR,0x03); //第二次软件复位。确保软件复位完全成功
- uDelay(3000);
- DM9000_Write(NCR,0x00);
- //3.3 使能中断
- DM9000_Write(IMR,0x80);//使能指向SRAM的指针的自动返回功能
- //3.4 清除原网络和中断状态
- DM9000_Write(NSR,0x2c); //清除各种状态标志位
- DM9000_Write(ISR,0xbf); //清除所有中断标志位,8-bit
- //3.5 对发射和接收进行新的控制
- // 对中断进行新的控制
- DM9000_Write(RXCR,0x39);//接收控制
- DM9000_Write(TXCR,0x00);//发送控制
- DM9000_Write(BPTR,0x3f);//设置RX的最低阀值,小于将产生拥塞
- DM9000_Write(FCTR,0x00);//接收FIFO门限3K,8K
- DM9000_Write(FCR,0xff);//启动一些控制功能
- DM9000_Write(SMCR,0x00);//未启动特殊模?
- ///
- ARPBUF = (ARP_HDR*)&Buffer; //ARPBUF指向Buffer
- //为ARPBUF赋值
- memcpy(ARPBUF->ethhdr.d_mac,host_mac_addr,6);
- memcpy(ARPBUF->ethhdr.s_mac,mac_addr,6);
- ARPBUF->ethhdr.type = HON(0x0806);
- ARPBUF->hwtype = HON(1);
- ARPBUF->protocol = HON(0x0800);
- ARPBUF->hwlen = 6;
- ARPBUF->protolen =4;
- ARPBUF->opcode = HON(1);
- memcpy(ARPBUF->s_mac, mac_addr,6);
- memcpy(ARPBUF->s_ipaddr, ip_addr,4);
- memcpy(ARPBUF->d_mac, host_mac_addr,6);//
- memcpy(ARPBUF->d_ipaddr, host_ip_addr,4);
- packet_len = 42;//14+28
- //将ARP发送出去
- DM9000_sendPacket(Buffer, packet_len);
- }
4 ARP协议接收处理函数
4.1 ARP协议接收数据包的处理过程
(1)根据 HON(ARPBUF->ethhdr.type)判断所接受的数据包是否符合ARP协议
(2)根据HON(ARPBUF->opcode)判断接收的是请求还是应答。
4.2 ARP协议接收处理函数
- U8 arp_process(void) //ARP接收函数,成功返回1,否则返回0
- {
- ARPBUF = (ARP_HDR*)&RX_Buffer; //ARPBUF指向Buffer
- if( HON(ARPBUF->ethhdr.type) != 0x0806)
- {
- Uart_Printf("非ARP协议包");
- return 0;
- }
- else
- {
- Uart_Printf("\r\n收到的为ARP协议包\r\n");
- }
- //简单判断ARP数据包是否有损坏,有损坏则丢弃,不予处理
- if(packet_len < 28) //数据包长度小于28个字节,为无效数据。
- {
- Uart_Printf("未进入,数据包长度小");
- return 0;
- }
- switch (HON(ARPBUF->opcode) )
- {
- case 1: //处理ARP请求
- if(
- //判断是否是自己的IP,是否向自己询问MAC地址
- (ARPBUF->d_ipaddr[0] == ip_addr[0]) &&
- (ARPBUF->d_ipaddr[1] == ip_addr[1]) &&
- (ARPBUF->d_ipaddr[2] == ip_addr[2]) &&
- (ARPBUF->d_ipaddr[3] == ip_addr[3]) )
- {
- ARPBUF->opcode = HON(2); //设置为ARP应答
- memcpy(ARPBUF->ethhdr.d_mac,ARPBUF->ethhdr.s_mac,6);
- memcpy(ARPBUF->ethhdr.s_mac,mac_addr,6);
- memcpy(ARPBUF->s_mac, mac_addr,6);
- memcpy(ARPBUF->s_ipaddr, ip_addr,4);
- memcpy(ARPBUF->d_mac, ARPBUF->s_mac,6);//
- memcpy(ARPBUF->d_ipaddr, ARPBUF->s_ipaddr,4);
- packet_len = 42;
- DM9000_sendPacket(RX_Buffer,packet_len); //发送ARP数据包
- Uart_Printf("TQ接收到ARP请求包");
- return 1;
- }
- else
- {
- return 0;
- }
- break;
- case 2: //处理ARP应答
- //Uart_Printf("\r\nARPBUF->d_ipaddr[3]=%d\r\n",ARPBUF->d_ipaddr[3]);
- //Uart_Printf("\r\n ip_addr[3]=%d\r\n",ip_addr[3]);
- if(//再次判断是否是自己的IP,是否向自己询问MAC地址
- (ARPBUF->d_ipaddr[0] == ip_addr[0]) &&
- (ARPBUF->d_ipaddr[1] == ip_addr[1]) &&
- (ARPBUF->d_ipaddr[2] == ip_addr[2]) &&
- (ARPBUF->d_ipaddr[3] == ip_addr[3])
- )
- {
- memcpy(host_mac_addr,ARPBUF->s_mac,6);//保存服务器MAC地址
- Uart_Printf("TQ接收到ARP应答包");
- return 1;
- }
- else
- {
- return 0;
- }
- break;
- default://不是ARP协议
- Uart_Printf("进入default");
- return 0;
- break;
- }
- }