ZLIP使用簡介
李章林(lin)1
( 1 南開大學電子應用實驗室,//515x.com.cn,版本:2005-11-28)
|
:TCP/IP協(xie)議(yi)棧程(cheng)序所在目錄。 :Icmp協議。 :IP層。 :網絡(luo)接(jie)口層。 :TCP協(xie)議層。 :TCPIP內存管理程序(xu)。 :網絡接(jie)口(kou)協議所在(zai)目錄。 :ARP協(xie)議。 :以太(tai)網接口協議。 :RTL8019AS以(yi)太網(wang)接口芯片(pian)驅動程序。 :全局函(han)數和宏(hong)定義所在目(mu)錄 :應用層協議所在目錄(lu) :主(zhu)程序(xu)(xu),這(zhe)里包含一個如何使用的(de)例子程序(xu)(xu)。 |
KeilC目錄(lu)下是KeilC51的工程文件所(suo)在(zai)目錄。用KeilC51打開Ex1.Uv2。
MCU目錄下是各(ge)種類(lei)型的51單片機(ji)的頭文件。
單片機上網技術,是當前的一個熱門技術。單片機上網技術中的一個重要部分是在單片上實現TCP/IP協議棧(zhan)。現在可獲(huo)得的(de)TCP/IP源代碼一(yi)般(ban)并不為51單(dan)片機設計,而51單(dan)片機和(he)KeilC51編(bian)譯器有其自身的(de)特(te)點:存儲類型、函數(shu)指(zhi)針、重入(ru)函數(shu)等,ZLIP就是針對(dui)這些特(te)點設計的(de)TCP/IP協議棧(zhan)。
ZLIP設計(ji)的目標是:
1) 精簡TCP/IP協議棧(zhan),以減小代碼量(liang)。ZLIP目前沒有支(zhi)持UDP協議,ICMP協(xie)議也只支(zhi)持其中的echo協議(響應ping數(shu)據包)。lwIP是一個功能(neng)全面(mian)的TCP/IP協議棧,但是相對51來說代碼量較(jiao)大。
2)
應用(yong)層接口簡單,以兼容通用(yong)的socket接口。uIP有很小的代碼量和減小代碼量(選擇AVR為目標器件時,代碼為5K左右)和RAM使用量(100字節左右)。uIP采用了不保存需要應答的數據包的RAM使用方案,沒有和BSD的套接字接口兼容,應用層接口較復雜。
3)
針對KeilC51編譯器設計。所有的外部變量都使用了xdata類型,全部指針都為明確存儲類型的指針,需要重入的函數已經聲明為reentant,使用KeilC的小模式下編譯。
使用12M晶振、KeilC編譯器、89C55單片下測試的技術參數如下:
表1:技術參數
代碼量(字節) |
外部RAM使用量(字節) |
發送速度(字節/秒(miao)) |
14841 |
11068 |
5.892K |
ZLIP的特(te)點如(ru)下(xia):
1)有適中代碼量和RAM使用量。
2)使用類似MFC的CScoket的套接字接口,使用方便。
3)支持多TCP連接、多網絡設備。能方便地移植到多任務操作系統和其它CPU下。能方便地替換網絡接口協議和網卡驅動設備。
4)支持ping命令的響應。
5)為單片機(ji)設計:所有的外部變量都使(shi)用(yong)了xdata類(lei)型,全部指針都為明確存儲(chu)類(lei)型的指針,需(xu)要重入的函數(shu)已經聲(sheng)明為reentant,使(shi)用(yong)KeilC的小模式編譯(yi)。
圖1:RTL8019AS電路(lu)左半部分
圖2:RTL8019AS電路(lu)右(you)半(ban)部分
該程序不能(neng)在KeilC下軟(ruan)件仿真,因為程序的(de)運行需要(yao)外部(bu)電路(lu)配合。該51系統(tong)的(de)外部電路(lu)主要有:以太網(wang)接口芯片RTL8019AS電路、外(wai)部RAM電路。
以太網接口(kou)芯片RTL8019AS電路(lu)圖,如圖1和圖2表示。A0~A4接地址線,D0~D7接數據線,CSRTL是片選線(低電平有效),RD-和WR-接(jie)讀寫信號線(xian)。
zlIP接口函數基(ji)本(ben)和BSD的(de)套(tao)接字接口(kou)相同。
TCPSocket()。
函(han)數原型:socket xdata * TCPSocket(IP_ADDR ScrIP)。
功能:申請一(yi)個套接字(zi)。ScrIP是這個套接字(zi)的本(ben)地IP地址。返(fan)回socket類型指針,如果申請失(shi)敗返(fan)回NULL。
TCPConnect()。
函數原(yuan)型: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地(di)址(zhi)為DestIP的(de)服(fu)務(wu)器的(de)DestPort端(duan)口發(fa)起連接(jie)。參數(shu)recv和(he)close用于設置當接收(shou)到數據包和對方要求(qiu)關(guan)閉TCP連接時應該(gai)調用的(de)回調函(han)數指針。連接成功返回TRUE,否則(ze)返(fan)回(hui)FALSE。
TCPSend()。
函數原型:BOOL TCPSend(socket xdata * pTCB,void xdata *buf,WORD DataSize)。
功能:發送數(shu)據(ju)。發送數(shu)據(ju)的TCP連接(jie)是套接(jie)字(zi)指針pTCB對(dui)應(ying)的(de)連(lian)接,發送的(de)數據(ju)的(de)起始地址為buf,大小為DataSize。發送成(cheng)功返回TRUE,否則返回FALSE。
TCPSendEx()
函數原型:BOOL TCPSendEx(socket xdata * pTCB,struct SMemHead xdata *MemHead) 。
功能(neng):快速發送數據。在使用(yong)TCPSend函數時(shi),你首先需(xu)要將(jiang)數據放(fang)入buf指(zhi)向的(de)內存中(zhong),然后(hou)調用TCPSend函數,接著該函數會將buf指向的(de)內存區數據拷(kao)貝到TCP緩沖(chong)區中。使用TCPSendEx 時你(ni)首先用TCPAllocate(DATA_SIZE)獲得一個TCP緩(huan)沖(chong)區(qu),然(ran)后(hou)直接將(jiang)數據放入(ru)TCP緩沖區(qu)中,從而比TCPSend函(han)數少(shao)一次數據拷(kao)貝,提(ti)高(gao)發送速度。
參(can)數(shu):發送(song)數(shu)據的TCP連(lian)接是(shi)套接字指針pTCB對應(ying)的連接(jie),發(fa)送的數據放在(zai)TCP緩存MemHead中。發送(song)成功返回TRUE,否則返回FALSE。
TCPListen()。
函數原型:BOOL TCPListen(socket xdata *pTCB,WORD ScrPort,void (code * accept)(socket xdata *pNewTCB)) 。
功能:使用套接字pTCB在ScrPort端口監(jian)聽。參數accept是當有(you)客戶端向(xiang)這個監聽端口連接成功時調用的(de)回調函數指針。
TCPClose()。
函數原型:void TCPClose(socket xdata *pTCB)。
功能:我方主動關(guan)閉(bi)連接時調用TCPClose函數(shu),它(ta)將(jiang)要求關閉套接字pTCB對應的連接(jie)。TCPClose返回以后這個(ge)TCP連接可能(neng)保(bao)持,因為(wei)另一方還沒有發起關閉請求。
TCPAbort()。
函數原型:void TCPAbort(socket xdata *pTCB)。
功能(neng):當使用完(wan)這個套接字以后,調用TCPAbort,將這(zhe)個(ge)套(tao)接字釋放,還給系(xi)統。
使用ZLIP時,在你的主程序(xu)中(請(qing)看示例程序的main.c文件(jian))需(xu)要做的步(bu)驟如下:
1)首先設置一個25ms的定時中斷函數(示例程(cheng)序為Timer函數(shu)(shu))。請在中斷函數(shu)(shu)中調用NetIfTimer(); ARPTimer(); TCPTimer();三個函數。
2)寫OnReceive函(han)數(shu)(shu),它應(ying)該有(you)如下的參數(shu)(shu)和返回值(zhi),函(han)數(shu)(shu)名可以(yi)任(ren)意:
void OnReceive1(void DT_XDATA * buf,WORD size) REENTRANT_MUL
在使用(yong)TCPConnect函(han)數時,OnReceive1將作為TCPConnect函數(shu)的一個參數(shu),也就是設置該socket的(de)接(jie)收函數(shu)。當TCP連接接收(shou)到(dao)對方數據時,將自動調(diao)用OnReceive1函數(shu)。buf指向接收的數據,size是(shi)接收的數據量的大(da)小。你(ni)可(ke)以在OnReceive1中(zhong)處理接(jie)收的(de)數據。當程序(xu)中(zhong)有多個TCP連接同時存在時,你需要給每個連接準備一個OnReceive函(han)數(shu)。
3)寫OnClose函數,它應該有如下的參數和返回值,函數名(ming)可以任意:
void OnClose1(socket DT_XDATA * pSocket) REENTRANT_MUL
類似于OnReceive函(han)數,當TCP連(lian)(lian)接的另一(yi)方(fang)首先向我(wo)方(fang)發起(qi)關閉連(lian)(lian)接的請(qing)求(qiu)時,系統將自(zi)動調用OnClose函數(shu)。pSocket指向將要(yao)關閉的socket。如果你(ni)想(xiang)立即關閉這(zhe)個連接則在OnClose函數中調用(yong)TCPClose函數。當(dang)程序中有多個(ge)TCP連(lian)接同時(shi)(shi)存在時(shi)(shi),你需要(yao)給每(mei)個(ge)連(lian)接準備一個(ge)OnClose函數。
4)寫OnAccept函數(shu)。如(ru)果(guo)你的程序中(zhong)用到TCPListen函數(shu)監聽某端口,這(zhe)時需要(yao)寫OnAccept函數。它應(ying)該(gai)有(you)如(ru)下(xia)的參數和返回值,函數名可以任意(yi):
void OnAccept1(socket DT_XDATA *pNewSocket) REENTRANT_MUL
當(dang)一個(ge)正在listen的socket接受(shou)了(le)對方的連接以后將會自(zi)動調用該(gai)函數。pNewSocket是將要獲(huo)得這個連接(jie)的(de)控(kong)制權的(de)socket指針。一般(ban)在OnAccept函數中做以下處理:
ExAccept = pNewSocket; //保存pNewSocket,以后可(ke)以用ExAccept發(fa)送數據
pNewSocket->recv = OnAcceptRecv; //設置pNewSocket的(de)OnReceive函數。
pNewSocket->close = OnClose; //設(she)置pNewSocket的OnClose函數(shu)。
當(dang)程序中有多個(ge)處于(yu)listen的socket時(shi),你需要給每個socket準備一個OnAccept函數
5)在主程序(xu)中做初始化工作:
/* init. the order is not important */
NetIfInit(); //初始化(hua)網絡接口
ARPInit(); //初(chu)始化ARP
TCPInit(); //初始化(hua)TCP
MemInit(); //初(chu)始化內存模(mo)塊
RTLInit(EtherAddr); //初(chu)始(shi)化RTL8019AS,EtherAddr為(wei)以太網地址(zhi)
/* init Devcie struct and init this device */
/* 初(chu)始化一個(ge)以太(tai)網(wang)接(jie)口設備(bei),并設置這個(ge)設備(bei)的發(fa)送和(he)接(jie)收驅動(dong)函(han)數(shu)。如果你(ni)的系統中以太(tai)網(wang)接(jie)口芯片的驅動(dong)不一樣,只要替換(huan)這里的發(fa)送和(he)接(jie)口驅動(dong)函(han)數(shu)就可以了*/
EtherDevInit(&DevRTL,EtherAddr,RTLSendPacket,RTLReceivePacket);
/* add this device to NetIf */
/* 添加(jia)一(yi)個(ge)網絡接口設備。參數含義(yi)是:該設備的IP地址、子網(wang)掩碼、網(wang)關、輸入函(han)數(shu)(shu)指(zhi)針、輸出函(han)數(shu)(shu)指(zhi)針、該設備的(de)(de)指(zhi)針。如(ru)果你的(de)(de)系統中有(you)多個網(wang)絡(luo)設備,比(bi)如(ru)moden,可(ke)以編寫moden的輸入(ru)輸出(chu)函數,使用NetIfAdd函數添加(jia)這個設備(bei)。*/
NetIfAdd(IPAddr,NetMask,GateWay,EtherInput,EtherOutput,&DevRTL);
6)啟動25ms的定時中(zhong)斷(duan)
7)使用類(lei)似(si)
ExConn = TCPSocket(IPAddr);
語句分配一個socket,并且綁定這個socket的(de)源IP地址。
8)
如果我方(fang)作為服務器方(fang),監聽(ting)某一端口則:
TCPListen(ExConn,Port1,OnAccept1);
當(dang)另(ling)一方向我方Port1端口進行連接時(shi),系統自動調用OnAccpet1函(han)數。
如果(guo)我方作為客戶端,向另一方的某個端口進行(xing)連接則:
TCPConnect(ExConn,IPAddr2,Port2,OnReceive2,OnClose2);
即(ji)向(xiang)IP地址為IPAddr2的(de)服(fu)務器的(de)Port2端(duan)口進行(xing)連接(jie)。在連接(jie)成功以后(hou),如(ru)果接(jie)收到另一方的數(shu)據則自動調用OnReceive1函數,如果接收到另(ling)一方的關閉(bi)請求則自動調用OnClose1函數。
9)當某個socket處于連接狀態時(shi),可(ke)以使用TCPSend或(huo)者(zhe)TCPSendEx函數(shu)發送數(shu)據。
10)需(xu)要關(guan)閉連接的時候(hou),使(shi)用TCPClose關閉連接。
11)當(dang)一個socket不再需(xu)要時,使用TCPAbort將這個socket還(huan)給系統。
修改Netif\RTL8019.h中的
#define
RTL_BASE_ADDRESS 0xb000
默認的基(ji)地(di)址(zhi)為(wei)0xb000。當單片(pian)機訪問0xb000開始的地址的時(shi)候,CSRTL信號線(xian)應(ying)給低電平,以選通RTL8019AS。
修改TCIPIP\TCPIPmem.h中的
#define TCPIP_BUF_SIZE 0x2000
默認為8K,建議大于4K。緩沖區過小(xiao),將會(hui)影響發送和(he)接(jie)收速度。
如(ru)果(guo)你的(de)系統中(zhong)有多(duo)個網絡設(she)備。修改TCPIP\NetIf.h中的(de)
#define NET_IF_MAX_NUM 1
默認情況(kuang)下(xia)為(wei)最多一(yi)個設備(bei)。
在(zai)主程(cheng)序中使用NetIfAdd函數添加(jia)網絡設備(bei)。
修改TCPIP\TCP.h中的
#define TCP_CONNECTION_MAX_NUM 10
默認情況下最多(duo)支持(chi)10個(ge)socket同時工作。
只有當你的程序(xu)使用以太網以外的網絡接口協議時,才需(xu)要修(xiu)改(gai)。修(xiu)改(gai)TCPIP\NetIf.h中的
#define NETIF_HEAD_MAX_LEN 14
默(mo)認是以(yi)太(tai)網幀(zhen)頭(tou)長度(du),即14個字節(jie)。
修改Netif\ARP.h中的
#define ARP_ENTRY_MAX_NUM 4
默認情(qing)況(kuang)下ARP表大小(xiao)為(wei)4個記錄。當(dang)ARP表已(yi)經滿的時候,新的記(ji)錄將會(hui)覆(fu)蓋最老的那個記(ji)錄。
如果不希(xi)望(wang)系統(tong)能夠(gou)響應(ying)ping命(ming)令,則修(xiu)改TCPIP\icmp.h中(zhong)的
#define ICMP_EN 1
默認情況下該開(kai)(kai)關是打開(kai)(kai)的。如(ru)果不需要此功能將其設置為0
zlIP雖然為51單片機設(she)計,但是也(ye)可(ke)以被移植到其(qi)它的CPU上。系(xi)統中的GloblDef\GlobleDef.h記(ji)錄了(le)CPU的信息,主要修改這個(ge)文件。
1) 設(she)置BYTE,WORD,DWORD,BOOL等類型的定義
2) 注(zhu)釋掉#define MCU_C51這一行。注(zhu)釋掉這個選項(xiang)開關以后將程序從C51變為ANSIC,程序中將沒有C51特有的關鍵字(zi)。
3) 字節(jie)順序設置。即設置多(duo)字節(jie)變(bian)量的高(gao)字節(jie)存在于低地址還是高(gao)地址。51單片機的(de)字(zi)節順序和0x8086CPU不(bu)一樣。刪(shan)除# define HOST_ORDER_AS_NET,如果字節順序和網絡字節順序不(bu)一樣。
4) 是(shi)否移(yi)植到具(ju)有多線程(cheng)的(de)51單(dan)片機程(cheng)序中。比如單(dan)片上運(yun)行了(le)RTOS51、uc/OS-II、Tiny51等單片上的多線程操作系統,則需要打開# define MULTI_THREAD開(kai)關(guan),此時程序(xu)中幾乎所有的函數都聲明為reentrant類型的(de)。
5) 如果需要運行在調試狀態打開(kai)# define DEBUG開(kai)關。
6) 對于IO和RAM不是統一編址的系統需要修改RTL8019.c文件(jian)中(zhong)的#define ReadReg(port) (*((BYTE DT_XDATA *)port))和#define WriteReg(port,value) (*((BYTE DT_XDATA *)port) = value),使程(cheng)序能夠訪(fang)問IO端口。
公布此源代碼(ma),旨在將我的心得和成果和大家(jia)共(gong)享,共(gong)同(tong)學(xue)習和進步。由于本人水平(ping)有(you)限,錯誤和疏漏之(zhi)處難免,還請各(ge)位同(tong)行指正。
參考文獻
[1] 李章林,張立民. TCP/IP在51單片上的實現特點和方法. “2003年全國單片機和嵌入式系統年會”論文集.2003年
[2] (電子文獻)Adam
Dunkels.uIP - A Free Small TCP/IP Stack[Z]. //dunkels.com/adam/uip/index.html.
2002-1-15.1
[3] (電子文獻)Adam Dunkels.lwIP - News
Archive[Z].//www.sics.se/~adam/lwip/news.html.2001-1-9.
[4] 李章林,張立民. ANSIC程序到KeilC51的移植心得. “2003年全國單片機和嵌入式系統年會”論文集.2003年