规范模式 Linux 串口
Canonical Mode Linux Serial Port
规范模式状态的 Termios 手册页 (http://man7.org/linux/man-pages/man3/termios.3.html):
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() 函数数据已准备好读了吗?
我之前没有考虑太多,默认(可能是错误的)认为 0xD0xA 位于传输线的末端。
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 last comment to you in your other post 回答了。
显然你不相信 man 页面或我,也不清楚“行定界符”、行终止符和 EOL 是什么意思。
“串口”或“硬件”没有“开头”[=60]的概念=]或“传输线”的“结束”。这只是 U[S]ART 的有效载荷数据。
只有在规范模式下使用 termios 读取串行终端缓冲区时,线路终止才有上下文。
请参阅 Linux serial drivers 以了解您的用户空间代码是如何从硬件中删除的。
Linux 使用 ASCII 代码为 0x0A 的换行符或换行符作为行终止符,如 man 页面(您已引用).
Termios 允许定义额外的 end-of-line 字符,即串行终端的 VEOL 和 VEOL2。
行分隔符的每一次出现都可能并且将导致(待定)规范 read() 到 return.
行分隔符将是缓冲区中最后一个 return 字符,除非用户缓冲区太小而无法包含整行。
为 EOF 定义的字符,即 VEOF,默认为 EOT 的 ASCII 代码 0x04,termios 处理方式略有不同。
EOF 字符的接收导致(待处理的)规范 read() 到 return 就像行分隔符一样,但 EOF 字符未存储在 return编辑缓冲区。
因此,当 EOF 前面有一个行分隔符时,read() 将有一个 return 代码为零,一个实际的空行!
如果你是一个多疑的 Thomas,那么你应该 cross-connect 一对 USB-RS232 适配器在一起,并测试使用 termios 从串行终端读取时会发生什么。
在第一个串行终端上使用 minicom 等终端仿真器程序输入数据,并使用以下 C 程序在另一个串行终端上查看规范读取。
#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[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("\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),但回车 return 也未定义为行分隔符。
因此,此 termios 配置中的回车 return 没有特殊含义,就像任何可打印字符一样通过。
所以输入(相当于)ABCDEFG^M^J
读作:
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 配置可以删除回车 returns 或将回车 returns 视为行分隔符。
规范模式状态的 Termios 手册页 (http://man7.org/linux/man-pages/man3/termios.3.html):
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() 函数数据已准备好读了吗?
我之前没有考虑太多,默认(可能是错误的)认为 0xD0xA 位于传输线的末端。
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 last comment to you in your other post 回答了。
显然你不相信 man 页面或我,也不清楚“行定界符”、行终止符和 EOL 是什么意思。
“串口”或“硬件”没有“开头”[=60]的概念=]或“传输线”的“结束”。这只是 U[S]ART 的有效载荷数据。
只有在规范模式下使用 termios 读取串行终端缓冲区时,线路终止才有上下文。
请参阅 Linux serial drivers 以了解您的用户空间代码是如何从硬件中删除的。
Linux 使用 ASCII 代码为 0x0A 的换行符或换行符作为行终止符,如 man 页面(您已引用).
Termios 允许定义额外的 end-of-line 字符,即串行终端的 VEOL 和 VEOL2。
行分隔符的每一次出现都可能并且将导致(待定)规范 read() 到 return.
行分隔符将是缓冲区中最后一个 return 字符,除非用户缓冲区太小而无法包含整行。
为 EOF 定义的字符,即 VEOF,默认为 EOT 的 ASCII 代码 0x04,termios 处理方式略有不同。
EOF 字符的接收导致(待处理的)规范 read() 到 return 就像行分隔符一样,但 EOF 字符未存储在 return编辑缓冲区。
因此,当 EOF 前面有一个行分隔符时,read() 将有一个 return 代码为零,一个实际的空行!
如果你是一个多疑的 Thomas,那么你应该 cross-connect 一对 USB-RS232 适配器在一起,并测试使用 termios 从串行终端读取时会发生什么。
在第一个串行终端上使用 minicom 等终端仿真器程序输入数据,并使用以下 C 程序在另一个串行终端上查看规范读取。
#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[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("\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),但回车 return 也未定义为行分隔符。
因此,此 termios 配置中的回车 return 没有特殊含义,就像任何可打印字符一样通过。
所以输入(相当于)ABCDEFG^M^J
读作:
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 配置可以删除回车 returns 或将回车 returns 视为行分隔符。