• c++实现串口功能之termios.h头文件研读<二>


    概述

            termios是在Posix规范中定义的标准接口,表示终端设备(包括虚拟终端、串口等)。因为串口是一种终端设备,所以通过终端编程接口对其进行配置和控制。因此在具体讨论串口相关编程之前,需要先了解一下终端的相关知识。

            终端是指用户与计算机进行对话的接口,如键盘、显示器和串口设备等物理设备,X Window上的虚拟终端。类UNIX操作系统都有文本式虚拟终端,使用【Ctrl+Alt】+F1~F6键可以进入文本式虚拟终端,在X Window上可以打开几十个以上的图形式虚拟终端。类UNIX操作系统的虚拟终端有xterm、rxvt、zterm、eterm等,而Windows上有crt、putty等虚拟终端。

            终端有三种工作模式,分别为规范模式(canonical mode)非规范模式(non-canonical mode)原始模式(raw mode)

    The setting of the ICANON canon flag in c_lflag determines
    whether the terminal is operating in canonical mode (ICANON set)
    or noncanonical mode (ICANON unset).  By default, ICANON is set.
    
    c_lflag 中的 ICANON 规范标志的设置决定了终端是在规范模式(ICANON 设置)
    还是非规范模式(ICANON 未设置)下运行。 默认情况下,设置了 ICANON。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    规范模式(canonical mode)

    Input is made available line by line.  An input line is
    available when one of the line delimiters is typed (NL, EOL,
    EOL2; or EOF at the start of line).  Except in the case of EOF,
    the line delimiter is included in the buffer returned by read(2).
    
    在规范模式下,输入是逐行的。当输入的是NL、EOL、EOL2或者行的开头是EOF的其中之一的时候,
    输入行才会有效。除了 EOF 外,行分隔符包含在 read(2) 返回的缓冲区中。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is
    set: WERASE, REPRINT, LNEXT).  A read(2) returns at most one
    line of input; if the read(2) requested fewer bytes than are
    available in the current line of input, then only as many bytes
    as requested are read, and the remaining characters will be
    available for a future read(2).
    
    在规范模式中,可以进行行编辑,而且一次调用read()最多只能读取一行数据。
    如果read()请求读取的数据字节少于当前行可读取的字节,则read()只读取被请求的字节数,
    剩下的字节下次再读。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    The maximum line length is 4096 chars (including the
    terminating newline character); lines longer than 4096 chars
    are truncated.  After 4095 characters, input processing (e.g.,
    ISIG and ECHO* processing) continues, but any input data after
    4095 characters up to (but not including) any terminating
    newline is discarded.  This ensures that the terminal can
    always receive more input until at least one line can be read.
    
    在规范模式下,最大行长度为 4096 个字符(包括终止换行符); 超过 4096 个字符的行
    被截断。 在 4095 个字符之后,输入处理(例如,ISIG 和 ECHO* 处理)继续,但之后的任何输入数据
    最多 4095 个字符(但不包括)任何终止符换行符被丢弃。 这确保了终端可以始终接收更多输入,
    直到可以读取至少一行。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    非规范模式(noncanonical mode)

    input is available immediately (without the user having to type a line-delimiter character),
    no input processing is performed, and line editing is disabled.  The read
    buffer will only accept 4095 chars; this provides the necessary space 
    for a newline char if the input mode is switched to canonical.
    
    在非规范模式下,输入是即时有效,用户不需要另外输入行结束符,不能进行行编辑。读
    缓冲区只接受 4095 个字符;如果再切换为规范模式的话,这样就可以为换行符提供了充足的空间
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    The settings of MIN (c_cc[VMIN]) and TIME (c_cc[VTIME]) determine the circumstances 
    in which a read(2) completes; there are four distinct cases:
    
    在非规范模式下,对参数MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的设置决定read()函数的调用方式。
    设置可以有4种不同的情况
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • MIN == 0, TIME == 0 (polling read 轮询读取)
    If data is available, read(2) returns immediately, with
    the lesser of the number of bytes available, or the number
    of bytes requested.  If no data is available, read(2) returns 0.
    
    • 1
    • 2
    • 3

    如果数据可用, read() 立即返回,并用字节数或数量中的较小者请求的字节数。 如果没有数据可用,read() 返回 0。

    • MIN > 0, TIME == 0 (blocking read 阻塞读取)
     read(2) blocks until MIN bytes are available, and returns up to the number 
     of bytes requested.
    
    • 1
    • 2

    read() 阻塞,直到 MIN 字节可用,并返回最多请求的字节数。

    • MIN == 0, TIME > 0 (read with timeout 超时读取)
    TIME specifies the limit for a timer in tenths of a
    second.  The timer is started when read(2) is called.
    read(2) returns either when at least one byte of data is
    available, or when the timer expires.  If the timer
    expires without any input becoming available, read(2)
    returns 0.  If data is already available at the time of
    the call to read(2), the call behaves as though the data
    was received immediately after the call.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    TIME 以十分之一秒为单位指定计时器的限制。当read()被调用的时候,计时器就会启动。当至少一个字节可用或者计时器超时,read都会返回数据。如果计时器到期而没有任何输入可用,则read (2)
    返回 0。如果在调用 read(2) 时数据已经可用,则调用的行为就像在调用后立即收到数据一样。

    • MIN > 0, TIME > 0 (read with interbyte timeout 字节间超时读取)
    TIME specifies the limit for a timer in tenths of a second.  
    Once an initial byte of input becomes available,
    the timer is restarted after each further byte is received。
    read(2) returns when any of the following conditions is met:
    
    TIME 以十分之一秒为单位指定计时器的限制。一旦输入的初始字节可用,
    每收到一个字节后,定时器重新启动。当满足以下任一条件时,read(2) 返回:
    
     * MIN bytes have been received.
       已收到 MIN 个字节。
       
     * The interbyte timer expires.
        字节间计时器到期。
    
     * The number of bytes requested by read(2) has been
       received.  (POSIX does not specify this termination
       condition, and on some other implementations read(2)
       does not return in this case.)
       已收到 read(2) 请求的字节数。(POSIX 没有指定这个终止条件,
       并且在其他一些实现中 read(2) 在这种情况下不会返回)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    原始模式(Raw mode)

    原始模式是一种特殊的非规范模式。这种模式下,所有输入数据以字节为单位处理。
    调用cfmakeraw()函数可将终端设置为原始模式。

    行控制(Line control)

    tcsendbreak()

    transmits a continuous stream of zero-valued bits for a specific duration, 
    if the terminal is using asynchronous serial data transmission.  If duration 
    is zero, it transmits zero-valued bits for at least 0.25 seconds, and not more than 0.5 seconds. 
     If duration is not zero, it sends zero-valued bits for -some implementation-defined length of time. 
     If the terminal is not using asynchronous serial data transmission, tcsendbreak() returns without taking any action.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果终端使用异步串行数据传输,则在特定持续时间内传输零值位的连续流。 如果持续时间为零,则它传输零值位至少 0.25 秒,不超过 0.5 秒。 如果持续时间不为零,它会在某些实现定义的时间长度内发送零值位。 如果终端没有使用异步串行数据传输,则 tcsendbreak() 不执行任何操作就返回。

    tcdrain()

     waits until all output written to the object referred to by fd has been transmitted.
     等待直到所有写入 fd 引用的对象的输出都已传输完毕。
    
    • 1
    • 2

    tcflush()

    discards data written to the object referred to by fd but not transmitted, 
    or data received but not read, depending on the value of queue_selector:
    刷新数据写入到fd的引用对象但不传输,或者只接收数据但是不读取,取决于queue_selector的值:
    
    TCIFLUSH
          flushes data received but not read.
          刷新接收的数据但是不读取
          
    TCOFLUSH
          flushes data written but not transmitted.
          刷新写入的数据但是不传输
          
    TCIOFLUSH
          flushes both data received but not read, and data written but not transmitted.
          同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    tcflow()

    suspends transmission or reception of data on the object referred to by fd, 
    depending on the value of action:
    对于fd引用对象上的数据是暂停传输,还是暂停接收,取决于action的值:
    
    TCOOFF 
        suspends output.
        暂停输出
        
    TCOON  
        restarts suspended output.
        重新开始已经暂停的输出
        
    TCIOFF 
        transmits a STOP character, which stops the terminal device from transmitting data to the system.
        发送一个 STOP 字符,停止终端设备向系统发送数据。
        
    TCION  
        transmits a START character, which starts the terminal device transmitting data to the system.
        发送一个 START 字符,启动终端设备向系统发送数据。
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    行速率(Line speed)

    波特率的常量值:

                B0
                B50
                B75
                B110
                B134
                B150
                B200
                B300
                B600
                B1200
                B1800
                B2400
                B4800
                B9600
                B19200
                B38400
                B57600
                B115200
                B230400
                B460800
                B500000
                B576000
                B921600
                B1000000
                B1152000
                B1500000
                B2000000
                
    These constants are additionally supported on the SPARC architecture:
    SPARC 体系结构还支持这些常量:
                B76800
                B153600
                B307200
                B614400
    
    These constants are additionally supported on non-SPARC architectures:
    非 SPARC 体系结构还支持这些常量:
                B2500000
                B3000000
                B3500000
                B4000000
    
    • 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

    cfgetospeed()

    返回输出的波特率

    cfsetospeed()

    设置输出的波特率

    cfgetispeed()

    获取输入的波特率

    cfsetispeed()

    设置输入的波特率

    示例

    speed_t SerialPort::getBaudRate(int baudrate) {
        switch (baudrate) {
            case 0:
                return B0;
            case 50:
                return B50;
            case 75:
                return B75;
            case 110:
                return B110;
            case 134:
                return B134;
            case 150:
                return B150;
            case 200:
                return B200;
            case 300:
                return B300;
            case 600:
                return B600;
            case 1200:
                return B1200;
            case 1800:
                return B1800;
            case 2400:
                return B2400;
            case 4800:
                return B4800;
            case 9600:
                return B9600;
            case 19200:
                return B19200;
            case 38400:
                return B38400;
            case 57600:
                return B57600;
            case 115200:
                return B115200;
            case 230400:
                return B230400;
            case 460800:
                return B460800;
            case 500000:
                return B500000;
            case 576000:
                return B576000;
            case 921600:
                return B921600;
            case 1000000:
                return B1000000;
            case 1152000:
                return B1152000;
            case 1500000:
                return B1500000;
            case 2000000:
                return B2000000;
            case 2500000:
                return B2500000;
            case 3000000:
                return B3000000;
            case 3500000:
                return B3500000;
            case 4000000:
                return B4000000;
            default:
                return -1;
        }
    }
    
    int SerialPort::setSpeed(int fd, int speed) {
        speed_t b_speed;
        struct termios cfg;
        b_speed = getBaudRate(speed);
        //获取属性
        if (tcgetattr(fd, &cfg)) {
            LOGD("Get attr failed");
            close(fd);
            return FALSE;
        }
        /**
        *
        *将终端设置为原始模式
        */
        cfmakeraw(&cfg);
        //设置输入的波特率
        cfsetispeed(&cfg, b_speed);
        //设置输出波特率
        cfsetospeed(&cfg, b_speed);
        //把相关属性设置到设备
        if (tcsetattr(fd, TCSANOW, &cfg)) {
            LOGD("Set attr failed");
            close(fd);
            return FALSE;
        }
    
        return TRUE;
    }
    
    • 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

    源码已同步gitHub:https://github.com/AinialJing/JniDemo
    参考地址:https://man7.org/linux/man-pages/man3/termios.3.html

  • 相关阅读:
    线性代数 | 矩阵运算 加减 数乘 矩阵的幂运算
    json字符串和对象之间的转换
    python sum()函数
    Go语言实践案例之简单字典
    java中的File类(文件操控者)
    【Leetcode-链表强训】
    什么是集成测试?集成测试方法有哪些?
    springboot构建RESTful 风格应用
    【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构
    关于各种PLMN的选择
  • 原文地址:https://blog.csdn.net/u011557841/article/details/126479786