103_电话线路的数据通信程序
1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
3 系统开发环境
3.1 系统开发环境
本系统采用的软件工具有:
Windows平台虚拟机:VMware-workstation-5.5.1-19175
操作系统: Radhat 9.0
编辑器:VI
编译器:gcc 3.2.2 20030222
函数库:glibc 2.3.2.so
系统头文件:glibc_header
3.2 gcc简介
Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。
Gcc编译器能将C、C++语言源程序、汇编程序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc则通过后缀来区别输入文件的类别。
虽然称gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。 【www.think58.com计算机毕业论文网】
命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤。当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。 [资料来源:http://THINK58.com]
gcc编译器的调用参数大约有100多个,其中多数参数可能根本就用不到,开发过程中使用最多的命令就是:gcc -O test test.c, test.c是C语言源程序,test是可执行文件,-O是gcc的参数,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
4 程序的设计
4.1 设计概要
此次设计之所以选择Linux,是因为Linux支持各种硬件平台和外部设备,Linux对串口的操作非常简单,功能齐全,且Linux有功能强大的C语言编译器(gcc),使程序的可移植性非常好。Linux又是完全开放的操作系统,很大程度上,Linux的开放性,赋予了其无穷的生命力。
本设计基于Linux操作系统,利用POSIX termios函数控制计算机串口,通过AT命令初始化本地MODEM并让本地MMODEM与异地MODEM进行拨号连接。本设计使用的是56K MODEM,不同于现在市面上常见的ADSL MODEM。 连接成功后,通过读写计算机串口来实现文件传输--这个过程是用一个自编的文件传输协议来完成的。整个程序使用C语言实现。总体设想如图3所示:
图3 预期设计效果
总体来说,设计可以分成两部分来进行:RS-232-C与MODEM。但这两部分并不独立,MODEM的初始化及拨号连接等等一切对MODEM的控制和访问,都要通过串口,因为串口是计算机与MODEM的唯一通道。两部分的关系如图4如示: think58
图4 RS-232-C
所以设计的关键是现实对RS-232-C控制,然后再完成MODEM的部分。
4.2 RS-232-C建立与打开
一个输入/输出(I/O)端口是用来将资料送入计算机及从计算机取出的通道,有许多种类的输出/输入端口,现在要处理的是串行端口,每一个串行端口必须有一个输入/输出地址,以及一个中断号码(IRQ),有四个串口设备对应到COM1- COM4:
ttyS0 (COM1) address 0x3f8 IRQ 4
ttyS1 (COM2) address 0x2f8 IRQ 3
ttyS2 (COM3) address 0x3e8 IRQ 4
ttyS3 (COM4) address 0x2e8 IRQ 3
ttyS*是串口在Linux中的命名方式,COM*是串口在Windows中的命名方式,Linux是以ttyS0开始命名的,与Windows从COM1开始命名不同。ttyS0和ttyS2使用同一个中断IRQ4,ttyS1和ttyS3使用同一个中断IRQ3,所以如果同时使用了ttyS0和ttyS2或者ttyS1和ttyS3,就必须重设它们的中断才可以,幸运的是,现在常见的设备都不使用串口,所以连接56K MODEM时,多数情况都不必再重设串口中断,如果是外置MODEM,则它占用的是ttyS0,如果是内置MODEM,则它占用的很可能是ttyS2。Linux下可以使用ls –l /dev/ttyS*来检查系统是否正确的建立了串口设备。
此次设计是在Windows VMware Workstation虚拟机环境下安装的Radhat9.0操作系统,所以在进行设计之前,必须为虚拟机添加串口设备,具体是在设置中点击添加,选择串口设备,然后一直选择下一步即可。如果不设置虚拟机串口设备,在虚拟机下将无法识别到串口。设置成功后,可以像使用真正的操作系统一样使用虚拟机,虚拟机下面的串口是真实的物理串口,而并非是虚拟出来的。VMware Workstation中设备的使用很多都是真实的,不是模拟出来的。 think58好,好think58
确定串口正确建立以后,使用open()函数来打开它。像所有的设备一样,Linux提供设备文件来挂载端口。要访问一个串行端口,只要像访问文件一样来访问设备文件即可。串行端口的设备文件名在Linux中是:/dev/ttyS*(*代表0,1,2…)。 以ttyS0以例,
int fd;
fd = open(“/dev/ttyS0”, O_RDWR | O_NOCTTY | O_NDELAY);
O_RDWR代表可读可写,O_NOCTTY告诉Linux该程序不想成为那个端口的“控制终端”。如果不指明这个,任何输入都会影响此进程。O_NDELAY标志该程序不关心DCD信号的输入状态,即无论另一端端口是否启用和运行。如果不指明这个标志,此进程将休眠,直到另一端发送DCD信号过来。
如果不是以root身份登陆,在打开串口时,可能会发生权限不允许的情况,这时候使用chmod a+rw /dev/ttyS0命令改变文件权限即可,或者直接用root身份登陆。
4.3 串口设置与读写
4.3.1 c_cflag控制项设置
在对串口进行读写之前,必须对串口进行设置,这也是串口操作部分最复杂,最重要的一步。串口技术已经很成熟,相应的它的设置也变得很复杂,但相当一部分功能是针对已经被淘汰的外部设备,所以这里只用到了其中一部分的功能。
多数系统支持POSIX termios终端串行接口来改变,例如波特率,字符尺寸等参数。首先必须引入文件<termios.h>。它定义了终端控制结构termios和POSIX控制函数。termios是一个结构体,它的结构成员如表2如示:
表2 Termios 结构成员
成员 描述
c_cflag 控制项
c_lflag 线路项
c_iflag 输入项
c_oflag 输出项
c_cc 控制字符
c_ispeed 输入波特(新接口)
c_ospeed 输出波特(新接口)
在对termios成员进行设置之前,首先要使用tcgetattr(fd, &option)读取当前串口的状态,termios成员设置完之后,再使用tcsetattr (fd,TCSANOW,&option)将设置的内容写入计算机串口,option是termios类型变量,TCSANOW表明所有的改变立即生效,而不等待发送或者接收的数据结束。
c_cflag成员控制波特率、数据位、校验、停止位和硬件流控制,所有支持的设置都有常数对应。对应此次设计的c_cflag设置如下:
think58
[资料来源:www.THINK58.com]
[资料来源:http://www.THINK58.com]cfsetispeed(&options, B115200); //Buadrate 115200
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CSIZE; //Mask the character size bits
options.c_cflag |= CS8; //Select 8 data bits
options.c_cflag &= ~PARENB; //NO
options.c_cflag &= ~CSTOPB; //One Stop bit copyright think58 [资料来源:www.THINK58.com]
cfsetispeed和cfsetospeed函数用于设备波特率,上面设置的是115200波特,可供选择的还有B76800,B19200,B1200等等。有些Linux版本并不支持cfsetispeed和cfsetospeed,但仍可以用options.c_cflag |= B115200这种方式进行设置。
不同于波特率,没有函数可以用来设置字符尺寸,需要使用比特掩码的操作来完成。CLOCAL代表本地线不改变端口的拥有者,CREAD表示接收有效。CLOCAL | CREAD将保证这个程序不会成为端口的拥有者,从而不会妨碍控制工作和挂起信号并使串行接口驱动读取进入的数据。
由异步通信可知,串行数据中数据可以有5位、6位、7位、8位,并且可以选择奇偶校验,或者无校验。最后的停止位也可以设置成1位或者2位。CS8,~PARENB,~CSTOPB将把串行数据设置为:八位数据位,无奇偶校验,1位停止位。CSIZE是数据位数掩码,必须在设置CS8之前使用,起清除的作用,会把之前对数据位数的设置清除掉。
c_cflag还包括对硬件流设备的常数,这里不会用到,可以不予理会。
4.3.2 c_iflag、c_lflag和c_oflag设置
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG)
options.c_iflag &= ~(INPCK | IGNPAR |IXON |IXOFF |IXANY);
options.c_oflag &= ~OPOST;
think58 [资料来源:www.THINK58.com]
输入模式成员c_iflag控制任何在端口接收字符的输入处理,INPCK和IGNPAR分别代表校验有效和忽略校验错误,但是并没有使用校验位,所以屏蔽这两项。IXON、IXOFF、IXANY分别是软件流控制开启、软件流控制关闭和允许任何字符再次启动流,为简单起见,设计中不使用软件流控制串行通迅,所以屏蔽这三项。
c_lflag控制输入字符如何被串行驱动使用,它和c_oflag的设置应该说是整个串口设置当中最重要的,它直接影响到串口的工作方式。简单讲,串行设备有三种不同的的输入方式,需要为程序选择合适的工作方式:
◆ 标准输入模式。这是终端设备的标准处理模式,这种方式下,read会传回一整行完整的输入,一行的结束默认是以NL、文件结束符,或是一个行结束字符。
◆ 非标准输入模式。此模式下,可以每次读取固定数量的字符,并允许使用字符接收时间定时器。这种模式可以用在每次读取固定长度字符串的程序中,或者所连接的设备会突然大量字符的情况下。
◆ 等待来自多信号源的输入。实际上这并不是另一种输入方式,它用于处理来自多个设备的数据。
非标准输入输出更适合此次项目,ICANON代表标准模式,所以屏蔽它。ECHO和ECHOE是关于回显的选项,如果串口另一端也要求回显的话,那就会造成串口两端反馈死循环,所以屏蔽它们。ISIG是让某些信号起作用,这里不需要这些信号,所以不也不考虑选择ISIG。 think58好,好think58 [资料来源:THINK58.com]
而c_oflag是对输出数据进行处理的成员项,相应的就有标准输出模式和非标准输出模式。非标准输出模式更加灵活,适合做为本次设计的输出模式,而对这种模式的选择是非常简单的,只要屏蔽c_cflag的OPOST项即可,屏蔽OPOST项,c_oflag其余的项就都被忽略。如果选择了OPOST项,那么就是标准输出模式。其余的项大多出于历史原因要追溯到打印机和终端还不能和串行数据流保持一致的时候,现在已经没有使用价值了。
4.3.3 c_cc控制字符设置
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 200;
c_cc的结构是一个字符数组,它包含了控制字符和超时参数。其中常用到的只有其中的VMIN和VTIME两个元素。VMIN和VTIME只有在非标准输入时才有效。
在非标准输入模式中,输入的数据并不组合成行,也不会进行erase、kill、delete等输入处理。这两个参数控制这种模式的输入行为:c_cc[VTIME]设定字符输入间隔时间计时器,而c_cc[VMIN]设置满足读取函数的最少字节数。
MIN > 0, TIME = 0: 函数在读到了MIN值的字符数后返回。
MIN = 0, TIME > 0: TIME决定了超时值,读取函数在读到一个字节的字符,或者等待读取时间超了TIME(t = TIME*0.1s)以后返回,也就是说,即使没有从串口读到数据,读取函数会在TIME时间后返回。 think58.com [版权所有:http://think58.com]
MIN > 0, TIME > 0: 读取函数会在收到了MIN字节的数据后,或者超TIME时间没有收到数据后返回。此时计时器会在每次收到字符的时候重新计时,且只在收到第一个字节后才启动。所以读取函数最少要读取一个字节才能正常返回。
MIN = 0, TIME = 0: 读取函数会立即返回。实际读取到的字符数,或者要读到的字符数,会作为返回值返回。
设计时考虑到不能让串口接收不到数据陷入无限等待的僵死状态,并且又得让串口有一定长的等待时间等待数据到来,所以选择了MIN = 0, TIME > 0的组合,即在没有数据到来时,会等待TIME时间,如果超过TIME仍没有数据到来,则读取串口的函数仍可以返回。TIME的值的大小可以根据不同情况而定,但是不能太长,超过30s程序就会报错,这里选择的是200(20s)。MIN和TIME需要配合fcntl(fd, F_SETFL,0)使用,fcntl(fd, F_SETFL,0)作用是当串口没有接收到字符时,阻塞等待一段时间。这时对MIN,TIME的设置才可以起作用。如果不想让串口阻塞,则可以设置fcntl(fd, F_SETFL,FNDELAY),这时它与MIN = 0, TIME = 0得到的结果相同。fcntl函数是在open函数后面加入的。可以用#include <fcntl.h>将其函数声明加入程序。
最后,将设置的内容存入串口tcsetattr (fd,TCSANOW,&option),串口设置就完成了。 本文来自think58
[资料来源:http://www.THINK58.com]
4.3.4 读写串口
写数据到串口是很简单的,只需要使用write()系统呼叫去传送数据:
n = write(fd, “ATZ/R”, 4);
if( n < 4 )
printf(“write to serial port failed!\n”);
从串口读取数据:
n=read(fd, rdata, 1);
rdata是预先先定义好的字符数组,要让rdata足够大,才不会发生数据溢出错误。串口本身有4K的读写缓存,不同的计算机可能缓存大小不一样。不能一次性读写超过串口缓存本身大小的字符数,不过4K已经足够使用了。
向串口写数据往往一次性就能成功,例如一次性往串口写100个字符,但若从串口读这100个字符,根据MIN和TIME设置的不同,可能需要读取两次才能将这100个字符完整读出来。所以MIN 和TIME的设置很重要。同时,串口波特率越高,一次性把串口数据读完的机率也越小。
think58.com
[来源:http://www.think58.com]
[资料来源:THINK58.com]