高級串口編程, 使用 ioctl 和 select

分類: LINUX

Chapter 4, Advanced Serial Programming

第四章,高級串口編程

This chapter covers advanced serial programming techniques using the ioctl(2) and select(2) system calls.

Serial Port IOCTLs

In Chapter 2, Configuring the Serial Port we used the tcgetattr and tcsetattr functions to configure the serial port. Under UNIX these functions use the ioctl(2) system call to do their magic. The ioctl system call takes three arguments:

int ioctl(int fd, int request, ...);

The fd argument specifies the serial port file descriptor. The request argument is a constant defined in the

<termios.h> header file and is typically one of the constants listed in Table 10.

本章主要談論使用ioctl(2)和select(2)系統調用的高級串口編程技術。

串行端口 IOCTLs

在第二章,配置串口中我們使用了 tcgetattr 和 tcsetattr 函數來做串口的設置。在 UNIX 下,這些函數都是使用 ioctl(2) 系統調用來完成他們的任務的。ioctl 系統調用有如下三個參數:

int ioctl(int fd, int request, ...);

參數 fd 指定了串行端口的文件描述符。

參數 request 是一個定義在 <termios.h> 頭文件裏的常量,它的一些常用值都在 表10 裏面定義了。

Table 10 - IOCTL Requests for Serial Ports

表10 - IOCTL 的串口調用

Request

Description

POSIX Function

TCGETS

Gets the current serial port settings.

讀取當前的串口屬性

tcgetattr

TCSETS

Sets the serial port settings immediately

設置串口屬性並立即生效

tcsetattr(fd, TCSANOW, &options)

TCSETSF

Sets the serial port settings after flushing the input and output buffers.

設置串口屬性,等到輸入輸出緩衝區都清空了再生效

tcsetattr(fd, TCSAFLUSH, &options)

TCSETSW

Sets the serial port settings after allowing the input and output buffers to drain/empty.

設置串口屬性,等到允許清空輸入輸出緩衝區了或數據傳完後設置生效

tcsetattr(fd, TCSADRAIN, &options)

TCSBRK

Sends a break for the given time.

在指定時間後發送break

tcsendbreak, tcdrain

TCXONC

Controls software flow control.

控制軟件流控

tcflow

TCFLSH

Flushes the input and/or output queue.

將輸入輸出隊列全部發出

tcflush

TIOCMGET

Returns the state of the "MODEM" bits.

返回 “MODEM” 位的狀態

None

TIOCMSET

Sets the state of the "MODEM" bits.

設置“MODEM”位的狀態

None

FIONREAD

Returns the number of bytes in the input buffer.

返回輸入緩衝區內的字節數

None

Getting the Control Signals

The TIOCMGET ioctl gets the current "MODEM" status bits, which consist of all of the RS-232 signal lines except RXD and TXD, listed in Table 11.

To get the status bits, call ioctl with a pointer to an integer to hold the bits, as shown in Listing 5.

獲得控制信號

TIOCMGET - ioctl 獲得當前“MODEM”的狀態位,其中包括了除 RXD 和 TXD 之外,所有的RS-232 信號線,見列表 11。

爲了獲得狀態位,使用一個包含比特位的整數的指針來調用 ioctl,見清單5。

Listing 5 - Getting the MODEM status bits.

清單 5 - 讀取 DODEM 的狀態位.

#include <unistd.h>

#include <termios.h>

int fd;

int status;

ioctl(fd, TIOCMGET, &status);  

Table 11 - Control Signal Constants

表 11 - 控制信號常量

Constant

Description

TIOCM_LE

DSR (data set ready/line enable)

TIOCM_DTR

DTR (data terminal ready)

TIOCM_RTS

RTS (request to send)

TIOCM_ST

Secondary TXD (transmit)

TIOCM_SR

Secondary RXD (receive)

TIOCM_CTS

CTS (clear to send)

TIOCM_CAR

DCD (data carrier detect)

TIOCM_CD

Synonym for TIOCM_CAR

TIOCM_RNG

RNG (ring)

TIOCM_RI

Synonym for TIOCM_RNG

TIOCM_DSR

DSR (data set ready)

Setting the Control Signals

The TIOCMSET ioctl sets the "MODEM" status bits defined above. To drop the DTR signal you can use the code in Listing 6.

設置控制信號

TIOCMSET - ioctl 設置“MODEM”上述定義的狀態位。可以使用 清單6 的代碼來給DTR信號置低。

Listing 6 - Dropping DTR with the TIOCMSET ioctl.

清單 6 - 使用 TIOCMSET ioctl 置低 DTR 信號

#include <unistd.h>

