ZLIP使用簡(jiǎn)介
李章林1
( 1 南開(kāi)大學(xué)電子應(yīng)用實(shí)驗(yàn)室,http://www.wyjjpt.com,版本:2005-11-28)
|
|
:TCP/IP協(xié)議棧程序所在目錄。 :Icmp協(xié)議。 :IP層。 :網(wǎng)絡(luò)接口層。 :TCP協(xié)議層。 :TCPIP內(nèi)存管理程序。 :網(wǎng)絡(luò)接口協(xié)議所在目錄。 :ARP協(xié)議。 :以太網(wǎng)接口協(xié)議。 :RTL8019AS以太網(wǎng)接口芯片驅(qū)動(dòng)程序。 :全局函數(shù)和宏定義所在目錄 :應(yīng)用層協(xié)議所在目錄 :主程序,這里包含一個(gè)如何使用的例子程序。 |
KeilC目錄下是KeilC51的工程文件所在目錄。用KeilC51打開(kāi)Ex1.Uv2。
MCU目錄下是各種類型的51單片機(jī)的頭文件。
單片機(jī)上網(wǎng)技術(shù),是當(dāng)前的一個(gè)熱門技術(shù)。單片機(jī)上網(wǎng)技術(shù)中的一個(gè)重要部分是在單片上實(shí)現(xiàn)TCP/IP協(xié)議棧�,F(xiàn)在可獲得的TCP/IP源代碼一般并不為51單片機(jī)設(shè)計(jì),而51單片機(jī)和KeilC51編譯器有其自身的特點(diǎn):存儲(chǔ)類型、函數(shù)指針、重入函數(shù)等,ZLIP就是針對(duì)這些特點(diǎn)設(shè)計(jì)的TCP/IP協(xié)議棧。
ZLIP設(shè)計(jì)的目標(biāo)是:
1) 精簡(jiǎn)TCP/IP協(xié)議棧,以減小代碼量。ZLIP目前沒(méi)有支持UDP協(xié)議,ICMP協(xié)議也只支持其中的echo協(xié)議(響應(yīng)ping數(shù)據(jù)包)。lwIP是一個(gè)功能全面的TCP/IP協(xié)議棧,但是相對(duì)51來(lái)說(shuō)代碼量較大。
2)
應(yīng)用層接口簡(jiǎn)單,以兼容通用的socket接口。uIP有很小的代碼量和減小代碼量(選擇AVR為目標(biāo)器件時(shí),代碼為5K左右)和RAM使用量(100字節(jié)左右)。uIP采用了不保存需要應(yīng)答的數(shù)據(jù)包的RAM使用方案,沒(méi)有和BSD的套接字接口兼容,應(yīng)用層接口較復(fù)雜。
3)
針對(duì)KeilC51編譯器設(shè)計(jì)。所有的外部變量都使用了xdata類型,全部指針都為明確存儲(chǔ)類型的指針,需要重入的函數(shù)已經(jīng)聲明為reentant,使用KeilC的小模式下編譯。
使用12M晶振、KeilC編譯器、89C55單片下測(cè)試的技術(shù)參數(shù)如下:
表1:技術(shù)參數(shù)
|
代碼量(字節(jié)) |
外部RAM使用量(字節(jié)) |
發(fā)送速度(字節(jié)/秒) |
|
14841 |
11068 |
5.892K |
ZLIP的特點(diǎn)如下:
1)有適中代碼量和RAM使用量。
2)使用類似MFC的CScoket的套接字接口,使用方便。
3)支持多TCP連接、多網(wǎng)絡(luò)設(shè)備。能方便地移植到多任務(wù)操作系統(tǒng)和其它CPU下。能方便地替換網(wǎng)絡(luò)接口協(xié)議和網(wǎng)卡驅(qū)動(dòng)設(shè)備。
4)支持ping命令的響應(yīng)。
5)為單片機(jī)設(shè)計(jì):所有的外部變量都使用了xdata類型,全部指針都為明確存儲(chǔ)類型的指針,需要重入的函數(shù)已經(jīng)聲明為reentant,使用KeilC的小模式編譯。

