串口环回/双工测试,在Bash或C中?(进程替换)

8

我有一个串口设备设置为环回模式(意味着它会简单地回显接收到的任何字符),我想测量有效的吞吐速度。为此,我希望可以使用 time 命令,如下所示:

time bash -c '...'

其中'...'是我可以运行的某些命令。

首先,我想以2000000 bps的速度使用设备,因此我不能使用ttylogscreen(它们似乎只支持最高115200 bps)。然而,将/dev/ttyUSB0作为文件(使用文件重定向和cat)来操作似乎很好用:

# initialize serial port
stty 2000000 -ixon icanon </dev/ttyUSB0

# check settings
stty -a -F /dev/ttyUSB0

# in one terminal - read from serial port
while (true) do cat -A /dev/ttyUSB0 ; done

# in other terminal - write to serial port
echo "1234567890" > /dev/ttyUSB0

# back to first terminal, I now have:
# $ while (true) do cat -A /dev/ttyUSB0 ; done
# 1234567890$
# ...

现在,我想做类似的事情 - 我想将一个文件cat到串口,并从串口读回 - 但只用一个终端命令(这样我可以将其用作time的参数)。
我认为我可以使用Bash进程替换,让“写入”和“读取”部分以“并行”的方式进行 - 如果我尝试使用命名管道,它可以工作:
# mkfifo my.pipe # same as below:
$ mknod my.pipe p

$ comm <(echo -e "test\ntest\ntest\n" > my.pipe) <(cat my.pipe)
    test
    test
    test
comm: file 2 is not in sorted order

在这里,我使用comm命令的唯一目的是将两个进程合并为一个命令(我想我也可以使用echo)。

不幸的是,这个技巧似乎不能用于串口,因为当我尝试时,有时会出现以下情况:

$ comm <(echo "1234567890" > /dev/ttyUSB0) <(while (true) do cat -A /dev/ttyUSB0 ; done)
cat: /dev/ttyUSB0: Invalid argument

..., 但通常情况下我根本没有任何输出。这告诉我:要么没有控制哪个进程首先启动,因此cat可能会在端口准备好之前开始读取(但是,在上面的第一个示例中似乎并不是问题);要么在Linux / Bash中,您不能同时读写串行端口,因此在读和写似乎同时发生的那些时刻会发生“Invalid argument”。

所以我的问题是:

  • 有没有办法做到这样的事情(将文件cat到配置为环回的串行端口;读取它并查看需要多长时间),只需使用Bash,而无需编写专用的C程序?
  • 如果我需要专用的C程序,是否有可以使用的网络源代码示例?

非常感谢任何回复,

干杯!

 

编辑:我知道上面写的while循环不会退出。那个命令行是为了进行初步测试,并使用Ctrl-C中断它。(我原则上可以使用类似于timeout -9 0.1 bash -c'while(true)do echo AA; done'的东西来中断它,但那将破坏time的目的,然后 :)

之所以有while,是因为目前从设备上通过cat读取会立即退出;有时,我确实已经设置了设备,以便在发出cat时,它实际上会阻塞并等待传入的数据;但是我还不能弄清楚发生了什么(部分原因是我正在寻找一种从命令行测试的方法)。

如果我没有使用while,我想用类似于以下内容的东西进行定时:

time bash -c 'comm <(echo "1234567890" > /dev/ttyUSB0) <(cat -A /dev/ttyUSB0)'

... 但是,为了使其工作,需要假设cat -A /dev/ttyUSB0首先启动并阻塞;然后echo写入串行端口(并退出);然后cat -A输出从串行端口读取的任何内容-然后退出。(我不确定串行端口是否可以像这样运行,也不确定是否可以使cat随意阻塞和退出)。

确切的方法并不重要;如果可能的话,我想避免编写自己的C程序来进行此类测试,这就是为什么我主要关注是否可以使用基本的Bash / Linux(即coreutils)运行这样的“全双工测试”的原因;(如果不行,是否有现成的代码可用于此类操作)。
编辑2:也可能相关的是:

如果您让这个程序正常工作,那么while循环将永远不会终止。您打算如何解决问题?在哪里放置time命令?您是否考虑过在读取循环中使用带有pv的第一种方法? - Dennis Williamson
嗨Dennis,非常感谢您的回复!我已经编辑了上面的帖子,希望它能澄清您提出的问题!顺便说一下,我以前从未听说过pv,我猜你是在谈论pv(1):监视通过管道传输的数据的进度-Linux man页面 - 我会看看这个... - sdaau
2个回答

6
好的,这里有一个部分答案——虽然关于使用bash的问题仍然未解决。我尝试查看了一些C代码解决方案,但似乎也不是一件容易的事情! :)
首先,让我们看看对于这种情况可能不起作用的东西——下面是来自"在write和read之间:串行端口-C"的示例:
// from: between write and read:serial port. - C - http://www.daniweb.com/forums/thread286634.html
// gcc -o sertest -Wall -g sertest.c

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

int main(int argc, char *argv[])
{
    char line[1024];
    int chkin;
    char input[1024];
    char msg[1024];
    char serport[24];

    // argv[1] - serial port
    // argv[2] - file or echo 

    sprintf(serport, "%s", argv[1]);

    int file= open(serport, O_RDWR | O_NOCTTY | O_NDELAY);

    if (file == 0)
    {
        sprintf(msg, "open_port: Unable to open %s.\n", serport);
        perror(msg);
    }
    else
        fcntl(file, F_SETFL, FNDELAY); //fcntl(file, F_SETFL, 0);

    while (1)
    {

        printf("enter input data:\n");
        scanf("%s",&input[0]);

        chkin=write(file,input,sizeof input);

        if (chkin<0)
        {
            printf("cannot write to port\n");
        }

        //chkin=read(file,line,sizeof line);

        while ((chkin=read(file,line,sizeof line))>=0)
        {
            if (chkin<0)
            {
                printf("cannot read from port\n");
            }
            else
            {
                printf("bytes: %d, line=%s\n",chkin, line);
            }
        }

        /*CODE TO EXIT THE LOOP GOES HERE*/
        if (input[0] == 'q') break;
    }

    close(file);
    return 0;
}

上述代码的问题在于它没有显式地初始化串口以进行字符(“原始”)操作;因此,根据先前设置端口的方式,会话可能如下所示:
$ ./sertest /dev/ttyUSB0 
enter input data:
t1
enter input data:
t2
enter input data:
t3
enter input data:
^C

换句话说,输入数据不会被回显。但是,如果串口设置正确,我们可以得到一个会话,如下所示:
$ ./sertest /dev/ttyUSB0 
enter input data:
t1
enter input data:
t2
bytes: 127, line=t1
enter input data:
t3
bytes: 127, line=t2
enter input data:
t4
bytes: 127, line=t3
enter input data:
^C

...(但是,即使这个sertest代码对于大于3个字符的输入单词也会失败。)

最后,通过一些在线搜索,我找到了“(已解决)串行编程,写入-读取问题”,其中提供了一个writeread.cpp的示例。然而,对于这种逐字节的“双工”情况,甚至那都不够 - 即“串行编程HOWTO”指出:“规范输入处理......是终端的正常处理模式......这意味着读取只会返回完整的输入行。默认情况下,一行以NL(ASCII LF)结尾......”;因此,我们必须通过ICANON在我们的代码中显式地将串行端口设置为“非规范”(或“原始”)模式(换句话说,仅通过open设置O_NONBLOCK不够的)- 一个示例在“3.2 如何从终端读取单个字符?-Unix编程常见问题-3.终端I/O”中给出。一旦完成,调用writeread将“正确”设置serport示例(上述),同时。

所以我将一些writeread代码改回了C语言,添加了所需的初始化内容,以及时间测量、发送字符串或文件的可能性和额外的输出流(用于将读取的串行数据传输到单独的文件中)。代码如下:writeread.cserial.h,通过它,我可以像在以下Bash会话中那样做:

$ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>myout.txt
stdalt opened; Alternative file descriptor: 3
Opening port /dev/ttyUSB0;
Got speed 2000000 (4107/0x100b);
Got file/string 'writeread.c'; opened as file (4182).

+++DONE+++
Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. 
Start: 1284422340 s 443302 us; End: 1284422347 s 786999 us; Delta: 7 s 343697 us. 
2000000 baud for 8N1 is 200000 Bps (bytes/sec).
Measured: write 569.47 Bps, read 569.47 Bps, total 1138.94 Bps.

$ diff writeread.c myout.txt 

$ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>/dev/null 
stdalt opened; Alternative file descriptor: 3
Opening port /dev/ttyUSB0;
Got speed 2000000 (4107/0x100b);
Got file/string 'writeread.c'; opened as file (4182).

+++DONE+++
Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. 
Start: 1284422380 s -461710 us; End: 1284422388 s 342977 us; Delta: 8 s 804687 us. 
2000000 baud for 8N1 is 200000 Bps (bytes/sec).
Measured: write 474.97 Bps, read 474.97 Bps, total 949.95 Bps.

好的:

  • 第一个惊喜 - 如果我写入文件,速度比我传输到/dev/null更快!
  • 此外,大约获得1000 Bps的速度 - 而设备显然设置为200000 BPS!!

此时,我认为减速是因为在writeread.c中每写入一个字节后,我们都等待读取中断清除标志,然后才继续读取串行缓冲区。可能,如果读取和写入是独立的线程,则读取和写入都可以尝试在单个readwrite调用中使用更大的字节块,因此带宽将被更好地利用?!(或者,也许中断处理程序确实像“线程”一样并行运行 - 因此通过将所有与读取相关的函数移动到中断处理程序中可以实现类似的效果?!

啊好吧 - 在这一点上,我非常愿意接受有关现有代码(如writeread.c),但是多线程的建议/链接 :) 当然,对于任何其他可能的Linux工具或可能的Bash方法(尽管似乎Bash无法施加这种控制...)

干杯!

 

writeread.c

/*
    writeread.c - based on writeread.cpp
    [SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/

    build with: gcc -o writeread -Wall -g writeread.c
*/

#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <stdlib.h>
#include <sys/time.h>

#include "serial.h"


int serport_fd;

void usage(char **argv)
{
    fprintf(stdout, "Usage:\n"); 
    fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 
    fprintf(stdout, "Examples:\n"); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt\n", argv[0]); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 \"some text test\"\n", argv[0]); 
}


