四十.DM9000网卡搭建

一.网络模型

1.OSI(开放式系统互联模型)


从上往下依次是:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层。

2.Linux四层模型:从上往下依次是应用层,传输层,IP层,网络接口层。基于TCP/IP协议栈的模型。


二.DM9000物理结构
1.原理图

工作实质就是MAC通过MII控制PHY的过程。

2.网卡和网络模型的映射关系


MAC对应的是数据链路层,PHY对应的是物理层

3.MAC的工作原理

当网络协议栈的IP包送到网卡的时候,先要到达MAC,MAC就根据数据链路层的协议对接收到的数据进行封装,将IP包封装成以太网包,完成数据帧的构建。当然它还具备数据纠错以及传送控制等功能。

4.关于PHY
PHY是物理接口收发器。主要和实际的传输硬件打交道。他接收到来自MAC的以太网包,先加上校检码。然后按照物理层的规则进行数据编码,然后传输到物理介质,接受过程则与之相反。

5.MII即媒体独立接口。表明在MAC一定情况下,更换PHY是不会影响系统的工作的。因为他们最后都要遵循MII接口。故MII起到了MAC和PHY之间通信的桥梁作用。


三.DM9000的编程接口
1.DM9000的接口不是绝对开放的,不能像访问nand控制器那样直接按照地址去访问相关寄存器。

2.但是他提供了两个可以供CPU访问的接口,一个是index另一个是数据端口。

3.index的地址在mini2440上是0x20000300.原因是
(1)mini2440的原理图中

dm9000的片选信号AEN就是接到nLAN_CS片选。再看CPU原理图


我们可以看到nLAN_CS实质接在nGCS4上。再看datasheet


可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。所以index的地址开头是0x2********。即片选地址


(2)再看DM9000的datasheet

再看DM9000的TXD【2:0】的引脚接线情况

发现都是0,所以I/O base的地址是300H。即相对片选地址的位置。(相对地址,片选地址相当于基地址)。


(3)不管是index端口还是数据端口都是用SD0-SD15来传递数据。所以要区分具体某一时刻,这些数据到底是给那一个接口用。于是利用CMD引脚来区别。当CMD引脚是高电平的时候,SD上的数据是给数据接口用的,当CMD是低电平的时候,是给index接口用的。index接口是用来传递偏移量的。可以看到CMD接到CPU的ADDR2,当他为1的时候,就是为数据接口传送数据,即地址是0x20000304.当ADDR2为0的时候,SD是为index接口传送偏移量,即ADDR2为0,即地址是0x20000300.故我们找到了,片选地址,I/O base地址,以及数据接口地址。

四.DM9000初始化
1.片选信息设置
(1)数据宽度(BWSCON)
(2)时序信号填写(BANKCON4)

void dm9000_cs()
{
	/*1.数据宽度设置*/
	
	BWSCON &= ~(3 << 16);
	BWSCON |= (1 <<16);
	
	/*2.时序信号设置*/
	
	BANKCON4 = (0 << 13) | (0 << 11) | (7 << 8) | (1 << 6) | (0 << 4) | (0 << 2) | (0 << 0);
		
}

2.中断初始化
(1)从原理图找到DM9000使用的中断源
(2)配置相应的中断引脚
(3)设置中断触发方式(高电平,EXTINT0)
(4)使能中断,设置中断屏蔽寄存器(INTMSK,EINTMSK)
(5)清除中断标志(SRCPND,INTPND,EINTPND)
void dm9000_int_init()
{
	/*1.设置中断引脚工作模式*/
	GPFCON &= ~(3 << 14);
	GPFCON |= (2 << 14);
	
 	
	/*2.配置中断触发方式*/
	EXTINT0 &= ~(7 << 28);
	EXTINT0 |= (1 << 28);
	
	
	
	/*3.使能中断*/
	EINTMASK &= ~(1 << 7);
	INTMSK &= ~(1 << 4);	
	
	/*4.清除之前的中断标志*/
	EINTPEND |= (1 << 7);
	SRCPND |= (1 << 4);
	INTPND |= (1 << 4);	
}

3.复位设备
(1)实现往DM9000读写数据的函数
(2)设置I/O为输出模式
(3)通过对GPIO0写0为内部的PHY提供电源
(4)软件复位(自动清0),MAC内部回环模式
(5)对(4)中的寄存器全部写入0
(6)重复(4)(5)
void dm9000_write(u16 reg,u16 data)
{
    DM_ADD = reg;	
    DM_DAT = data;	
}

u8 dm9000_read(u16 reg)
{
    DM_ADD = reg;
    return DM_DAT;	
}
void dm9000_reset()
{
	/*1.设置I/O为输出模式*/
	dm9000_write(DM9000_GPCR, GPCR_GPIO0_OUT);
	
	
	/*2.通过对GPIO0写入0为内部的PHY提供电源*/
	dm9000_write(DM9000_GPR, 0);
	
	
	/*3.软件复位(自动清0),MAC内部回环模式*/
	dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
	
	
	/*4.对上一步的寄存器写入全0*/
	dm9000_write(DM9000_NCR, 0);
	
	/*5.重复(3)(4),用两次实现真正复位*/	
	dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
	dm9000_write(DM9000_NCR, 0);
}

4.捕获网卡
(1)读取厂家ID
(2)读取product的ID
(3)将两个ID组合与之前预定义的网卡ID进行对比
u8 dm9000_probe()
{
	u32 id_val;
	/*1.读取厂家ID*/
	id_val = dm9000_read(DM9000_VIDL);
	id_val |= dm9000_read(DM9000_VIDH) << 8;
	
	/*2.读取产品ID并将其和厂家ID组合*/
	id_val |= dm9000_read(DM9000_PIDL) << 16;
	id_val |= dm9000_read(DM9000_PIDH) << 24;
	
	
	if (id_val == DM9000_ID) {
		printf("dm9000 is found !\n\r");
		return 0;
	} else {
		printf("dm9000 is not found !\n\r");
		return -1;
	}	
}
5.MAC初始化
参照u-boot设置MAC
void dm9000_mac_init()
{
	/* Program operating register, only internal phy supported */
	dm9000_write(DM9000_NCR, 0x0);
	/* TX Polling clear */
	dm9000_write(DM9000_TCR, 0);
	/* Less 3Kb, 200us */
	dm9000_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
	/* Flow Control : High/Low Water */
	dm9000_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
	/* SH FIXME: This looks strange! Flow Control */
	dm9000_write(DM9000_FCR, 0x0);
	/* Special Mode */
	dm9000_write(DM9000_SMCR, 0);
	/* clear TX status */
	dm9000_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
	/* Clear interrupt status */
	dm9000_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
}

6.填充MAC地址
利用循环分别填写六个寄存器,对应MAC地址的6个段

