1. 程式人生 > >Linux--串列埠通訊詳解

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:

Table 3 - Termios Structure Members
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)
The c_cflag member controls the baud rate, number of data bits, parity, stop bits, and hardware flow control. There are constants for all of the supported configurations.
Table 4 - Constants for the c_cflag Member
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.

Table 5 - Constants for tcsetattr
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.

Table 6 - Constants for the c_lflag Member
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);         }