ZLIP使用简介
李章林1
( 1 南开大学电子应用实验室,http://www.joybabycare.com,版本: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(ipadsdr,NetMask,GateWay,EtherInput,EtherOutput,&DevRTL);
6)启动25ms的定时中断
7)使用类似
ExConn = TCPSocket(ipadsdr);
语句分配一个socket,并且绑定这个socket的源IP地址。
8)
如果我方作为服务器方,监听某一端口则:
TCPListen(ExConn,Port1,OnAccept1);
当另一方向我方Port1端口进行连接时,系统自动调用OnAccpet1函数。
如果我方作为客户端,向另一方的某个端口进行连接则:
TCPConnect(ExConn,ipadsdr2,Port2,OnReceive2,OnClose2);
即向IP地址为ipadsdr2的服务器的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年