新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > s3c2440的網(wǎng)卡接口擴展

s3c2440的網(wǎng)卡接口擴展

作者: 時間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
網(wǎng)絡(luò)對于嵌入式系統(tǒng)來說必不可少??墒?strong>s3c2440沒有集成以太網(wǎng)接口,所以要想使s3c2440具備以太網(wǎng)的功能,就必須擴展網(wǎng)卡接口。在這里,我們外接DM9000,使其可以與以太網(wǎng)相連接。

DM9000可以直接與ISA總線相連,也可以與大多數(shù)CPU相連。在這里,我們當(dāng)然是要讓DM9000與s3c2440相連接了。DM9000對外來說只有兩個端口——地址口和數(shù)據(jù)口,地址口用于輸入內(nèi)部寄存器的地址,而數(shù)據(jù)口則完成對某一寄存器的讀寫。DM9000的CMD引腳用來區(qū)分這兩個端口,當(dāng)CMD引腳為0時,DM9000的數(shù)據(jù)線上傳輸?shù)氖羌拇嫫鞯刂罚?dāng)CMD引腳為1時,傳輸?shù)氖亲x寫數(shù)據(jù)。我們把DM9000的A8和A9接為高電平,把A4~A7接為低電平,并且把DM9000的AEN接到s3c2440的nGCS4引腳上,則DM9000的端口基址為0x20000300,如果再把DM9000的CMD引腳接到s3c2440的ADDR2引腳上,則我們就可以定義DM9000的這兩個端口地址,它們分別為:

#define DM_ADDR_PORT(*((volatile unsigned short *) 0x20000300))//地址口
#define DM_DATA_PORT(*((volatile unsigned short *) 0x20000304))//數(shù)據(jù)口

如果要寫入DM9000中的某個寄存器,則先把該寄存器的地址賦予DM_ADDR_PORT,然后再把要寫入的數(shù)據(jù)賦予DM_DATA_PORT即可。讀取DM9000中的某個寄存器也類似。下面的函數(shù)的作用分別是DM9000的讀、寫寄存器操作:

//寫DM9000寄存器
void __inline dm_reg_write(unsigned char reg, unsigned char data)
{
DM_ADDR_PORT = reg;//將寄存器地址寫到地址端口
DM_DATA_PORT = data;//將數(shù)據(jù)寫到數(shù)據(jù)端口
}

//讀DM9000寄存器
unsigned char __inline dm_reg_read(unsigned char reg)
{
DM_ADDR_PORT = reg;
return DM_DATA_PORT;//將數(shù)據(jù)從數(shù)據(jù)端口讀出
}

完成了對DM9000寄存器的讀寫函數(shù)的編寫,下面我們就可以初始化DM9000,它的過程就是適當(dāng)配置DM9000寄存器的過程。DM9000的內(nèi)部寄存器在這里就不做介紹,而且DM9000的應(yīng)用數(shù)據(jù)手冊也有如何初始化DM9000的步驟,我們這里只給出具體的程序:

void dm_init(void)
{
dm_reg_write(DM9000_NCR,1);//軟件復(fù)位DM9000
delay(30);//延時至少20μs
dm_reg_write(DM9000_NCR,0);//清除復(fù)位位

dm_reg_write(DM9000_NCR,1);//為了確保復(fù)位正確,再次復(fù)位
delay(30);
dm_reg_write(DM9000_NCR,0);

dm_reg_write(DM9000_GPCR,1);//設(shè)置GPIO0為輸出
dm_reg_write(DM9000_GPR,0);//激活內(nèi)部PHY

dm_reg_write(DM9000_NSR,0x2c);//清TX狀態(tài)
dm_reg_write(DM9000_ISR,0xf);//清中斷狀態(tài)

dm_reg_write(DM9000_RCR,0x39);//設(shè)置RX控制
dm_reg_write(DM9000_TCR,0);//設(shè)置TX控制
dm_reg_write(DM9000_BPTR,0x3f);
dm_reg_write(DM9000_FCTR,0x3a);
dm_reg_write(DM9000_FCR,0xff);
dm_reg_write(DM9000_SMCR,0x00);

dm_reg_write(DM9000_PAR1,0x00);//設(shè)置MAC地址:00-01-02-03-04-05
dm_reg_write(DM9000_PAR2,0x01);
dm_reg_write(DM9000_PAR3,0x02);
dm_reg_write(DM9000_PAR4,0x03);
dm_reg_write(DM9000_PAR5,0x04);
dm_reg_write(DM9000_PAR6,0x05);

dm_reg_write(DM9000_NSR,0x2c);//再次清TX狀態(tài)
dm_reg_write(DM9000_ISR,0xf);//再次清中斷狀態(tài)

dm_reg_write(DM9000_IMR,0x81);//打開接受數(shù)據(jù)中斷
}

