在Linux系统中,termios
结构体中的cflag
、iflag
、oflag
、和lflag
是控制终端接口行为的重要字段。以下是这些标志字段的常见属性和默认值的解释。
c_cflag
(控制标志,cflag
)
c_cflag
字段控制终端硬件的特性设置,通常用于控制数据传输的格式、波特率、硬件流控等。
常见属性:
-
CSIZE:控制数据位的掩码,取值范围:
CS5
:5位数据位CS6
:6位数据位CS7
:7位数据位CS8
:8位数据位(通常默认值)
-
CSTOPB:停止位选择:
- 设置时使用两个停止位
- 未设置时使用一个停止位(默认未设置)
-
PARENB:启用奇偶校验:
- 设置时启用奇偶校验
- 未设置时禁用奇偶校验(默认未设置)
-
PARODD:奇偶校验类型选择:
- 设置时使用奇校验
- 未设置时使用偶校验(
PARENB
设置时生效)
-
CLOCAL:忽略调制解调器状态线(通常设置):
- 设置时忽略调制解调器状态线(默认设置)
- 未设置时考虑调制解调器状态线
-
CREAD:启用接收器:
- 设置时启用接收器(默认设置)
- 未设置时禁用接收器
-
HUPCL:关闭最后一个文件描述符时挂断:
- 设置时挂断终端(默认设置)
- 未设置时不挂断
c_iflag
(输入标志,iflag
)
c_iflag
字段控制输入数据的处理方式,如输入的流控、换行符转换、字节大小等。
常见属性:
-
IGNPAR:忽略奇偶校验错误的字节:
- 设置时忽略有奇偶校验错误的字节
- 未设置时处理奇偶校验错误字节
-
PARMRK:标记奇偶校验错误的字节:
- 设置时标记奇偶校验错误的字节
- 未设置时不标记(默认未设置)
-
INPCK:启用输入奇偶校验:
- 设置时启用奇偶校验
- 未设置时禁用(通常默认未设置)
-
ISTRIP:去掉第八个比特(把输入的每个字符的最高位设为0):
- 设置时去掉第八个比特
- 未设置时保留所有位(通常默认未设置)
-
IXON:启用输出的XON/XOFF流控(软件流控):
- 设置时启用流控
- 未设置时禁用流控(默认未设置)
-
IXOFF:启用输入的XON/XOFF流控(软件流控):
- 设置时启用流控
- 未设置时禁用流控(默认未设置)
-
ICRNL:将输入的CR(回车)转换为NL(换行):
- 设置时转换CR为NL
- 未设置时不转换(通常默认设置)
c_oflag
(输出标志,oflag
)
c_oflag
字段控制输出数据的处理方式,如输出的换行符转换等。
常见属性:
-
OPOST:执行输出处理(如换行符转换):
- 设置时处理输出(默认设置)
- 未设置时不处理输出
-
ONLCR:将输出的NL(换行)转换为CR-NL(回车-换行):
- 设置时转换NL为CR-NL(通常默认设置)
- 未设置时不转换
-
OCRNL:将输出的CR(回车)转换为NL(换行):
- 设置时转换CR为NL
- 未设置时不转换
-
ONOCR:在列号为0时,不输出CR(通常用于防止多余的回车):
- 设置时不输出CR
- 未设置时正常输出CR
-
ONLRET:换行同时执行回车功能:
- 设置时换行执行回车
- 未设置时不执行
c_lflag
(本地标志,lflag
)
虽然你没有特别提到lflag
,但它在串口编程中也非常重要。它控制终端驱动程序的本地模式,如是否启用回显、是否使用规范模式等。
常见属性:
-
ICANON:启用规范模式:
- 设置时输入是行缓冲的,输入以换行或EOF结束(默认设置)
- 未设置时为原始模式,输入立即处理
-
ECHO:启用输入回显:
- 设置时输入字符回显(默认设置)
- 未设置时不回显输入字符
-
ECHOE:回显删除字符(退格)时擦除字符:
- 设置时回显删除字符时擦除字符
- 未设置时不擦除(
ECHO
设置时生效)
-
ISIG:启用信号触发(如
Ctrl-C
发送SIGINT
):- 设置时启用信号(默认设置)
- 未设置时禁用信号
默认值
在很多系统中,串口的默认设置可能与终端类型和操作系统有关。典型默认值如下:
c_cflag
:CS8 | CREAD | HUPCL | CLOCAL
c_iflag
:通常IGNPAR | ICRNL
,不启用流控和奇偶校验c_oflag
:通常为OPOST | ONLCR
c_lflag
:ICANON | ECHO | ECHOE | ISIG
具体默认值可能因环境不同而有所变化,可以通过tcgetattr()
获取当前设置。
模板:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
int main() {
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open serial port");
return -1;
}
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8位数据位
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1位停止位
options.c_cflag &= ~CRTSCTS; // 无硬件流控
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式
options.c_oflag &= ~OPOST; // 原始模式
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10; // 1秒超时
tcsetattr(fd, TCSANOW, &options);
const char *msg = "Hello, Serial Port!";
int n = write(fd, msg, strlen(msg));
if (n < 0) {
perror("write failed");
}
char buffer[256];
n = read(fd, buffer, sizeof(buffer));
if (n < 0) {
perror("read failed");
} else if (n == 0) {
printf("No data available\n");
} else {
buffer[n] = '\0';
printf("Read: %s\n", buffer);
}
close(fd);
return 0;
}