圖1:RTL8019AS電路左半部分

圖2:RTL8019AS電路右半部分
該程序不能在KeilC下軟件仿真,因?yàn)槌绦虻倪\(yùn)行需要外部電路配合。該51系統(tǒng)的外部電路主要有:以太網(wǎng)接口芯片RTL8019AS電路、外部RAM電路。
以太網(wǎng)接口芯片RTL8019AS電路圖,如圖1和圖2表示。A0~A4接地址線,D0~D7接數(shù)據(jù)線,CSRTL是片選線(低電平有效),RD-和WR-接讀寫(xiě)信號(hào)線。
zlIP接口函數(shù)基本和BSD的套接字接口相同。
TCPSocket()。
函數(shù)原型:socket xdata * TCPSocket(IP_ADDR ScrIP)。
功能:申請(qǐng)一個(gè)套接字。ScrIP是這個(gè)套接字的本地IP地址。返回socket類型指針,如果申請(qǐng)失敗返回NULL。
TCPConnect()。
函數(shù)原型:BOOL TCPConnect(socket xdata * pTCB, IP_ADDR DestIP, WORD DestPort,void (code * recv)(void xdata * buf,WORD size),void (code * close)(socket xdata * pSocket))。
功能:向IP地址為DestIP的服務(wù)器的DestPort端口發(fā)起連接。參數(shù)recv和close用于設(shè)置當(dāng)接收到數(shù)據(jù)包和對(duì)方要求關(guān)閉TCP連接時(shí)應(yīng)該調(diào)用的回調(diào)函數(shù)指針。連接成功返回TRUE,否則返回FALSE。
TCPSend()。
函數(shù)原型:BOOL TCPSend(socket xdata * pTCB,void xdata *buf,WORD DataSize)。
功能:發(fā)送數(shù)據(jù)。發(fā)送數(shù)據(jù)的TCP連接是套接字指針pTCB對(duì)應(yīng)的連接,發(fā)送的數(shù)據(jù)的起始地址為buf,大小為DataSize。發(fā)送成功返回TRUE,否則返回FALSE。
TCPSendEx()
函數(shù)原型:BOOL TCPSendEx(socket xdata * pTCB,struct SMemHead xdata *MemHead) 。
功能:快速發(fā)送數(shù)據(jù)。在使用TCPSend函數(shù)時(shí),你首先需要將數(shù)據(jù)放入buf指向的內(nèi)存中,然后調(diào)用TCPSend函數(shù),接著該函數(shù)會(huì)將buf指向的內(nèi)存區(qū)數(shù)據(jù)拷貝到TCP緩沖區(qū)中。使用TCPSendEx 時(shí)你首先用TCPAllocate(DATA_SIZE)獲得一個(gè)TCP緩沖區(qū),然后直接將數(shù)據(jù)放入TCP緩沖區(qū)中,從而比TCPSend函數(shù)少一次數(shù)據(jù)拷貝,提高發(fā)送速度。
參數(shù):發(fā)送數(shù)據(jù)的TCP連接是套接字指針pTCB對(duì)應(yīng)的連接,發(fā)送的數(shù)據(jù)放在TCP緩存MemHead中。發(fā)送成功返回TRUE,否則返回FALSE。
TCPListen()。
函數(shù)原型:BOOL TCPListen(socket xdata *pTCB,WORD ScrPort,void (code * accept)(socket xdata *pNewTCB)) 。
功能:使用套接字pTCB在ScrPort端口監(jiān)聽(tīng)。參數(shù)accept是當(dāng)有客戶端向這個(gè)監(jiān)聽(tīng)端口連接成功時(shí)調(diào)用的回調(diào)函數(shù)指針。
TCPClose()。
函數(shù)原型:void TCPClose(socket xdata *pTCB)。
功能:我方主動(dòng)關(guān)閉連接時(shí)調(diào)用TCPClose函數(shù),它將要求關(guān)閉套接字pTCB對(duì)應(yīng)的連接。TCPClose返回以后這個(gè)TCP連接可能保持,因?yàn)榱硪环竭沒(méi)有發(fā)起關(guān)閉請(qǐng)求。
TCPAbort()。
函數(shù)原型:void TCPAbort(socket xdata *pTCB)。
功能:當(dāng)使用完這個(gè)套接字以后,調(diào)用TCPAbort,將這個(gè)套接字釋放,還給系統(tǒng)。
使用ZLIP時(shí),在你的主程序中(請(qǐng)看示例程序的main.c文件)需要做的步驟如下:
1)首先設(shè)置一個(gè)25ms的定時(shí)中斷函數(shù)(示例程序?yàn)?/span>Timer函數(shù))。請(qǐng)?jiān)谥袛嗪瘮?shù)中調(diào)用NetIfTimer(); ARPTimer(); TCPTimer();三個(gè)函數(shù)。
2)寫(xiě)OnReceive函數(shù),它應(yīng)該有如下的參數(shù)和返回值,函數(shù)名可以任意:
void OnReceive1(void DT_XDATA * buf,WORD size) REENTRANT_MUL
在使用TCPConnect函數(shù)時(shí),OnReceive1將作為TCPConnect函數(shù)的一個(gè)參數(shù),也就是設(shè)置該socket的接收函數(shù)。當(dāng)TCP連接接收到對(duì)方數(shù)據(jù)時(shí),將自動(dòng)調(diào)用OnReceive1函數(shù)。buf指向接收的數(shù)據(jù),size是接收的數(shù)據(jù)量的大小。你可以在OnReceive1中處理接收的數(shù)據(jù)。當(dāng)程序中有多個(gè)TCP連接同時(shí)存在時(shí),你需要給每個(gè)連接準(zhǔn)備一個(gè)OnReceive函數(shù)。
3)寫(xiě)OnClose函數(shù),它應(yīng)該有如下的參數(shù)和返回值,函數(shù)名可以任意:
void OnClose1(socket DT_XDATA * pSocket) REENTRANT_MUL
類似于OnReceive函數(shù),當(dāng)TCP連接的另一方首先向我方發(fā)起關(guān)閉連接的請(qǐng)求時(shí),系統(tǒng)將自動(dòng)調(diào)用OnClose函數(shù)。pSocket指向?qū)⒁P(guān)閉的socket。如果你想立即關(guān)閉這個(gè)連接則在OnClose函數(shù)中調(diào)用TCPClose函數(shù)。當(dāng)程序中有多個(gè)TCP連接同時(shí)存在時(shí),你需要給每個(gè)連接準(zhǔn)備一個(gè)OnClose函數(shù)。
4)寫(xiě)OnAccept函數(shù)。如果你的程序中用到TCPListen函數(shù)監(jiān)聽(tīng)某端口,這時(shí)需要寫(xiě)OnAccept函數(shù)。它應(yīng)該有如下的參數(shù)和返回值,函數(shù)名可以任意:
void OnAccept1(socket DT_XDATA *pNewSocket) REENTRANT_MUL
當(dāng)一個(gè)正在listen的socket接受了對(duì)方的連接以后將會(huì)自動(dòng)調(diào)用該函數(shù)。pNewSocket是將要獲得這個(gè)連接的控制權(quán)的socket指針。一般在OnAccept函數(shù)中做以下處理:
ExAccept = pNewSocket; //保存pNewSocket,以后可以用ExAccept發(fā)送數(shù)據(jù)
pNewSocket->recv = OnAcceptRecv; //設(shè)置pNewSocket的OnReceive函數(shù)。
pNewSocket->close = OnClose; //設(shè)置pNewSocket的OnClose函數(shù)。
當(dāng)程序中有多個(gè)處于listen的socket時(shí),你需要給每個(gè)socket準(zhǔn)備一個(gè)OnAccept函數(shù)
5)在主程序中做初始化工作:
/* init. the order is not important */
NetIfInit(); //初始化網(wǎng)絡(luò)接口
ARPInit(); //初始化ARP
TCPInit(); //初始化TCP
MemInit(); //初始化內(nèi)存模塊
RTLInit(EtherAddr); //初始化RTL8019AS,EtherAddr為以太網(wǎng)地址
/* init Devcie struct and init this device */
/* 初始化一個(gè)以太網(wǎng)接口設(shè)備,并設(shè)置這個(gè)設(shè)備的發(fā)送和接收驅(qū)動(dòng)函數(shù)。如果你的系統(tǒng)中以太網(wǎng)接口芯片的驅(qū)動(dòng)不一樣,只要替換這里的發(fā)送和接口驅(qū)動(dòng)函數(shù)就可以了*/
EtherDevInit(&DevRTL,EtherAddr,RTLSendPacket,RTLReceivePacket);
/* add this device to NetIf */
/* 添加一個(gè)網(wǎng)絡(luò)接口設(shè)備。參數(shù)含義是:該設(shè)備的IP地址、子網(wǎng)掩碼、網(wǎng)關(guān)、輸入函數(shù)指針、輸出函數(shù)指針、該設(shè)備的指針。如果你的系統(tǒng)中有多個(gè)網(wǎng)絡(luò)設(shè)備,比如moden,可以編寫(xiě)moden的輸入輸出函數(shù),使用NetIfAdd函數(shù)添加這個(gè)設(shè)備。*/
NetIfAdd(IPAddr,NetMask,GateWay,EtherInput,EtherOutput,&DevRTL);
6)啟動(dòng)25ms的定時(shí)中斷
7)使用類似
ExConn = TCPSocket(IPAddr);
語(yǔ)句分配一個(gè)socket,并且綁定這個(gè)socket的源IP地址。
8)
如果我方作為服務(wù)器方,監(jiān)聽(tīng)某一端口則:
TCPListen(ExConn,Port1,OnAccept1);
當(dāng)另一方向我方Port1端口進(jìn)行連接時(shí),系統(tǒng)自動(dòng)調(diào)用OnAccpet1函數(shù)。
如果我方作為客戶端,向另一方的某個(gè)端口進(jìn)行連接則:
TCPConnect(ExConn,IPAddr2,Port2,OnReceive2,OnClose2);
即向IP地址為IPAddr2的服務(wù)器的Port2端口進(jìn)行連接。在連接成功以后,如果接收到另一方的數(shù)據(jù)則自動(dòng)調(diào)用OnReceive1函數(shù),如果接收到另一方的關(guān)閉請(qǐng)求則自動(dòng)調(diào)用OnClose1函數(shù)。
9)當(dāng)某個(gè)socket處于連接狀態(tài)時(shí),可以使用TCPSend或者TCPSendEx函數(shù)發(fā)送數(shù)據(jù)。
10)需要關(guān)閉連接的時(shí)候,使用TCPClose關(guān)閉連接。
11)當(dāng)一個(gè)socket不再需要時(shí),使用TCPAbort將這個(gè)socket還給系統(tǒng)。
修改Netif\RTL8019.h中的
#define
RTL_BASE_ADDRESS 0xb000
默認(rèn)的基地址為0xb000。當(dāng)單片機(jī)訪問(wèn)0xb000開(kāi)始的地址的時(shí)候,CSRTL信號(hào)線應(yīng)給低電平,以選通RTL8019AS。
修改TCIPIP\TCPIPmem.h中的
#define TCPIP_BUF_SIZE 0x2000
默認(rèn)為8K,建議大于4K。緩沖區(qū)過(guò)小,將會(huì)影響發(fā)送和接收速度。
如果你的系統(tǒng)中有多個(gè)網(wǎng)絡(luò)設(shè)備。修改TCPIP\NetIf.h中的
#define NET_IF_MAX_NUM 1
默認(rèn)情況下為最多一個(gè)設(shè)備。
在主程序中使用NetIfAdd函數(shù)添加網(wǎng)絡(luò)設(shè)備。
修改TCPIP\TCP.h中的
#define TCP_CONNECTION_MAX_NUM 10
默認(rèn)情況下最多支持10個(gè)socket同時(shí)工作。
只有當(dāng)你的程序使用以太網(wǎng)以外的網(wǎng)絡(luò)接口協(xié)議時(shí),才需要修改。修改TCPIP\NetIf.h中的
#define NETIF_HEAD_MAX_LEN 14
默認(rèn)是以太網(wǎng)幀頭長(zhǎng)度,即14個(gè)字節(jié)。
修改Netif\ARP.h中的
#define ARP_ENTRY_MAX_NUM 4
默認(rèn)情況下ARP表大小為4個(gè)記錄。當(dāng)ARP表已經(jīng)滿的時(shí)候,新的記錄將會(huì)覆蓋最老的那個(gè)記錄。
如果不希望系統(tǒng)能夠響應(yīng)ping命令,則修改TCPIP\icmp.h中的
#define ICMP_EN 1
默認(rèn)情況下該開(kāi)關(guān)是打開(kāi)的。如果不需要此功能將其設(shè)置為0
zlIP雖然為51單片機(jī)設(shè)計(jì),但是也可以被移植到其它的CPU上。系統(tǒng)中的GloblDef\GlobleDef.h記錄了CPU的信息,主要修改這個(gè)文件。
1) 設(shè)置BYTE,WORD,DWORD,BOOL等類型的定義
2) 注釋掉#define MCU_C51這一行。注釋掉這個(gè)選項(xiàng)開(kāi)關(guān)以后將程序從C51變?yōu)?/span>ANSIC,程序中將沒(méi)有C51特有的關(guān)鍵字。
3) 字節(jié)順序設(shè)置。即設(shè)置多字節(jié)變量的高字節(jié)存在于低地址還是高地址。51單片機(jī)的字節(jié)順序和0x8086CPU不一樣。刪除# define HOST_ORDER_AS_NET,如果字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序不一樣。
4) 是否移植到具有多線程的51單片機(jī)程序中。比如單片上運(yùn)行了RTOS51、uc/OS-II、Tiny51等單片上的多線程操作系統(tǒng),則需要打開(kāi)# define MULTI_THREAD開(kāi)關(guān),此時(shí)程序中幾乎所有的函數(shù)都聲明為reentrant類型的。
5) 如果需要運(yùn)行在調(diào)試狀態(tài)打開(kāi)# define DEBUG開(kāi)關(guān)。
6) 對(duì)于IO和RAM不是統(tǒng)一編址的系統(tǒng)需要修改RTL8019.c文件中的#define ReadReg(port) (*((BYTE DT_XDATA *)port))和#define WriteReg(port,value) (*((BYTE DT_XDATA *)port) = value),使程序能夠訪問(wèn)IO端口。
公布此源代碼,旨在將我的心得和成果和大家共享,共同學(xué)習(xí)和進(jìn)步。由于本人水平有限,錯(cuò)誤和疏漏之處難免,還請(qǐng)各位同行指正。
參考文獻(xiàn)
[1] 李章林,張立民. TCP/IP在51單片上的實(shí)現(xiàn)特點(diǎn)和方法. “2003年全國(guó)單片機(jī)和嵌入式系統(tǒng)年會(huì)”論文集.2003年
[2] (電子文獻(xiàn))Adam
Dunkels.uIP - A Free Small TCP/IP Stack[Z]. http://dunkels.com/adam/uip/index.html.
2002-1-15.1
[3] (電子文獻(xiàn))Adam Dunkels.lwIP - News
Archive[Z].http://www.sics.se/~adam/lwip/news.html.2001-1-9.
[4] 李章林,張立民. ANSIC程序到KeilC51的移植心得. “2003年全國(guó)單片機(jī)和嵌入式系統(tǒng)年會(huì)”論文集.2003年