DM9000內(nèi)部有0x3FF大小的SRAM用于接受和發(fā)送數(shù)據(jù)緩存。在發(fā)送或接收數(shù)據(jù)包之前,數(shù)據(jù)是暫存在這個SRAM中的。當(dāng)需要連續(xù)發(fā)送或接收數(shù)據(jù)時,我們需要分別把DM9000寄存器MWCMD或MRCMD賦予數(shù)據(jù)端口,這樣就指定了SRAM中的某個地址,并且在傳輸完一個數(shù)據(jù)后,指針會指向SRAM中的下一個地址,從而完成了連續(xù)訪問數(shù)據(jù)的目的。但當(dāng)我們在發(fā)送或接受一個數(shù)據(jù)后,指向SRAM的數(shù)據(jù)指針不需要變化時,則要把MWCMDX或MRCMDX賦予數(shù)據(jù)端口。下面的程序為DM9000發(fā)送數(shù)據(jù)的函數(shù),它的兩個輸入?yún)?shù)分別為要發(fā)送數(shù)據(jù)數(shù)組首地址和數(shù)據(jù)數(shù)組長度。在這里我們已經(jīng)知道數(shù)據(jù)的寬為16位,它是由DM9000的硬件引腳設(shè)置實現(xiàn)的。

void dm_tran_packet(unsigned char *datas, int length)
{
int i;

dm_reg_write(DM9000_IMR, 0x80);//在發(fā)送數(shù)據(jù)過程中禁止網(wǎng)卡中斷

dm_reg_write(DM9000_TXPLH, (length>>8) & 0x0ff);//設(shè)置發(fā)送數(shù)據(jù)長度
dm_reg_write(DM9000_TXPLL, length & 0x0ff);

DM_ADDR_PORT = DM9000_MWCMD;//發(fā)送數(shù)據(jù)緩存賦予數(shù)據(jù)端口

//發(fā)送數(shù)據(jù)
for(i=0;i{
delay(50);
DM_DATA_PORT = datas[i]|(datas[i+1]<<8);//8位數(shù)據(jù)轉(zhuǎn)換為16位數(shù)據(jù)輸出
}

dm_reg_write(DM9000_TCR, 0x01);//把數(shù)據(jù)發(fā)送到以太網(wǎng)上

while((dm_reg_read(DM9000_NSR) & 0x0c) == 0)
;//等待數(shù)據(jù)發(fā)送完成

delay(50);

dm_reg_write(DM9000_NSR, 0x2c);//清除TX狀態(tài)
dm_reg_write(DM9000_IMR, 0x81);//打開DM9000接收數(shù)據(jù)中斷
}

發(fā)送數(shù)據(jù)比較簡單,接收數(shù)據(jù)就略顯復(fù)雜,因為它是有一定格式要求的。在接收到的一包數(shù)據(jù)中的首字節(jié)如果為0x01,則表示這是一個可以接收的數(shù)據(jù)包;如果為0x0,則表示沒有可接收的數(shù)據(jù)包。因此在讀取其他字節(jié)時,一定要先判斷首字節(jié)是否為0x01。數(shù)據(jù)包的第二個字節(jié)為數(shù)據(jù)包的一些信息,它的高字節(jié)的格式與DM9000的寄存器RSR完全一致。第三個和第四個字節(jié)為數(shù)據(jù)包的長度。后面的數(shù)據(jù)就是真正要接收的數(shù)據(jù)了。下面就是DM9000接收數(shù)據(jù)的程序,其中輸入?yún)?shù)為存放輸入數(shù)據(jù)數(shù)組的首地址,輸出參數(shù)為接收數(shù)據(jù)的長度。

