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

圖1:RTL8019AS電路左半部分

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