串口设备文件
在Linux系统中,串口设备通常以文件形式存在于/dev/
目录下:
/dev/ttyS0
- COM1 (传统串口)
/dev/ttyUSB0
- USB转串口设备
/dev/ttyAMA0
- Raspberry Pi等平台的串口
tcgetattr 和 tcsetattr 函数详解
这两个函数是Linux系统编程中用于配置终端(包括串口)属性的核心函数,它们操作termios
结构体来控制终端的各种行为。
函数原型
1 2 3 4 5
| #include <termios.h> #include <unistd.h>
int tcgetattr(int fd, struct termios *termios_p); int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
|
tcgetattr 函数
功能
获取与文件描述符fd
关联的终端的当前参数,并将其存储在termios_p
指向的结构体中。
参数
fd
: 打开终端设备的文件描述符
termios_p
: 指向struct termios
的指针,用于存储获取的参数
返回值
示例
1 2 3 4 5
| struct termios tty; if (tcgetattr(fd, &tty) != 0) { perror("tcgetattr failed"); return -1; }
|
tcsetattr 函数
功能
使用termios_p
指向的结构体中的参数来设置与文件描述符fd
关联的终端的参数。
参数
fd
: 打开终端设备的文件描述符
optional_actions
: 指定何时应用更改
termios_p
: 指向包含新参数的struct termios
结构体
optional_actions 参数
TCSANOW
: 立即应用更改
TCSADRAIN
: 等待所有输出完成后应用更改
TCSAFLUSH
: 等待所有输出完成,并丢弃所有未读输入后应用更改
返回值
示例
1 2 3 4
| if (tcsetattr(fd, TCSANOW, &tty) != 0) { perror("tcsetattr failed"); return -1; }
|
struct termios 结构体详解
termios
结构体包含多个字段来控制终端的行为:
1 2 3 4 5 6 7
| struct termios { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_cc[NCCS]; };
|
[!TIP]
重点关注 c_cflag 即可。
c_cflag - 控制模式标志
标志 |
说明 |
常用值 |
CSIZE |
数据位掩码 |
CS5 , CS6 , CS7 , CS8 |
CSTOPB |
停止位 |
0=1位, 1=2位 |
PARENB |
奇偶校验使能 |
0=禁用, 1=启用 |
PARODD |
奇偶校验类型 |
0=偶校验, 1=奇校验 |
CREAD |
接收使能 |
必须设置才能接收数据 |
CLOCAL |
本地模式 |
忽略调制解调器控制线 |
CRTSCTS |
硬件流控 |
启用RTS/CTS流控 |
CBAUD |
波特率掩码 |
已废弃,用cfsetispeed/cfsetospeed |
c_iflag - 输入模式标志
标志 |
说明 |
IGNBRK |
忽略BREAK条件 |
BRKINT |
BREAK产生中断 |
IGNPAR |
忽略奇偶错误 |
PARMRK |
标记奇偶错误 |
INPCK |
启用奇偶检查 |
ISTRIP |
剥离第8位 |
INLCR |
将NL映射为CR |
IGNCR |
忽略CR |
ICRNL |
将CR映射为NL |
IXON |
启用输出XON/XOFF流控 |
IXOFF |
启用输入XON/XOFF流控 |
IXANY |
允许任何字符重新开始输出 |
c_oflag - 输出模式标志
标志 |
说明 |
OPOST |
启用输出处理 |
ONLCR |
将NL映射为CR-NL |
OCRNL |
将CR映射为NL |
ONOCR |
在第0列不输出CR |
ONLRET |
NL执行CR功能 |
OFILL |
使用填充字符 |
OFDEL |
填充字符是DEL |
NLDLY |
NL延迟选择 |
CRDLY |
CR延迟选择 |
TABDLY |
TAB延迟选择 |
BSDLY |
BS延迟选择 |
VTDLY |
VT延迟选择 |
FFDLY |
FF延迟选择 |
c_lflag - 本地模式标志
标志 |
说明 |
ISIG |
使能信号 |
ICANON |
规范模式 |
ECHO |
回显输入字符 |
ECHOE |
规范模式下ERASE视觉反馈 |
ECHOK |
规范模式下KILL视觉反馈 |
ECHONL |
回显NL |
NOFLSH |
禁止信号后的清空 |
TOSTOP |
后台写产生信号 |
IEXTEN |
启用实现定义功能 |
c_cc[] - 特殊控制字符数组
索引 |
符号常量 |
说明 |
默认值 |
0 |
VINTR |
中断字符 |
Ctrl-C (0x03) |
1 |
VQUIT |
退出字符 |
Ctrl-\ (0x1C) |
2 |
VERASE |
擦除字符 |
Backspace (0x7F) |
3 |
VKILL |
终止行字符 |
Ctrl-U (0x15) |
4 |
VEOF |
文件结束字符 |
Ctrl-D (0x04) |
5 |
VTIME |
非规范模式读取超时 |
- |
6 |
VMIN |
非规范模式最小字符数 |
- |
7 |
VSWTC |
开关字符 |
- |
8 |
VSTART |
开始字符 |
Ctrl-Q (0x11) |
9 |
VSTOP |
停止字符 |
Ctrl-S (0x13) |
完整配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| int configure_serial(int fd, int baudrate) { struct termios tty; if (tcgetattr(fd, &tty) != 0) { perror("tcgetattr"); return -1; } cfsetispeed(&tty, baudrate); cfsetospeed(&tty, baudrate); tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; tty.c_cflag |= CREAD; tty.c_cflag |= CLOCAL; tty.c_iflag &= ~(IXON | IXOFF | IXANY); tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | INPCK); tty.c_oflag &= ~OPOST; tty.c_oflag &= ~ONLCR; tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG); tty.c_cc[VTIME] = 1; tty.c_cc[VMIN] = 0; if (tcsetattr(fd, TCSANOW, &tty) != 0) { perror("tcsetattr"); return -1; } return 0; }
|
C 程序示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <errno.h>
#define SERIAL_PORT "/dev/ttyS1" #define BAUDRATE B115200 #define BUFFER_SIZE 128
int open_serial(const char *device) { int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC); if (fd < 0) { perror("Error opening serial port"); return -1; } return fd; }
int configure_serial(int fd, speed_t baudrate) { struct termios tty; if (tcgetattr(fd, &tty) != 0) { perror("Error getting tty attributes"); return -1; }
cfsetospeed(&tty, baudrate); cfsetispeed(&tty, baudrate);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; tty.c_cflag |= (CLOCAL | CREAD); tty.c_cflag &= ~(PARENB | PARODD); tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CRTSCTS;
tty.c_iflag &= ~(IXON | IXOFF | IXANY); tty.c_iflag &= ~(ICRNL | INLCR);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tty.c_oflag &= ~OPOST;
tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 10;
if (tcsetattr(fd, TCSANOW, &tty) != 0) { perror("Error setting tty attributes"); return -1; }
return 0; }
int send_data(int fd, const char *data) { int len = strlen(data); int bytes_written = write(fd, data, len);
if (bytes_written < 0) { perror("Error writing to serial port"); return -1; } return bytes_written; }
int receive_data(int fd, char *buffer, int buffer_size) { int bytes_read = read(fd, buffer, buffer_size);
if (bytes_read < 0) { perror("Error reading from serial port"); return -1; } return bytes_read; }
void close_serial(int fd) { if (close(fd) != 0) { perror("Error closing serial port"); } }
int main() { int serial_fd; char recv_buffer[BUFFER_SIZE];
serial_fd = open_serial(SERIAL_PORT); if (serial_fd < 0) return 1;
if (configure_serial(serial_fd, BAUDRATE) != 0) { close_serial(serial_fd); return 1; }
const char *message = "Hello World!\n"; if (send_data(serial_fd, message) < 0) { close_serial(serial_fd); return 1; } printf("Sent: %s", message);
int bytes_received = receive_data(serial_fd, recv_buffer, BUFFER_SIZE - 1); if (bytes_received > 0) { recv_buffer[bytes_received] = '\0'; printf("Received: %s\n", recv_buffer); } else if (bytes_received == 0) { printf("No data received.\n"); }
close_serial(serial_fd);
return 0; }
|