void dm9000_fill_macadd()
{
	u16 oft = 0,i = 0;
	/* fill device MAC address registers */
	for (i = 0; i < 6; i++)
		dm9000_write(DM9000_PAR + i, mac_addr[i]);
		
	/*maybe this is some problem*/
	for (i = 0, oft = 0x16; i < 8; i++, oft++)
		dm9000_write(oft, 0xff);
		/* read back mac, just to be sure */
	for (i = 0, oft = 0x10; i < 6; i++, oft++)
		printf("%02x:", dm9000_read(oft));
	printf("\n\r");

}
7.激活DM9000
参照u-boot设置相关寄存器即可。
void dm9000_active()
{
	/* RX enable */
	dm9000_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);//要加上RCR_ALL,否则他会自动丢弃来自外部的广播包
	/* Enable TX/RX interrupt mask */
	dm9000_write(DM9000_IMR, IMR_PAR);
		
}


8.初始化代码
void dm9000_init()
{
	/*1.片选信息设置*/
	dm9000_cs();
	
	
	/*2.中断初始化*/
	dm9000_int_init();
	
	
	/*3.复位设备*/
	dm9000_reset();
	
	/*4.捕获网卡*/
	if (dm9000_probe() < 0)
		return ;
	
	/*5.MAC初始化*/
	dm9000_mac_init();

	
	
	/*6.填充MAC地址*/
	dm9000_fill_macadd();
	
	
	
	/*7.激活DM9000*/
	dm9000_active();
		
}

五.发送函数(有两个参数,待发送数据和发送长度)
1.禁止中断,避免干扰
(1)DM9000的IMR寄存器。

2.写入发送数据的长度
将长度分两次写入寄存器


3.写入待发送的数据
(1)将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中。
(2)利用循环,将数据写入数据端口


4.启动发送
往TCR寄存器中写入命令,请求开始发送

5.等待发送结束
当发送结束的时候,TCR的0位会自动清0,所以去等待他变0即可。


6.判断发送结果是否正确,清除发送状态
往NSR寄存器中写入0x2C即可

7.打开中断,等待接收数据
将IMR的中断打开即可。(设置最后一位为1即可)


void dm9000_tx(u8* data, u32 length)
{
	u32 i = 0;
	/*1.禁止中断。避免干扰*/
	dm9000_write(DM9000_IMR,0x80);
	
	/*2.清除发送标志位*/
	dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
	
	/*3.写入发送数据长度*/
	dm9000_write(DM9000_TXPLL, length & 0xff);
    	dm9000_write(DM9000_TXPLH, (length >> 8) & 0xff);
	
	/*4.写入待发送数据*/
	DM_ADD = DM9000_MWCMD;
   
	for(i=0;i<length;i+=2)
	 {
	    	DM_DAT = data[i] | (data[i+1]<<8);
	 }
	
	/*5.启动发送*/
	dm9000_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
	
	/*6.等待发送完成*/
	while(dm9000_read(DM9000_TCR) & 0x01);
	
	
	/*7.清除发送状态*/
	dm9000_write(DM9000_NSR,0x2c);
	
	/*8.清除发送标志位*/
	dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */

	
	/*9.使能接收中断*/
	dm9000_write(DM9000_IMR,0x81);
	
}


六.接收函数
接收是中断处理的,接收到一个包就会产生中断。在中断处理的时候调用接收函数。

1.判断是否产生中断,是就继续,否则退出接收函数
读取ISR寄存器第0位即可。

2.清除中断
ISR寄存器第0位写1即可。


3.空读
读取MRCMDX寄存器


4.读取包的状态和长度
读取MRCMD寄存器得到状态,此时地址端口的数据就是对应MRCMD的偏移量,所以可以直接读取此时数据寄存器的值,不用再重新指定偏移量,就可以得到长度。在长度后面会自动送入有效的数据,所以后面可以页直接读数据寄存器得到有效数据。


5.读取包的数据
在读取数据之前应该对读到的长度进行检查,看是否小于以太网包的最大长度。然后利用for循环读取数据,注意数据的组合方式。

