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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3701955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:09:18  来源:igfitidea点击:

Serial port loopback/duplex test, in Bash or C? (process substitution)

clinuxbashserial-portloopback

提问by sdaau

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,如

time bash -c '...'

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

其中“ ...”将是我可以运行的一些命令。

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

现在,第一个问题是我想以 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$
# ...

Now, I'd like to do something similar - I'd like to cata 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).

现在,我想做一些类似的事情 - 我想要cat一个文件到一个串行端口,并让串行端口读回 - 但是来自一个终端命令(所以我可以将它用作 的参数time)。

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:

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

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

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

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

..., however, usually I just get no output whatsoever. This tells me that: either there is no control of which process starts first, and so catmay 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.

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

So my questions are:

所以我的问题是:

  • Is there a way to do something like this (cata 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?
  • 有没有办法cat只在 Bash 中执行这样的操作(配置为环回的串行端口的文件;读回它并查看需要多长时间),而无需编写专用的 C 程序?
  • 如果我需要一个专用的 C 程序,我可以使用网络上的任何源代码示例吗?

Thanks a lot for any responses,

非常感谢您的任何回复,

Cheers!

干杯!

 

 

EDIT: I am aware that the whileloop 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上面写的循环没有退出;该命令行用于初步测试,我使用 Ctrl-C 中断它。(原则上我可以用类似的东西打断它timeout -9 0.1 bash -c 'while (true) do echo AA ; done',但这会破坏time, 然后 :)

The reason that whileis there, is that for the time being, reading via catfrom the device exits immediately; at times, I haveset up the device, so that when catis 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是,暂时cat从设备读取通过立即退出;有时,我已经设置了设备,以便在cat发出时,它实际上会阻塞并等待传入​​的数据;但我还不能弄清楚发生了什么(部分原因是我正在寻找一种从命令行进行测试的方法)。

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

如果我没有使用while,我想是为了计时,我会使用类似的东西:

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

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

... however for this to be working, sort of, assumes that cat -A /dev/ttyUSB0starts first and blocks; then the echowrites to the serial port (and exits); and then cat -Aoutputs 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 catcan be made to block and exit arbitrarily like that).

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

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).

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

EDIT2: Also possibly relevant:

EDIT2:也可能相关:

采纳答案by sdaau

Well, I managed to put writeread.cin a threaded version using pthread(code is below - I don't think serial.hchanged 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.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 
$ 

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:

嗯,测量现在报告了高达 99% 的预期波特率,所以我想这意味着这个程序的分析方面应该可以工作。注意:

  • For this device, the writeis executed in a single chunk (as the PC should be able to handle the sequencing to packets, if necessary),
  • while the readgoes 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)
  • 对于此设备,write在单个块中执行(因为 PC 应该能够处理数据包的排序,如有必要),
  • read在较小的块中进行时(可能表明设备不会等待整个块到达 - 相反,它会在收到足够的信息后立即开始发回较小的块

Well, I guess this is what I needed originally; I also guess it is probably not possible to arrange catand echovia 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).

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

Cheers!

干杯!

 

 

writeread.c- threaded version

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] = '
// 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;
}
'; 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; }

回答by sdaau

Well, here is something like a partial answer - although the question about the use of bash is still open. I tried to look a little bit in some C code solutions - and that, it seems, isn't trivial either! :)

好吧,这有点像部分答案 - 尽管有关 bash 使用的问题仍然存在。我试图查看一些 C 代码解决方案——这似乎也不是微不足道的!:)

First, let's see what possibly doesn'twork for this case - below is an example from "between write and read:serial port. - C":

首先,让我们看看什么可能不适用于这种情况 - 以下是“写和读之间的示例:串行端口。-C”:

$ ./sertest /dev/ttyUSB0 
enter input data:
t1
enter input data:
t2
enter input data:
t3
enter input data:
^C

The problem with the above code is that it doesn't explicitly initialize the serial port for character ("raw") operation; so depending on how the port was set previously, a session may look like this:

上面代码的问题在于它没有为字符(“原始”)操作显式初始化串行端口;因此,根据之前端口的设置方式,会话可能如下所示:

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

... in other words, there is no echoing of the input data. However, if the serial port is set up properly, we can get a session like:

