Re: 8ms Timer for serial port access

From: Byron A Jeff (byron_at_cc.gatech.edu)
Date: 10/30/03


Date: 30 Oct 2003 14:16:43 -0500

In article <BBC5EE32.1D21%jens.schumache@gmx.net>,
Jens Schumacher <jens.schumache@gmx.net> wrote:
-Am 29/10/03 17:05 Uhr schrieb "Floyd Davidson" unter <floyd@barrow.com> in
-87ism7vgx1.fld@barrow.com:
-
-> Let me add a few comments to what Grant has indicated here.

And I'll add a few more.
I realize that I have the solution too. And it won't require a bunch of work
to implement. I ran into these same types of timing issues when I was building
my digital synthesizer for my PhD.

-> The only time one would want to call a function like
-> serial_check_buffer() is if data is arriving asynchronously at
-> random intervals and the program thread has something to do
-> while waiting for data to arrive.

Correct.

-> Moreover, if that is the case
-> then the only sane way to write a serial_check_buffer() function
-> is to include a minimal sleep, which will cause 10ms of that
-> timing granularity to be given to other processes!

Correct again. sleep has a grainularity of 10ms. But my question is now
does read and/or select have the same grainularity? What about usleep or
nanosleep? If so then I can see why Jens is upset.

I'm interested now, so I guess I'll spend a couple of minutes setting up
some experiments. I'll be back. OK I'm back with the first test. I have this
loop:

-------
   for(i=0;i<10;i++) {
      delay.tv_sec = 0;
      delay.tv_usec = 100;
      select(0,NULL,NULL,NULL,&delay);
      TIMER_GET(ticks[i]);
   }
------------
TIMER_GET is a macro that simply marks the time with gettimeofday. I have
another routine that converts into microseconds from a start time. Here are
the results:

0: 2533
1: 12367
2: 22483
3: 32533
4: 42552
5: 52388
6: 62441
7: 72364
8: 82694
9: 92360

So there is no doubt that even though we told select to delay 100 uS, that
it took 10 ms for it to get back to us. Bummer.

Here's a quick look at the sleeps [usleep, and nanosleep].

usleep is bad:
0: 16078
1: 53925
2: 66021
3: 85939
4: 105932
5: 126035
6: 145946
7: 167601
8: 185940
9: 208655

I won't bother with nanosleep because the manual page indicates that it is
subject to the 10ms timer.

There's no way to test a read using two processes because once one goes to
sleep, then the other will run for 10 ms.

So here's the solution: Drum roll please!

You have to run the process in a real time scheduling mode. It requires
very few things. Here is my sample code:

------------------------------
#include <sched.h>

main()
{
   struct sched_param pri[1];

   pri->sched_priority = 10;
   sched_setscheduler(0,SCHED_FIFO,pri);

// Proceed as usual...

}
------------------------------

The will put the process into a real time FIFO scheduler queue. The upshot
is that whenever a real time process is running, it preempts any other
running process. This fixes the problem. Note that now the process must
be run as root, because only the superuser can put a process in a real time
scheduling mode.

Here's the updated usleep run:

0: 112
1: 214
2: 316
3: 417
4: 519
5: 620
6: 722
7: 824
8: 925
9: 1027
---------------------------------------
Just about as close to 100 uS as you can get. Here's the updated select:

0: 7195
1: 17180
2: 27177
3: 37196
4: 47195
5: 57192
6: 67190
7: 77193
8: 87182
9: 97182
----------------------------
Hmmm. No change. interesting.

But it presents a solution. But listen to the important part: If your real
time scheduled process doesn't sleep when it's waiting for something, nothing
else on the system runs! It is critical to understand that because you can
lock up your system tight as a drum running one of these.

So now let's finish the discussion with this new information...

->
-> Now, that _sounds_ exactly like what Jens wants to avoid. But
-> in fact, I don't think he does...
-
-Yes sounds like you're right with this. The Problem is that I started the to
-write the driver without knowing that there are limitations in the kernel.

Not exactly limitations in the kernel, but limitations that the kernel imposes
upon ordinary processes. Remember that Linux isn't a real time kernel, so
a bit of latency usually isn't an issue. But it is here. Note that there is
a simple solution for the problem you are trying to solve.