#include <termios.h>

int fd; int status;

ioctl(fd, TIOCMGET, &status);

status &= ~TIOCM_DTR;

ioctl(fd, TIOCMSET, &status);  

The bits that can be set depend on the operating system, driver, and modes in use. Consult your operating system documentation for more information.

能進行設置的比特位有操作系統,驅動以及使用的模式決定。查詢你的操作系統的檔案可以獲取更多的信息。

Getting the Number of Bytes Available

The FIONREAD ioctl gets the number of bytes in the serial port input buffer. As with TIOCMGET you pass in a pointer to an integer to hold the number of bytes, as shown in Listing 7.

獲取可供讀取的字節數

FIONREAD - ioctl 讀取串行端口輸入緩衝區中的字節數。與 TIOCMGET 一起傳遞一個包含字節數的整數的指針,如 清單7 所示。

Listing 7 - Getting the number of bytes in the input buffer.

清單7 - 讀取串行端口輸入緩衝區中的字節數

#include <unistd.h>

#include <termios.h>

int fd;

int bytes;

ioctl(fd, FIONREAD, &bytes);  

This can be useful when polling a serial port for data, as your program can determine the number of bytes in the input buffer before attempting a read.

在查詢串口是否有數據到來的時候這一段是很有用的,可以讓程序在準備讀取之前用來確定輸入緩衝區裏面的可讀字節數。

Selecting Input from a Serial Port

While simple applications can poll or wait on data coming from the serial port, most applications are not simple and need to handle input from multiple sources.

UNIX provides this capability through the select(2) system call. This system call allows your program to check for input, output, or error conditions on one or more file descriptors. The file descriptors can point to serial ports, regular files, other devices, pipes, or sockets. You can poll to check for pending input, wait for input indefinitely, or timeout after a specific amount of time, making the select system call extremely flexible.

Most GUI Toolkits provide an interface to select; we will discuss the X Intrinsics ("Xt") library later in this chapter.

從串行端口選擇輸入

雖然簡單的程序可以通過poll串口或者等待串口數據到來讀取串口,大多數的程序卻並不這麼簡單,有時候需要處理來自多個源的輸入。

UNIX 通過 select(2) 系統調用提供了這種能力。這個系統調用允許你的程序從一個或多個文件描述符獲取輸入/輸出或者錯誤信息。文件描述符可以指向串口,普通文件,其他設備,管道,或者socket 。你可以查詢待處理的輸入,等待不確定的輸入,或者在一指定的超時到達後超時退出,這些都使得 select 系統調用非常的靈活。

大多數的 GUI 工具提供一個藉口指向 select,我們會在本章後面的部分討論 X Intrinsics 庫(“Xt”)。

The SELECT System Call

The select system call accepts 5 arguments:

int select(int max_fd, fd_set *input, fd_set *output, fd_set *error, struct timeval *timeout);

The max_fd argument specifies the highest numbered file descriptor in the input, output, and error sets. The input, output, and error arguments specify sets of file descriptors for pending input, output, or error conditions; specify NULL to disable monitoring for the corresponding condition. These sets are initialized using three macros:

FD_ZERO(fd_set);

FD_SET(fd, fd_set);

FD_CLR(fd, fd_set);

The FD_ZERO macro clears the set entirely. The FD_SET and FD_CLR macros add and remove a file descriptor from the set, respectively.

The timeout argument specifies a timeout value which consists of seconds (timeout.tv_sec) and microseconds (timeout.tv_usec). To poll one or more file descriptors, set the seconds and microseconds to zero. To wait indefinitely specify NULL for the timeout pointer.

The select system call returns the number of file descriptors that have a pending condition, or -1 if there was an error.

SELECT 系統調用

Select 系統調用可以接受 5 個參數:

int select(int max_fd, fd_set *input, fd_set *output, fd_set *error, struct timeval *timeout);

參數 max_fd 定義了所有用到的文件描述符(input, output, error集合)中的最大值。

參數 input, output, error 定義了待處理的輸入,輸出,錯誤情況;置 NULL 則代表不去監查相應的條件。這幾個集合用三個宏來進行初始化

FD_ZERO(fd_set);

FD_SET(fd, fd_set);

FD_CLR(fd, fd_set);

宏 FD_ZERO 清空整個集合; FD_SET 和 FD_CLR 分別從集合中添加、刪除文件描述符。

參數 timeout 定義了一個由秒(timeout.tv_sec)和毫秒(timeout.tv_usec)組成的一個超時值。要查詢一個或多個文件描述符,把秒和毫秒置爲 0 。要無限等待的話就把 timeout 指針置 NULL 。

