从串口读取始终返回写入的内容

时间:2016-12-28 19:06:37

标签: c serial-port usb

我在设备模式下使用Raspberry Pi Zero,在主机模式下使用Raspberry Pi B.我用USB线连接两者。我现在的目标只是在两个Pi之间来回发送简单,任意的数据。

问题是,Pi写入串口后,最先读取它所写的内容。我编写的程序让设备发送d\n,主机发送h\n。因此,如果设备首先写入,则主机正确读取d\n,然后将h\n写入串行端口。但设备最终会阅读d\n!如果我将其切换以便主机先写入,问题仍然存在。

我尝试在写入之后但在阅读之前向程序添加各种tcflush调用,但它不起作用。我也试过睡了不少时间。我读过每个字符等待100微秒,我已经睡了几秒钟。

我的设置要求我不要同时在两个Pi之间保持连接,因为Pi Zero的单个数据能力的USB端口。所以,为了测试,我实际上是插入键盘并运行程序,然后插入适当的电缆来传输数据。我可以传输数据,但不能在写入之后,因为程序只是回读它写的内容。

我开始认为自己陷入了一个我无法理解的陷阱陷阱。这是我正在使用的代码:

#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;
}

2 个答案:

答案 0 :(得分:4)

除了不禁用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和/或USB转串口适配器)并进行传输。
如果你仍然得到回声,那么它是在本地生成的,由 termios ECHO属性控制。
如果您不再获得回音,那么它的远程单元会将其输入重复发送回发件人。

顺便说一下你发布的程序编写得不干净 它缺少#include <string.h>
您复制的 termios 初始化例程(但随后被错误地修改)会正确检查返回值,但您的读写例程不会检查错误。

答案 1 :(得分:0)

我从每个人的答案中学到了很多东西,并对他们表示感谢,因为随着这个项目的继续,我将需要他们。但是,对于特定情况,问题最终成为权限。是的,ttyGSO上的文件权限。

我完全期望来自permission denied的{​​{1}}错误而且永远不会有错误,所以我从未考虑过这种可能性。特别是自从我以open模式打开文件后,我可以写入串行文件,出现我正在读取数据(虽然是相同的数据)我从来没有意识到我也许没有读权限。