TCP/IP最先是在UNIX系统里实现的,后来的LINUX、DOS、WINDOWS也实现了TCP/IP,随后TCP/IP协议也被移植到其它嵌入式的处理器上,例如8位的MCS51单片机、AVR单片机,16位的ARM、C166以及32位的MIPS、ARM等芯片上。 TCP/IP协议的最底层IP层,很多定义都是16位或32位的,例如源IP地址(32位)目的IP地址(32位),校验值(16位),特别是较验值,是以16位为单位进行计算的,这样使得能够处理16位、32位运算的CPU,比如80286、80386……,ARM、MIPS、DSP,就有很大的速度上的优势。而8位机MCS51处理则会慢很多。
由于指令的原因,以及资源上的原因,在UNIX上实现的TCP/IP协议的原代码并不能够直接移植到8位的单片机上。最早期的LINUX1.0版的内核是最小的实现TCP/IP的操作系统,它的程序的大小大概在1兆字节。而现在的红旗LINUX,红帽子LINUX,内核多达几十兆,整个系统要几张光盘来装。早期的LINIX因为小,而被移植到掌上电脑,PDA等产品中。 单片机的程序空间是极为有限的,直接寻址的空间仅64K字节,这跟电脑的存储空间相比要差几个数量级。除了程序空间小之外,可用的内存RAM也是非常小的,最多只能扩64K的RAM,而电脑的RAM至少在1兆以上。单片机的运算速度也极为有限,一般只有2MIPS,而电脑上的处理能力在100MIPS以上。
有些人提到有没有必要在单片机上实现TCP/IP的问题。因为TCP/IP是一种标准,以太网也成为局域网的标准。在很多情况下运用以太网和TCP/IP,能够简化结构。比如目前较热的智能小区,因为布线的原因,不能为每个家庭布很多线,而以太网的8芯双绞是一定有的。例如宽带上网,是直接通过以太网的,如果你制造的设备,比如安全产品,远程抄表产品,家居智能产品能够走以太网的话,可以利用现成的以太网络。但如果走其它网络,比如RS485、CAN单线、LONWORKS等,那么需要另外布线。布线是复杂的,还涉及到消防安全等。从成本看,用以太网实现联网要比CAN、LONWORKS等更为便宜,集线器、交换机现在都非常便宜,而且将来有三网合一的趋势,电话、电视、计算机三网合一。将来的趋势可能是高速的以太网的天下,电话信号、电视信号、联网都在以太网上跑。尽管目前还未能实现,但是这种趋势是不可避免的。
有网友提到就算要利用以太网,也没有必要跑TCP/IP。那么为什么要跑TCP/IP呢? TCP/IP是一标准,这个标准使得数据传输不一定是要局域网,而可以在互联网、跨地区跨国界。例如你在某一区域安装了很多监控产品,但数据中心可能不设在那个区域,而设在其他地区。TCP/IP有两种协议TCP和UDP;TCP保证了数据传输的正确性,(如果你的数据只跑以及网层,那么你的数据完整性是要你的编程来保证的,校验的计算。数据包的丢失需要你手工处理,而TCP把这些所有你要处理细节都帮你处理了。UDP可以面向广播的、视频的、音频的等方面的应用。实现TCP/IP的协议的好处是可以统一平台,比如智能小区的产品,如果大家都遵守TCP/IP的协议,那么大家的产品才能兼容,假设一个大型的智能社区,这个社区可能由多家设备供应商进行建设,可能有某些厂商做平台、做软件,一些厂商做硬件。如果大家遵守TCP/IP协议,各自的远程抄表产品,智能防盗产品就有可能兼容,对地产开发商来说,可以选择多个供应商,有利于竞争,也避免某个厂家倒闭造成重大影响。
题外话说的多了,还是回到本章要谈的内容吧。由于单片机与电脑的差别很大,两者的实现有很大的不同。在电脑里编写TCP/IP程序,你可以不考虑代码大小、代码速度,但在单片机上这些都是你要考虑的问题。 综合来说,单片机实现与UNIX实现TCP/IP有如下区别: (1)、操作系统。不论是WINDOWS、UNIX、LINUX,它们都有一个多任务操作系统,这使得代码编写简单化,而在单片机上,因为资源的原因而无法使用多任务操作系统,这使得代码结构变为顺序执行+硬件中断的方式,而在电脑里却可以并发地执行。对程序执行结构,单片机要考虑更多。
(2)、内存分配。WINDOWS或UNIX的内存分配是动态的,根据需要随时分配,随时撤消 。我们阅读一些关于LINUX、UNIX的书,它们都是mbuf的存储结构。mbuf是一个存储链,这个链可以动态地增加和减小。比如在数据包很少的情况下,UNIX分配一个2K字节的缓冲区可能就够用了,但如果数据包很多,就有可能要分配64K甚至更多的缓冲区,可分配的内存要根据CPU的可用内存来调整。 但是在单片机却不能够这样做。一个最大的以太网数据包有1500多个字节,分配一包的缓冲区就要1.5K字节,而一般实现TCP/IP的单片机只外接一块32K字节的RAM。而这32K字节的RAM要被各个协议所用,而不仅仅是存放收到的数据包。一般的做法是分配一个256×6=1536个字节的RAM来存放收到的以太网数据包。收到一包就处理一包。而UNIX却可以收很多包才处理。在单片机里,存放收到的以太网数据包的RAM是固定的,而不是动态分配的。所有UNIX关于内存管理、内存分配、mbuf的结构在单片机里并不适用。这些代码对单片机是无用的。
(3)、指针。在电脑里,指针只有一种,就是指向某一地址的RAM,而在单片机里指针有几种:
1、 指向外部RAM的指针 例uchar xdata *p 使用指令 movx @dptr 占二个字节
2、 指向程序ROM的指针 例uchar code *p 使用指令 movc 占二个字节
3、 指向内部的RAM的指针 例uchar data *p 使用指令 mov @ri 占一个字节
4、 指向外部RAM的分页指针 例uchar pdata *p 使用指令 movx @ri 占一个字节
5、 一般指针,可以指向以上的任何一种 占三个字节
6. 还有其它用于分组切换的指针。
在电脑里,所有程序都必须先放在RAM里才能运行,所以它的指针只有一种情况,就是指向RAM。而单片机的结构和电脑的结构有很大差别,指针类型很多,对指针运算的速度也不一样,由于第5种指针"一般指针"运算很慢,同时又需要占用很多程序空间,这使得指针运算不能从UNIX源代码直接移植到单片机上,而UNIX实现TCP/IP的源代码中,用的最多的就是指针,而在单片机里一般要求少用指针,或使用特定类型的指针。这使用UNIX的源代码需要作很多的改动。
(4)、参数传递。在UNIX实现的TCP/IP源代码中,一般有很多的参数传递,而在单片机里允许传递的参数是有限的(因为受到内部RAM的限制),同时参数传递的过程要浪费程序代码空间,也降低单片机执行速度。所以在单片机的实现里,一般不要做太多的参数传递,而多使用公共的全局变量来实现调用的过程。这种情况下,UNIX的一般源程序是相对独立的,受其它函数或变量的影响很小,而单片机里各程序的相互依赖程度要大。因为在单片机里往往共享某一数据、某一变量。
(5)变量定义。UNIX和KEIL C51虽然都是C语言,但两者又有所不同,对于一些变量的定义,两者却不能通用。例如,单片机的特殊寄存器定义,sfr sfr16 sbit等,在标准C里是没有的。在标准C里支持的结构,在KEIL C里也有可能不支持,比如一些C++的语法。在处理上的特殊性,也可能不一样,比如IP地址类型,在UNIX里一般将IP地址定义为数组:
uchar ip[4]; 而在单片机里,我的定义是
union IP_address_type{uchar bytes[4];
uint words[2];
ulong dwords;}
IP 被定义为共用体,而不是简单的一个数组。为什么要这样做,是因为单片机处理的特殊性,例如比较两个IP地址IP1,IP2是否相等,如果使用数组,比较是麻烦的: 要写成 if(IP1[0]==IP2[1])**IP1[1]=IP2[1]**…… 用共用体可以简化为
if(IP1.dwords==IP2.dwords)…… 有时候,我们又要把IP地址按16位来计算,比如较验和计算,那么IP地址按16位加可以写成: IP.dwords[0]+ IP.dwords[1], 有时,我们又要对IP地址按字节赋值,比如IP地址从24C02里读出来,需要按字节赋值:可以为 IP.bytes[0]=×× IP.bytes[1]=×× IP.bytes[2]=×× IP.bytes[3]=××
如果不作这样的定义,运算将复杂很多。而且一些编译会认为类型混乱而无法编译。 在单片机里使用共用体会简化很多。而在UNIX里要对这些值作改变,一般是利用指针进行的。在电脑里,用指针运算是方便的,而且速度也快,但在单片机里,却不能够方便地使用指针。 在UNIX里的一些结构类型的定义都要被改写。这样也使得UNIX的源代码不能直接用在单片机上。
(6)、协议支持。在UNIX里可支持比较完整的TCP/IP协议,但在单片机里无法做到,这是因为单片机根本没有足够的代码空间来支持这些协议。一般在单片机里实现与需要有关的部分,而不使用的协议则一概不支持。例如文件共享SMB协议,在UNIX、WINDOWS都支持,但单片机上却没有必要。一般只能在单片机中实现:ARP、IP,ICMP、TCP、UDP这些协议,而更高层的协议,http、smtp、ftp一般是不需要支持的。虽然有些单片机例如AVR上网方案实现了http、smtp、ftp协议,但我们认为实用性不太,因为AVR上网方案用的是MEGA103,而该芯片要150元左右,高昂的造价使得AVR上网方案没有得到广泛的应用。单片机应用的TCP/IP协议大多是为了完成数据采集和数据传输,而不是网页浏览、文件传输这些功能。就对某一协议而言,例如ARP协议,UNIX系统支持以太网、令牌环等网络的ARP,但单片机里只支持以太网,也就是说,对于某一协议,也有可能要作简化。IP包最大可以为65K,可以分段传输,而在单片机里根本无法容纳如此大的数据包,因此一般是不支持分段的。单片机一般采用发送小数据包的方式,以避免分段。
(7)、硬件接口。在UNIX或WINDOWS里,对网卡驱动无一例外都是采用中断方式。而在单片机的应用中,大部份的方案都是查询式的。因为电脑的处理速度快,一次中断的处理时间也很短,不会影响系统内的其它中断。但在单片机里就不行了,处理一次中断,收取一个数据包一般要几毫秒的时间,这将封锁其它中断的产生(只有高优先级的中断可以执行),而单片机往往还存在其它一些中断,比如串口按收中断,A/D条件中断、键盘中断等需要被执行,这就使得消耗时间长的网卡中断改为查询式执行。在电脑里,对网卡的驱动相对简单,而在单片机里需要处理的事情更多。比如缓冲区溢出,阅读一些驱动程序源代码,你可能发现在电脑里的一些程序根本没有处理溢出的代码。因为电脑执行快,网卡缓冲区的溢出几乎是不会发生的,不要说10M网卡,就是100M网卡,电脑也能够很快处理。电脑往往采用即插即用方式来驱动网卡,而单片机却不能这样做,因为即插即用要很多代码来实现,而使用跳线方式,电脑里驱动NE2000的网卡,一般都是用16位DMA的方式,而在单片机里却只能用8位DMA方式。这也使用UNIX对网卡驱动的代码不能直接移植。