English 中文(简体)
Serial programming: measuring time between characters
原标题:

I am sending/receiving data over a serial line in Linux and I would like to find the delay between characters.

Modbus uses a 3.5 character delay to detect message frame boundaries. If there is more than a 1.5 character delay, the message frame is declared incomplete.

I m writing a quick program in C which is basically

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
// setup newtio
....
tcsetattr(fd, TCSANOW, &newtio);
for(;;) {
    res = read(fs, buf, 1);
    if (res > 0) {
        // store time in milliseconds?
        //do stuff
    }
}

Is there some way of measuring the time here? Or do I need to look at retrieving data from the serial line in a different way?

I ve also tried hooking into SIGIO to get a signal whenever there is data but I seem to get data 8 bytes at a time.

(yes, I know there exist some modbus libraries but I want to use this in other applications)

最佳回答

MODbus is like a lot of old protocols and really hates modern hardware.

The reason you re getting 8 bytes at a time is : Your PC has a (at least) 16 byte serial FIFO on receive and transmit, in the hardware. Most are 64byte or bigger.

It is possible to tell the uart device to time out and issue a received interrupt after a number of char times.

The Trigger Level is adjustable, but the low-level driver sets it "smartly". try low-latency mode using setserial) You can fiddle with the code in the serial driver if you must. Google it (mature content warning) it is not pretty.

so the routine is as pseudocode

int actual=read (packet, timeout of 1.5 chars)

look at actual # of received bytes

if less than a packet, has issues, discard.

not great.

问题回答

The simple answer is... you cannot (not without writing you own serial driver)!

If you are writing a MODBUS master there is some hope: You can either detect the end of a slave response by waiting any amount of time (provided its longer than 3.5 chars) without receiving anything (select(2) can help you here), or by parsing the response on the fly, as you read it (the second method wastes much less time). You must also be careful to wait at least 3.5 characters-time before staring to transmit a new request, after receiving the response to the previous request. "At least" is operative here! Waiting more doesn t matter. Waiting less does.

If you a writing a MODBUS slave then you re out of luck. You simply cannot do it reliably from userspace Linux. You have to write you own serial driver.

BTW, this is not Linux s fault. This is due to the unbelievable stupidity of MODBUS s framing method.

You can t use timeouts. On higher baud rates 3.5 character timeout means a few milliseconds, or even hundreds of microseconds. Such timeouts can t be handled in the Linux user space.

On the client side, it isn t a big deal since Modbus doesn t send asynchronous messages. So it s up to you not to send 2 consecutive messages within 3.5 character timeout.

On the server side, the problem is that if your clients have an extremely short response timeouts and Linux is too busy you can t write a bullet-proof framing solution. There is a chance that read() function will return more than one packet. Here is (a little contrived) example.

  1. Client writes a packet to server. Timeout is let s say 20 ms.

  2. Let s say that Linux is at the moment very busy, so kernel doesn t wake up your thread within next 50 ms.

  3. After 20 ms client detects that it didn t receive any response so it sends another packet to server (maybe resent the previous one).

  4. If Linux wakes up your reading thread after 50 ms, read() function can get 2 packets or even 1 and half depending to how many bytes were received by the serial port driver.

In my implementation I use a simple method that tries to parse bytes on-the-fly - first detecting the function code and then I try to read all remaining bytes for a specific function. If I get one and half packet I parse just the first one and remaining bytes are left in the buffer. If more bytes come within a short timeout I add them and try to parse, otherwise I discard them. It s not a perfect solution (for instance some sub-codes for function 8 doesn t have a fixed size) but since MODBUS RTU doesn t have any STX ETX characters, it s the best one I were able to figure out.

I think you are going about this the wrong way. There is a built in mechanism for ensuring that characters come in all together.

Basically, you are going to want to use ioctl() and set the VMIN and VTIME parameters appropriately. In this case, it seems like you d want VMIN (minimum number of characters in a packet) to be 0 and VTIME (minimum amount of time allowed between characters to be 15 (they are tenths of seconds).

Some really basic example code:

struct termio t;
t.c_cc[ VMIN ] = 0;
t.c_cc[ VTIME ] = 15;
if (ioctl( fd, TCSETAW, &t ) == -1)
{
    printf( msg, "ioctl(set) failed on port %s. Exiting...", yourPort);
    exit( 1 );
}

Do this before your open() but before your read(). Here s a couple of links that I ve found wildly helpful:

Serial Programming Guide

Understanding VMIN and VMAX

I hope that at least helps/points you in the right direction even if it isn t a perfect answer for your question.





相关问题
Fastest method for running a binary search on a file in C?

For example, let s say I want to find a particular word or number in a file. The contents are in sorted order (obviously). Since I want to run a binary search on the file, it seems like a real waste ...

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

Tips for debugging a made-for-linux application on windows?

I m trying to find the source of a bug I have found in an open-source application. I have managed to get a build up and running on my Windows machine, but I m having trouble finding the spot in the ...

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

Encoding, decoding an integer to a char array

Please note that this is not homework and i did search before starting this new thread. I got Store an int in a char array? I was looking for an answer but didn t get any satisfactory answer in the ...

热门标签