... 换句话说,输入数据没有回显。但是,如果串口设置正确,我们可以得到一个像这样的会话:

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

... (but even then, this sertestcode fails on input words greater than 3 characters.)

...(但即便如此,此sertest代码在输入大于 3 个字符的单词时也会失败。

Finally, through some online digging, I managed to find "(SOLVED) Serial Programming, Write-Read Issue", which offers a writeread.cppexample. However, for this byte-by-byte "duplex" case, not even that was enough - namely, "Serial Programming HOWTO" notes: "Canonical Input Processing ... is the normal processing mode for terminals ... which means that a read will only return a full line of input. A line is by default terminated by a NL (ASCII LF) ..." ; and thus we have to explicitlyset the serial port to "non-canonical" (or "raw") mode in our code via ICANON(in other words, just setting O_NONBLOCKvia openis notenough) - an example for that is given at "3.2 How can I read single characters from the terminal? - Unix Programming Frequently Asked Questions - 3. Terminal I/O". Once that is done, calling writereadwill "correctly" set the serial port for the serportexample (above), as well.

最后,通过一些网上的挖掘,我设法找到了“ (SOLVED) Serial Programming, Write-Read Issue”,它提供了一个writeread.cpp例子。然而,对于这种逐字节的“双工”情况,这还不够——即,“串行编程 HOWTO”指出:“规范输入处理......是终端的正常处理模式......这意味着一个read 只会返回一整行输入。默认情况下,一行以 NL (ASCII LF) ..." ; 因此我们必须通过(换句话说,在我们的代码中将串行端口显式设置为“非规范”(或“原始”)模式ICANONO_NONBLOCKopen3.2 如何从终端读取单个字符?- Unix 编程常见问题 - 3. 终端 I/O“。一旦完成,调用writeread将“正确”设置serport示例(上图)的串行端口。

So I changed some of that writereadcode back to C, added the needed initialization stuff, as well as time measurement, possibility to send strings or files, and additional output stream (for 'piping' the read serial data to a separate file). The code is below as writeread.cand serial.h, and with it, I can do something like in the following Bash session:

因此,我将其中一些writeread代码改回 C,添加了所需的初始化内容,以及时间测量、发送字符串或文件的可能性以及额外的输出流(用于将读取的串行数据“管道”到单独的文件中)。代码在下面作为writeread.cand serial.h,有了它,我可以在下面的 Bash 会话中做类似的事情:

/*
    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]='
/* 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] = '##代码##';
    else
        resp[iIn] = '##代码##';

    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;
}
'; 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; }

Well:

好:

  • First surprise - it goes faster if I'm writing to a file, than if I'm piping to /dev/null!
  • Also, getting around 1000 Bps - whereas the device is apparently set for 200000 BPS!!
  • 第一个惊喜 - 如果我正在写入文件,它会比我通过管道传输更快/dev/null
  • 此外,获得大约 1000 BPS - 而该设备显然设置为 200000 BPS!

At this point, I'm thinking that the slowdown is because after each written byte in writeread.c, we wait for a flag to be cleared by the read interrupt, before we proceed to read the serial buffer. Possibly, if the reading and writing were separate threads, then both reading and writing could try to use bigger blocks of bytes in single reador writecalls, and so bandwidth would be used better ?! (Or, maybe the interrupt handler does act, in some sense, like a "thread" running in parallel - so maybe something similar could be achieved by moving all read related functions to the interrupt handler ?!)

在这一点上,我认为速度变慢是因为在 中的每个写入字节之后writeread.c,我们在继续读取串行缓冲区之前等待读取中断清除标志。可能,如果读取和写入是单独的线程,那么读取和写入都可以尝试在单个readwrite调用中使用更大的字节块,这样带宽会得到更好的利用?!(或者,也许中断处理程序在某种意义上确实像一个并行运行的“线程”一样 - 所以也许可以通过将所有与读取相关的函数移动到中断处理程序来实现类似的事情?!

Ah well - at this point, I am very open to suggestions / links for existing code like writeread.c, but multithreaded :) And, of course, for any other possible Linux tools, or possibly Bash methods (although it seems Bash will not be able to exert this kind of control...)

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

Cheers!

干杯!

 

 

writeread.c:

写读.c

##代码##

serial.h:

序列号

##代码##