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

查看:27
本文介绍了规范模式 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?

my在您的其他帖子中对您的最后评论.
显然您不相信手册页面或我,并且也不清楚行分隔符"、行终止和 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 的换行符作为行终止符,如手册页(您已引用)中明确说明的那样.
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
", 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
", 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
", 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!
", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d
", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple canonical input */
    do {
        unsigned char buf[83];
        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("
    "%s"

", buf);
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s
", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Nothing read. EOF?
");
        }               
        /* repeat read */
    } while (1);
}


注意程序并没有去掉' '字符(即属性IGNCR被清除),但回车也没有定义为行分隔符.
因此,这个 termios 配置中的回车没有特殊意义,就像任何可打印的字符一样被传递.


Note that the program does not strip out ' ' 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天全站免登陆