串口环回/双工测试,用Bash还是C? (流程替代)

我有一个串行设备设置为环回(意味着它将回显它收到的任何字符),我想测量有效的吞吐速度.为此,我希望我可以利用时间,如同

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$
# ...

现在,我想做类似的事情 – 我想把一个文件写入一个串口,并让串口回读 – 但是从一个终端命令(所以我可以用它作为时间参数).

我认为我可以使用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中,您不能同时读取和写入串行端口,因此在读取和写入似乎同时发生的那些时刻会出现“无效参数”.

所以我的问题是:

>有没有办法做这样的事情(把文件连接到串口配置为环回;读回来看看需要多长时间)只在Bash中,而不是求助于编写专用的C程序?
>如果我需要一个专用的C程序,我可以使用网上的任何源代码示例吗?

非常感谢任何回复,

干杯!

编辑:我知道上面写的while循环不会退出;该命令行用于初步测试,我使用Ctrl-C中断它. (我原则上可以用超时-9 0.1 bash -c’中断它,而(true)做回声AA;完成’,但那会破坏时间的目的,然后:))

暂时存在的原因是,暂时从设备中读取猫只会立即退出;有时,我已经设置了设备,因此当发出cat时,它实际上会阻塞并等待传入​​的数据;但我还不知道发生了什么(部分原因是我正在寻找一种从命令行进行测试的方法).

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

时间bash -c’commp<(echo“1234567890”> / dev / ttyUSB0)<(cat -A / dev / ttyUSB0)' …然而,为了使这个工作,有点,假设cat -A / dev / ttyUSB0首先启动并阻塞;然后echo写入串口(并退出);然后cat -A输出从串口读取的任何内容 – 然后退出. (而且我也不确定串口是否可以按照这种方式运行,也不能确定cat是否可以像这样任意阻塞和退出). 确切的方法确实无关紧要;如果可能的话,我只是想避免编写我自己的C程序来进行这种测试 – 这就是为什么我的主要兴趣是如果以某种方式可以使用基本Bash运行这样的“全双工测试”/ Linux(即coreutils); (如果没有,如果有一个现成的代码我可以用于这样的事情). 编辑2:也可能相关:
> Parallel processes in Bash – Ubuntu Forums

好吧,这里有点像部分答案 – 尽管关于使用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;
}

上面代码的问题是它没有显式初始化字符(“raw”)操作的串口;所以根据先前设置端口的方式,会话可能如下所示:

$./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”注意:“规范输入处理……是终端的正常处理模式……这意味着读取将只返回一整行输入.一行默认以NL(ASCII LF)终止……“;因此我们必须通过ICANON在我们的代码中明确地将串口设置为“非规范”(或“原始”)模式(换句话说,仅通过打开设置O_NONBLOCK是不够的) – 一个例子给出了“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 Bps – 而该设备显然设置为200000 BPS!

此时,我认为减速是因为在writeread.c中的每个写入字节之后,我们等待读取中断清除一个标志,然后再继续读取串行缓冲区.可能,如果读取和写入是单独的线程,那么读取和写入都可能尝试在单个读取或写入调用中使用更大的字节块,因此可以更好地使用带宽?! (或者,在某种意义上,中断处理程序可能会像并行运行的“线程”一样起作用 – 所以可能通过将所有与读取相关的函数移动到中断处理程序来实现类似的东西?!)

好吧 – 在这一点上,我对现有代码的建议/链接非常开放,比如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;
}
相关文章
相关标签/搜索