int main( int argc, char **argv ) 
{

    if( argc != 4 ) { 
        usage(argv);
        return 1; 
    }

    char *serport;
    char *serspeed;
    speed_t serspeed_t;
    char *serfstr;
    int serf_fd; // if < 0, then serfstr is a string
    int bytesToSend; 
    int sentBytes; 
    char byteToSend[2];
    int readChars;
    int recdBytes, totlBytes; 

    char sResp[11];

    struct timeval timeStart, timeEnd, timeDelta;
    float deltasec; 

    /* Re: connecting alternative output stream to terminal - 
    * http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html 
    * send read output to file descriptor 3 if open, 
    * else just send to stdout
    */
    FILE *stdalt;
    if(dup2(3, 3) == -1) {
        fprintf(stdout, "stdalt not opened; ");
        stdalt = fopen("/dev/tty", "w");
    } else {
        fprintf(stdout, "stdalt opened; ");
        stdalt = fdopen(3, "w");
    }
    fprintf(stdout, "Alternative file descriptor: %d\n", fileno(stdalt));

    // Get the PORT name
    serport = argv[1];
    fprintf(stdout, "Opening port %s;\n", serport);

    // Get the baudrate
    serspeed = argv[2];
    serspeed_t = string_to_baud(serspeed);
    fprintf(stdout, "Got speed %s (%d/0x%x);\n", serspeed, serspeed_t, serspeed_t);

    //Get file or command;
    serfstr = argv[3];
    serf_fd = open( serfstr, O_RDONLY );
    fprintf(stdout, "Got file/string '%s'; ", serfstr);
    if (serf_fd < 0) {
        bytesToSend = strlen(serfstr);
        fprintf(stdout, "interpreting as string (%d).\n", bytesToSend);
    } else {
        struct stat st;
        stat(serfstr, &st);
        bytesToSend = st.st_size;
        fprintf(stdout, "opened as file (%d).\n", bytesToSend);
    }


    // Open and Initialise port
    serport_fd = open( serport, O_RDWR | O_NOCTTY | O_NONBLOCK );
    if ( serport_fd < 0 ) { perror(serport); return 1; }
    initport( serport_fd, serspeed_t );

    sentBytes = 0; recdBytes = 0;
    byteToSend[0]='x'; byteToSend[1]='\0';
    gettimeofday( &timeStart, NULL );

    // write / read loop - interleaved (i.e. will always write 
    // one byte at a time, before 'emptying' the read buffer ) 
    while ( sentBytes < bytesToSend )
    {
        // read next byte from input...
        if (serf_fd < 0) { //interpreting as string
            byteToSend[0] = serfstr[sentBytes];
        } else { //opened as file 
            read( serf_fd, &byteToSend[0], 1 );
        }

        if ( !writeport( serport_fd, byteToSend ) ) { 
            fprintf(stdout, "write failed\n"); 
        }
        //~ fprintf(stdout, "written:%s\n", byteToSend );

        while ( wait_flag == TRUE );

        if ( (readChars = readport( serport_fd, sResp, 10)) >= 0 ) 
        {
            //~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp);
            recdBytes += readChars;
            fprintf(stdalt, "%s", sResp);
        }

        wait_flag = TRUE; // was ==
        //~ usleep(50000);
        sentBytes++;
    }

    gettimeofday( &timeEnd, NULL );

    // Close the open port
    close( serport_fd );
    if (!(serf_fd < 0)) close( serf_fd );

    fprintf(stdout, "\n+++DONE+++\n");

    totlBytes = sentBytes + recdBytes;
    timeval_subtract(&timeDelta, &timeEnd, &timeStart);
    deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6;

    fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes. \n", sentBytes, recdBytes, totlBytes);
    fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. \n", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec);
    fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).\n", serspeed, atoi(serspeed)/10);
    fprintf(stdout, "Measured: write %.02f Bps, read %.02f Bps, total %.02f Bps.\n", sentBytes/deltasec, recdBytes/deltasec, totlBytes/deltasec);

    return 0;
}

serial.h:

/* serial.h
    (C) 2004-5 Captain http://www.captain.at

    Helper functions for "ser"

    Used for testing the PIC-MMC test-board
    http://www.captain.at/electronic-index.php
*/

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h>

#define TRUE    1
#define FALSE   0

int wait_flag = TRUE;   // TRUE while no signal received

// Definition of Signal Handler
void DAQ_signal_handler_IO ( int status )
{
    //~ fprintf(stdout, "received SIGIO signal %d.\n", status);
    wait_flag = FALSE;
}


int writeport( int fd, char *comm ) 
{
    int len = strlen( comm );
    int n = write( fd, comm, len );

    if ( n < 0 ) 
    {
        fprintf(stdout, "write failed!\n");
        return 0;
    }

    return n;
}


int readport( int fd, char *resp, size_t nbyte ) 
{
    int iIn = read( fd, resp, nbyte );
    if ( iIn < 0 ) 
    {
        if ( errno == EAGAIN ) 
        {
            fprintf(stdout, "SERIAL EAGAIN ERROR\n");
            return 0;
        } 
        else 
        {
            fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno));
            return 0;
        }
    }

    if ( resp[iIn-1] == '\r' )
        resp[iIn-1] = '\0';
    else
        resp[iIn] = '\0';

    return iIn;
}