u16 dm9000_rx(u8* data)
{
	u32 i = 0;
	u16 status;
	
	u16 tmp,len;
	u8 ready;
	
	/*1.判断是否产生中断*/
	if (!(dm9000_read(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
		return 0;

	/*2.清除中断*/
	dm9000_write(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
	
	
	/*3.空读*/
	ready = dm9000_read(DM9000_MRCMDX);
	/*if((ready & 0x01) != 0x01){
		ready = dm9000_read(DM9000_MRCMDX);
    		if ((ready & 0x01) != 0x01)
    	    		return 0;	
	}*/
	
	
	/*4.读取包的状态和长度*/
	status = dm9000_read(DM9000_MRCMD);
	len = DM_DAT;
	
	/*5.读取包的数据*/
	if(len<DM9000_PKT_MAX)
	{
	       for(i=0;i<len;i+=2)
	       {
	           tmp = DM_DAT;
	           data[i] = tmp & 0x0ff;
	           data[i+1] = (tmp>>8)&0x0ff;
	       }
	}
	return len;
}

七.中断处理函数
1.调用接收函数存放接收到的数据

2.清除中断标志(SRCPND,INTPND,EINTPND)。

void int_issue()
{
    
    packet_len = dm9000_rx(&buffer[0]);
    
    net_handle(&buffer[0],packet_len);	
    
    EINTPEND |= (1 << 7);
    SRCPND |= (1 << 4);
    INTPND |= (1 << 4);	
}

3.中断文件的关键代码


else if(var1 == (4)){
			val = (*(EINTPEND))&(1 << 7);
			switch(val){
				
				case (1<<7):
					int_issue();
					break;
				default:
					break;	
			}	
	}		

3.关于ARP函数的实现请看下文
void dm9000_arp()
{
    while(1)
        arp_request();
}


八.ARP协议实现

1.以太网通讯格式
在计算机网络中,根据不同层的协议对要传输的数据进行封装,最重要实现的协议通常是以太网协议,即数据链路层协议。以太网包格式。

2.ARP功能简介
在计算机网络中,MAC地址是物理层的地址,是计算机唯一的身份标识。两台计算机要实现通信,必须知道彼此的MAC地址,但是一般情况用户只知道目标机的IP地址,这个时候,就可以用ARP(地址解析协议)来向局域网中的所有计算机发送ARP请求包,当然这个请求包有目标机的IP地址,符合条件的目标机接收到请求包以后,会作出应答,不符合条件的忽略该请求包。在应答包中目标机会将自己的MAC地址反馈给用户。所以ARP是一种通过IP地址获取MAC地址的协议。

3.ARP包格式


4.代码实现
(1)发送ARP请求包
--》构造请求包
          --》ARP帧类型是0806
--》当未知MAC地址的时候,设置为全F
--》硬件类型是指硬件地址对应的类型,即以太网类型,编号为1
--》协议类型是指协议地址对应的类型,在这里协议地址是IP地址,IP类型的编号是0800
--》硬件地址长度即以太网地址长度,6个字节
--》协议地址长度即Ip地址长度4个字节
--》op为1表示请求,为0表示应答
--》发送端以太网地址就是以太网源地址,事先制定的,6个字节
--》发送端Ip地址是事先制定的,4个字节,注意:要和PC在同一网段
--》目的以太网地址,暂时不填,要获取的就是该数据
--》目的Ip地址就是PC的Ip实际地址。

--》调用DM9000发送函数发送包
          --》可以讲上述构建的ARp请求包的地址赋值给u8 的指针,让该数组指向该请求包
          --》然后发送长度为:(以太网)14 + (ARP真正请求部分)28.
--》头文件对数据结构进行创建

#ifndef __ARP_H__
#define __ARP_H__

typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;

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

ARP_HDR arpbuf;


/*网络协议类型*/
#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 u16 packet_len;
#endif

那些extern变量在dm9000.c里定义,根据自己PC的IP来决定host_ip_addr
u8 buffer[1500];

u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
u8 mac_addr[6] = {9,8,7,6,5,4};
u8 ip_addr[4] = {192,168,1,30};//根据自己的电脑设置,要和电脑IP在同一网段
u8 host_ip_addr[4] = {192,168,1,100};//根据自己的电脑的IP设置
u16 packet_len;

(2)在网络中默认主机是大端格式的,涉及到网络字节序。所以在发送端存储数据的时候要按照大端格式存储。所以整个包都要征对象的修改,地址可以不用改,但是数据要改。只针对由两个字节构成的数据,一个字节长度的不用改。涉及到地址都会用到memcpy函数。
#include "arp.h"


void arp_request()
{
	/*1.构成ARP请求包*/
	
	memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6);	//以太网目的地址
	memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);		//以太网源地址
	arpbuf.ethhdr.frame_type = SWAP(0x0806);		//帧类型
	
	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.dipaddr,host_ip_addr,4);		//目的IP地址
	
	
	packet_len = 14+28;				//包的长度
	
	/*2.调用dm9000发送函数,发送请求包*/	
        dm9000_tx((u8*)&arpbuf,packet_len);
}

(3)解析ARP应答包,提取目标机MAC地址。
--》返回类型为u8
--》打印信息时候注意格式控制

u8 arp_respond(u8* buf,u32 len)
{
	
	ARP_HDR* p = (ARP_HDR*)buf;
	u32 i = 0;
	
	if(packet_len < 28)
		return 0;
		
	switch(SWAP(p->opcode)){
		case 2://对PC发到开发板的应答包解析
			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;
		case 1://响应PC发到开发板的请求包
			memcpy(arpbuf.ethhdr.d_mac,p->ethhdr.s_mac,6);	//以太网目的地址
			memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);		//以太网源地址
			arpbuf.ethhdr.frame_type = SWAP(0x0806);		//帧类型
			
			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地址
			
			
			packet_len = 14+28;				//包的长度
			
			/*2.调用dm9000发送函数,发送请求包*/	
        		dm9000_tx((u8*)&arpbuf,packet_len);
		
			break;
			
		default:
			break;	 
		 
	}
    	
    	return 1;
} 
(4)抓包效果
--》我的PC的参数



--》Wireshark的效果

表明发送请求成功
--》看串口输出信息


表明解析成功。所以网卡驱动以及ARP协议都实现了。
注意:抓包的时候,必须让PC和开发板连接到用一个路由器。而且这个路由器最好不要联通外网,否则抓包效果不明显,因为很可能一旦接通外网,有很多计算机和你会在同一网段,你会接受到来自其他计算机的广播,不一定是ARP广播。


附上6410代码
/****************************
@File:dm9000.c
@
@Tiny6410裸机下学期代码
@网卡配置文件
@Author:小君君
@****************************/

#include "common.h"
#include "dm9000.h"
#include "arp.h"



u8 buffer[1536];

u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
u8 mac_addr[6] = {9,8,7,6,5,4};
u8 ip_addr[4] = {192,168,1,30};//根据自己的电脑设置,要和电脑IP在同一网段
u8 host_ip_addr[4] = {192,168,1,112};//根据自己的电脑的IP设置
u16 packet_len;
u16 dm9000_rx(u8* data);

extern void net_handle(u8* buf,u32 len);
void 
dm9000_handle(void)
{
	
	//printf("dm9000 int\r\n");
	packet_len = dm9000_rx(&buffer[0]);
    
        net_handle(&buffer[0],packet_len);
}


/*片选信息设置,就是SROM1的时序信息以及数据宽度的选择*/
void 
dm9000_cs()
{
	/*Not using UB/LB
	  WAIT disable
	  Data bus width control for Memory Bank1===》16bit*/
	(vi SROM_BW) &= ~(3 << 6);
	(vi SROM_BW) |= (1 << 4);
	
	/*Tacs = 2-cycle Tacp = 2-cycle
	  Tcos = 2-cycle Tcoh = 2-cycle
	  Tacc = 3-cycle Tcah = 2-cycle*/
	(vi SROM_BC1) |= ((2 << 4) | (2 << 8) | (2 << 12) | (2 << 16) | (2 << 24) | (2 << 28));	
}

/*中断初始化,用的是EINT7,中断处理函数在irq文件已初始化*/
void 
dm9000_int_init()
{
	
	
	/*1.设置中断引脚的工作模式*/
	(vi GPNCON) &= ~(3 << 14);
	(vi GPNCON) |= (0x1 << 15);
	

	/*2.配置中断触发方式*/
	(vi EXT_INT_0_CON) &= ~(0x7 << 12);/*高电平触发*/
	(vi EXT_INT_0_CON) |= (1 << 12);
	
	/*3.使能中断*/
	(vi EXT_INT_0_MASK) = 0; /*使能EINT0--EINT27所有的外部中断*/	
	(vi VIC0INTENABLE) |= (1 << 1); /* bit0: eint0~3, bit1: eint4~11 ,我们用到EINT7*/
	
	/*4.清除之前的中断标志*/
	(vi EXT_INT_0_PEND) = ~0x0;  
    	(vi VIC0ADDRESS) = 0; 
    	
	/*5.注册中断处理函数*/
	(vi EINT1_VECTADDR) = (unsigned long)dm9000_handle;
	
	
	/*6.开启中断模式*/
	__asm__( 
	    "mov r0, #0x53\n"
	    "msr CPSR_cxsf, r0\n"           
	    : 
	    : 
  	);
}

/*向指定的寄存器写入数据*/
void 
dm9000_write(u16 reg,u16 data)
{
	(*(volatile u16 *)DM_ADD) = reg;
	(*(volatile u16 *)DM_DAT) = data;
}


/*读取数据*/
u8
dm9000_read(u16 reg)
{
	
	(*(volatile u16 *)DM_ADD) = reg;
	
	return (*(volatile u16 *)DM_DAT);
	
}

