Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Real time clock -- 15 ms??

17 views
Skip to first unread message

Steve Kenshulo

unread,
May 25, 1993, 4:52:46 PM5/25/93
to
I need a way to check a real-time clock to make sure that two events
happen at least 15 milliseconds apart. All the teal time clocks I can
find in Turbo C books operate on seconds. (I am using Turbo C++ 3.0,
student edition)
Here is some psudocode:

Send pulse to hardware device
check real time clock
check limit switches
check user input
do other things
has it been 15 ms?
if not, loop until it has been 15ms
send out second pulse
loop back to start

I hope this is clear... and any help is greatly appreacated

Steve Kenshalo
sken...@mondrian.csufresno.edu

Dan Pop

unread,
May 26, 1993, 4:38:47 AM5/26/93
to

>I need a way to check a real-time clock to make sure that two events
>happen at least 15 milliseconds apart. All the teal time clocks I can
>find in Turbo C books operate on seconds. (I am using Turbo C++ 3.0,
>student edition)

You can't expect any Turbo C support for this, because the real time clock
on PC's has a 55 millisecond granularity. So, you have to get your hands
dirty and reprogram the 8254 channel responsible for the real time clock
and provide your own handler for the real time clock interrupt. Not a very
elegant solution, but the only one I can think of.

Dan

--
Dan Pop
Tel: +41.22.767.4534
Email: dan...@cernapo.cern.ch
Mail: CERN - PPE, Bat. 20 R-054, CH-1211 Geneve 23, Switzerland

ka...@icce.rug.nl

unread,
May 26, 1993, 4:51:11 AM5/26/93
to

>Steve Kenshalo
>sken...@mondrian.csufresno.edu

The clock problem that you are describing is a general real-time matter. I
guess you know about the timer tick interrupt, which updates a double word
(long) in the BIOS data area 18.2 times per second. So, to get a 55 msec.
resolution all you need to do is inspect the timer ticks.

The trick with the timer is that you can speed it up though. If you speed it
up by a factor 4, the tick counter will be updated approx. 72.8 times per
second. That gives you a resolution of approx. 13 mseconds.
I hope that this is close enough for you.

The timer speedup is performed as follows: (assembly code)
rate equ 4 ; your speedup rate
maxcount equ 65536 / rate ; max. timer count to pass
; before updating BIOS area

mov al, 00110110b ; timer counter control byte selection
out 43h, al ; select it
mov al, maxcount mod 100h
out 40h, al ; set new least-sig-byte
mov al, maxcount / 100h
out 40h, al ; set most sig. byte

BIOS stores the count of timer ticks at a double word in the BIOS data area.
The address of this long is 0x0040:0x006c. So, to read it out you could use
the following code:

long far *tickp;
long tickcount;

tickp = (long far *) 0x0040006c;
tickcount = *tickp;
while (1)
{
do_whatever_you_want ();
while (tickcount == *tickp)
/* wait */ ;
tickcount = *tickp;
}
This code would call your function do_whatever_you_want() once every 13 msec.

There is one drawback to this line of work. Since the timer tick interrupt is
speeded up, the clock will run 4 times faster as well. If you don't mind that,
that's ok, but there is also a way to make the clock run correctly. Simply
install an interrupt handler for int 8. This is the timer interrupt, which,
when activated updates the system clock. Your interrupt handler would
typically do nothing 3 times out of each 4 calls, and would call the original
int 8 code once out of each 4 activations. Simple, no?

Now, I have to say that I have used this technique successfully to get a 1
msec. resolution (yes.. 1 msec) by speeding up the timer by a great deal. So I
know that this approach works. But I have not checked the above code
extensively, so there may be a bug or two (or three, four) in it.

Anyway, I hope this helps you. Mail/post if you have questions..
Cheers,
Karel.
--
The ICCE usenet account
providing access to usenet news
for the ICCE Experience
_WERKEN_AAN_DE_GRENZEN_VAN_HET_KUNNEN_

keith danekind

unread,
May 26, 1993, 12:32:38 PM5/26/93
to
When I need a real time clock I use the 'timer' class built into Borland C++
3.0/3.1. The timer class is a self calabrating clock with high resolution.
If you have access to Borland C++ you could save a lot of time :-)


Keith Danekind

--
======================================================================
Keith Danekind | "Imagination is greater than |
Colorado State University | knowledge." -Einstein |
Internet: dane...@CS.ColoState.edu | |

Jerry Donovan

unread,
May 26, 1993, 12:46:57 PM5/26/93
to
Steve Kenshulo (sken...@mondrian.CSUFresno.EDU) wrote:
: I need a way to check a real-time clock to make sure that two events

: happen at least 15 milliseconds apart. All the teal time clocks I can
: find in Turbo C books operate on seconds. (I am using Turbo C++ 3.0,
: student edition)


Depending on you application and how it going to be used, you might just
try running your own timing loop. Somewhere earlier in the program,
determine how long it takes to loop through 1 bunch of operations then
decide how many loops are required to do your 15 msec. The calculation
should be nearly trivial. The main gotchas to avoid are to not let the
compiler optimize the loop to nothing, and turn off interupts (You might
want to do this in assembly). If the other solutions sound more robust,
use them first, but this method isn't too bad for being quick and dirty.

Stan Brown

unread,
May 26, 1993, 8:08:32 PM5/26/93
to
In article <C7Lon...@zimmer.CSUFresno.EDU> sken...@mondrian.CSUFresno.EDU (Steve Kenshulo) writes:
>I need a way to check a real-time clock to make sure that two events
>happen at least 15 milliseconds apart. All the teal time clocks I can
>find in Turbo C books operate on seconds. (I am using Turbo C++ 3.0,
>student edition)

