POSIX操作系统的串口编程指南(1)

5th Edition
Michael R. Sweet
Copyright 1994-1999, All Rights Reserved

 

原文:http://digilander.libero.it/robang/rubrica/serial.htm

 

第一次翻译文档,不当之处请指正。

 

 

引言

POSIX操作系统的串口编程指南》将会教你如何成功、高效和可移植的在UNIX环境或PC上对串口进行编程。每一章提供的例程都使用POSIX(Portable Standard for UNIX)终端控制函数,只需极少的修改就可运行在IRIX HP-UX SunOS Solaris Digital UNIX Linux等大多数类UNIX操作系统。操作系统间最大的不同是串口文件名和文件锁。

本指南包括以下章节和附录:

第一章:串口通信基础

第二章:配置串口

第三章:与MODEM通信

第四章:高级串口编程

附录ARS-232引脚

附录BASCII编码

 

 

第一章:串口通信基础

本章介绍串口通信,RS-232和其他多数计算机使用的标准,还有如何用C语言访问串口。

 

什么是串口通信

计算机传送信息(数据)时,每次传送一个或多个位。串口每次只传送一个位。串口通信包括多数网络设备、键盘、鼠标、MODEM和终端。

进行串口通信时,每次只发送一位,不是0就是1。你有时会听到一些专业术语,mark表示on状态,space表示off状态。

串行数据的速度通常表示为位每秒(bps)或波特率(baud),这只是表示一秒内能发送多少个数字01。在计算机诞生的初期,300baud已经被认为很快了,但是今天的计算机可以将RS-232的速度提高至430800baud!当波特率超过1000时,你通常会看到速率显示为kilo baudkbps。当波特率超过1000000时,会显示为mega baudMbps

当提到串行设备或串行接口时,他们通常称为数据通信设备(DCE)或数据终端设备(DTE),两者之间的区别很简单——每一对信号,例如发送和接收,它们是交叉的。如果要把两个DCE设备或两个DTE设备连接到一起,需要用串行交叉线或转接器将两个信号交换。

 

什么是RS-232

RS-232 Electronic Industries Association ("EIA")定义的一个标准串行通信电气接口。RS-232事实上有三种(ABC),它们分别使用不同的电平定义onoff。使用最多的是RS-232C,它定义-3V-12V的电压作为mark位(on),定义+3V+12V的电压作为space位(off)。RS-232C标准最远可以传输8米。你通常可以使信号传的更远,只要波特率设置的够低。

除了数据输入和输出线,还有其他信号用来提供计时、状态和握手:

Table 1 - RS-232 Pin Assignments

Pin

Description

Pin

Description

1

Earth Ground

14

Secondary TXD

2

TXD- Transmitted Data

15

Transmit Clock

3

RXD- Received Data

16

Secondary RXD

4

RTS- Request To Send

17

Receiver Clock

5

CTS- Clear To Send

18

Unassigned

6

DSR- Data Set Ready

19

Secondary RTS

7

GND- Logic Ground

20

DTR- Data Terminal Ready

8

DCD- Data Carrier Detect

21

Signal Quality Detect

9

Reserved

22

Ring Detect

10

Reserved

23

Data Rate Select

11

Unassigned

24

Transmit Clock

12

Secondary DCD

25

Unassigned

13

Secondary CTS

 

 

你还可以看到另外两个串行标准——RS-422RS-573RS-422使用更低的电平和差分信号,从而将传输距离提高到300mRS-573定义了通常PC上使用的9针串口和电平。

 

信号定义

RS-232为串口通信定义了18个不同的信号。在UNIX环境中通常只使用其中的6个。

GND - Logic Group逻辑地)

严格的说,逻辑地不是一个信号,但是没有它其他信号就不能用了。基本上,逻辑地是一个参考电平,通过它来判断电压的正负。

TXD - Transmitted Data(发送数据)

TXD信号将数据从你的工作站中发送到另一端的计算机或设备(例如MODEM)。mark电平被当做1space电平被当做0.

RXD - Received Data接收数据)

RXD信号将数据从其他计算机或设备上发送到你的工作站中。信号解析与TXD相同。

DCD - Data Carrier Detect(数据监听)

