Linux--串列埠通訊詳解
This chapter discusses how to configure a serial port from C using the POSIX termios interface.
Most systems support the POSIX terminal (serial) interface for changing parameters such as baud rate, character size, and so on. The first thing you need to do is include the file<termios.h>; this defines the terminal control structure as well as the POSIX control functions.
The two most important POSIX functions are tcgetattr(3) and tcsetattr(3). These get and set terminal attributes, respectively; you provide a pointer to atermios structure that contains all of the serial options available:
Member | Description |
---|---|
c_cflag | Control options |
c_lflag | Line options |
c_iflag | Input options |
c_oflag | Output options |
c_cc | Control characters |
c_ispeed | Input baud (new interface) |
c_ospeed | Output baud (new interface) |
Constant | Description |
---|---|
CBAUD | Bit mask for baud rate |
B0 | 0 baud (drop DTR) |
B50 | 50 baud |
B75 | 75 baud |
B110 | 110 baud |
B134 | 134.5 baud |
B150 | 150 baud |
B200 | 200 baud |
B300 | 300 baud |
B600 | 600 baud |
B1200 | 1200 baud |
B1800 | 1800 baud |
B2400 | 2400 baud |
B4800 | 4800 baud |
B9600 | 9600 baud |
B19200 | 19200 baud |
B38400 | 38400 baud |
B57600 | 57,600 baud |
B76800 | 76,800 baud |
B115200 | 115,200 baud |
EXTA | External rate clock |
EXTB | External rate clock |
CSIZE | Bit mask for data bits |
CS5 | 5 data bits |
CS6 | 6 data bits |
CS7 | 7 data bits |
CS8 | 8 data bits |
CSTOPB | 2 stop bits (1 otherwise) |
CREAD | Enable receiver |
PARENB | Enable parity bit |
PARODD | Use odd parity instead of even |
HUPCL | Hangup (drop DTR) on last close |
CLOCAL | Local line - do not change "owner" of port |
LOBLK | Block job control output |
CNEW_RTSCTS CRTSCTS |
Enable hardware flow control (not supported on all platforms) |
The c_cflag member contains two options that should always be enabled,CLOCAL and CREAD. These will ensure that your program does not become the 'owner' of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes.
The baud rate constants (CBAUD, B9600, etc.) are used for older interfaces that lack thec_ispeed and c_ospeed members. See the next section for information on the POSIX functions used to set the baud rate.
Never initialize the c_cflag (or any other flag) member directly; you should always use the bitwise AND, OR, and NOT operators to set or clear bits in the members. Different operating system versions (and even patches) can and do use the bits differently, so using the bitwise operators will prevent you from clobbering a bit flag that is needed in a newer serial driver.
Setting the Baud Rate
The baud rate is stored in different places depending on the operating system. Older interfaces store the baud rate in thec_cflag member using one of the baud rate constants in table 4, while newer implementations provide thec_ispeed and c_ospeed members that contain the actual baud rate value.
The cfsetospeed(3) and cfsetispeed(3) functions are provided to set the baud rate in thetermios structure regardless of the underlying operating system interface. Typically you'd use the following code to set the baud rate:
Listing 2 - Setting the baud rate.
struct termios options; /* * Get the current options for the port... */ tcgetattr(fd, &options); /* * Set the baud rates to 19200... */ cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); /* * Enable the receiver and set local mode... */ options.c_cflag |= (CLOCAL | CREAD); /* * Set the new options for the port... */ tcsetattr(fd, TCSANOW, &options);
The tcgetattr(3) function fills the termios structure you provide with the current serial port configuration. After we set the baud rates and enable local mode and serial data receipt, we select the new configuration usingtcsetattr(3). The TCSANOW constant specifies that all changes should occur immediately without waiting for output data to finish sending or input data to finish receiving. There are other constants to wait for input and output to finish or to flush the input and output buffers.
Most systems do not support different input and output speeds, so be sure to set both to the same value for maximum portability.
Constant | Description |
---|---|
TCSANOW | Make changes now without waiting for data to complete |
TCSADRAIN | Wait until everything has been transmitted |
TCSAFLUSH | Flush input and output buffers and make the change |
Setting the Character Size
Unlike the baud rate, there is no convienience function to set the character size. Instead you must do a little bitmasking to set things up. The character size is specified in bits:
options.c_cflag &= ~CSIZE; /* Mask the character size bits */ options.c_cflag |= CS8; /* Select 8 data bits */
Setting Parity Checking
Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.
- No parity (8N1):
options.c_cflag &= ~PARENB options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;
- Even parity (7E1):
options.c_cflag |= PARENB options.c_cflag &= ~PARODD options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS7;
- Odd parity (7O1):
options.c_cflag |= PARENB options.c_cflag |= PARODD options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS7;
- Space parity is setup the same as no parity (7S1):
options.c_cflag &= ~PARENB options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;
Setting Hardware Flow Control
Some versions of UNIX support hardware flow control using the CTS (Clear To Send) and RTS (Request To Send) signal lines. If theCNEW_RTSCTS or CRTSCTS constants are defined on your system then hardware flow control is probably supported. Do the following to enable hardware flow control:
options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS */
Similarly, to disable hardware flow control:
options.c_cflag &= ~CNEW_RTSCTS;
The local modes member c_lflag controls how input characters are managed by the serial driver. In general you will configure thec_lflag member for canonical or raw input.
Constant | Description |
---|---|
ISIG | Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals |
ICANON | Enable canonical input (else raw) |
XCASE | Map uppercase \lowercase (obsolete) |
ECHO | Enable echoing of input characters |
ECHOE | Echo erase character as BS-SP-BS |
ECHOK | Echo NL after kill character |
ECHONL | Echo NL |
NOFLSH | Disable flushing of input buffers after interrupt or quit characters |
IEXTEN | Enable extended functions |
ECHOCTL | Echo control characters as ^char and delete as ~? |
ECHOPRT | Echo erased character as character erased |
ECHOKE | BS-SP-BS entire line on line kill |
FLUSHO | Output being flushed |
PENDIN | Retype pending input at next read or input char |
TOSTOP | Send SIGTTOU for background output |
Choosing Canonical Input
Canonical input is line-oriented. Input characters are put into a buffer which can be edited interactively by the user until a CR (carriage return) or LF (line feed) character is received.
When selecting this mode you normally select the ICANON, ECHO, andECHOE options:
options.c_lflag |= (ICANON | ECHO | ECHOE);
Choosing Raw Input
Raw input is unprocessed. Input characters are passed through exactly as they are received, when they are received. Generally you'll deselect theICANON, ECHO, ECHOE, and ISIG options when using raw input:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
==================================================================================================================
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <termios.h>
int main(void) { int fd; struct termios opts;
fd = open("/dev/ttyS0", O_RDWR); if (fd < 0) { perror("open ttys0"); return 1; }
tcgetattr(fd, &opts);
opts.c_cflag |= CLOCAL; opts.c_cflag &= ~CRTSCTS;
opts.c_cflag &= ~CSIZE; opts.c_cflag |= CS8; opts.c_cflag &= ~CSTOPB;
opts.c_cflag &= ~PARENB;
cfsetispeed(&opts, B9600); cfsetospeed(&opts, B9600);
tcsetattr(fd, TCSANOW, &opts);
while (1) { write(fd, "hello", 5); sleep(2);
char buf[512];
fd = open("/dev/ttyS0", O_RDWR); if (fd < 0) { perror("open ttys0"); return 1; }
tcgetattr(fd, &opts);
opts.c_cflag |= CLOCAL; opts.c_cflag |= CREAD; opts.c_cflag &= ~CRTSCTS;
opts.c_cflag &= ~CSIZE; opts.c_cflag |= CS8; opts.c_cflag &= ~CSTOPB;
opts.c_cflag &= ~PARENB;
cfsetispeed(&opts, B9600); cfsetospeed(&opts, B9600);
tcsetattr(fd, TCSANOW, &opts);
while (1) { ret = read(fd, buf, 512); buf[ret] = '\0'; printf("from : %s\n", buf); }