This of course has nothing to do with comp.lang.c, but is
operating-system dependent. From the mention of "Turbo C" I infer that
the operating system in question is MS-DOS. I shall mail the questioner
a copy of the relevant question and answer from the FAQ list for
comp.os.msdos.programmer.

For anyone else, my standard offer: If you don't know how to retrieve
FAQ lists, send me email >>> with a valid reply-to address <<< and I'll
bounce back a canned message that explains how to retrieve FAQ lists by
email or ftp.
--
Stan Brown, Oak Road Systems br...@Ncoast.ORG
"Well, all in all, life's not that bad, once you get over the
disappointment that it stinks." --Rich Orloff, {The Whole Shebang}

Neil Sequeira

unread,
May 26, 1993, 2:16:34 PM5/26/93
to
>I need a way to check a real-time clock to make sure that two events
>happen at least 15 milliseconds apart. All the teal time clocks I can
>find in Turbo C books operate on seconds. (I am using Turbo C++ 3.0,
>student edition)

There's a clock() function in Borland C++ defined in time.h (i assume it's in Turbo C++ also). From the manual:

"return value: The clock function returns the processor time elapsed since the beginning of the program invocation. If the processor time is not available, or its value cannot be represented, the function returns a value of -1."

The manual also says to divide the return value by the value of the macro CLK_TCK to get the time in seconds.

The prototype is

clock_t clock(void);

It also says clock() is compatible with ANSI C

Hope that helps.


Neil.


Niclas Antti

unread,
May 27, 1993, 5:28:38 AM5/27/93
to

Terve,
no need to reprogram the 8254 to get the accurate time (well almost) from
a PC. The circuit has a 16-bit counter which can be read by your code. The
running frequency is high enough to allow the use of the msb only. The function
below reads the counter msb (1 count == 0.107264 ms) and the number of system
ticks (54.919 ms) and returns the number of msecs since the program started.

I haven't actually tested just how accurate the given time is, but I suppose
it's pretty good considering that a msb-count is 1/10 of a msec.

I used BC++ 3.1 and it worked just fine. The test in main can be used to check
wheter this stuff works or not. It doesn't tell you anything about accuray though.

Sorry about the magic numbers.

-------------------------------------------------------------------------------

#include <stdio.h>
#include <conio.h>

#include <dos.h>
#include <time.h>


void main(void);
unsigned long getMillisecs(void);


void main(void)
{
unsigned long ms1, ms2;
int key;

do
{
printf("Ready to try? Hit a key.\n");
while (!kbhit());
ms1 = getMillisecs();
getch();

printf("Hit another to stop the timer.\n");
while (!kbhit()) ;
ms2 = getMillisecs();
getch();

printf("Milliseconds = %lu\n\n", ms2-ms1);

printf("Again?\n");
while (!kbhit()) ;
key = getch();

} while (key == 'a');
}


/****************************************************************************/
/****************************************************************************/
/* The timer circuit 8254 is operating in mode 3. The out pin toggles every
* time the counter reaches 0. An interrupt occures when out goes high.
* Out is available at bit 7 of the status byte.
*/

unsigned long getMillisecs(void)
{
clock_t ticks ;
unsigned int status ;
unsigned int hbyte ;
unsigned int newStatus ;

do
{

disable(); /* Disable interrupts */
outp(0x43,0xC2); /* Counter readback command, select timer 0 */
status = inp(0x40); /* bit 7 is the out pin of the counter. */
hbyte = inp(0x40); /* lsb, not used.*/
hbyte = 0xFF-inp(0x40); /* msb, one count = 0.107264 ms */
ticks = clock() ; /* System ticks */
outp(0x43,0xE2); /* Latch the status */
newStatus = inp(0x40); /* Read the new status */
enable(); /* Enable interrupts */

/* If the out pin has changed (bit 7) or there was a null count
(bit 6) while reading the counter, read again. (bits0-5 const).
*/

} while(status != newStatus);


if ((status & 0x80) != 0x80) /* second counter round? */
{
hbyte += 0x100;
}

/* One (system) tick is 54.919 milliseconds long */

return((unsigned long) (54.919*ticks + .107264*hbyte)) ;

} /* getMillisecs */

---------------------------------------------
Niclas Antti
EMail na&ray.fi

Dan Pop

unread,
May 28, 1993, 6:58:42 AM5/28/93
to
In <1993May27....@ray.fi> n...@ray.fi (Niclas Antti) writes:
> do
> {

> disable(); /* Disable interrupts */
> outp(0x43,0xC2); /* Counter readback command, select timer 0 */
> status = inp(0x40); /* bit 7 is the out pin of the counter. */
> hbyte = inp(0x40); /* lsb, not used.*/
> hbyte = 0xFF-inp(0x40); /* msb, one count = 0.107264 ms */
> ticks = clock() ; /* System ticks */
> outp(0x43,0xE2); /* Latch the status */
> newStatus = inp(0x40); /* Read the new status */
> enable(); /* Enable interrupts */

> /* If the out pin has changed (bit 7) or there was a null count
> (bit 6) while reading the counter, read again. (bits0-5 const).
> */

> } while(status != newStatus);

This code has a small portability problem across different C compilers on
IBM PC's. It assumes that CLK_TCK is 18.2 and the clock() function returns
the number of ticks from the start of the program. This is true for Borland
compilers, but not for Microsoft. I've been using MSC 5.1 and I remember that
CLK_TCK was 1000. So, it would be safer to use a far pointer to read the
BIOS tick counter directly. This has the additional advantage of avoiding a
library call in a block of code which executes with interrupts disabled.

0 new messages