2021年2月1日
Linux串口编程(termios结构体说明)
termios结构体定义:
struct termios
{
tcflag_t c_cflag; //控制标志
tcflag_t c_iflag; //输入标志
tcflag_t c_oflag; //输出标志
tcflag_t c_lflag; //本地标志
tcflag_t c_cc[NCCS];//控制字符
}
需要包含的头文件<termios.h>
下面介绍一下各个标志的选项
控制标志c_cflag:
波特率相关:
| 标志 | 说明 | 标志 | 说明 |
|---|---|---|---|
| CBAUD | 波特率位屏蔽 | B4800 | 4800 位/秒 |
| B0 | 0 位/秒(挂起) | B9600 | 9600 位/秒 |
| B110 | 100 位/秒 | B19200 | 19200 位/秒 |
| B134 | 134 位/秒 | B57600 | 57600 位/秒 |
| B1200 | 1200 位/秒 | B115200 | 115200 位/秒 |
| B2400 | 2400 位/秒 | B460800 | 460800 位/秒 |
数据位相关:
| 标志 | 说明 |
|---|---|
| CSIZE | 数据位屏蔽 |
| CS5 | 5 位数据位 |
| CS6 | 6 位数据位 |
| CS7 | 7 位数据位 |
| CS8 | 8 位数据位 |
停止位相关:
| 标志 | 说明 |
|---|---|
| CSTOPB | 2 位停止位,否则为 1 位 |
校验位:
| 标志 | 说明 |
|---|---|
| PARENB | 进行奇偶校验 |
| PARODD | 奇校验,否则为偶校验 |
其他:
| 标志 | 说明 |
|---|---|
| CREAD | 启动接收 |
| HUPCL | 最后关闭时断开 |
| CLOCAL | 忽略调制调解器状态行 |
输入标志c_iflag:
| 标志 | 说明 | 标志 | 说明 |
|---|---|---|---|
| INPCK | 打开输入奇偶校验 | IXOFF | 启用/停止输入控制流起作用 |
| IGNPAR | 忽略奇偶错字符 | IGNBRK | 忽略BREAK条件 |
| PARMRK | 标记奇偶错字符 | INLCR | 讲输入的NL转换为CR |
| ISTRIP | 剥除字符第八位 | IGNCR | 忽略CR |
| IXON | 启用/停止输出控制流起作用 | ICRNL | 将输入的CR转换为NL |
输出标志c_oflag:
| 标志 | 说明 | ||
|---|---|---|---|
| BSDLY | 退格延迟屏蔽 | OLCUC | 将输出的小写字符转换为大写字符 |
| CMSPAR | 标志或空奇偶性 | ONLCR | 将 NL 转换为 CR-NL |
| CRDLY | CR 延迟屏蔽 | ONLRET | NL 执行 CR 功能 |
| FFDLY | 换页延迟屏蔽 | ONOCR | 在 0 列不输出 CR |
| OCRNL | 将输出的 CR 转换为 NL | OPOST | 执行输出处理 |
| OFDEL | 填充符为 DEL,否则为 NULL | OXTABS | 将制表符扩充为空格 |
| OFILL | 对于延迟使用填充符 |
本地标志c_lflag:
| 标志 | 说明 | 标志 | 说明 |
|---|---|---|---|
| ISIG | 启用终端产生的信号 | NOFLSH | 在中断或退出键后禁用刷清 |
| ICANON | 启用规范输入 | IEXTEN | 启用扩充的输入字符处理 |
| XCASE | 规范大/小写表示 | ECHOCTL | 回送控制字符为(char) |
| ECHO | 进行回送 | ECHOPRT | 硬拷贝的可见擦除方式 |
| ECHOE | 可见擦除字符 | ECHOKE | Kill 的可见擦除 |
| ECHOK | 回送 kill 符 | PENDIN | 重新打印未决输入 |
| ECHONL | 回送 NL | TOSTOP | 对于后台输出发送 SIGTTOU |
控制字符组:
| 标志 | 说明 | 标志 | 说明 |
|---|---|---|---|
| VINTR | 中断 | VEOL | 行结束 |
| VQUIT | 退出 | VMIN | 需读取的最小字节数 |
| VERASE | 擦除 | VTIME | 与“VMIN”配合使用,是指限定的传输或等待的最长时间 |
| VEOF | 行结束 |
函数封装:
struct termios
{
// 串口配置
int uart_config(int fd, int baude, int c_flow, int bits, char parity, int stop)
{
struct termios uart;
if (tcgetattr(fd, &uart) != 0)
{
perror("tcgetattr failed!");
return -1;
}
switch (baude)
{
case 4800:
cfsetispeed(&uart, B4800);//设置输入波特率
cfsetospeed(&uart, B4800);//设置输出波特率
break;
case 9600:
cfsetispeed(&uart, B9600);
cfsetospeed(&uart, B9600);
break;
case 19200:
cfsetispeed(&uart, B19200);
cfsetospeed(&uart, B19200);
break;
case 38400:
cfsetispeed(&uart, B38400);
cfsetospeed(&uart, B38400);
break;
case 115200:
cfsetispeed(&uart, B115200);
cfsetospeed(&uart, B115200);
break;
default:
fprintf(stderr, "Unknown baude!\n");
return -1;
}
switch (c_flow)
{
case 'N':
case 'n':
uart.c_cflag &= ~CRTSCTS;//不进行硬件流控制
break;
case 'H':
case 'h':
uart.c_cflag |= CRTSCTS;//进行硬件流控制
break;
case 'S':
case 's':
uart.c_cflag |= (IXON | IXOFF | IXANY);//进行软件流控制
break;
default:
fprintf(stderr, "Unknown c_cflag");
return -1;
}
switch (bits)
{
case 5:
uart.c_cflag &= ~CSIZE;//屏蔽其他标志位
uart.c_cflag |= CS5;//数据位为5位
break;
case 6:
uart.c_cflag &= ~CSIZE;
uart.c_cflag |= CS6;
break;
case 7:
uart.c_cflag &= ~CSIZE;
uart.c_cflag |= CS7;
break;
case 8:
uart.c_cflag &= ~CSIZE;
uart.c_cflag |= CS8;
break;
default:
fprintf(stderr, "Unknown bits!");
return -1;
}
switch (parity)
{
case 'n':
case 'N':
uart.c_cflag &= ~PARENB;//PARENB:产生奇偶校验
uart.c_cflag &= ~INPCK;//INPCK:使奇偶校验起作用
break;
case 's':
case 'S':
uart.c_cflag &= ~PARENB;
uart.c_cflag &= ~CSTOPB;//使用两位停止位
break;
case 'o':
case 'O':
uart.c_cflag |= PARENB;
uart.c_cflag |= PARODD;//使用奇校验
uart.c_cflag |= INPCK;
uart.c_cflag |= ISTRIP;//使字符串剥离第八个字符,即校验位
break;
case 'e':
case 'E':
uart.c_cflag |= PARENB;
uart.c_cflag &= ~PARODD;//非奇校验,即偶校验
uart.c_cflag |= INPCK;
uart.c_cflag |= ISTRIP;
break;
default:
fprintf(stderr, "Unknown parity!\n");
return -1;
}
switch (stop)
{
case 1:
uart.c_cflag &= ~CSTOPB;//CSTOPB:使用两位停止位
break;
case 2:
uart.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr, "Unknown stop!\n");
return -1;
}
uart.c_oflag &= ~OPOST;//OPOST:表示数据经过处理后输出
uart.c_cc[VTIME] = 0;//设置等待时间为0
uart.c_cc[VMIN] = 1;//设置最小接受字符为1
tcflush(fd, TCIFLUSH);//清空输入缓冲区
if (tcsetattr(fd, TCSANOW, &uart) < 0)//激活配置
{
perror("tcgetattr failed!");
return -1;
}
return 0;
}
// 打开串口
int uart_open(const char *pathname)
{
assert(pathname);//检测串口路径是否存在
Log("打开串口设备:%s\n", pathname);
int fd = open(pathname, O_RDWR | O_NOCTTY | O_NONBLOCK);//以只读形式、不将此终端作为此进程的终端控制器、非阻塞的形式打开串口
if (fd == -1)
{
Log("uart open[%s] failed! error[%d:%s]\n", pathname, errno, strerror(errno));
return -1;
}
if (fcntl(fd, F_SETFL, 0) < 0)//设置串口非阻塞,因为这里是以非阻塞形式打开的,所以第三个参数为0,后面会详细介绍fcntl函数
{ Log("fcntl[%d] failed! error[%d:%s]\n", fd, errno, strerror(errno)); return -1; } return fd; }
// 读串口数据
int safe_read(int fd, char* vptr, size_t len) {
size_t left;
left = len;
ssize_t nread;
char* ptr;
ptr = vptr;
while (left > 0)
{
if ((nread = read(fd, ptr, left)) < 0)
{
/*
if (errno == EINIR)
{
nread = 0;
}
else */
if (nread == 0)
{
break;
}
}
left -= nread;//read成功后,剩余要读取的字节自减
ptr += nread;//指针向后移,避免后读到的字符覆盖先读到的字符
}
return (len - left);
}
// 读取串口数据
int uart_read(int fd, char* r_buf, size_t lenth)
{
fd_set rfds;
struct timeval time;
ssize_t cnt = 0;
/*将读文件描述符加入描述符集合*/
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
/*设置超时为15s*/
time.tv_sec = 15;
time.tv_usec = 0;
/*实现多路IO*/
ret = select(fd + 1, &rfds, NULL, NULL, &time);
switch (ret) {
case -1:
fprintf(stderr, "select error!\n");
break;
case 0:
//fprintf(stderr, "time over!\n");
break;
default:
cnt = safe_read(fd, r_buf, lenth);
if (cnt == -1)
{
fprintf(stderr, "safe read failed!\n");
return -1;
}
break;
}
return cnt;
}
// 关闭串口
int uart_close(int fd)
{
if (fd <= 0)
return 0;
Log("Close uart fd[%d]\n", fd);
assert(fd);//assert先检查文件描述符是否存在
close(fd);
fd = -1;
return 0;
}
}
调用方法:
int fd = uart_open(“/dev/ttyS0”); uart_config(fd, 115200, 'n', 8, 'n', 1)
参考:
https://blog.csdn.net/qq_37212828/article/details/102565785
https://blog.csdn.net/continue_862/article/details/79919028