DCD信号是从位于串口线另一端的计算机或设备接收到的。该信号上的space电平意味着计算机或设备已经处于连接状态。DCD不一定有效,有些设备是没有DCD信号的。

DTR - Data Terminal Ready

DTR信号是由你的工作站产生的,它用来告诉另一端的计算机或设备,你已经准备好(space电平)或没有准备好(mark电平)。一般情况下,在你打开工作站的串口时,DTR就被自动使能了。

CTS - Clear To Send

CTS信号也是从另一端的计算机接收到的。该信号上的space电平意味着你可以发送更多的数据。CTS通常被用来管理从你的工作站到另一端的数据流。

RTS - Request To Send

如果RTS信号被你的工作站设置为space电平,表示有很多数据已经准备好发送了。

就像CTSRTS一样,它也用来管理工作站与另一端计算机之间的数据流。多数工作站会一直把它设为space电平。

 

异步通信

为了让计算机理解传给它的串行数据,要用些方法来确定字符的开始和结束位置。这专门有异步串行数据来处理。

在异步模式下,串行数据线一直保持mark(1)状态,直到接收到一个字符。每个字符都有一个开始(start)位,在它之后紧跟着字符的每一位,然后是一个可选的parity(奇偶)位,还有一个或多个停止(stop)位。开始(start)位总是一个space0),它告诉计算机新的串行数据就要到了。数据可以在任何时候发生和接收。因此叫做异步。

Figure 1 - Asynchronous Data Transmission

可选的parity位是简单的数据位的和,它表示数据中的mark1)位的个数是奇数还是偶数。奇校验(even parity)时,如果字符中包含奇数个1parity位就是0。偶校验(odd parity)时,如果数据中有偶数个1parity为就是0。你可能还听说过space paritymark paritynoparitySpace parity就是parity位一直是0mark parity就是parity位一直是1no parity就是没有parity位。

剩余的位叫做停止位(stop bits),可以是1位,1.5位或2位,位于字符之间,停止位的值总是1。过去,停止位用来让计算机有时间处理已经接收到的字符,但是现在,它只是用来同步接收计算机和接收的字符。

异步数据格式通常设为“8N1”,“7E1”,诸如此类。它们的意思分别是“8位数据位,没有奇偶校验,1位停止位”和“7位数据位,奇校验,1位停止位”。

 

什么是全双工和半双工

全双工(Full duplex)的意思是,计算机可以同时接收和发送数据,这意味着有两个分开的数据通道(一个输入,一个输出)。

半双工的意思是,计算机不能在同一时间发生或接收数据。通常这意味着只有一个信号数据通道用来通信。这并不是有些RS-232信号没有使用,而是,通信连接使用了与RS-232不同的不支持全双工的标准。

 

流控制

经常有必要对两个串口间传送的数据流进行控制。这是由串行通信连接中的某个串口或存储介质的限制造成的。在异步通信中,通常有两种方法。

第一种方法叫做“软件”流控制,使用特殊的字符来开始(XONDC1,021 octal)或停止(XOFFDC3,023 octal)数据流。这些字符在美国信息互换标准代码(ASCII)中定义。当传输文本信息时,这些代码是有用的,它们不能用来传输没有经过特殊编程的其他类型的信息。

 

什么是Break

正常情况下,发生或接受信号会一直保持mark电平,直到一个新的字符被传送。如果一个信号降为space电平,并保持一段时间,通常是1/41/2秒,这时就存在break的情况了。

Break有事被用来重启通信连接或改变通信设备(如MODEM)的操作模式,第三章会深入讨论这些应用。

 

同步通信

与异步数据不同,同步数据是一个恒定的比特流。为了读取链接中的数据,计算机必须提供或接收一个公共的位时钟,这样发送方和接收放就同步了。

即使是同步通信,计算机也必须以某种方式标记数据的开始。常见的做法是使用数据包协议,例如 Serial Data Link Control ("SDLC")High-Speed Data Link Control ("HDLC")

每个协议都定义了明确的比特序列来表示一个数据包的开始和结束。协议还定义了一个用来表示没有数据的比特序列。这些比特序列可以使计算机确定数据包的开端。

