Keywords: Serial Programming | Linux Systems | C Language | POSIX Standards | termios Structure
Abstract: This article provides an in-depth exploration of serial port communication programming in C on Linux systems. Covering device opening, parameter configuration, data transmission, and error handling, it presents detailed code examples and theoretical analysis. Based on POSIX standards, the guide demonstrates proper serial attribute configuration, blocking mode settings, and data transfer techniques, offering robust solutions applicable across various Linux distributions.
Fundamentals of Serial Port Communication
Serial port communication is widely used in embedded systems and industrial control applications. In Linux systems, serial devices are typically represented as files in the /dev directory, such as /dev/ttyUSB0 or /dev/ttyS0. Standard file operation interfaces enable read and write control over these serial devices.
Device Opening and Initialization
Opening a serial port device requires using the open() system call with appropriate flags:
int fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening device: %s\n", strerror(errno));
return -1;
}
The O_NOCTTY flag prevents the device from becoming a controlling terminal, while O_SYNC ensures synchronous write operations.
Serial Port Parameter Configuration
Proper serial port configuration is crucial for communication stability. Always use tcgetattr() to obtain current attributes and modify them, rather than creating a new termios structure from scratch:
int set_interface_attribs(int fd, int speed, int parity) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_iflag &= ~IGNBRK;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
Blocking Mode Configuration
Adjusting the VMIN and VTIME parameters controls the blocking behavior of read operations:
void set_blocking(int fd, int should_block) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5;
if (tcsetattr(fd, TCSANOW, &tty) != 0)
printf("Error setting term attributes: %s\n", strerror(errno));
}
Data Transmission Operations
After configuration, standard write() and read() functions handle data transmission:
// Send data
char *msg = "Hello!\n";
int n = write(fd, msg, strlen(msg));
// Receive data
char buf[100];
int rdlen = read(fd, buf, sizeof(buf));
if (rdlen > 0) {
buf[rdlen] = '\0';
printf("Received: %s", buf);
}
Common Parameter Settings
Baud rates use predefined constants: B115200, B9600, B38400, etc. Parity configurations include: 0 (no parity), PARENB (even parity), PARENB|PARODD (odd parity).
Error Handling and Debugging
Comprehensive error handling is essential in serial port programming. Always check return values of system calls and use errno with strerror() for detailed error information. For cross-platform compatibility, be aware of feature availability such as CMSPAR.