规范模式Linux串行端口 [英] Canonical Mode Linux Serial Port

查看:117
本文介绍了规范模式Linux串行端口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Termios手册页( http://man7.org/linux/man-pages/man3/termios.3.html )用于规范模式状态:

The Termios man pages (http://man7.org/linux/man-pages/man3/termios.3.html) for canonical mode state:

逐行提供输入.输入线可用 当键入行定界符之一(NL,EOL,EOL2;或EOF为 行的开头).除EOF以外,行定界符 包含在read(2)返回的缓冲区中.

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).

我的问题是:当一块硬件输出符合规范的数据时,是否将0xD0xA(CRLF)字节放在传输线的开头以告知read()函数数据已准备好读取?

My question is: when a piece of hardware outputs data to be canonical compliant - is it putting the 0xD0xA (CRLF) bytes at the beginning of the transmission line to tell the read() function that data is ready to be read?

我以前没有考虑过很多,因此默认(可能是错误地)认为0xD0xA位于传输线的末端.

I've not thought about this a lot before and have defaulted (potentially erroneously) to thinking that the 0xD0xA is at the end of the transmission line.

推荐答案

是否将0xD0xA(CRLF)字节放在传输线的开头以告知read()函数数据已准备好被读取?

is it putting the 0xD0xA (CRLF) bytes at the beginning of the transmission line to tell the read() function that data is ready to be read?

我的问题已经基本回答了您在其他帖子中对您的最后评论.
显然您不相信 man 页面或我,也不清楚行定界符",行终止和EOL的含义.

Your question has essentially already been answered by my last comment to you in your other post.
Apparently you don't believe the man page or me, and are also unclear what "line delimiter", line termination, and EOL mean.

串行端口" 硬件" 没有开始" 结束" 传输线" .都是到U [S] ART的有效载荷数据.
仅当在标准模式下使用termios读取串行终端缓冲区时,线路终端才具有上下文.
请参阅 Linux串行驱动程序,以了解如何删除用户空间代码硬件.

The "serial port" or "hardware" has no concept of a "beginning" or an "end" of the "transmission line". It's all just payload data to the U[S]ART.
Line termination only has context when using termios in canonical mode to read the serial terminal buffer.
See Linux serial drivers to understand how removed your userspace code is from the hardware.

Linux使用换行符或具有ASCII代码0x0A的换行符作为行终止符,如 man 页(已引用)中明确指出的那样.
Termios允许定义其他行尾字符,即用于串行终端的VEOL和VEOL2. 每次出现行定界符都会并且将导致(待定)规范的 read()返回.
行定界符将是缓冲区中返回的最后一个字符,除非用户缓冲区太小而无法包含整行.

Linux uses the newline character, or linefeed which has ASCII code 0x0A, as the line terminator, as clearly stated in the man page (which you have quoted).
Termios allows the definition of additional end-of-line characters, i.e. VEOL and VEOL2 for serial terminals.
Each and every occurrence of a line delimiter character can and will cause a (pending) canonical read() to return.
The line delimiter character will be the last character returned in the buffer, unless the user buffer is too small to contain the entire line.

为EOF定义的字符,即 VEOF ,默认为EOT的ASCII代码0x04,由termios处理的略有不同.
收到EOF字符会导致(待定)规范的 read()返回,就像行定界符一样,但是EOF字符未存储在返回的缓冲区中.
因此,当EOF前面有行定界符时, read()的返回码为零,即实际的空行!

The character defined for EOF, i.e. VEOF which defaults to the ASCII code 0x04 for EOT, is handled slightly different by termios.
The receipt of the EOF character causes a (pending) canonical read() to return just like a line delimiter character, but the EOF character is not stored in the returned buffer.
Hence, when the EOF is preceded by a line delimiter, read() will have a return code of zero, an actual empty line!

如果您是一位怀疑的托马斯,那么您应该将一对USB-RS232适配器交叉连接在一起,并测试使用termios从串行终端读取数据时会发生什么.
在第一个串行终端上使用终端仿真器程序(例如 minicom )输入数据,并使用以下C程序查看另一个串行终端上的规范读取.

If you're such a Doubting Thomas, then you should cross-connect a pair of USB-RS232 adapters together, and test what happens when reading from a serial terminal using termios.
Use a terminal emulator program such as minicom on the first serial terminal to enter data, and use the following C program to see the canonical reads on the other serial terminal.

#define SERIALTERMINAL      "/dev/ttyUSB1"
#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= CLOCAL | CREAD;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    tty.c_lflag |= ICANON | ISIG;  /* canonical input */
    tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

    tty.c_iflag &= ~IGNCR;  /* preserve carriage return */
    tty.c_iflag &= ~INPCK;
    tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */

    tty.c_oflag &= ~OPOST;

    tty.c_cc[VEOL] = 0;
    tty.c_cc[VEOL2] = 0;
    tty.c_cc[VEOF] = 0x04;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}


int main()
{
    char *portname = SERIALTERMINAL;
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);

    /* simple output */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple canonical input */
    do {
        unsigned char buf[81];
        unsigned char *p;
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
            buf[rdlen] = 0;
            printf("Read %d:", rdlen);
            /* first display as hex numbers then ASCII */
            for (p = buf; rdlen-- > 0; p++) {
                printf(" 0x%x", *p);
                if (*p < ' ')
                    *p = '.';   /* replace any control chars */
            }
            printf("\n    \"%s\"\n\n", buf);
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Nothing read. EOF?\n");
        }               
        /* repeat read */
    } while (1);
}


请注意,程序不会去除'\ r'字符(即属性IGNCR被清除),但是回车符也未定义为行定界符.
因此,在此termios配置中,回车符没有特殊含义,并且像任何可打印字符一样通过.


Note that the program does not strip out '\r' characters (i.e. attribute IGNCR is cleared), but carriage return is not defined as a line delimiter either.
Hence carriage returns in this termios configuration have no special meaning, and are passed through just like any printable character.

因此,键入(等同于)ABCDEFG^M^J的意思是:

So typing (the equivalent of) ABCDEFG^M^J is read as:

Read 9: 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0xd 0xa
    "ABCDEFG.."

123^Mabc^J读取为:

Read 8: 0x31 0x32 0x33 0xd 0x61 0x62 0x63 0xa
    "123.abc."

另一种termios配置可以去除回车符或将回车符视为行定界符.

An alternate termios configuration could strip out the carriage returns or treat the carriage returns as a line delimiter.

这篇关于规范模式Linux串行端口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