int getbaud( int fd ) 
{
    struct termios termAttr;
    int inputSpeed = -1;
    speed_t baudRate;
    tcgetattr( fd, &termAttr );
    // Get the input speed
    baudRate = cfgetispeed( &termAttr );
    switch ( baudRate )
    {
        case B0:      inputSpeed = 0; break;
        case B50:     inputSpeed = 50; break;
        case B110:    inputSpeed = 110; break;
        case B134:    inputSpeed = 134; break;
        case B150:    inputSpeed = 150; break;
        case B200:    inputSpeed = 200; break;
        case B300:    inputSpeed = 300; break;
        case B600:    inputSpeed = 600; break;
        case B1200:   inputSpeed = 1200; break;
        case B1800:   inputSpeed = 1800; break;
        case B2400:   inputSpeed = 2400; break;
        case B4800:   inputSpeed = 4800; break;
        case B9600:   inputSpeed = 9600; break;
        case B19200:  inputSpeed = 19200; break;
        case B38400:  inputSpeed = 38400; break;
        case B115200: inputSpeed = 115200; break;
        case B2000000: inputSpeed = 2000000; break; //added
    }
    return inputSpeed;
}


/* ser.c
    (C) 2004-5 Captain http://www.captain.at

    Sends 3 characters (ABC) via the serial port (/dev/ttyS0) and reads
    them back if they are returned from the PIC.

    Used for testing the PIC-MMC test-board
    http://www.captain.at/electronic-index.php

*/


int initport( int fd, speed_t baudRate ) 
{
    struct termios options;
    struct sigaction saio;  // Definition of Signal action

    // Install the signal handler before making the device asynchronous
    saio.sa_handler = DAQ_signal_handler_IO;
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    sigaction( SIGIO, &saio, NULL );

    // Allow the process to receive SIGIO
    fcntl( fd, F_SETOWN, getpid() );
    // Make the file descriptor asynchronous (the manual page says only 
    // O_APPEND and O_NONBLOCK, will work with F_SETFL...)
    fcntl( fd, F_SETFL, FASYNC );
    //~ fcntl( fd, F_SETFL, FNDELAY); //doesn't work; //fcntl(file, F_SETFL, 0);

    // Get the current options for the port...
    tcgetattr( fd, &options );
/*       
    // Set port settings for canonical input processing
    options.c_cflag = BAUDRATE | CRTSCTS | CLOCAL | CREAD;
    options.c_iflag = IGNPAR | ICRNL;
    //options.c_iflag = IGNPAR;
    options.c_oflag = 0;
    options.c_lflag = ICANON;
    //options.c_lflag = 0;
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 0;
*/   
    /* ADDED - else 'read' will not return, unless it sees LF '\n' !!!!
    * From: Unix Programming Frequently Asked Questions - 3. Terminal I/O - 
    * http://www.steve.org.uk/Reference/Unix/faq_4.html 
    */
    /* Disable canonical mode, and set buffer size to 1 byte */
    options.c_lflag &= (~ICANON);
    options.c_cc[VTIME] = 0;
    options.c_cc[VMIN] = 1; 

    // Set the baud rates to...
    cfsetispeed( &options, baudRate );
    cfsetospeed( &options, baudRate );

    // Enable the receiver and set local mode...
    options.c_cflag |= ( CLOCAL | CREAD );
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    // Flush the input & output...
    tcflush( fd, TCIOFLUSH );

    // Set the new options for the port...
    tcsetattr( fd, TCSANOW, &options );

    return 1;
}


/* 
    ripped from 
    http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/stty.c
*/

#define STREQ(a, b)     (strcmp((a), (b)) == 0)

struct speed_map
{
  const char *string;       /* ASCII representation. */
  speed_t speed;        /* Internal form. */
  unsigned long int value;  /* Numeric value. */
};

