一.网络模型
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;
}