只能通过串口读取一个字节

时间:2012-10-05 13:40:15

标签: python c windows linux serial-port

我的机器上有一个C / Python设置,我正在使用串行通信进行一些测试,出于某种原因,我从来没有读过超过1个字节。

我的设置:我有一台Windows 7机器,在虚拟机中运行OpenSUSE。我有2个USB-RS232转换器和它们之间的适配器(因此它是从一个USB端口到另一个的循环)。

在Windows方面,我能够让他们通过Python-to-Python和C-to-Python相互通信。一旦我使用Linux VM,我就可以从C(Linux)到Python(Windows)获取数据,但是当我这样做时,我只能得到1个字节。我认为我打开文件或执行Linux C代码上的读取有问题,但我不确定可能是什么问题。

Python代码(使用PySerial):

>>> import serial
>>> ser = serial.Serial(3)
>>> ser
Serial<id=0x2491780, open=True>(port='COM4', baudrate=9600, bytesize=8, 
parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
>>> ser.read(5)
'Hello'
>>> ser.write("hi you")
6L

C代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

int open_port()
{
    int fd;
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd < 0)
      perror("open_port: Unable to open /dev/ttyUSB0 - ");
    else
      fcntl(fd, F_SETFL, 0);
    return fd;
}

int swrite(int fd, char * str)
{
    int n;
    n = write(fd, str, strlen(str));
    if (n<0)
        printf("write() of %d bytes failed\n", strlen(str));
    return n;
}

int main()
{
    int fd, databytes;
    char buf[100] = {0};
    struct termios options;

    fd = open_port();

    //Set the baud rate to 9600 to match
    tcgetattr(fd, &options);
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    tcsetattr(fd, TCSANOW, &options);
    tcgetattr(fd, &options);

    databytes = swrite(fd, "Hello");
    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);

    databytes = read(fd, buf, 100);
    if(databytes < 0)
      printf("Error! No bytes read\n");
    else
      printf("We read %d bytes, message: %s\n", databytes, buf);

    close(fd);

    return 0;
}

我要回来了:

mike@linux-4puc:~> gcc serial_com.c
mike@linux-4puc:~> ./a.out 
Wrote 5 bytes
We read 1 bytes, message: h

所以Linux-&gt; Windows写入正在运行,python正在显示正确的“Hello”字符串,但由于某种原因,我只能在Windows-&gt; Linux方面获得一个字节。

有人看错了吗?

编辑:
根据我收到的反馈,我尝试了两次调整代码。听起来我无法保证所有数据都存在,所以我试过了:

1)睡觉

    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);
    sleep(15);                 // Hack one to get the data there in time, worked
    databytes = read(fd, buf, 100);

2)while循环

while(1){  // Hack two to catch the data that wasn't read the first time. Failed
           // this only saw 'h' like before then sat waiting on the read()
  databytes = read(fd, buf, 100);
  if(databytes < 0)
    printf("Error! No bytes read\n");
  else
    printf("We read %d bytes, message: %s\n", databytes, buf);
}

似乎循环不起作用,所以未读取的数据是否会被删除? 的 /修改

4 个答案:

答案 0 :(得分:7)

来自read(2) manual;

  

成功时,返回读取的字节数(零表示结束   (文件),文件位置按此编号前进。 不是   如果此数字小于请求的字节数,则出错;   例如,这可能发生,因为实际可用的字节更少   现在(也许是因为我们接近文件结尾,或者因为我们   从管道或终端读取数据

换句话说,由于您在写入后立即从套接字读取并且您的计算机比串行端口快得多,很可能只有一个字符可用于读取和{{ 1}}只返回该字符。

答案 1 :(得分:4)

read的手册页说

  

...尝试读取计数字节...

您的代码看起来假设单个read始终返回完整缓冲区;它对多次调用返回的数据有效。

检查read使用errno == EINTR返回-1并在此之后重试(如果在GNU系统上运行时使用TEMP_FAILURE_RETRY)也是一种好习惯。 read如果被信号中断,可能会返回瞬态错误。

答案 2 :(得分:3)

正如其他人已经回答的那样,C read()函数只通过返回一个字节就完成了它的契约。

C read()和Python read()函数完全不同。

PySerial说,关于read(),

  

从串口读取大小字节。如果设置了超时,则可能会根据请求返回较少的字符。没有超时,它将阻塞,直到读取所请求的字节数。

虽然C API没有这样的保证。它将返回缓冲区中可用的任何字符(即使为0,如果还没有任何字符)。

如果你希望C端表现得像Python那样,你需要使用不同的功能。像这样:

int read_exact_chars(int fildes, char *buf, size_t nbyte)
{
    ssize_t chars_read;
    int chars_left = nbyte;
    while (chars_left) {
        chars_read = read(fildes, buf, chars_left)
        if (chars_read == -1) {
            /* An error occurred; bail out early. The caller will see that
               we read fewer bytes than requested, and can check errno
             */
            break;
        } else {
            buf += chars_read;
            chars_left -= chars_read;
        }
    }
    /* return the actual number of characters read */
    return nbyte - chars_left;
}

答案 3 :(得分:3)

得到了一些好的答案并且煽动了这个(+ 1的全部!),而所有的答案都得出了正确的结论(关于读取没有获得数据),没有任何输入实际上“修复”我遇到的问题。

这是我最终如何工作的方式:

termios结构中的设置让我感到害怕。当我设置一些的标志时,我没有设置所有标志。所以这个:

tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
tcsetattr(fd, TCSANOW, &options); 

改为:

tcgetattr(fd, &options);
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
options.c_iflag = 0; // Disable Input flags
options.c_lflag = 0; // Disable Local mode flags
options.c_oflag = 0; // Disable Output flags
options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; //Data bits per character (8)
options.c_cc[VMIN] = 50;  // Wait for 50 characters or
options.c_cc[VTIME] = 50; // Wait for 5 seconds
options.c_cflag |= (CLOCAL | CREAD | HUPCL);   // Ignore modem status lines, 
                                               // enable receiver, 
                                               // and hang up on last close
options.c_cflag &= ~(PARENB | PARODD); //Clearing even and odd parity
options.c_cflag &= ~CSTOPB;            //Clear double stop bits
tcsetattr(fd, TCSANOW, &options);

现在通过这些更改,我获得了我用Python编写的C Linux代码中的数据。

我使用这两个来源获得了很多关于termios结构选项的“事实”:

  1. flag description
  2. Quick Overview