/*复位操作*/
void
dm9000_reset()
{
	int i = 1000;
	/*1.设置IO为输出模式*/
	dm9000_write(DM9000_GPCR, GPCR_GPIO0_OUT);
	
	/*2.通过对GPIO0写入0为内部的PHY提供电源*/
	dm9000_write(DM9000_GPR, 0);
	
	
	/*3.软件复位(自动清零),MAC内部回环模式*/
	dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
	
	do {
		printf("resetting the DM9000, 1st reset\r\n");
		
		i = 1000;
		while(i--); /* Wait at least 20 us */
	} while (dm9000_read(DM9000_NCR) & 1);
	
	/*4.对上一部的寄存器写入全0*/
	dm9000_write(DM9000_NCR, 0);
	
	/*5.重复(3)(4),用两次实现真正复位*/
	dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
	do {
		printf("resetting the DM9000, 1st reset\r\n");
		
		i = 1000;
		while(i--); /* Wait at least 20 us */
	} while (dm9000_read(DM9000_NCR) & 1);
	if ((dm9000_read(DM9000_PIDL) != 0x0) ||
	    (dm9000_read(DM9000_PIDH) != 0x90))
		printf("ERROR: resetting DM9000 -> not responding\r\n");
		
}

u8 
dm9000_probe()
{
	u32 id_val;
	/*1.读取厂家ID*/
	id_val = dm9000_read(DM9000_VIDL);
	id_val |= dm9000_read(DM9000_VIDH) << 8;
	
	/*2.读取产品ID并将其和厂家ID组合*/
	id_val |= dm9000_read(DM9000_PIDL) << 16;
	id_val |= dm9000_read(DM9000_PIDH) << 24;
	
	
	if (1) {
		printf("dm9000 i/o: 0x%X, id: 0x%X \r\n", CONFIG_DM9000_BASE,
		       id_val);
		return 0;
	} else {
		printf("dm9000 is not found !\r\n");
		return -1;
	}	
}
void 
dm9000_mac_init()
{
	/* Program operating register, only internal phy supported */
	dm9000_write(DM9000_NCR, 0x0);
	/* TX Polling clear */
	dm9000_write(DM9000_TCR, 0);
	/* Less 3Kb, 200us */
	dm9000_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
	/* Flow Control : High/Low Water */
	dm9000_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
	/* SH FIXME: This looks strange! Flow Control */
	dm9000_write(DM9000_FCR, 0x0);
	/* Special Mode */
	dm9000_write(DM9000_SMCR, 0);
	/* clear TX status */
	dm9000_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
	/* Clear interrupt status */
	dm9000_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
}
void 
dm9000_fill_macadd()
{
	u16 oft = 0,i = 0;
	/* fill device MAC address registers */
	for (i = 0; i < 6; i++)
		dm9000_write(DM9000_PAR + i, mac_addr[i]);
		
	/*maybe this is some problem*/
	for (i = 0, oft = 0x16; i < 8; i++, oft++)
		dm9000_write(oft, 0xff);
		/* read back mac, just to be sure */
	printf("MAC:");
	for (i = 0, oft = 0x10; i < 6; i++, oft++)
		printf("%02X:", dm9000_read(oft));
	printf("\n\r");

}
static u16
phy_read(int reg)
{
	u16 val;
	int i = 500000; //udelay(100);	

	/* Fill the phyxcer register into REG_0C */
	dm9000_write(DM9000_EPAR, DM9000_PHY | reg);
	dm9000_write(DM9000_EPCR, 0xc);	/* Issue phyxcer read command */
	while(i--);			/* Wait read complete */
	dm9000_write(DM9000_EPCR, 0x0);	/* Clear phyxcer read command */
	val = (dm9000_read(DM9000_EPDRH) << 8) | dm9000_read(DM9000_EPDRL);

	/* The read data keeps on REG_0D & REG_0E */
	printf("phy_read(0x%x): 0x%x\r\n", reg, val);
	return val;
}

/*
   Write a word to phyxcer
*/
static void
phy_write(int reg, u16 value)
{
	int i = 500000;

	/* Fill the phyxcer register into REG_0C */
	dm9000_write(DM9000_EPAR, DM9000_PHY | reg);

	/* Fill the written data into REG_0D & REG_0E */
	dm9000_write(DM9000_EPDRL, (value & 0xff));
	dm9000_write(DM9000_EPDRH, ((value >> 8) & 0xff));
	dm9000_write(DM9000_EPCR, 0xa);	/* Issue phyxcer write command */
	while(i--);			/* Wait write complete */
	dm9000_write(DM9000_EPCR, 0x0);	/* Clear phyxcer write command */
	printf("phy_write(reg:0x%x, value:0x%x)\r\n", reg, value);
}


void 
dm9000_active()
{
	/* RX enable */
	dm9000_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);//要加上RCR_ALL,否则他会自动丢弃来自外部的广播包
	/* Enable TX/RX interrupt mask */
	dm9000_write(DM9000_IMR, IMR_PAR);
		
}
void 
dm9000_init()
{
	int i = 0,m = 5000000,lnk;
	/*1.片选信息设置*/
	dm9000_cs();
	
	
	/*2.中断初始化*/
	dm9000_int_init();
	/*2.1.禁止中断。避免干扰*/
	//dm9000_write(DM9000_IMR,0x80);
	
	/*3.复位设备*/
	dm9000_reset();
	
	/*4.捕获网卡*/
	if (dm9000_probe() < 0)
		return ;
	
	/*5.MAC初始化*/
	dm9000_mac_init();

	
	
	/*6.填充MAC地址*/
	dm9000_fill_macadd();
	
	
	
	/*7.激活DM9000*/
	dm9000_active();
	
	i = 0;
	
	while (!(phy_read(1) & 0x20)) {	/* autonegation complete bit */
		m = 5000000; //udelay(1000);
		while(m--);
		i++;
		if (i == 10000) {
			printf("could not establish link\r\n");
			return ;
		}
	}

	/* see what we've got */
	lnk = phy_read(17) >> 12;
	printf("operating at ");
	switch (lnk) {
	case 1:
		printf("10M half duplex ");
		break;
	case 2:
		printf("10M full duplex ");
		break;
	case 4:
		printf("100M half duplex ");
		break;
	case 8:
		printf("100M full duplex ");
		break;
	default:
		printf("unknown: %d ", lnk);
		break;
	}
	printf("mode\r\n");
	return ;
		
}