static struct speed_map const speeds[] =
{
  {"0", B0, 0},
  {"50", B50, 50},
  {"75", B75, 75},
  {"110", B110, 110},
  {"134", B134, 134},
  {"134.5", B134, 134},
  {"150", B150, 150},
  {"200", B200, 200},
  {"300", B300, 300},
  {"600", B600, 600},
  {"1200", B1200, 1200},
  {"1800", B1800, 1800},
  {"2400", B2400, 2400},
  {"4800", B4800, 4800},
  {"9600", B9600, 9600},
  {"19200", B19200, 19200},
  {"38400", B38400, 38400},
  {"exta", B19200, 19200},
  {"extb", B38400, 38400},
#ifdef B57600
  {"57600", B57600, 57600},
#endif
#ifdef B115200
  {"115200", B115200, 115200},
#endif
#ifdef B230400
  {"230400", B230400, 230400},
#endif
#ifdef B460800
  {"460800", B460800, 460800},
#endif
#ifdef B500000
  {"500000", B500000, 500000},
#endif
#ifdef B576000
  {"576000", B576000, 576000},
#endif
#ifdef B921600
  {"921600", B921600, 921600},
#endif
#ifdef B1000000
  {"1000000", B1000000, 1000000},
#endif
#ifdef B1152000
  {"1152000", B1152000, 1152000},
#endif
#ifdef B1500000
  {"1500000", B1500000, 1500000},
#endif
#ifdef B2000000
  {"2000000", B2000000, 2000000},
#endif
#ifdef B2500000
  {"2500000", B2500000, 2500000},
#endif
#ifdef B3000000
  {"3000000", B3000000, 3000000},
#endif
#ifdef B3500000
  {"3500000", B3500000, 3500000},
#endif
#ifdef B4000000
  {"4000000", B4000000, 4000000},
#endif
  {NULL, 0, 0}
};

static speed_t
string_to_baud (const char *arg)
{
  int i;

  for (i = 0; speeds[i].string != NULL; ++i)
    if (STREQ (arg, speeds[i].string))
      return speeds[i].speed;
  return (speed_t) -1;
}



/* http://www.gnu.org/software/libtool/manual/libc/Elapsed-Time.html
Subtract the `struct timeval' values X and Y,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0.  */
int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
{
    /* Perform the carry for the later subtraction by updating y. */
    if (x->tv_usec < y->tv_usec) {
     int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
     y->tv_usec -= 1000000 * nsec;
     y->tv_sec += nsec;
    }
    if (x->tv_usec - y->tv_usec > 1000000) {
     int nsec = (x->tv_usec - y->tv_usec) / 1000000;
     y->tv_usec += 1000000 * nsec;
     y->tv_sec -= nsec;
    }

    /* Compute the time remaining to wait.
      tv_usec is certainly positive. */
    result->tv_sec = x->tv_sec - y->tv_sec;
    result->tv_usec = x->tv_usec - y->tv_usec;

    /* Return 1 if result is negative. */
    return x->tv_sec < y->tv_sec;
}

4

好的,我使用 pthreadwriteread.c 转换为线程版本(下面是代码 - 我认为 serial.h 没有太大改动;在线程版本中也没有太多使用)。我还将速度降低到 115200,现在可以通过下面的示例命令行会话与设备确认这些测量结果:

$ ./writeread /dev/ttyUSB0 115200 writeread.c 3>myout.txt
stdalt opened; Alternative file descriptor: 3
Opening port /dev/ttyUSB0;
Got speed 115200 (4098/0x1002);
Got file/string 'writeread.c'; opened as file (6131).
write_thread_function spawned
   write: 6131
   read: 18
   read: 64
   read: 110
   read: 156
   read: 202
...
   read: 6066
   read: 6089
   read: 6123
   read: 6131

+++DONE+++
Wrote: 6131 bytes; Read: 6131 bytes; Total: 12262 bytes. 
Start: 1284462824 s 141104 us; End: 1284462824 s 682598 us; Delta: 0 s 541494 us. 
115200 baud for 8N1 is 11520 Bps (bytes/sec).
Measured: write 11322.38 Bps (98.28%), read 11322.38 Bps (98.28%), total 22644.76 Bps.