int dm_rec_packet(unsigned char *datas)
{
unsigned char int_status;
unsigned char rx_ready;
unsigned short rx_status;
unsigned short rx_length;
unsigned short temp;
int i;

int_status = dm_reg_read(DM9000_ISR);//讀取ISR
if(int_status & 0x1)//判斷是否有數(shù)據(jù)要接受
{
rx_ready = dm_reg_read(DM9000_MRCMDX);//先讀取一個無效的數(shù)據(jù)
rx_ready = (unsigned char)DM_DATA_PORT;//真正讀取到的數(shù)據(jù)包首字節(jié)

if(rx_ready == 1)//判讀首字節(jié)是否為1或0
{
DM_ADDR_PORT = DM9000_MRCMD;//連續(xù)讀取數(shù)據(jù)包內(nèi)容

rx_status = DM_DATA_PORT;//狀態(tài)字節(jié)

rx_length = DM_DATA_PORT;//數(shù)據(jù)長度

if(!(rx_status & 0xbf00) && (rx_length < 10000))//判讀數(shù)據(jù)是否符合要求
{
for(i=0; i{
delay(50);
temp = DM_DATA_PORT;
datas[i] = temp & 0x0ff;
datas[i + 1] = (temp >> 8) & 0x0ff;
}
}
}
else if(rx_ready !=0)//停止設(shè)備
{
//dm_reg_write(DM9000_IMR,0x80);//停止中斷
//dm_reg_write(DM9000_ISR,0x0F);//清中斷狀態(tài)
//dm_reg_write(DM9000_RCR,0x0);//停止接收
//還需要復(fù)位系統(tǒng),這里暫時沒有處理
}
}
dm_reg_write(DM9000_ISR, 0x1);//清中斷
return rx_length;
}

關(guān)于DM9000的設(shè)置我們就介紹到這里,下面就是s3c2440的設(shè)置。在這里,網(wǎng)卡發(fā)送數(shù)據(jù)利用的是查詢方式,接收數(shù)據(jù)利用的是中斷方式,因此我們把DM9000的INT引腳連接到了s3c2440的EINT7上。另外我們還是用UART0接口來控制和顯示網(wǎng)卡數(shù)據(jù)。這兩個接口的初始化為:

//uart0 port
rGPHCON = 0x00faaa;
rGPHUP= 0x7ff;
rULCON0 = 0x3;
rUCON0 = 0x5;
rUFCON0 = 0;
rUMCON0 = 0;
rUBRDIV0 = 26;

rSRCPND = (0x1<<27)|(0x1<<28);
rSUBSRCPND = 0x1;
rINTPND = (0x1<<27)|(0x1<<28);
rINTSUBMSK = ~(0x1);
rINTMSK = ~((0x1<<27)|(0x1<<28));
pISR_UART0 = (U32)uartISR;

//EINT7
rGPFCON = 2<<14;
rEXTINT0 = (rEXTINT0 & (~(0x07<<28))) | (0x01<<28);
rEINTMASK &= ~(1<<7);
rSRCPND = rSRCPND | (0x1<<4);
rINTPND = rINTPND | (0x1<<4);
rINTMSK &= ~(1<<4);
pISR_EINT4_7 = (U32)DM9000ISR;

下面就利用DM9000來進行簡單的網(wǎng)卡傳輸數(shù)據(jù)的測驗。由于以太網(wǎng)傳輸數(shù)據(jù)都是基于某種協(xié)議的,因此要傳輸數(shù)據(jù),必須遵循一定的協(xié)議格式。這里我們實現(xiàn)較為簡單的ARP協(xié)議。用于以太網(wǎng)的ARP請求/應(yīng)答分組格式為:14個字節(jié)的以太網(wǎng)首部+28個字節(jié)ARP請求/應(yīng)答。以太網(wǎng)首部的格式為:6個字節(jié)的以太網(wǎng)目標地址+6個字節(jié)以太網(wǎng)源地址+2個字節(jié)幀類型,對于ARP來說,幀類型為0x0806。ARP請求/應(yīng)答的格式為:2個字節(jié)的硬件類型+2個字節(jié)的協(xié)議類型+1個字節(jié)的硬件地址長度+1個字節(jié)的協(xié)議地址長度+2個字節(jié)的操作碼+6個字節(jié)的發(fā)送端以太網(wǎng)地址+4個字節(jié)的發(fā)送端IP地址+6個字節(jié)的目標以太網(wǎng)地址+4個字節(jié)的目標IP地址。硬件類型為1表示的是以太網(wǎng),協(xié)議類型為0x0800表示的是IP地址,硬件地址長度和協(xié)議地址長度分別為6和4,它們都是以字節(jié)為單位的,操作碼為1表示的是ARP請求,為2表示的是ARP應(yīng)答。

在下面的測試程序中,我們用交叉網(wǎng)線把開發(fā)板與PC機(操作系統(tǒng)為Windows XP,網(wǎng)卡的IP地址為192.168.1.120)相連接,我們通過UART發(fā)出一個命令,讓開發(fā)板發(fā)出一個ARP請求數(shù)據(jù)包,然后接收來自PC機的應(yīng)答,并把該應(yīng)答信息通過UART顯示出來。其中UART的中斷復(fù)位程序為:

void __irq uartISR(void)
{
char ch;
rSUBSRCPND |= 0x1;
rSRCPND |= 0x1<<28;
rINTPND |= 0x1<<28;
ch=rURXH0;
if(ch == 0x33)
comm=3;//表示發(fā)送一個ARP數(shù)據(jù)請求包

rUTXH0=ch;
}

另外我們還要事先定義一個遵循ARP協(xié)議格式的數(shù)組:

unsigned char arpsendbuf[42]={

0xff,0xff,0xff,0xff,0xff,0xff,//以太網(wǎng)目標地址,全1表示為廣播地址
0x00,0x01,0x02,0x03,0x04,0x05,//以太網(wǎng)源地址
0x08,0x06,//幀類型:ARP幀

0x00,0x01,//硬件類型:以太網(wǎng)
0x08,0x00,//協(xié)議類型:IP協(xié)議
0x06,//硬件地址長度:6字節(jié)
0x04,//協(xié)議地址長度:4字節(jié)
0x00,0x01,//操作碼:ARP請求

0x00,0x01,0x02,0x03,0x04,0x05,//發(fā)送端以太網(wǎng)硬件地址
192, 168, 1, 50,//發(fā)送端IP協(xié)議地址
0x00,0x00,0x00,0x00,0x00,0x00,//接收端以太網(wǎng)硬件地址
192, 168, 1, 120//接收端IP協(xié)議地址
};

其中發(fā)送端硬件地址,即以太網(wǎng)源地址(00-01-02-03-04-05)是我們初始化DM9000時定義的。而發(fā)送端IP協(xié)議地址是我們?nèi)我舛x的。