-Never meet this problems before because I never did something with hardware
-and which meant to be running in specific time intervals of ms.

Right. So a new paradigm had to be introduced.

-
->>>>> The time I measure is mostly 10ms. Sometimes I get 0ms.
->
-> Note that the reason for the 0ms on some occasions and 10ms on
-> most is the inability to sync a 10ms interrupt tick with an 8.333
-> ms data interval. What happens is every so many times there are
-> *two* sets of data sent within that 10ms interval. For example,
-> data is sent at 1 ms into it and at 9 ms into it, and then you
-> read at 10ms into it and get all of the data for two sets.
->
-> But the timer can't measure in microseconds, and the second data
-> _read_ happens literally microseconds (all within the same
-> time slice) after the first one. Hence the timer says there was
-> 0ms between them, because its smallest granularity is 10ms.

Not exactly correct. You sleep for 10ms if no data is there, and you
don't sleep at all on the read if data is there.

Note from the above run you don't want to use select. You want to keep running
track of the last time you did a read, sleep until 8333 uS after the last read
then read again. The difference with the real-time schedule is that as soon
as the 8333 uS elapses, the kernel will immediately preempt whatever is running
and restart your process.

->
-Yes and no, Yes it seems that I read two frames in one interval, but you can
-measure microseconds with gettimeofdate();

Right. That's how I tested this stuff.

-
-> But that just points out that you don't really need to read the
-> data every 8 ms. You could read the data every 16 ms or every
-> 32 ms and be just fine. You just get twice or four times as
-> much data with each read that way.
-
-No I can't the reason comes later...

I do understand. You are correct.

-> My understanding of what Jens said earlier (and he can correct
-> me if I'm wrong here, because I might well be) is that he is
-> graphing the data for display, and he believes that if he
-> doesn't grab the data as it becomes available there will be
-> buffer overflows and he will lose the data.
->
-...basically right I want to display something on the monitor.
-The reason why I have to read the data when it arrives is pretty easy.
-I wrote earlier that I have to detect saccades. I'm doing this with a
-5 point differentiator. Because I'm doing this online this means that I take
-the current point and use the 4 data frames I got before. You see here is
-already a delay of more than 2 frames for the detection.

Right. So as you pointed out from the beginning, it's a soft real time process.

-When I'm reading the data every 32 milliseconds, and the subject starts to
-make a saccadic eye-movement after 8 ms, the possibility to detect the
-saccade and to react gets pretty small. This is why it's important to get
-every frame of data as soon as possible.

Correct. So make the process run on the real time scheduler, Make sure that
it usleeps when it's waiting, and it'll wake up right on time.

-> However, if this data is being *used* for something that is time
-> critical, then yes there is a problem. For example, if you need
-> to read the data, and send feedback to the device *before* it
-> sends another packet of data... that is a serious real time
-> constraint that a standard Linux kernel is not going to provide.
-
-I don't need to send a feedback, but how I described, I need the data pretty
-was after the eye-movement occurred.

I hope this helps. I used the real time scheduling queue to get precise timing
for my PhD results. I'm sure that it'll help you in your application.

BAJ



Relevant Pages

  • Re: Re-entrancy???
    ... know about the need to change the timer interval. ... I really didn't think that he was doing a critical process in real time. ... The OP wasn't against the use of Sleep, but he undestood that using Sleep ... means to remove Doevents. ...
    (microsoft.public.vb.general.discussion)
  • Re: [PATCH 1/1] driver: Tpm hardware enablement
    ... >> and then start sleeping, but please do sleep for a real time, not just ... Keep in mind that msleepwill ignore all signals & waitqueue events ... send the line "unsubscribe linux-kernel" in ...
    (Linux-Kernel)
  • Re: I need to "Delay" or "Pause" my program for a few seconds.
    ... I just put a program on our freeware site for you to ... Execute with no options or with -? ... If you absolutely must process orders in real time like ... then just sleep about 30 seconds after you get the ...
    (comp.databases.pick)
  • Re: Re-entrancy???
    ... DoEvents statements. ... then adding Sleep is detrimental to the program. ... If you are comunicating _two or more_ devices in real time, ... In Windows you can't response in real time even if you want, ...
    (microsoft.public.vb.general.discussion)