由于同步协议不需要在每个字符都使用同步位,它在性能上比异步通信提高了至少25%,更适合远程网络,可以配置多个串行接口。

尽管同步通信有速度优势,多数RS-232硬件还是不支持它,以免增加额外的软件和硬件。

 

 

使用串口

像所有的文件一样,UNIX提供通过设备文件访问串口。要使用一个串口你只要打开相应的设备文件。

 

串口文件

每个UNIX系统下的串口都有一个或多个设备文件(在/dev文件夹下)与它关联:

Table 2 - Serial Port Device Files

System

Port 1

Port 2

IRIX®

/dev/ttyf1

/dev/ttyf2

HP-UX

/dev/tty1p0

/dev/tty2p0

Solaris®/SunOS®

/dev/ttya

/dev/ttyb

Linux®

/dev/ttyS0

/dev/ttyS1

Digital UNIX®

/dev/tty01

/dev/tty02

 

打开一个串口

串口也是一个文件,可以用open2)函数打开。有一个问题是,UNIX系统的设备文件通常是不允许普通用户使用的。解决这个问题的办法包括:改变文件的使用权限,在root用户下运行程序,或者改变你的程序的USERID,使它可以作为设备文件的所有者运行。

现在假设设备文件可以被所有用户使用。在IRIX系统下打开串口设备的代码如下:

Listing 1 - Opening a serial port.

#include <stdio.h>   /* Standard input/output definitions */

#include <string.h>  /* String function definitions */

#include <unistd.h>  /* UNIX standard function definitions */

#include <fcntl.h>   /* File control definitions */

#include <errno.h>   /* Error number definitions */

#include <termios.h> /* POSIX terminal control definitions */

 

/*

 * 'open_port()' - Open serial port 1.

 *

 * Returns the file descriptor on success or -1 on error.

 */

 

int

open_port(void)

{

  int fd; /* File descriptor for the port */

 

 

  fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);

  if (fd == -1)

  {

   /*

    * Could not open the port.

    */

 

    perror("open_port: Unable to open /dev/ttyf1 - ");

  }

  else

    fcntl(fd, F_SETFL, 0);

 

  return (fd);

}

在其他操作系统上需要使用相应的设备文件名。

Open选项

你应该留言到了,我们打开设备文件的时候,在读写模式后面使用了两个标志:

fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);

O_NOCTTY标志告诉UNIX,这个程序不想成为“控制终端”。如果你不指定这个标志,任何输入(例如键盘中断信号)将会影响你的进程。getty1M/8程序会在启动登录进程的时候使用这个特性,正常的用户程序是不需要这个特性的。

O_NDELAY标志告诉UNIX,程序不关注DCD信号线的状态——串口的另一端是否已经打开并运行。如果不指定这个标志,你的进程将会进入睡眠状态,直到DCD信号线为space电平

 

向串口写数据

向串口写数据很容易——只需要用write(2)系统调用发生数据即可:

n = write(fd, "ATZ/r", 4);

if (n < 0)

  fputs("write() of 4 bytes failed!/n", stderr);

write函数返回发生的字节数,如果调用失败返回-1这种情况一直持续到关闭串口。

常见的错误是EIO,当MODEM或数据连接将Data Carrier Detect (DCD)信号线弄掉时会发生这个错误。

 

从串口读取数据

从串口读取数据有一点麻烦。当你在raw data模式下操作串口时,每个read2)调用都会返回串口输入缓冲区里实际接收到的字符的个数。如果没有字符是有效的,函数会阻塞(等待),直到有字符输入、定时器过期、或发生错误。可以用下面的代码使read函数立即返回:

fcntl(fd, F_SETFL, FNDELAY);

FNDELAY选项会使read函数在串口没有有效字符时返回0。要恢复正常(阻塞)属性,调用没有FNDELAY选项的fcntl函数:

fcntl(fd, F_SETFL, 0);

这个函数也可以在用O_NDELAY 选项打开串口后调用。

 

关闭串口

要关闭串口,只需要使用close系统调用:

close(fd);

关闭串口后通常会将DTR信号设为低电平,这会使多数MODEM挂起。

 

你可能感兴趣的:(编程,工作,unix,Solaris,table,终端)