該測試程序的主程序為:

void Main(void)
{
…………
//一些必要的初始化

comm=0;//命令
flag=0;//發(fā)送ARP請求包標識

dm_init();//DM9000初始化

while(1)
{
if(comm.==3)
{
comm=0;
dm_tran_packet(arpsendbuf, 42 );//發(fā)送ARP請求包
flag=1;//置標識
}
}
}

接收網(wǎng)絡(luò)上的數(shù)據(jù)是通過外部中斷方式的,在這個中斷處理程序中,主要完成的是接收網(wǎng)卡數(shù)據(jù),并把接收到的數(shù)據(jù)發(fā)送到UART,讓其顯示到PC機上。這里我們還需解決一個問題,那就是當(dāng)我們發(fā)送一個ARP請求包的時候,XP系統(tǒng)并不會應(yīng)答一個ARP數(shù)據(jù)包,而是應(yīng)答一個IP協(xié)議數(shù)據(jù)包,當(dāng)再多次發(fā)出ARP請求包后,才會得到ARP應(yīng)答包。因此當(dāng)s3c2440發(fā)送ARP請求包后,它首先要檢查所接收到的數(shù)據(jù)包,如果不是ARP應(yīng)答包,它就要再次發(fā)送ARP請求包,直到得到ARP應(yīng)答包為止。因此中斷處理程序為:

