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

How do you get accurate timing (~60 Hz) on a PC? (No loops...)

55 views
Skip to first unread message

David Calabrese

unread,
Nov 22, 1993, 7:09:05 AM11/22/93
to

I'm writing an interrupt routine, and I'd like to time-stamp
some data. The project requires a resolution of 10 or so
milliseconds, and the timing function I have access to through Turbo C++
says it only counts at 18.2 Hz.

Is there a better clock that's standard on PCs? I know the
Mac has a 60Hz clock, which would be great, and I thought the PCs
used a 100Hz clock for something, but it looks like this might not
be true.

(The function I'm referring to in C++ is "clock()".)

Also, since another part of the code is polling a
non-interrupt driven interface, looping and waiting is out of the
question. (So much for the brute force technique...)


-= djc =-

Dick Kressman

unread,
Nov 23, 1993, 5:48:13 AM11/23/93
to
--

You can re-program the timer IC to interrupt any speed you like >18Hz -
sounds like you need 100Hz, or 50Hz if +/-10mS is acceptable. Do that
and nothing else and everything speeds up with it - DOS time, floppy drive
motor timeouts, the lot, but there is a way around it provided you don't
mind replacing the timer ISR.

The trick is to note that the normal timer division ratio is 65536 (64K),
so the surrogate timer ISR goes something like:

1. On every timer tick, increment a counter; this is your 'clock'
for time-stamping.

2. Add the timer division ratio (set during installation) into a
16-bit accumulator in memory.

IF this generates a carry, call or jump to the original timer
ISR (which will link to any TSR's or other programs that may
have intercepted the tick, and eventually to the original ISR
in BIOS)

ELSE issue an EOI to the interrupt controller and exit (iret).

Do not reset or change the accumulator except as above.

N.B. you have to remember the division ratio, 'cos it can't be read back
from the chip.

The divisor to use is 1,193,180 / desired tick frequency (Hz).

The net result is that you have the precision you need, but the rest of the
system still runs at the old 18.2Hz (albeit with some jitter), so needs no
modification.

I've used this running at 20Hz where I needed (reasonably) precise 1 second
interrupts, which of course you can't get from a 18.2Hz timebase. I've also
gone a stage further and adjust the divisor on the fly to phase-lock the
timer to an external real-time reference - GPS in one case! Result - DOS
clock with short term precision ~+/- 20mS, long term a few millisec/millenium,
self-setting anywhere in the world!

From the sound of your application, you might also find it useful to take
a look at the Ctask22 DOS multi-tasking package (freeware, on Simtel20 +
mirrors). It has a lot to offer this type of project, particularly if it
at all complex, but takes some getting to know if you don't have experience
with multi-threading/multi-tasking.

Hope this helps
Dick.


-----------------------------------------------------
As an economy measure, the light at the end of the
tunnel will be switched off while the recession lasts.
-----------------------------------------------------

Brian

unread,
Nov 24, 1993, 2:17:01 PM11/24/93
to
u_...@vc.nbs.ac.uk (Dick Kressman) writes:
> In article <57...@sdcc12.ucsd.edu>, dcal...@sdcc15.ucsd.edu (David Calabrese
> >
> > [Normal PC clock] only counts at 18.2 Hz.

> > Is there a better clock that's standard on PCs? I know the

> but there is a way around it provided you don't


> mind replacing the timer ISR.
> The trick is to note that the normal timer division ratio is 65536 (64K),
> so the surrogate timer ISR goes something like:

Not required. See following included text.
----------------- cut here ----------------
The standard United States television color-burst crystal is
at 3.579545 MHz. Divide that by three (3) gives 1.1931816 MHz.
This is the fastest clock normally available on a PC platform.

A 16-bit counter (65536) clocked with this signal would overflow
18.206507 times per second. This is the PC platform clock interrupt
rate.

If this clock, in turn, were to clock another 16-bit counter,
a carry would occur every 3599.5922 seconds, only 0.1ppt (0.01%) away
from once per hour. Actually, application programs that periodically
disable interrupts make the PC clock fall back a little. These two
errors offset each other.

Demo assembly software follows:

; PMPTIME.ASM, initiated 3/93 by BjM (Opus-OVH)
; Low level assembly transmit and receive routines.
; Based on work by Andrew C. Payne, July 1989
; Reworked for Power-C compiler by Brian J. Mork, ka9snf
;
; This module implements:
; long int _cc(int slowticks)
; Accepts a number of 18.2mSec ticks and returns the number
; of BITTIME ticks seen during this time.
; int _time()
; returns the current timer state in AX.
; void _transit(int time)
; waits until time given, and causes and output transition.
; int _waittrans(int til) waits a given number of ticks for a
; RxD transition. Returns the time of the transition or the
; given time if nothing arrives by then.
; void _waituntil(int t)
; Waits until the timer passes t.
;
smallmm EQU 1
TIMER EQU 040H ; base I/O Port for timer
LATCH EQU TIMER+3 ; port to latch count

ifdef smallmm
rtnsz equ 2
else
rtnsz equ 4
endif
; 6 bytes of external data
DREF RXPort, RXBit, TXPort, TXBit

READTIME MACRO ; reads BIOS timer into AX
MOV AL,0 ; latch count
OUT LATCH,AL
IN AL,TIMER ; read LSB
XCHG AL,AH ; save
IN AL,TIMER ; read MSB
XCHG AL,AH ; make order correct
ENDM

idt PMPtime ; show this set of routines on link map
;------------------------------------------
; This subroutine accepts
; an integer (16bit) value indicating how many known slow (65536/3600sec)
; clock ticks to monitor the fast clock.
;
; It returns a long int (32bits) indicating how many fast ticks occured.
; If the fast clock overflows 32bits, a zero is immediately returned to
; let the caller know to request a shorter delay.
;
; The C caller can compare to the two counts to determine the appropriate
; BITTIME variable for the Poor Man's Packet program's configuration file.
;
; Variable usage:
; Slow count delay (16bits) arrives on the stack
; Return fast count (32bits) goes back in ....
; DX - MSW of fast count
; CX - fast tick "start" reference
; DI - slow tick "done" reference
; BP - recent-underflow flag
; BX - 1click targets
;
ldef cc ; make available to the world
cc:
push bp ; save for C-caller
MOV BP,SP ; copy into BP so we can use it
MOV DI,[BP+rtnsz+2] ; reach past ret addr for slow tick delay
OR DI,DI
JZ ZEROIZE ; if requested delay is zero, return zero
MOV AX,040H ; slow clock segment offset
MOV ES,AX ; put clock prefix into ES
XOR DX,DX ; clear MSW of fast clock
MOV BX,ES:[06CH] ; current slow clock tick into AX
ADD DI,BX ; slow clock "done" target now in DI
XOR AL,AL
OUT LATCH,AL
IN AL,TIMER
MOV CL,AL
IN AL,TIMER
MOV CH,AL ; fast clock snapshot into CX
XOR BP,BP ; clear "recent underflow" flag
WAIT1:
MOV AX,ES:[06CH]
CMP AX,BX ; CMP current,target
JBE WAIT1 ; wait for clock to click one up
MOV BX,AX ; update "1click" reference point
XOR AL,AL
OUT LATCH,AL
IN AL,TIMER
XCHG AH,AL
IN AL,TIMER
XCHG AH,AL ; current fast clock into AX
OR AX,AX ; update flags
JS UNDRFLOW
XOR BP,BP ; clear recent-carry flag
JMPS QDONE
UNDRFLOW:
NOT BP ; set recent-carry flag
INC DX ; carry into MSW
JNC QDONE ; still within 32bits
ZEROIZE:
XOR DX,DX ; zeroise 32bit fast count and return
XOR AX,AX
JMPS FINI
QDONE: ; "Query if done"
CMP BX,DI ; check clock against "done" target
JB WAIT1 ; wait for slow clock to equal target
NEG AX ; two's complement to get counts below 0000H
ADD AX,CX ; add in how long it took to the *first* 0000H
ADC DX,0 ; carry into DX, if necessary
FINI:
pop bp ; restore for caller routine
RET ; return 32bits in DXAX

;------------------------------------------
; Returns current value of Counter #0
;
ldef timer ; make available to the world
timer:
READTIME ; read the current time into AX
RET

;------------------------------------------
; Waits until the time given.
;
ldef waituntil
waituntil:
PUSH BP
MOV BP,SP ; get stack base address into BP
MOV BX,[bp+rtnsz+2] ; get beyond pushed BP and rtn
wuloop:
READTIME ; get current time
SUB AX,BX
JNS wuloop ; wait 'til AX has decreased to target
POP BP ; restore BP
RET

;--------------------------------------------------
; Causes an output TX data transition to occur at the time given
;
ldef transit
transit:
PUSH BP ; save BP
MOV BP,SP ; get bottom address
MOV BX,[bp+rtnsz+2] ; 1st int beyond BP
tloop:
READTIME ; get current time
SUB AX,BX
JNS tloop ; delay until passed time
MOV DX,[TXPort] ; point to the TXPort
IN AL,DX ; get current port value
XOR AL,[TXBit] ; invert TX data bit
OUT DX,AL ; write it out
READTIME ; return finish-time in AX
POP BP ; restore stack frame
RET

;----------------------------------------------------------------
; Waits until the time given for a transition. Returns the time of the
; transition or input time if no tranisition occurred.
;
ldef waittrans
waittrans:
PUSH BP
MOV BP,SP ; set up stack frame
MOV BX,[bp+rtnsz+2] ; get ending time into a register
MOV DX,[RXPort] ; get input port
IN AL,DX ; read current value
MOV CL,AL ; save
waitt:
IN AL,DX ; read current value
XOR AL,CL ; compare bits
AND AL,[RXBit] ; mask the RX data bit
JNZ waitt1 ; got transition, return time
READTIME ; get current time into AX
SUB AX,BX ; subtract off desired wait
JNS waitt ; wait until AX decreases below
MOV AX,BX ; return ending time parameter
JMP waitt2 ; all done
waitt1: ; got a transition, get time
READTIME ; current time into AX
waitt2:
POP BP ; restore BP
RET
; end of file


---
Brian Mork Internet bm...@opus-ovh.spk.wa.us
. . . . Amateur Radio (AX.25) ka9snf@wb7nnf.#spokn.wa.usa
... . .. USMail 6006-B Eaker, Fairchild, WA 99011

0 new messages