/*
  Hardware start transmission.
  Send a packet to media from the upper layer.
*/
void
dm9000_tx(u8* data, u32 length)
{
	u32 i = 0;
	/*1.禁止中断。避免干扰*/
	dm9000_write(DM9000_IMR,0x80);
	
	/*2.清除发送标志位*/
	dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
	
	/*3.写入发送数据长度*/
	dm9000_write(DM9000_TXPLL, length & 0xff);
    	dm9000_write(DM9000_TXPLH, (length >> 8) & 0xff);
	
	/*4.写入待发送数据*/
	(*(volatile u16 *)DM_ADD) = DM9000_MWCMD;
   
	for(i=0;i<length;i+=2)
	 {
	    	(*(volatile u16 *)DM_DAT) = data[i] | (data[i+1]<<8);
	 }
	
	/*5.启动发送*/
	dm9000_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
	
	/*6.等待发送完成*/
	while(dm9000_read(DM9000_TCR) & 0x01);
	
	
	/*7.清除发送状态*/
	dm9000_write(DM9000_NSR,0x2c);
	
	/*8.清除发送标志位*/
	dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */

	
	/*9.使能接收中断*/
	dm9000_write(DM9000_IMR,0x81);
	
	//printf("transmit done\r\n\n");
	
}

u16 dm9000_rx(u8* data)
{
	u32 i = 0;
	u16 status;
	
	u16 tmp,len;
	u8 ready;
	
	/*1.判断是否产生中断*/
	 if(dm9000_read(DM9000_ISR) & 0x01)
       	 	dm9000_write(DM9000_ISR,0x01);
    	else
        	return 0;

	/*2.清除中断*/
	dm9000_write(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
	
	
	/*3.空读*/
	ready = dm9000_read(DM9000_MRCMDX);
	/*if((ready & 0x01) != 0x01){
		ready = dm9000_read(DM9000_MRCMDX);
    		if ((ready & 0x01) != 0x01)
    	    		return 0;	
	}*/
	
	
	/*4.读取包的状态和长度*/
	status = dm9000_read(DM9000_MRCMD);
	len = (*(volatile u16 *)DM_DAT);
	
	/*5.读取包的数据*/
	if(len<DM9000_PKT_MAX)
	{
	       for(i=0;i<len;i+=2)
	       {
	           tmp = (*(volatile u16 *)DM_DAT);
	           data[i] = tmp & 0x0ff;
	           data[i+1] = (tmp>>8)&0x0ff;
	       }
	}
	return len;
}


void dm9000_arp()
{
    while(1)//可以让他每次只请求一次ARP,每输入一个字符在请求一次,看main函数的while循环就知道,但是要把你PC断开外网,否则会接收到来自其他PC的请求
        arp_request();
}

void do_dmirq(void)
{
	void (*the_isr)(void);

	//printf("dm9000 int\r\n");
	// 读VICxADDRESS寄存器获得目前正在发生的中断的处理函数
	the_isr = (vi VIC0ADDRESS);

	// 调用中断处理函数
	the_isr();
	
	// 清中断
	(vi EXT_INT_0_PEND)   |= (1 << 7);
	(vi VIC0ADDRESS) = 0;

}

/****************************
@File:arp.c
@
@Tiny6410裸机下学期代码
@ARP协议文件
@Author:小君君
@****************************/

#include "arp.h"


void arp_request()
{
	/*1.构成ARP请求包*/
	
	memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6);	//以太网目的地址
	memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);		//以太网源地址
	arpbuf.ethhdr.frame_type = SWAP(0x0806);		//帧类型
	
	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.dipaddr,host_ip_addr,4);		//目的IP地址
	
	
	packet_len = 14+28;				//包的长度
	
	/*2.调用dm9000发送函数,发送请求包*/	
        dm9000_tx((u8*)&arpbuf,packet_len);
}


u8 arp_respond(u8* buf,u32 len)
{
	//printf("dm_arp int\r\n");
	ARP_HDR* p = (ARP_HDR*)buf;
	u32 i = 0;
	
	if(packet_len < 28)
	{
		printf("rx failed!\r\n");
		return 0;
	}
		
	switch(SWAP(p->opcode)){
		case 2://对PC发到开发板的应答包解析
			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;
		case 1://响应PC发到开发板的请求包
			memcpy(arpbuf.ethhdr.d_mac,p->ethhdr.s_mac,6);	//以太网目的地址
			memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);		//以太网源地址
			arpbuf.ethhdr.frame_type = SWAP(0x0806);		//帧类型
			
			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地址
			
			
			packet_len = 14+28;				//包的长度
			
			/*2.调用dm9000发送函数,发送请求包*/	
        		dm9000_tx((u8*)&arpbuf,packet_len);
		
			break;
			
		default:
			break;	 
		 
	}
    	
    	return 1;
} 
extern void tftp_process(u8* buf,u32 len,u16 port);
/*对UDP协议的包,进行处理。TFTP传输是属于UDP协议的,而UDP又是属于IP协议的*/
void udp_respond(u8* buf,u32 len)
{
	
	
}

/*对IP协议的包进行处理,因为可能涉及到IP协议的情况不只是TFTP传输,所以为以后程序功能升级做准备,就单独写一个判断函数*/
void ip_respond(u8* buf,u32 len)
{
	IP_HDR* iphdr = (IP_HDR*)buf;
	//printf("dm_ip int\r\n");
	if(iphdr->proto == PROTO_UDP)
		udp_respond(buf,len);
}


/*对来自外部的包进行处理*/
void net_handle(u8* buf,u32 len)
{
	ETH_HDR* p = (ETH_HDR*)buf;
	//printf("dm_handle int%x\r\n",SWAP(p->frame_type));
	switch(SWAP(p->frame_type)){
		case PROTO_ARP:
			arp_respond(buf,len);	//ARP协议应答,目的是获取PC的MAC地址
			break;
		case PROTO_IP:
			ip_respond(buf,len);	//对数据传送的包进行处理
			break;
		default:
			break;
	}
	return;
}

/****************************
@File:common.h
@
@Tiny6410裸机下学期代码
@常用头文件定义
@Author:小君君
@****************************/



#ifndef __COMMON_H__
#define __COMMON_H__

#define  vi   *( volatile unsigned long * ) 

#define ulong unsigned long

typedef signed char s8;
typedef unsigned char u8;

typedef signed short s16;
typedef unsigned short u16;

typedef signed int s32;
typedef unsigned int u32;

typedef signed long long s64;
typedef unsigned long long u64;



/*取消使用mmu*/

//#define MMU_ENABLE 1
/*LED初始化*/

#ifndef MMU_ENABLE
	#define LED_CON 0x7F008800
	#define LED_DAT  0x7F008808
#else
	#define LED_CON 0xA0008800
	#define LED_DAT  0xA0008808
#endif

void mmu_init();

void led_init();
void led_on();
void led_off();
void led1_on();
void led2_on();
void led3_on();
void led4_on();
void led5_on();
void led6_on();
void led7_on();
void led8_on();


/*按键相关初始化*/
#define KEYCON  0x7f008830
#define KEYCON1 0x7f008814

