从串行端口读取总是 returns 写入的内容
reading from serial port always returns what was written
我在设备模式下使用 Raspberry Pi 零,在主机模式下使用 Raspberry Pi B。我正在用 USB 数据线连接两者。我现在的目标只是在两个 Pi 之间来回发送简单的任意数据。
问题是无论哪个 Pi 先写入串行端口,最终都会读取它写入的内容。我写的程序有设备发送 d\n
和主机发送 h\n
。因此,如果设备先写入,则主机正确读取 d\n
,然后将 h\n
写入串口。但是设备最终读取 d\n
!如果我切换它以便主机先写入,问题仍然存在。
我试过在编写之后但在阅读之前向程序添加各种 tcflush
调用,但它不起作用。我也试过睡不同的时间。我读过每个写的字符等待 100 微秒,我睡了几秒钟。
由于 Pi Zero 的单个支持数据的 USB 端口,我的设置要求我不能同时在两个 Pi 之间保持持续连接。所以,为了测试,我实际上是插入键盘和 运行 程序,然后插入适当的电缆来传输数据。我可以传输数据,但在写入之后不能,因为程序只是读回它写入的内容。
我开始觉得我掉进了一个我无法理解的菜鸟陷阱。这是我正在使用的代码:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
/*
* gcc -o device_rw -DDEVICE serial_rw.c
* gcc -o host_rw serial_rw.c
*/
#define SERIAL_DEVICE "/dev/ttyGS0"
#define SERIAL_HOST "/dev/ttyACM0"
#ifdef DEVICE
#define _TTY SERIAL_DEVICE
#else
#define _TTY SERIAL_HOST
#endif
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 &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_iflag |= IGNPAR | IGNCR;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag |= ICANON;
tty.c_iflag &= ~OPOST;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void
write_serial (int fd, const char *buf, int len)
{
printf("WRITE: %s\n", buf);
write(fd, buf, len);
}
void
read_serial (int fd, char *buf, int len)
{
ssize_t nread = read(fd, buf, len);
if (nread > 0 && nread <= len) {
buf[nread] = 0;
printf(" READ: %s\n", buf);
}
}
int
main (int argc, char **argv)
{
char buf[80];
int fd = open(_TTY, O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %s\n", _TTY, strerror(errno));
goto exit;
}
if (set_interface_attribs(fd, B115200) < 0) {
goto exit;
}
#ifdef DEVICE
printf("device: %s\n", _TTY);
write_serial(fd, "d\n", 2);
usleep((2 + 25) * 100);
read_serial(fd, buf, 2);
#else
printf("host: %s\n", _TTY);
read_serial(fd, buf, 2);
//usleep((2 + 25) * 100);
write_serial(fd, "h\n", 2);
#endif
close(fd);
exit:
return 0;
}
除了不禁用 ECHO 属性(正如@MarkPlotnick 评论的那样),您还有两个误用的分配:
tty.c_iflag |= ICANON;
ICANON 属于 lflag 成员,
tty.c_iflag &= ~OPOST;
OPOST 属于 oflag 会员
考虑到这些错误,您是否正确应用了@MarkPlotnick 的建议?
请参阅 Working with linux serial port in C, Not able to get full data 了解有效的规范设置。
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 &= ~INPCK;
tty.c_iflag |= IGNCR;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
请注意,也可以在远端启用回声。
要确定回声是在本地还是从远端生成,只需断开远程设备(假设您使用的是 UART and/or USB 转串口适配器),然后传输。
如果你仍然得到回声,那么它是在本地生成的,由 termios ECHO 属性控制。
如果您不再收到回声,则说明远程单元正在将其输入重复回发件人。
顺便说一句,您发布的程序编译不干净。
不见了 #include <string.h>
您复制(但随后错误修改)的 termios 初始化例程可以正确检查 return 值,但您的读写例程不会检查错误。
我从每个人的回答中学到了很多东西,我很感激他们,因为在我继续这个项目的过程中我需要他们。但是,对于 this 特殊情况,问题最终是权限问题。是的,ttyGSO
.
的文件权限
我完全预料到 open
会出现 permission denied
错误,但从未遇到过,所以我从未考虑过这种可能性。特别是因为我在 RDWR
模式下打开文件,我可以写入串行文件,它 出现 我正在读取数据(尽管是相同的数据)我从来没有意识到也许我没有读取权限。
我在设备模式下使用 Raspberry Pi 零,在主机模式下使用 Raspberry Pi B。我正在用 USB 数据线连接两者。我现在的目标只是在两个 Pi 之间来回发送简单的任意数据。
问题是无论哪个 Pi 先写入串行端口,最终都会读取它写入的内容。我写的程序有设备发送 d\n
和主机发送 h\n
。因此,如果设备先写入,则主机正确读取 d\n
,然后将 h\n
写入串口。但是设备最终读取 d\n
!如果我切换它以便主机先写入,问题仍然存在。
我试过在编写之后但在阅读之前向程序添加各种 tcflush
调用,但它不起作用。我也试过睡不同的时间。我读过每个写的字符等待 100 微秒,我睡了几秒钟。
由于 Pi Zero 的单个支持数据的 USB 端口,我的设置要求我不能同时在两个 Pi 之间保持持续连接。所以,为了测试,我实际上是插入键盘和 运行 程序,然后插入适当的电缆来传输数据。我可以传输数据,但在写入之后不能,因为程序只是读回它写入的内容。
我开始觉得我掉进了一个我无法理解的菜鸟陷阱。这是我正在使用的代码:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
/*
* gcc -o device_rw -DDEVICE serial_rw.c
* gcc -o host_rw serial_rw.c
*/
#define SERIAL_DEVICE "/dev/ttyGS0"
#define SERIAL_HOST "/dev/ttyACM0"
#ifdef DEVICE
#define _TTY SERIAL_DEVICE
#else
#define _TTY SERIAL_HOST
#endif
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 &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_iflag |= IGNPAR | IGNCR;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag |= ICANON;
tty.c_iflag &= ~OPOST;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void
write_serial (int fd, const char *buf, int len)
{
printf("WRITE: %s\n", buf);
write(fd, buf, len);
}
void
read_serial (int fd, char *buf, int len)
{
ssize_t nread = read(fd, buf, len);
if (nread > 0 && nread <= len) {
buf[nread] = 0;
printf(" READ: %s\n", buf);
}
}
int
main (int argc, char **argv)
{
char buf[80];
int fd = open(_TTY, O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %s\n", _TTY, strerror(errno));
goto exit;
}
if (set_interface_attribs(fd, B115200) < 0) {
goto exit;
}
#ifdef DEVICE
printf("device: %s\n", _TTY);
write_serial(fd, "d\n", 2);
usleep((2 + 25) * 100);
read_serial(fd, buf, 2);
#else
printf("host: %s\n", _TTY);
read_serial(fd, buf, 2);
//usleep((2 + 25) * 100);
write_serial(fd, "h\n", 2);
#endif
close(fd);
exit:
return 0;
}
除了不禁用 ECHO 属性(正如@MarkPlotnick 评论的那样),您还有两个误用的分配:
tty.c_iflag |= ICANON;
ICANON 属于 lflag 成员,
tty.c_iflag &= ~OPOST;
OPOST 属于 oflag 会员
考虑到这些错误,您是否正确应用了@MarkPlotnick 的建议?
请参阅 Working with linux serial port in C, Not able to get full data 了解有效的规范设置。
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 &= ~INPCK;
tty.c_iflag |= IGNCR;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
请注意,也可以在远端启用回声。
要确定回声是在本地还是从远端生成,只需断开远程设备(假设您使用的是 UART and/or USB 转串口适配器),然后传输。
如果你仍然得到回声,那么它是在本地生成的,由 termios ECHO 属性控制。
如果您不再收到回声,则说明远程单元正在将其输入重复回发件人。
顺便说一句,您发布的程序编译不干净。
不见了 #include <string.h>
您复制(但随后错误修改)的 termios 初始化例程可以正确检查 return 值,但您的读写例程不会检查错误。
我从每个人的回答中学到了很多东西,我很感激他们,因为在我继续这个项目的过程中我需要他们。但是,对于 this 特殊情况,问题最终是权限问题。是的,ttyGSO
.
我完全预料到 open
会出现 permission denied
错误,但从未遇到过,所以我从未考虑过这种可能性。特别是因为我在 RDWR
模式下打开文件,我可以写入串行文件,它 出现 我正在读取数据(尽管是相同的数据)我从来没有意识到也许我没有读取权限。