$ diff writeread.c myout.txt 
$ 

现在测量报告显示预期波特率的达到了99%,因此我想这意味着该程序的配置文件应该可以正常工作。请注意:

  • 对于此设备,write 在单个块中执行(如果需要,PC应该能够处理数据包的排序
  • read则以较小的块进行(可能表明设备不等待整个块到达 - 相反,它会尽快发送回较小的块,一旦收到足够的数据

好的,我想这就是我最初需要的内容;我也猜想可能无法通过进程替换安排catecho以这种“线程化”的方式执行 :)(现在,在2000000波特率下做同样的事情会有问题,但这表明设备编程存在问题)。

干杯!

writeread.c - 线程版本

/*
    writeread.c - based on writeread.cpp
    [SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/

    build with: gcc -o writeread -lpthread -Wall -g writeread.c
*/

#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <stdlib.h>
#include <sys/time.h>

#include <pthread.h>

#include "serial.h"


int serport_fd;

//POSIX Threads Programming - https://computing.llnl.gov/tutorials/pthreads/#PassingArguments
struct write_thread_data{
   int  fd;
   char* comm; //string to send
   int bytesToSend;
   int writtenBytes;
};

void usage(char **argv)
{
    fprintf(stdout, "Usage:\n"); 
    fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 
    fprintf(stdout, "Examples:\n"); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt\n", argv[0]); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 \"some text test\"\n", argv[0]); 
}

// POSIX threads explained - http://www.ibm.com/developerworks/library/l-posix1.html
// instead of writeport
void *write_thread_function(void *arg) {
    int lastBytesWritten;
    struct write_thread_data *my_data;
    my_data = (struct write_thread_data *) arg;

    fprintf(stdout, "write_thread_function spawned\n");

    my_data->writtenBytes = 0; 
    while(my_data->writtenBytes < my_data->bytesToSend)
    {
        lastBytesWritten = write( my_data->fd, my_data->comm + my_data->writtenBytes, my_data->bytesToSend - my_data->writtenBytes );   
        my_data->writtenBytes += lastBytesWritten;  
        if ( lastBytesWritten < 0 ) 
        {
            fprintf(stdout, "write failed!\n");
            return 0;
        }
        fprintf(stderr, "   write: %d - %d\n", lastBytesWritten, my_data->writtenBytes);
    }
    return NULL; //pthread_exit(NULL)
}

