串行端口环回/双工测试,在 Bash 或 C 中?(过程替换) [英] Serial port loopback/duplex test, in Bash or C? (process substitution)

查看:17
本文介绍了串行端口环回/双工测试,在 Bash 或 C 中?(过程替换)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a serial device set up as loopback (meaning it will simply echo back any character it receives), and I'd like to measure effective throughput speed. For this, I hoped I could use time, as in

time bash -c '...'

其中 '...' 将是我可以运行的一些命令.

where '...' would be some command I could run.

现在,第一个问题是我想以 2000000 bps 使用设备,所以我不能使用 ttylogscreen (它们似乎都上升了仅到 115200 bps).但是,使用 /dev/ttyUSB0 作为文件(使用文件重定向和 cat)似乎可以正常工作:

Now, the first problem is that I want to use the device at 2000000 bps, so I cannot use ttylog or screen (they both seem to go up to 115200 bps only). However, working with /dev/ttyUSB0 as a file (using file redirection and cat) seems to work fine:

# 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 的参数).

Now, I'd like to do something similar - I'd like to cat a file to a serial port, and have the serial port read back - but from a single terminal command (so I could use it as argument to time).

我认为我可以使用 Bash 进程替换,让写入"和读取"部分以某种方式并行"运行 - 如果我尝试使用命名管道,它可以工作:

I thought that I could use a Bash process substitution, to have the "writing" and "reading" part go, sort of, in "parallel" - if I try it with named pipes, it works:

# mkfifo my.pipe # same as below:
$ mknod my.pipe p

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

在那里,我没有将 comm 用于任何其他目的,而不是将两个进程(某种程度上)合并为一个命令(我想,我也可以使用 echo 代替).

Up there, I'm not using comm for any other purpose, than to (sort of) merge the two processes into a single command (I guess, I could have just as well used echo instead).

不幸的是,这个技巧似乎不适用于串行端口,因为当我尝试它时,我有时会得到:

Unfortunately, that trick does not seem to work with a serial port, because when I try it, I sometimes get:

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

...但是,通常我只是没有得到任何输出.这告诉我:要么无法控制首先启动哪个进程,因此 cat 可能会在端口准备好之前开始读取(但是,在第一个示例中这似乎不是问题多于);或者在 Linux/Bash 中,您不能同时读取和写入串行端口,因此Invalid argument"会发生在读取和写入似乎都发生在同时.

..., however, usually I just get no output whatsoever. This tells me that: either there is no control of which process starts first, and so cat may start reading before the port is ready (however, that doesn't seem to be a problem in the first example above); or in Linux/Bash, you cannot both read and write to a serial port at the same time, and so the "Invalid argument" would occur in those moments when both read and write seem to happen at the same time.

所以我的问题是:

  • 有没有办法只在 Bash 中做这样的事情(cat 一个文件到配置为环回的串行端口;读回来看看需要多长时间),而无需编写专用C程序?
  • 如果我需要一个专用的 C 程序,我可以使用网上的任何源示例吗?
  • Is there a way to do something like this (cat a file to a serial port configured as loopback; read it back and see how long it takes) only in Bash, without resorting to writing a dedicated C program?
  • If I need a dedicated C program, any source examples out there on the net I could use?

非常感谢您的任何回复,

Thanks a lot for any responses,

干杯!

 

我知道上面写的 while 循环不会退出;该命令行用于初步测试,我使用 Ctrl-C 中断它.(原则上我可以用 timeout -9 0.1 bash -c 'while (true) do echo AA ; done' 来打断它,但这会破坏 time 的目的,然后 :) )

I am aware that the while loop written above does not exit; that command line was for preliminary testing, and I interrupt it using Ctrl-C. ( I could in principle interrupt it with something like timeout -9 0.1 bash -c 'while (true) do echo AA ; done', but that would defeat the purpose of time, then :) )

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

The reason that while is there, is that for the time being, reading via cat from the device exits immediately; at times, I have set up the device, so that when cat is issued, it in fact blocks and waits for incoming data; but I cannot as of yet figure what's going on (and partially that is why I'm looking for a way to test from the command line).

如果我没有使用 while,我想对于时间安排,我会使用类似的东西:

In case I didn't use the while, I imagine for timing, I'd use something like:

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

...但是,为了使其正常工作,有点假设 cat -A/dev/ttyUSB0 首先启动并阻塞;然后 echo 写入串行端口(并退出);然后 cat -A 输出它从串行端口读取的任何内容 - 然后退出.(而且我不确定串行端口是否完全可以以这种方式运行,也不确定 cat 是否可以像那样任意阻止和退出).