#define K1_MSK (3 << 0)
#define K2_MSK (3 << 2)
#define K3_MSK (3 << 4)
#define K4_MSK (3 << 6)
#define K5_MSK (3 << 8)
#define K6_MSK (3 << 10)
#define K7_MSK (0xF << 12)
#define K8_MSK (0xF << 16)


#define K1_OK (2 << 0)
#define K2_OK (2 << 2)
#define K3_OK (2 << 4)
#define K4_OK (2 << 6)
#define K5_OK (2 << 8)
#define K6_OK (2 << 10)
#define K7_OK (0x3 << 12)
#define K8_OK (0x3 << 16)

void button_init();


/*中断控制器相关的寄存器地址*/
#define EXT_INT_0_CON       0x7f008900   
#define EXT_INT_1_CON       0x7f008904  
#define EXT_INT_0_MASK      0x7f008920 
#define EXT_INT_0_PEND      0x7f008924     

#define VIC0INTENABLE       0x71200010   
#define VIC1INTENABLE       0x71300010

#define EINT0_VECTADDR      0x71200100 /*每个中断源有一个寄存器存放相应的中断处理函数的地址,共32+32 = 64个*/
#define EINT1_VECTADDR      0x71200104
#define EINT2_VECTADDR      0x71200108
#define EINT3_VECTADDR      0x7120010C
#define EINT4_VECTADDR      0x71200110
#define EINT5_VECTADDR      0x71200114/*以上是VIC0,见6410datasheet的P414*/

#define EINT19_VECTADDR     0x71300100     
#define EINT20_VECTADDR     0x71300104 /*以上是VIC1*/


#define VIC0ADDRESS         0x71200f00   
#define VIC1ADDRESS         0x71300f00

void irq_init();


/*nandflash相关寄存器定义*/
#define NFCONF  0x70200000 
#define NFCONT  0x70200004 
#define NFCMMD  0x70200008 
#define NFADDR  0x7020000C 
#define NFDATA  0x70200010 
#define NFDATA8 (*(volatile unsigned char *)0x70200010) 
#define NFSTAT  0x70200028

int nand_erase(unsigned int block_addr);
int Nand_PageWrite(unsigned long start_addr,char *buf);


/*UART相关寄存器定义*/
#define UARTCON     0x7F008000
#define ULCON0      0x7F005000
#define UCON0       0x7F005004
#define UFCON0      0x7F005008
#define UMCON0      0x7F00500C
#define UTRSTAT0    0x7F005010
#define UFSTAT0     0x7F005018

#define UTXH0      (*((volatile unsigned char *)0x7F005020))//注意是char类型的
#define URXH0      (*((volatile unsigned char *)0x7F005024))
#define UBRDIV0    (*((volatile unsigned short *)0x7F005028))//注意是short类型的
#define UDIVSLOT0  (*((volatile unsigned short *)0x7F00502C))

#define UART_FIFO_ENABLE 1  //最好使用FIFO模式,就是必须有这个宏定义

void uart_init();

#ifdef	UART_FIFO_ENABLE
	char getchar(void);
	void putchar(char c);
	void send_string(char* str);
#else
	unsigned char getchar(void);
	void putchar(unsigned char c);
	void send_string(unsigned char* str);
#endif


/*DMA相关定义*/

#define UTXH0_DMA 0x7F005020
#define DMA_BASE 0x75000000

#define DMACC0SrcAddr  (DMA_BASE + 0x100)
#define DMACC0DestAddr (DMA_BASE + 0x104)
#define DMACC0Control0 (DMA_BASE + 0x10C)

#define DMACC0Control1 (DMA_BASE + 0x110)
#define DMACC0Configuration (DMA_BASE + 0x114)
#define DMACC0ConfigurationExp (DMA_BASE + 0x118)

#define DMACC0LLI (DMA_BASE + 0x108)
#define DMACEnbldChns (DMA_BASE + 0x01C)
#define DMACConfiguration (DMA_BASE + 0x030)
#define DMACSync (DMA_BASE + 0x034)
#define DMA_SEL 0x7E00F110

void dma_init();
void dma_start();

/*LCD相关寄存器定义*/
#define GPECON  0x7F008080
#define GPEDAT  0x7F008084
#define GPEPUD  0x7F008088
#define GPFCON  0x7F0080A0
#define GPFDAT  0x7F0080A4
#define GPFPUD  0x7F0080A8
#define GPICON  0x7F008100
#define GPIPUD  0x7F008108
#define GPJCON  0x7F008120
#define GPJPUD  0x7F008128


/* display controller */
#define MIFPCON  	0x7410800C
#define SPCON        	0x7F0081A0
#define VIDCON0      	0x77100000
#define VIDCON1      	0x77100004
#define VIDTCON0     	0x77100010
#define VIDTCON1     	0x77100014
#define VIDTCON2     	0x77100018
#define WINCON0      	0x77100020
#define VIDOSD0A      	0x77100040
#define VIDOSD0B      	0x77100044
#define VIDOSD0C      	0x77100048
#define VIDW00ADD0B0    0x771000A0
#define VIDW00ADD1B0    0x771000D0
#define VIDW00ADD2      0x77100100
#define DITHMODE        0x77100170

#define FRAME_BUFFER   0x54000000
#define ROW				272
#define COL				480
#define HSPW 				(2)
#define HBPD			 	(40- 1)
#define HFPD 				(5 - 1)
#define VSPW				(2)
#define VBPD 				(8 - 1)
#define VFPD 				(9 - 1)
#define LINEVAL 			(271)
#define HOZVAL				(479)

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   479
#define RightBotY   271

void lcd_init();
void lcd_draw_pixel(int row, int col, int color);
void lcd_clear_screen(int color);
void lcd_draw_hline(int row, int col1, int col2, int color);
void lcd_draw_vline(int col, int row1, int row2, int color);
void lcd_draw_cross(int row, int col, int halflen, int color);
void lcd_draw_circle(void);

#define WIDTHEIGHT	480
#define HEIGHT	272


/*触摸屏相关定义*/
#define ADCCON 0x7E00B000	//ADC Control Register
#define ADCTSC 0x7E00B004	//ADC Touch Screen Control Register 
#define ADCDLY 0x7E00B008 	//ADC Start or Interval Delay Register 
#define ADCDAT0 0x7E00B00C 	//ADC Conversion Data Register
#define ADCDAT1 0x7E00B010 	//ADC Conversion Data Register
#define ADCUPDN 0x7E00B014	//Stylus Up or Down Interrupt Register
#define ADCCLRINT 0x7E00B018	//Clear ADC Interrupt 
#define ADCCLRINTPNDNUP 0x7E00B020	//Clear Pen Down/Up Interrupt 

#define ADCINT_VECTADDR     0x7130017C     
#define IPDOWNINT_VECTADDR     0x71300178 /*以上是VIC1*/
#define VIC1SELECT     0x7130000C /*以上是VIC1*/


void ts_init();

int read_adc(int ch);