int main( int argc, char **argv ) 
{

    if( argc != 4 ) { 
        usage(argv);
        return 1; 
    }

    char *serport;
    char *serspeed;
    speed_t serspeed_t;
    char *serfstr;
    int serf_fd; // if < 0, then serfstr is a string
    int sentBytes; 
    int readChars;
    int recdBytes, totlBytes; 

    char* sResp;
    char* sRespTotal;

    struct timeval timeStart, timeEnd, timeDelta;
    float deltasec, expectBps, measReadBps, measWriteBps; 

    struct write_thread_data wrdata;
    pthread_t myWriteThread;

    /* Re: connecting alternative output stream to terminal - 
    * http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html 
    * send read output to file descriptor 3 if open, 
    * else just send to stdout
    */
    FILE *stdalt;
    if(dup2(3, 3) == -1) {
        fprintf(stdout, "stdalt not opened; ");
        stdalt = fopen("/dev/tty", "w");
    } else {
        fprintf(stdout, "stdalt opened; ");
        stdalt = fdopen(3, "w");
    }
    fprintf(stdout, "Alternative file descriptor: %d\n", fileno(stdalt));

    // Get the PORT name
    serport = argv[1];
    fprintf(stdout, "Opening port %s;\n", serport);

    // Get the baudrate
    serspeed = argv[2];
    serspeed_t = string_to_baud(serspeed);
    fprintf(stdout, "Got speed %s (%d/0x%x);\n", serspeed, serspeed_t, serspeed_t);

    //Get file or command;
    serfstr = argv[3];
    serf_fd = open( serfstr, O_RDONLY );
    fprintf(stdout, "Got file/string '%s'; ", serfstr);
    if (serf_fd < 0) {
        wrdata.bytesToSend = strlen(serfstr);
        wrdata.comm = serfstr; //pointer already defined 
        fprintf(stdout, "interpreting as string (%d).\n", wrdata.bytesToSend);
    } else {
        struct stat st;
        stat(serfstr, &st);
        wrdata.bytesToSend = st.st_size;
        wrdata.comm = (char *)calloc(wrdata.bytesToSend, sizeof(char));
        read(serf_fd, wrdata.comm, wrdata.bytesToSend);
        fprintf(stdout, "opened as file (%d).\n", wrdata.bytesToSend);
    }

    sResp = (char *)calloc(wrdata.bytesToSend, sizeof(char));
    sRespTotal = (char *)calloc(wrdata.bytesToSend, sizeof(char));

    // Open and Initialise port
    serport_fd = open( serport, O_RDWR | O_NOCTTY | O_NONBLOCK );
    if ( serport_fd < 0 ) { perror(serport); return 1; }
    initport( serport_fd, serspeed_t );

    wrdata.fd = serport_fd;

    sentBytes = 0; recdBytes = 0;

    gettimeofday( &timeStart, NULL );

    // start the thread for writing.. 
    if ( pthread_create( &myWriteThread, NULL, write_thread_function, (void *) &wrdata) ) {
        printf("error creating thread.");
        abort();
    }

    // run read loop 
    while ( recdBytes < wrdata.bytesToSend )
    {

        while ( wait_flag == TRUE );

        if ( (readChars = read( serport_fd, sResp, wrdata.bytesToSend)) >= 0 ) 
        {
            //~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp);
            // binary safe - add sResp chunk to sRespTotal
            memmove(sRespTotal+recdBytes, sResp+0, readChars*sizeof(char));
            /* // text safe, but not binary:
            sResp[readChars] = '\0'; 
            fprintf(stdalt, "%s", sResp);
            */
            recdBytes += readChars;
        } else {
            if ( errno == EAGAIN ) 
            {
                fprintf(stdout, "SERIAL EAGAIN ERROR\n");
                return 0;
            } 
            else 
            {
                fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno));
                return 0;
            }           
        }
        fprintf(stderr, "   read: %d\n", recdBytes);        

        wait_flag = TRUE; // was ==
        //~ usleep(50000);
    }

    if ( pthread_join ( myWriteThread, NULL ) ) {
        printf("error joining thread.");
        abort();
    }

    gettimeofday( &timeEnd, NULL );

    // binary safe - dump sRespTotal to stdalt
    fwrite(sRespTotal, sizeof(char), recdBytes, stdalt);

    // Close the open port
    close( serport_fd );
    if (!(serf_fd < 0)) { 
        close( serf_fd );
        free(wrdata.comm); 
    } 
    free(sResp);
    free(sRespTotal);

    fprintf(stdout, "\n+++DONE+++\n");

    sentBytes = wrdata.writtenBytes; 
    totlBytes = sentBytes + recdBytes;
    timeval_subtract(&timeDelta, &timeEnd, &timeStart);
    deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6;
    expectBps = atoi(serspeed)/10.0f; 
    measWriteBps = sentBytes/deltasec;
    measReadBps = recdBytes/deltasec;

    fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes. \n", sentBytes, recdBytes, totlBytes);
    fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. \n", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec);
    fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).\n", serspeed, (int)expectBps);
    fprintf(stdout, "Measured: write %.02f Bps (%.02f%%), read %.02f Bps (%.02f%%), total %.02f Bps.\n", measWriteBps, (measWriteBps/expectBps)*100, measReadBps, (measReadBps/expectBps)*100, totlBytes/deltasec);

    return 0;
}

从这个代码中并不明显如何设置内部环回。你是否使用过TCIOM_LOOP - 0andriy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接