select 系統調用返回那個有待處理條件的文件描述符的值,或者,如果有錯誤發生的話就返回 -1。

Using the SELECT System Call

Suppose we are reading data from a serial port and a socket. We want to check for input from either file descriptor, but want to notify the user if no data is seen within 10 seconds. To do this we'll need to use the select system call, as shown in Listing 8.

使用 select 系統調用

假設我們正在從一個串口和一個socket 讀取數據。我們想確認來自各文件描述符的輸入,又希望如果10秒鐘內都沒有數據的話通知用戶。要完成這些工作,我們可以像 清單8 這樣使用select系統調用:

Listing 8 - Using SELECT to process input from more than one source.

清單8 - 使用select 處理來自多個源的輸入

#include <unistd.h>

#include <sys/types.h>

#include <sys/time.h>

#include <sys/select.h>

int n;

int socket;

int fd;

int max_fd;

fd_set input;

struct timeval timeout; /* Initialize the input set 初始化輸入集合*/

FD_ZERO(input);

FD_SET(fd, input);

FD_SET(socket, input);

max_fd = (socket > fd ? socket : fd) + 1; /* Initialize the timeout structure 初始化 timeout 結構*/

timeout.tv_sec = 10;

timeout.tv_usec = 0;

/* Do the select */

n = select(max_fd, &input, NULL, NULL, &timeout);

/* See if there was an error 看看是否有錯誤發生*/

if (n < 0)

  perror("select failed");

else if (n == 0)

  puts("TIMEOUT");

else { /* We have input 有輸入進來了*/

  if (FD_ISSET(fd, input))

    process_fd();

  if (FD_ISSET(socket, input))

    process_socket(); 

}  

You'll notice that we first check the return value of the select system call. Values of 0 and -1 yield the appropriate warning and error messages. Values greater than 0 mean that we have data pending on one or more file descriptors.

To determine which file descriptor(s) have pending input, we use the FD_ISSET macro to test the input set for each file descriptor. If the file descriptor flag is set then the condition exists (input pending in this case) and we need to do something.

你會發現,我們首先檢查select系統調用的返回值。如果是 0 和 -1 則表示相應的警告和錯誤信息。大於 0 的值代表我們在一個或多個文件描述符上有待處理的數據。

要知道是哪個文件描述符有待處理的輸入,我們要使用 FDISSET 宏來測試每個文件描述符的輸入集合。如果文件描述符標誌被設置了,則符合條件(這時候就有待處理的輸入了)我們需要進行一些操作。

Using SELECT with the X Intrinsics Library

The X Intrinsics library provides an interface to the select system call via the XtAppAddInput(3x) and XtAppRemoveInput(3x) functions:

int XtAppAddInput(XtAppContext context, int fd, int mask, XtInputProc proc, XtPointer data);

void XtAppRemoveInput(XtAppContext context, int input);

The select system call is used internally to implement timeouts, work procedures, and check for input from the X server. These functions can be used with any Xt-based toolkit including Xaw, Lesstif, and Motif.

The proc argument to XtAppAddInput specifies the function to call when the selected condition (e.g. input available) exists on the file descriptor. In the previous example you could specify the process_fd or process_socket functions.

Because Xt limits your access to the select system call, you'll need to implement timeouts through another mechanism, probably via XtAppAddTimeout(3x).

在X Intrinsics library 中使用select

X Intrinsics library 提供了一個接口,通過 XtAppAddInput(3x) 和 XtAppRemoveInput(3x) 函數來使用 select 系統調用。

int XtAppAddInput(XtAppContext context, int fd, int mask, XtInputProc proc, XtPointer data);

void XtAppRemoveInput(XtAppContext context, int input);

select 系統調用是用來實現內部的超時,工作過程,並且檢查來自 X Server 的輸入。這些函數可以在任何 基於Xt 的工具上使用,包括Xaw, Lesstif, 和 Motif。

XtAppAddInput 的參數 proc 定義了當某一個文件描述符上的select條件滿足時函數的調用(比如有輸入進來)。在之前的例子裏,你可以定義process_fd 或 processs_socket 函數。

由於 Xt 限制了你對 select 系統調用的訪問,你需要通過其他機制實現超時,可以通過 XtAppAddTimeout(3x)。

Carol: 已獲原作者簡體中文翻譯授權。

爲避免重複勞動,願意參與翻譯的朋友請直接與我聯繫 puccacarol AT hotmail DOT com

原文名稱:Serial Programming Guide for POSIX Operating Systems

<http://www.easysw.com/~mike/serial/>

此翻譯爲初稿,語言較粗糙,不斷完善中。。。 歡迎大家提出寶貴意見。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章