/*dm9000相关寄存器定义*/
#define CONFIG_DRIVER_DM9000	1
#define CONFIG_DRIVER_DM9000_NO_EEPROM	1
#define CONFIG_DM9000_USE_16BIT 1
#define CONFIG_DM9000_BASE	0x18000300
#define DM_ADD CONFIG_DM9000_BASE
#define DM_DAT (CONFIG_DM9000_BASE+4)

#define SROM_BW 0x70000000
#define SROM_BC1 0x70000008 
#define GPNCON 0x7F008830 
#define GPNDAT 0x7F008834 
#define GPNPUD 0x7F008838 
void dm9000_init();

#endif

#ifndef __ARP_H__
#define __ARP_H__
#include "common.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;

ARP_HDR arpbuf;


/*网络协议类型*/
#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 u16 packet_len;
#endif
@****************************
@File:start.S
@
@Tiny6410裸机下学期代码
@
@Author:小君君
@****************************

.text
.global _start


@异常向量表
_start:
	b	reset
	ldr 	pc,	_Undefined_instruction
	ldr 	pc,	_Software_interrupt
	ldr	pc,	_Command_abort
	ldr	pc,	_Data_abort
	ldr	pc,	_Nothing_used
	ldr	pc,	_IRQ
	ldr	pc,	_FIQ
	
	
	
	
@存放实际异常入口地址开辟的存储单元	
_Undefined_instruction:
	.word Undefined_instruction
	
_Software_interrupt:
	.word Software_interrupt
	
_Command_abort:
	.word Command_abort
	
_Data_abort:
	.word Data_abort
	
_Nothing_used:
	.word Nothing_used
	
_IRQ:
	.word IRQ
	
_FIQ:
	.word FIQ


@各种实际的异常处理函数
Undefined_instruction:
	nop

Software_interrupt:
	nop

Command_abort:
	nop
	
Data_abort:
	nop
	
Nothing_used:
	nop
	
IRQ:
	ldr	sp,=0x5E000000
	sub 	lr,lr,#4
	stmdb	sp!,{r0-r12,lr}
	bl	do_dmirq
	ldmia	sp!,{r0-r12,pc}^ 
	
FIQ:
	nop
	
	
	
@上电复位以后就执行这里
reset:
	bl	set_svc
	bl 	set_peri_port
	bl	disable_watchdog
	bl 	disable_interrupt
	bl	disable_mmu
	bl	clock_init
	bl	mem_init	
	bl	init_stack
	bl	clean_bss
	
	bl	nand_init
	bl	copy_to_ddr
	@bl	light_led
	
	ldr	pc,	=main
	
halt:
	b halt
	
	

@通过读-修改-写的方式控制CPSR从而改变工作模式的svc模式
set_svc:
	
	mrs	r0,	cpsr
	bic	r0,r0,#0x1F
	orr	r0,r0,#0xD3
	msr	cpsr,	r0
	mov	pc,	lr


@关闭看门狗
#define	pWTCON 0x7E004000	
disable_watchdog:
	ldr	r0,	=pWTCON
	mov	r1,	#0x00
	str	r1,	[r0]
	mov 	pc,	lr
	


@屏蔽中断,要操作两个寄存器,目的是将使能中断寄存器的相关位清除
#define VIC0INTENCLEAR 0x71200014 
#define VIC1INTENCLEAR 0x71300014 
disable_interrupt:
	ldr	r0,	=VIC0INTENCLEAR
	mvn 	r1,	#0x0
	str	r1,	[r0]
	
	ldr	r0,	=VIC1INTENCLEAR
	str	r1,	[r0]
	
	mov 	pc,	lr	
	
@关闭MMU和Cache	
disable_mmu:
	mcr	p15,0,r0,c7,c7,0
	mrc	p15,0,r0,c1,c0,0
	bic	r0,r0,#0x07
	mcr 	p15,0,r0,c1,c0,0
	mov 	pc,	lr




@外设地址初始化,要放在进入svc模式以后的第一步操作
set_peri_port:		
	ldr r0, =0x70000000 					@对于6410来说,内存(0x00000000~0x60000000),外设(0x70000000-0x7fffffff)
	orr r0, r0, #0x13						@外设大小:256M
	mcr p15,0,r0,c15,c2,4       			 @把r0的值(包括了外设基地址+外设大小)告诉cpu
	mov	pc,	lr


@时钟初始化
#define APLL_LOCK 0x7E00F000
#define CLK_DIV0 0x7E00F020
#define OTHERS 0x7E00F900
#define MPLL_CON 0x7E00F010
#define APLL_CON 0x7E00F00C
#define CLK_SRC 0x7E00F01C


@#define ARM_RATIO  	0   						@ARMCLK 	= DOUTAPLL / (ARM_RATIO + 1)  	= 532/(0+1) = 532  MHz
@#define MPLL_RATIO	0   						@ DOUTMPLL = MOUTMPLL / (MPLL_RATIO + 1)   = 532/(0+1) = 532  MHz
@#define HCLKX2_RATIO 1   						@ HCLKX2 	= HCLKX2IN / (HCLKX2_RATIO + 1) = 532/(1+1) = 266  MHz
@#define HCLK_RATIO   1   						@ HCLK 	= HCLKX2   / (HCLK_RATIO + 1)   = 266/(1+1) = 133  MHz
@#define PCLK_RATIO   3   						@ PCLK   	= HCLKX2   / (PCLK_RATIO + 1)   = 266/(3+1) = 66.5 MHz

#define DIV_VAL  ((0 << 0)|(0 << 4)|(1 << 8)|(1 << 9)|(3 << 12))	@注意这里不能用#define DIV_VAL (ARM_RATIO) | (MPLL_RATIO << 4) | (HCLK_RATIO << 8) | (HCLKX2_RATIO << 9) | (PCLK_RATIO << 12)
									@原因暂时未查明,不知道是不是我电脑最近鼠标抽风,把字符编码格式改变了导致的结果