void __irq DM9000ISR(void)
{
int i;

rSRCPND = rSRCPND | (0x1<<4);
rINTPND = rINTPND | (0x1<<4);

if(rEINTPEND&(1<<7))
{
rEINTPEND = rEINTPEND | (0x1<<7);

packet_len = dm_rec_packet(buffer);//接收網(wǎng)卡數(shù)據(jù)

if((buffer[12]==0x08)&&(buffer[13]==0x06))//是ARP協(xié)議
{
//通過UART顯示出來
for(i=0;i{
while(!(rUTRSTAT0 & 0x2)) ;
rUTXH0 = buffer[i];
}

flag=0;//清標志
}
else if(flag==1)//如果在發(fā)出ARP請求包后,接收到的數(shù)據(jù)不是ARP協(xié)議
{
comm=3;//繼續(xù)發(fā)送ARP請求包
}
}
}

這樣,整個網(wǎng)卡程序就編寫完畢。為了使大家對程序的因果關(guān)系認識得更加清晰,我們再敘述一遍程序的流程:首先初始化UART0,使其用中斷方式接收數(shù)據(jù),查詢方式發(fā)送數(shù)據(jù);初始化EINT7,這是因為DM9000的數(shù)據(jù)中斷引腳INT是連接到s3c2440的外部中斷7引腳上的;然后初始化DM9000,主要是配置一些它的寄存器,并使其用中斷方式接收網(wǎng)卡數(shù)據(jù),查詢方式發(fā)送數(shù)據(jù),這與UART0相似,最后是死循環(huán)等待UART0接收中斷服務(wù)程序中得到的發(fā)送ARP請求包命令。當(dāng)?shù)玫桨l(fā)送ARP請求包命令后,調(diào)用DM9000發(fā)送數(shù)據(jù)命令,發(fā)送事先準備好的一組數(shù)據(jù)。在發(fā)送完ARP數(shù)據(jù)后,PC機會應(yīng)答該請求,從而引發(fā)s3c2440外部中斷7中斷,在該中斷服務(wù)程序中,主要是完成接收ARP應(yīng)答包的任務(wù),并把它通過UART0顯示出來。

當(dāng)程序被執(zhí)行完,并在PC機上通過串口調(diào)試軟件顯示出了一個正確的ARP應(yīng)答包后,我們還可以通過下列方法來進一步驗證該程序的正確性:打開Windows XP系統(tǒng)只帶的“命令提示符”小軟件,在提示符下輸入:arp –a,會出現(xiàn)我們所設(shè)置的開發(fā)板的MAC地址(00-01-02-03-04-05)和IP地址(192.168.1.50),則說明Windows XP系統(tǒng)已經(jīng)把我們開發(fā)板上的網(wǎng)卡信息添加到了它的靜態(tài)列表中。

我們對該系統(tǒng)進一步分析還會發(fā)現(xiàn),當(dāng)開發(fā)板上電并且DM9000初始化完成后,Windows XP系統(tǒng)會向該開發(fā)板發(fā)送一些目標地址為廣播地址(FF-FF-FF-FF-FF-FF)的ARP數(shù)據(jù)包和IP數(shù)據(jù)包,只要我們正確讀取它們,就可以在開發(fā)板上電后,自動知道與其相連的系統(tǒng)的MAC地址和IP地址了。另外,如果對這一部分感興趣,還可以編寫ICMP協(xié)議的數(shù)據(jù)包,這樣就可以讓PC機ping通我們的開發(fā)板了。


關(guān)鍵詞: s3c2440網(wǎng)卡接口擴

評論


技術(shù)專區(qū)

關(guān)閉