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

    技术2022-05-18  13

    5th EditionMichael R. SweetCopyright 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挂起。

     


    最新回复(0)