clock_init:
	@ 1. 设置各PLL的LOCK_TIME,使用默认值  
	ldr 	r0, 	=APLL_LOCK  						@ APLL_LOCK,供cpu使用 
	ldr 	r1, 	=0x0000FFFF
	str 	r1, 	[r0]
	
	str 	r1, 	[r0, #4]	 						@ MPLL_LOCK,供AHB(存储/中断/lcd等控制器)/APB(看门狗,定时器,SD等)总线上的设备使用 
	str 	r1, 	[r0, #8]	 						@ EPLL_LOCK,供UART,IIS,IIC使用 
	
	@ 2. 设置为异步模式 
	ldr 	r0, 	=OTHERS							@ OTHERS
												@ 《linux installation for u-boot》3.7中:用MPLL作为HCLK和PCLK的Source是异步(ASYNC)模式
												@ 用APLL是同步(SYNC)模式
	ldr 	r1, 	[r0]
	bic r1, r1, #0xc0							@ bit[6:7]清0,即SYNCMODE=0/SYNCMUXSEL=0
	str 	r1, 	[r0]
loop_clock:			
	ldr 	r0, 	=OTHERS
	ldr 	r1, 	[r0]
	and r1, r1, #0xf00					
	cmp 	r1, 	#0
	bne 	loop_clock

	@ 3. 设置分频系数  
	ldr 	r0, 	=CLK_DIV0  						@CLK_DIV0
	ldr 	r1,	=DIV_VAL
	str 	r1, 	[r0]
	
	@ 4. 设置PLL,放大时钟  
	@ 4.1 配置APLL  
#define APLL_CON_VAL  ((1<<31) | (266 << 16) | (3 << 8) | (1))
	ldr 	r0, 	=APLL_CON							@ APLL_CON
	ldr 	r1, 	=APLL_CON_VAL						@ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
	str 	r1, 	[r0]		
	
	@ 4.2 配置MPLL  
#define MPLL_CON_VAL  ((1<<31) | (266 << 16) | (3 << 8) | (1))
	ldr 	r0, 	=MPLL_CON							@ MPLL_CON
	ldr 	r1, 	=MPLL_CON_VAL						@ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
	str 	r1, 	[r0]		

#define MPLL_SEL 1
#define APLL_SEL 1	
	@ 5.选择PLL的输出作为时钟源  
	ldr 	r0, 	=CLK_SRC							@ CLK_SRC
	ldr 	r1, 	=(MPLL_SEL<<1) | (APLL_SEL<<0)
	str 	r1, 	[r0]
	
	mov 	pc, 	lr

@将bin文件从_start开始到bss_start结束的数据搬移到_start指定的链接地址(0x50008000)
copy_to_ddr:
	mov	r0,	#0x00
	ldr	r1,	=_start
	ldr	r2,	=bss_start
	sub 	r2,r2,r1
	
	mov	ip,lr
	bl	copy2ddr
	mov	lr,ip
	mov	pc,lr
	
@copy_loop:
@	ldr	r3,[r0],#4
@	str	r3,[r1],#4
@	cmp	r1,r2
@	bne	copy_loop
	
@	mov	pc,	lr



@初始化栈
init_stack:
	mrs	r0,	cpsr@用读-修改-写 的方式设置CPU的工作模式(CPSR寄存器)
	bic	r0,r0,#0x1F
	orr	r0,r0,#0xD2
	msr	cpsr,	r0
	
	ldr	sp,	=0x5F000000 @设置IRQ模式的堆栈
	
	mrs	r0,	cpsr
	bic	r0,r0,#0x1F
	orr	r0,r0,#0xD3
	msr	cpsr,	r0
	
	ldr	sp,	=0x5FF00000@设置SVC模式的堆栈
	
	mov	pc,	lr
	

@清除BSS段
clean_bss:
	ldr	r0,	=bss_start
	ldr	r1,	=bss_end
	
	cmp	r0,	r1
	moveq	pc,	lr
	
clean_loop:
	mov	r2,	#0x0
	str	r2,[r0],#4
	cmp	r0,	r1
	bne	clean_loop
	
	mov	pc,	lr
	
	
		

@点亮LED
#define GPKCON0 0x7F008800
#define GPKDAT  0x7F008808
light_led:	
	@设置GPKCON0
	ldr r1, =GPKCON0 					
	ldr r0, =0x11110000																  
	str r0, [r1]
	
	
	@设置GPKDAT点亮4颗LED
	ldr r0, =GPKDAT
	ldr r1, =0x0
	str r1, [r0]
	
	
	@延时函数,将会使得开机的时候4颗led先亮一段时间,大约4秒钟左右,之后只亮两颗LED表明进入C语言的main函数
	ldr	r0,	=0xFFFFFF
loop_led:
	sub	r0,r0,#1
	cmp	r0,	#1
	bne	loop_led
	
	mov pc, lr
	

		
	
	

/****************************
@File:main.c
@
@Tiny6410裸机下学期代码
@网卡测试文件
@Author:小君君
@****************************/

#include "common.h"

int main(void)
{
	int num = 1000;
	
	
	
	led_init();//LED的GPIO初始化
	
	button_init();//按键初始化
	
	
	
	led_on();//点亮4颗LED
	
	uart_init();//串口初始化
	
	putchar('a');
	putchar('\r');
	putchar('\n');
	putchar('b');
	putchar('\r');
	putchar('\n');
	
	
	dma_init();//DMA初始化
	dma_start();//启动DMA发送数据到串口
	
	uart_init();//串口再次初始化,使得串口恢复中断或者轮询模式
	
	lcd_init();
	lcd_clear_screen(0xFFFFFF);
	
	
	
	dm9000_init();
	
	dm9000_arp();
	
	while(1){
		dm9000_arp();
		printf("=================================================\n\r");
		printf("===================JUN-BOOT======================\n\r");
		printf("0.Send the ARP to get yhe host's MAC address\n\r");
		printf("1.Download the linux kernel from tftp\n\r");
		printf("2.Boot linux OS from SDRAM\n\r");
		printf("3.Junjun is a houmorous\n\r");
		printf("=================================================\n\r");
		printf("===================LCD_TEST======================\n\r");
		printf("4.清屏\n\r");
		printf("5.画横线\n\r");
		printf("6.画竖线\n\r");
		printf("7.画十字架\n\r");
		printf("8.画同心圆\n\r");
		printf("9.AD转换\n\r");
		printf("                                                   \n\r");
		printf("请输入0-8任意一个数字:\n\r");
		
		scanf("%d",&num);	
		
		switch(num){
			case 0:
				printf("请支持成都国嵌\n\r");
				break;
			case 1:
				printf("国嵌学院=====打造你的嵌入式人生\n\r");
				break;
			case 2:
				printf("学ARM,学Linux,学C++,学安卓,学嵌入式,就到成都国嵌学院\n\r");
				break;
			case 3:
				printf("只要你肯努力,你的明天就会等你!!\n\r");
				break;
			case 4:
				lcd_clear_screen(0x000000);
				break;
			case 5:
				lcd_clear_screen(0x000000);
				lcd_draw_hline(HEIGHT/2, 100, WIDTHEIGHT-100, 0xff0000);
				break;
			case 6:
				lcd_clear_screen(0x000000);
				lcd_draw_vline(WIDTHEIGHT/2, 50, HEIGHT-50, 0xff0000);
				break;
			case 7:
				lcd_clear_screen(0x000000);
				lcd_draw_cross(HEIGHT/2, WIDTHEIGHT/2, 20, 0x777777);
				break;
			case 8:
				lcd_clear_screen(0x000000);
				lcd_draw_circle();
				break;
			case 9:
				read_adc(0);
				break;
			default:
				printf("只要你肯努力,你的明天就会等你!!\n\r");
				break;
			
		}
	}
	return 0;	
}



你可能感兴趣的:(四十.DM9000网卡搭建)