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