代码之家  ›  专栏  ›  技术社区  ›  sdaau

串行端口环回/双工测试,在bash或c中?(过程替换)

  •  8
  • sdaau  · 技术社区  · 14 年前

    我将一个串行设备设置为环回(意味着它将简单地回显它接收到的任何字符),我想测量有效的吞吐量速度。为此,我希望我可以 time ,如

    time bash -c '...'
    

    哪里? ... 我可以执行一些命令。

    现在,第一个问题是我想以2000000 bps的速度使用这个设备,所以我不能使用 ttylog screen (两者似乎都只上升到115200个基点)。但是,与 /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$
    # ...
    

    现在,我想做点类似的事情-我想 一个文件到一个串行端口,并将串行端口读回-但是从一个终端命令(因此我可以使用它作为参数 时间 )

    我认为我可以使用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
    

    但是,通常我什么都得不到输出。这告诉我:要么是无法控制哪个进程首先启动,所以 可能在端口准备就绪之前就开始读取(不过,在上面的第一个示例中这似乎不是问题);或者在Linux/bash中,不能同时读取和写入串行端口,因此“ Invalid argument “当读和写似乎同时发生时,就会出现这种情况。

    所以我的问题是:

    • 有什么办法可以做这种事吗( 一个文件到一个被配置为环回的串行端口;读回它,看看它需要多长时间)只在bash中,而不需要编写一个专用的C程序?
    • 如果我需要一个专用的C程序,我可以在网上使用任何源代码示例吗?

    非常感谢您的回复,

    干杯!

    编辑:我知道 while 上面写的循环不退出;该命令行用于初步测试,我使用ctrl-c中断它。( 原则上我可以用 timeout -9 0.1 bash -c 'while (true) do echo AA ; done' 但这将破坏 时间 ,然后:) )

    原因是 虽然 有,是暂时的吗,通过阅读 从设备中立即退出;有时 设置设备,以便 实际上,它会阻止并等待传入的数据;但我目前还无法确定发生了什么(部分原因是我正在寻找一种从命令行进行测试的方法)。

    以防我没用 虽然 我想,对于时间安排,我会使用如下的方法:

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

    …然而,对于这项工作,某种程度上,假设 cat -A /dev/ttyUSB0 先开始然后阻塞;然后 回声 写入串行端口(并退出);然后 cat -A 输出从串行端口读取的任何数据,然后退出。( 我也不确定串行端口是否可以这样工作,也不确定 可以这样任意阻挡和退出 )

    确切的方法实际上并不重要;如果可能的话,我只是想避免编写自己的C程序来进行这种测试——这就是为什么我的主要兴趣是是否可以使用bash/linux(即 coreutils ); 如果没有,如果有现成的代码,我可以用它来做类似的事情 )

    编辑2:也可能相关:

    2 回复  |  直到 14 年前
        1
  •  5
  •   sdaau    14 年前

    好吧,这里有一个部分答案——尽管关于bash使用的问题仍然是开放的。我试着在一些C代码解决方案中寻找一些东西——而且,这似乎也不是一件小事!:)

    首先,让我们看看有什么可能 适用于此情况-以下是来自“的示例 between write and read:serial port. - 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个字符的单词时代码失败。 )

    最后,通过一些网上挖掘,我终于找到了 (SOLVED) Serial Programming, Write-Read Issue “,它提供了 writeread.cpp 例子。然而,对于这个逐字节的“双工”情况,即使这样也不够——即, Serial Programming HOWTO “备注:” 规范输入处理…是终端的正常处理模式…这意味着一个read只返回一整行输入。行默认以nl(ascii-lf)结尾… “因此我们必须 明确地 在我们的代码中,通过将串行端口设置为“非规范”(或“原始”)模式。 ICANON ( 换句话说,只是设置 O_NONBLOCK 通过 open 足够地 )-“的示例如下” 3.2 How can I read single characters from the terminal? - Unix Programming Frequently Asked Questions - 3. Terminal I/O “。完成后,打电话 writeread 将“正确”设置的串行端口 serport 示例(如上所述)。

    所以我改变了一些 写入读取 代码返回到C,添加了所需的初始化内容,以及时间度量,发送字符串或文件的可能性,以及附加的输出流。( 对于“管道”,将读取的串行数据传送到单独的文件 )代码如下: writeread.c serial.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个基点-而设备显然设置为200000个基点!!

    在这一点上,我认为减速是因为在 写入读取.c 在继续读取串行缓冲区之前,我们等待一个标志被读取中断清除。可能,如果读写是单独的线程,那么读写都可以尝试在单个线程中使用更大的字节块。 read write 呼叫,那么带宽会被更好地使用吗?! 或者,在某种意义上,中断处理程序确实起到了类似于并行运行的“线程”的作用——那么,通过将所有与读取相关的函数移动到中断处理程序中,也许可以实现类似的功能?! )

    啊,好吧-现在,我对现有代码的建议/链接非常开放,比如 写入读取.c ,但是多线程的:)当然,对于任何其他可能的Linux工具,或者可能的bash方法(尽管bash似乎不能应用这种控制…)

    干杯!

    写入读取.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;
    }
    

    序列号.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;
    }
    
        2
  •  4
  •   sdaau    14 年前

    嗯,我设法把 writeread.c 在线程版本中,使用 pthread ( 代码低于-我不认为 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 以小块的形式进行( 可能表明设备没有等待整个数据块到达,而是在收到足够的数据块后立即开始发送较小的数据块。 )

    好吧,我想这是我最初需要的;我也想这可能是不可能安排的 cat echo 通过在此过程中执行的进程替换,我们称之为“线程化”,方式:)( 现在,我确实有一个问题,在2000000波特的情况下做同样的事情,但这表明设备的编程有问题。 )

    干杯!

    写入读取.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;
    }
    
    推荐文章