... however for this to be working, sort of, assumes that cat -A /dev/ttyUSB0 starts first and blocks; then the echo writes to the serial port (and exits); and then cat -A outputs whatever it read from the serial port - and then exits. (And I'm not really sure neither if a serial port can behave this way at all, nor if cat can be made to block and exit arbitrarily like that).

确切的方法真的不重要;如果可能的话,我只想避免编写自己的 C 程序来进行这种测试——这就是为什么我的主要兴趣是是否可以使用基本的 Bash/运行这样的全双工测试"Linux(即coreutils);(如果没有,如果有现成的代码我可以用于类似的事情).

The exact method really doesn't matter; if at all possible, I'd just like to avoid coding my own C program to do this kind of testing - which is why my primary interest is if it is somehow possible to run such a "full-duplex test" using basic Bash/Linux (i.e. coreutils); (and if not, if there is a ready-made code I can use for something like this).

也可能相关:

推荐答案

好吧,我设法使用 pthreadwriteread.c 放入线程版本(代码在下面 - 我认为 serial.h 没有太大变化;无论如何,它在线程版本中并没有太多使用).我还将速度降低到 115200,现在我可以在下面的示例命令行会话中使用设备确认这些测量:

Well, I managed to put writeread.c in a threaded version using pthread (code is below - I don't think serial.h changed much; it's not used that much in the threaded version anyways). I have also lowered the speed to 115200, and now I can confirm these measurements with the device, in the sample command line session below:

$ ./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% 的预期波特率,所以我想这意味着该程序的分析方面应该可以工作.注意:

Well, measurements now report up to 99% of the expected baud rate, so I guess that means that the profiling aspect of this program should work. Notice:

  • 对于此设备,write 在单个块中执行(如果必要,PC 应该能够处理数据包的排序),
  • read 以较小的块进行时(可能表明设备不会等待整个块到达 - 相反,它会在收到较小的块后立即开始发送回收到足够的)
  • For this device, the write is executed in a single chunk (as the PC should be able to handle the sequencing to packets, if necessary),
  • while the read goes on in smaller chunks (probably indicating that the device doesn't wait for the entire chunk to arrive - instead it starts sending back smaller chunks as soon as it has received enough)

嗯,我想这就是我最初需要的;我也猜想可能无法通过进程替换来安排 catecho 在此执行,我们称其为线程",方式:) (现在,我在 2000000 波特率下做同样的事情确实有问题,但这表明设备的编程有问题).

Well, I guess this is what I needed originally; I also guess it is probably not possible to arrange cat and echo via process substitution to execute in this, let's call it "threaded", manner :) (Now, I do have a problem with doing the same at 2000000 baud, but that indicates a problem with the programming of the device).

干杯!

 

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:
"); 
    fprintf(stdout, "%s port baudrate file/string
", argv[0]); 
    fprintf(stdout, "Examples:
"); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt
", argv[0]); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 "some text test"
", 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
");

    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!
");
            return 0;
        }
        fprintf(stderr, "   write: %d - %d
", 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
", fileno(stdalt));

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

    // Get the baudrate
    serspeed = argv[2];
    serspeed_t = string_to_baud(serspeed);
    fprintf(stdout, "Got speed %s (%d/0x%x);
", 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).
", 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).
", 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
", readChars, sResp);
            // binary safe - add sResp chunk to sRespTotal
            memmove(sRespTotal+recdBytes, sResp+0, readChars*sizeof(char));
            /* // text safe, but not binary:
            sResp[readChars] = ''; 
            fprintf(stdalt, "%s", sResp);
            */
            recdBytes += readChars;
        } else {
            if ( errno == EAGAIN ) 
            {
                fprintf(stdout, "SERIAL EAGAIN ERROR
");
                return 0;
            } 
            else 
            {
                fprintf(stdout, "SERIAL read error: %d = %s
", errno , strerror(errno));
                return 0;
            }           
        }
        fprintf(stderr, "   read: %d
", 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, "
+++DONE+++
");

    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. 
", sentBytes, recdBytes, totlBytes);
    fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. 
", 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).
", serspeed, (int)expectBps);
    fprintf(stdout, "Measured: write %.02f Bps (%.02f%%), read %.02f Bps (%.02f%%), total %.02f Bps.
", measWriteBps, (measWriteBps/expectBps)*100, measReadBps, (measReadBps/expectBps)*100, totlBytes/deltasec);

    return 0;
}

这篇关于串行端口环回/双工测试,在 Bash 或 C 中?(过程替换)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